How do I make Makefile to recompile only changed files?
Asked Answered
M

3

83

I have been struggling a bit to get make to compile only the files that have been edited. However I didn't have much success and all the files get recompiled. Can someone explain me why?

My files are:

main.c
a_functions.c

where main.c includes main.h and a_functions.c includes a.h

Here is my makefile:

CC=gcc
CFLAGS=-Wall -I. -c
EXEC_FILE=program1


all: program

a_functions.o: a_functions.c
a_functions.c: a.h
main.o: main.c
main.c: main.h

objects: a_functions.c main.c
    $(CC) a_functions.c main.c $(CFLAGS)

program: a_functions.o main.o
    $(CC) a_functions.o main.o -o $(EXEC_FILE)

Changing the makefile as per suggestions seems to have the same problem::

all: program

a_functions.o: a_functions.c a.h
    gcc a_functions.c -c

main.o: main.c main.h
    gcc main.c -c

program: a_functions.o main.o
    gcc a_functions.o main.o -o program1
Martijn answered 19/10, 2011 at 0:49 Comment(10)
-I. strikes me as strange. What's up with that?Barbarese
@sarnold: just means search for include files in the current directory as well as implementation-defined ones. Whether it's needed is debatable but I don't think it would cause any harm.Polyethylene
@paxdiablo, perhaps, but I'm guessing it is a symptom of another problem. If his platform doesn't provide system-wide headers for something and he's got to provide his own versions, that's one thing, but I'm guessing it is a mistake as it is...Barbarese
@sarnold: How is that possibly a mistake? There will almost certainly be header files that need to be included in the current directory in pretty much any program that you'd ever want to compile.Manutius
@Barbarese I removed it and it still has the same problemMartijn
@sarnold, I was more thinking for things like a.h in the current directory. The default may well have (eg) /usr/include for the system headers but not . for local stuff. I'm pretty certain every gcc I've ever used also looked in the current directory by default (the actual behaviour is implementation-defined) but it may be a weird variant here.Polyethylene
It's not a good idea to change questions in a way that invalidates answers. Feel free to augment the information (see my edit) otherwise SO becomes a lot less useful since answers don't match the questions.Polyethylene
@Polyethylene you're right. Will keep in mind for future questions :)Martijn
@paxdiablo, in that case, why not use #include "a.h" to include the file rather than #include <a.h>?Barbarese
@Barbarese We can't #include <a.h> as the <> symbols are used to look for the header in the system directories. I do indeed use #include "a.h" as expected. I am not sure though why not having ANY include but using -I. instead doesn't work.Martijn
F
112

The specific problem you're talking about -- Make rebuilds program1 (by relinking the objects) even when nothing has changed -- is in this rule:

program: a_functions.o main.o
    gcc a_functions.o main.o -o program1

The target of this rule is program, and Make assumes that it is a file. But since there is no such file, every time you run Make, Make thinks that this file needs to be rebuilt, and executes the rule. I suggest this:

program1: a_functions.o main.o
    gcc a_functions.o main.o -o program1

Or better, this:

program1: a_functions.o main.o
    gcc $^ -o $@

Or better still this:

$(EXEC_FILE): a_functions.o main.o
    $(CC) $^ -o $@

