Arnaud F.
Arnaud F.

Reputation: 8452

Adding dependencies changes the target to the first C file

Consider the following makefile:

.SUFFIXES:

SRC:=../Src
OBJ:=../Obj

# Sources
SOURCES := $(SRC)/App/a.c $(SRC)/App/b.c $(SRC)/App/c.c
HEADERS := $(wildcard $(SRC)/App/*.h)

# Directories
INC_DIRS := $(SRC)/App
OBJ_INC_DIRS := $(INC_DIRS:$(SRC)/%=$(OBJ)/%)

# Objects
OBJECTS := $(SOURCES:$(SRC)%=$(OBJ)%.obj)

# Dependencies
DEPS := $(SOURCES:$(SRC)%.c=$(OBJ)%.d)
-include $(DEPS)

GCC_INCLUDES := $(foreach directory, $(INC_DIRS), -I$(directory))

all: target

target: $(OBJECTS)
    touch target

#Objects
$(OBJ)%.c.obj: $(SRC)%.c
    @echo Compiling $@
    @touch $@

# Dependencies 
$(OBJ)%.d: $(SRC)%.c
    @echo Checking dependencies for $<
    @gcc -MM $< $(GCC_INCLUDES) -MT '$(patsubst %.d,%.c.obj,$@)' -MT '$@' -MF '$@'
    @[ ! -s $@ ] && rm -f $@

# Creating directory tree before checking dependencies    
$(DEPS):|$(OBJ_INC_DIRS)

$(OBJ_INC_DIRS):
    @mkdir $@

clean:
    echo clean
    @rm $(OBJ_INC_DIRS)

When running the first time, I get:

Checking dependencies for ../Src/App/a.c
Checking dependencies for ../Src/App/b.c
Checking dependencies for ../Src/App/c.c
clean
Compiling ../Obj/App/a.c.obj
Compiling ../Obj/App/b.c.obj
Compiling ../Obj/App/c.c.obj
touch target

It's ok, but now, make again (without modifying any file):

make: `../Obj/App/a.c.obj' is up to date.

Now if I modify the file a.c

Checking dependencies for ../Src/App/a.c
Compiling ../Obj/App/a.c.obj

target isn't remade !

It's like my file a.c is the target but it isn't... Can someone explain me what's wrong here?

If I remove the include to the DEPS, I observe the expected behavior...

Thanks


EDIT

By putting the include at the end as mentioned by @Beta works but now I added the target clean and show the result...

Upvotes: 0

Views: 3339

Answers (1)

Beta
Beta

Reputation: 99124

I'll have to do some experiments to be sure, but I think the problem is:

-include $(DEPS)
...
all: target

You include $(DEPS) before the first target. So if you modify a.c, Make sees that it must rebuild a.d, then since it includes that file it must start over, and now a.c.obj is an earlier target than all.

Try moving -include $(DEPS) to the end of the makefile.

EDIT:
(Two small points: your clean rule is incorrect, since it tries to rm a directory, and I would do make clean; make all rather than make all, since I am not certain that Make promises to build targets in the given order in all cases.)

Yes, this makefile will rebuild the DEPS even when running clean. The makefile includes those files and has a rule for them, so if they are missing or out of date it must rebuild them and restart, no matter what the target is. The best way to deal with this is by Advanced Auto-Dependency Generation; basically, the commands that build dependency files go in the %.obj rule, so that a.d is a side effect of building a.c.obj. It's a sophisticated technique, not obvious, but it works beautifully. (Let us know if you try this and have trouble setting it up.)

Upvotes: 1

Related Questions