Home > other >  Makefile recompiles every time
Makefile recompiles every time

Time:11-24

I'm a beginner with C and have been trying to tackle a few university tasks in regards to the makefile. It's a multi-file project and the idea is to compile nave.c porto.c meteo.c. and master.c.

All but the last need to be linked with common_ipcs and utils and are separately compiled. That said this all seems to work fine and all but my issue is that if I run any of the two target (debug or release) multiple times, they keep compiling everything from scratch even though nothing had been modified.

Any ideas? Here's my makefile with the not relevant targets removed. dbg is also removed as it is identical to rls.


# basic rules
CC      = gcc
LDFLAGS = -lm
COMMON  = common_ipcs.c utils.c
TARGET  = master
SOURCE  = master.c nave.c porto.c meteo.c
 
# directories
DIRSRC  = source
DIRHDR  = headers
DIRDBG  = debug
DIRRLS  = release
 
# compiling flags
CFLAGS  = -std=c89
DCFLAGS = $(CFLAGS) -g -O0 -pedantic -DDEBUG
RCFLAGS = $(CFLAGS) -O2 -Wall -Wextra -DNDEBUG
 
# $< is the first prerequisite
# $@ is the target
# $^ is all the prerequisites
 
.PHONY: all prep rls dbg drun run clean
 
# build all the prerequisites for compiling the project
all: clean prep rls
 
prep:
    @mkdir -p $(DIRDBG) $(DIRRLS)
 
rls: $(addprefix $(DIRSRC)/, $(SOURCE))
    $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/nave.c -o $(DIRRLS)/nave.o $(LDFLAGS)
    $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/porto.c -o $(DIRRLS)/porto.o $(LDFLAGS)
    $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/meteo.c -o $(DIRRLS)/meteo.o $(LDFLAGS)
    $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/master.c -o $(DIRRLS)/$(TARGET) $(LDFLAGS)


I've been messing with this makefile for days, it has gone through an inconceivable amount of rewrites and at this point I am not even sure what to try anymore. Relevant to this iteration, I've also tried to remove .PHONY, without any change in the recompilation as I had been explained that .PHONY also forces recompilation of all files. I've looked at different similar questions posted and none really seem to be applicable to this specific makefile issue.

CodePudding user response:

I think you're confused about what a makefile rule is. A rule consists of three parts: one or more targets, zero or more prerequisites, and a recipe of commands to run.

Make will compare timestamps of each target to all its listed prerequisites, and if any prerequisite is newer than the target, it will start a shell to run the recipe which is expected to bring the target up to date.

You have this rule:

rls: $(addprefix $(DIRSRC)/, $(SOURCE))
        $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/nave.c -o $(DIRRLS)/nave.o $(LDFLAGS)
        $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/porto.c -o $(DIRRLS)/porto.o $(LDFLAGS)
        $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/meteo.c -o $(DIRRLS)/meteo.o $(LDFLAGS)
        $(CC) $(RCFLAGS) $(addprefix $(DIRSRC)/, $(COMMON)) $(DIRSRC)/master.c -o $(DIRRLS)/$(TARGET) $(LDFLAGS)

We will ignore, for the moment, the incorrect invocation of the compiler (you are building programs but naming them as .o files which is not right, or at least not a good idea).

What does this rule say? It tells make that there is a target named rls and it has the prerequisites source/master.c, source/nave.c, source/porto.c, and source/meteo.c, and that if the target rls is out of date it should run a recipe consisting of four invocations of the compiler which will create four different programs: three named release/nave.o, release/porto.o, release/meteo.o, and finally one named release/master.

So, make dutifully checks the timestamp of a file on your disk named rls, which doesn't exist so it can never be considered up to date, and since it's out of date it runs the recipe which invokes all four of those compile lines. Every time.

When you write a makefile rule you should follow these rules: (a) each recipe should build EXACTLY ONE target, never more than one; and (b) the target name of the rule must be the exact name of the file that the recipe creates: if the recipe creates a file named blahblah.o, then the target of the rule must be blahblah.o. If the recipe creates a file named some/other/dir/foo then the target of the rule must be some/other/dir/foo.

When you get better at makefiles you'll realize that there are times when it's useful and a good idea to violate these rules. But these are a good starting point.

  • Related