(And don't forget to change the all rule to match.)

A few other points:

  1. As @paxdiablo pointed out,

    a_functions.o: a_functions.c a.h
    main.o: main.c main.h
    
  2. It doesn't make sense to link these objects together unless something in one (probably main.o) calls something in the other (probably a_functions.o), so I would expect to see a dependency like this:

    main.o: a.h
    

    So I suspect that you have some misplaced declarations.

  3. You declare an objects rule, but never refer to it. So you never actually use it; Make uses the default rule for %.o: %.c. I suggest this:

    OBJECTS = a_functions.o main.o
    $(OBJECTS): %.o: %.c
        $(CC) $< $(CFLAGS) -o $@
    

    (In which case you can change $(EXEC_FILE): a_functions.o main.o to $(EXEC_FILE): $(OBJECTS).) Or just this:

    %.o: %.c
        $(CC) $< $(CFLAGS) -o $@
    
Flabbergast answered 19/10, 2011 at 2:33 Comment(7)
Good catch, +1, I think we have a winner.Polyethylene
Would .PHONY have helped? (Probably a GNU make only feature, though.)Typography
@DavidPoole, conceptually yes, it would tell Make that program is not expected to be a file (and should be executed even if such a file happens to exist). Practically, no, since Make would still execute that rule every time, even if nothing had changed.Flabbergast
Concerning $(OBJECTS): %: %.c. I have never seen anything like that before (two colons in one row). Is it a typo? If not, what does it mean? EDIT: It should be $(OBJECTS): %.c. - Should it not?Prostitute
@Henke: No, it is not a typo, it is a static pattern rule. $(OBJECTS): %.c would not work, because it would instruct Make to build both object files from a source file names "%.c", and there's no such file.Flabbergast
Thanks. I see now how $(OBJECTS): %.c doesn't make any sense. So the rule %.o: %.c<newline><TAB>$(CC) $< $(CFLAGS) -o $@ means "for each .c file, compile the corresponding .o object file". And the rule $(OBJECTS): %: %.c<newline><TAB>$(CC) $< $(CFLAGS) -o $@ means "for each .o file in OBJECTS compile the corresponding .c file into a .o object file".(?) - I notice a discrepancy between your $(OBJECTS): %: %.c and the $(objects): %.o: %.c in the docs - they write %.o where you write only %. Docs typo? Or both valid?Prostitute
@Henke: Good catch, that was a typo on my part -- which nobody noticed for nine years! I'll edit.Flabbergast
P
14

Not sure if this is causing your specific problem but the two lines:

a_functions.c: a.h
main.c: main.h

are definitely wrong, because there's generally no command to re-create a C file based on a header it includes.

C files don't depend on their header files, the objects created by those C files do.

For example, a main.c of:

#include <hdr1.h>
#include <hdr2.h>
int main (void) { return 0; }

would be in the makefile as something like:

main.o: main.c hdr1.h hdr2.h
    gcc -c -o main.o main.c

Change:

a_functions.o: a_functions.c
a_functions.c: a.h
main.o: main.c
main.c: main.h

to:

a_functions.o: a_functions.c a.h
main.o: main.c main.h

(assuming that a_functions.c includes a.h and main.c includes main.h) and try again.

If that assumption above is wrong, you'll have to tell us what C files include what headers so we can tell you the correct rules.


If your contention is that the makefile is still building everything even after those changes, you need look at two things.

The first is the output from ls -l on all relevant files so that you can see what dates and times they have.

The second is the actual output from make. The output of make -d will be especially helpful since it shows what files and dates make is using to figure out what to do.


In terms of investigation, make seems to work fine as per the following transcript:

=====
pax$ cat qq.h
#define QQ 1
=====
pax$ cat qq.c
#include "qq.h"
int main(void) { return 0; }
=====
pax$ cat qq.mk
qq: qq.o
        gcc -o qq qq.o

qq.o: qq.c qq.h
        gcc -c -o qq.o qq.c
=====
pax$ touch qq.c qq.h
=====
pax$ make -f qq.mk
gcc -c -o qq.o qq.c
gcc -o qq qq.o
=====
pax$ make -f qq.mk
make: `qq' is up to date.
Polyethylene answered 19/10, 2011 at 0:53 Comment(4)
I did as you said but it still recompiles everything. I even tryied to copy your example by having main.o: main.c main.h \n $(CC) main.c $(CFLAGS) but still same problemMartijn
@Pithikos: Show us the output of the compiler. Why do you say it's compiling every time.Manutius
@Manutius I can see that it recompiles as the program1's date changes every time I 'make'.Martijn
The objects don't get recompiled. It's only the program1 that gets relinked.Martijn
V
2

This very late after the fact, and actually it's based on paxdiablo's excellent answer, but while experimenting i found that you don't need separate targets for each .o file unless you're doing something clever.

So for my makefile:

all: program

program: a_functions.o main.o
    gcc a_functions.o main.o -o program

clean: 
    rm -f *.o
    rm -f program

In make 4.1 it compiles the .o files automatically, you don't have to do anything if your .o files have the same names as your source files. So, it will go and hunt for a_functions.c, a_functions.h, main.h and main.c automagically. If you don't have the expected source code names, then you'll get an error like:

make: *** No rule to make target 'a_functions.o', needed by 'program'.  Stop.

I have also added a "clean" target for you. "Clean" is useful if you need to release your software as source code, or you just want to remove all the compiled object files and / or binaries.

And this is all you need to do.

Save this to "Makefile" and run:

$ make

to build your binary. And

$ make clean 

to clean up the compiled objects and binaries in your code.

Vaudevillian answered 3/11, 2019 at 2:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.