user3512397
user3512397

Reputation:

Make ignores implicit rule dependency

In this partial Makefile when I execute make with no argument and .PHONY disabled it returns:

make: Nothing to be done for 'debug'.

With .PHONY enabled (or make -r) it go to 'build' without make any object file, so GCC don't can open any object file because there is no object files in target directories yet.

arm-none-eabi-gcc: error: obj/debug/ThirdParty/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_adc.o: No such file or directory

This makefile separate objs into folders obj/debug or obj/release.

File structure:

bin
inc
Src
ThirdParty  // thirdparty source files
obj // mkdir -p should create this directories tree
    debug
        Src
        ThirdParty
    release
        ...

Makefile

.PHONY: build debug release clean $(COBJ) $(SOBJ)

# Main target
debug:  CC_FLAGS += $(DEBUG)  
debug:  ELF = debug.elf 
debug:  OBJPATH = obj/debug
debug:  COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o))  # C contains c code
debug:  SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o))  # S contains asm code
debug:  build

release: CC_FLAGS += $(RELEASE)
release: OBJPATH = obj/release
release: COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o))
release: SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o))
release: ELF = release.elf 
release: build


build: $(COBJ) $(SOBJ)      
    $(CC) $(COBJ) $(SOBJ) $(LIBS) $(LD_FLAGS)  -o bin/$(ELF)


%.o: %.c
    echo $@
    @mkdir -p $(OBJPATH)/$(dir $@)  
    $(CC) $(CC_FLAGS) -c   $< -o $(OBJPATH)/$@

%.o: %.s
    @mkdi -p $(OBJPATH)/$(dir $@)
    $(CC) $(CC_FLAGS) -c   $< -o $(OBJPATH)/$@

One sample of $(COBJ):

obj/debug/ThirdParty/FreeRTOS/queue.o

Linux x86-64

GNU Make 4.2.1

Arm-none-eabi-gcc - I think this don't matter

Upvotes: 0

Views: 702

Answers (2)

user3512397
user3512397

Reputation:

As MadScientist mentioned the limited scope of target-specific variables I put out of makefile a selector and run make with TARGET = 'target' argument like this:

make TARGET = debug
make TARGET = release

No elegant but functional!

Makefile:

O  = $(C:%.c=%.o)
O += $(S:%.s=%.o)

ifeq ($(TARGET), release)
    ELF = bin/release.elf
    CC_FLAGS += -O3
    OBJPATH = obj/release
else
    ELF = bin/debug.elf
    CC_FLAGS += -g3
    OBJPATH = obj/debug
endif

OBJ = $(addprefix $(OBJPATH)/, $(O))


all: makepath build

build: $(OBJ) 
    @echo ---- LINKING ----
    $(CC) $(OBJ) $(LIBS) $(LD_FLAGS)  -o $(ELF) 

makepath: 
    @mkdir -p $(dir $(OBJ)) 


$(OBJPATH)/%.o:%.c
    @echo ---- C ----
    $(CC) $(CC_FLAGS) -c $<  -o $@

$(OBJPATH)/%.o:%.s
    @echo ---- S ----
    $(CC) $(CC_FLAGS) -c $<  -o $@

clean:
    find -name *.o -delete
    find -name *.elf -delete

Upvotes: 0

MadScientist
MadScientist

Reputation: 101081

You are missing a critical note from the GNU make manual regarding target-specific variables:

As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).

So, in your makefile:

debug:  COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o))  # C contains c code
debug:  SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o))  # S contains asm code

build: $(COBJ) $(SOBJ)
        ...

At this point $(COBJ) and $(SOBJ) refer to the globally-set values of the COBJ and SOBJ variables (because as above, target-specific values are available only within the recipe, NOT in the prerequisites list). These variables have no global values, so they expand to the empty string, and your makefile essentially has just:

build:
        ...

with no prerequisites, which is why you're seeing the behavior you are.

There are multiple ways you could manage this. One is to use recursive make: remove the release: build and debug: build lines and add this:

debug release:
        @$(MAKE) COBJ='$(COBJ)' SOBJ='$(SOBJ)' build

Another way is to use secondary expansion (you can't do it the way I originally suggested but you can do it with constructed variable names:

OBJPREFIX := obj
COBJ = $(patsubst ./%,$(OBJPREFIX)/$@/%,$(C:.c=.o))
SOBJ = $(patsubst ./%,$(OBJPREFIX)/$@/%,$(S:.s=.o))

# Main target
debug:  CC_FLAGS += $(DEBUG)
debug:  ELF = debug.elf

release: CC_FLAGS += $(RELEASE)
release: ELF = release.elf 

.SECONDEXPANSION:
release debug: $$(COBJ) $$(SOBJ)      
        $(CC) $(COBJ) $(SOBJ) $(LIBS) $(LD_FLAGS)  -o bin/$(ELF)

This uses the name of the target in the output object name.

Another way is to use generated makefiles.

You might consider reading the series of posts here: http://make.mad-scientist.net/category/metaprogramming/ (starting with the oldest one first).

Upvotes: 2

Related Questions