Makefile compiles all the files everytime

隐身守侯 提交于 2021-02-11 17:01:08

问题


My Makefile compiles all the files everytime I run it though the files have not been changed. I know that this question has been asked several times but none of the provided solutions seem to work for me. I am new to Makefile and most of the times I do not understand the jargon used in the solution. Also, I want to save all the generated .o files under the folder 'obj'

Here is my folder structure

project (-)
        gen (-)
            display (-)
                .c and .h files
            logic (-)
                .c and .h files
        lib (-)
            include (-)
                .h files
            .lib files  
        man (-)
            .c and .h files
        obj (-)
            want to save all the .o files here

I am running this on Windows OS using MinGW

Here is my Makefile:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.o logic.o man.o
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c

logic.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c

man.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c

clean:
    @echo "Cleaning up.."
    -rm -rf *.o
    -rm *.exe

NOTE: glut and oglx files are present in the lib folder. Display.o, lib.o and man.o do not have corresponding .c files. They are just folder names with many c files in them.

I understand this could be the problem. As there are no display.o, logic.o and man.o files created, MAKE complies the rule associated with it eveytime. SO how do I tell it to check for the actual .o filename1.o, filename2.o etc for the timestamp and recompile ONLY if they are older than the corresponding c files and h files maybe even the lib files they depend on.

I tried the following to create dependencies and avoid compiling of files everytime. But this did not help.

%.d: %.c
     @set -e; rm -f $@; \
     $(CC) -M $(CFLAGS) $< > $@.$$$$; \
     sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
     rm -f $@.$$$$

回答1:


At a basic level, make is looking for lines like:

target: dependency
    command

If target does not exist, it calls the rule for dependency and then runs command. If target does exist, it tests if dependency is newer or does not exist. If so, it calls the rule for dependency and then runs command. Otherwise, it stops.

Significantly, the rule for dependency will only be called if (a) dependency doesn't exist, or (b) dependency is newer than target.

In the question, assume we run make demo. Then make looks for the line that begins demo: and notices it declares dependencies. So it looks at each dependency in turn to see if they require action. It first discovers display.o. It notices that display.o: does not exist, so it runs the associated rule. It does the same for the other *.o.

To avoid the *.o rules always being run because no associated file exists, you could rewrite like:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.ts logic.ts man.ts
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.ts: gen/display/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > display.ts

logic.ts: gen/logic/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > logic.ts

man.ts: man/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > man.ts

clean:
    @echo "Cleaning up.."
    -rm -rf *.o *.ts
    -rm *.exe



回答2:


Problem is that your binary object targets (like display.o) do not actually match files produced by their rules. If you tell make it needs to make target display.o, it (normally, except for phony targets, but those always rerun) expect the corresponding file to be produced by the rule's recipe and it can track if the target needs to be remade. If no such file is produces, this target always evaluates as outdated and needing remaking.

A bit of a silly example of this would be the following tree:

.
├── Makefile
├── main.c
└── test
    └── file.c

and Makefile:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -c test/*.c

There is no test.o file and target needs to be remade... the rule runs, produces file.o (again). Since this target was remade and is prerequisite of main... everything always gets remade.

Now with this small modification:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -o $@ -c test/*.c

test.o target indeed produces test.o file and the rule needs no remaking if test.c does not change... and with test.o unchanged and main.c perhaps as well, we get:

$ make
make: 'main' is up to date.

It still is not entirely correct as it really should read:

main: test.o main.o
    $(CC) -o main $+

test.o: test/*.c
    $(CC) $(CFLAGX) -o $@ -c $^

Where I declare depend prerequisites of test.o and reference both them and the target by automatic variable in the rule's recipe. And Same goes for prerequisites for linking. Of course in this simple example I could just rely on implicit pattern rules and do this:

main: test/file.o main.c

test/file.o: test/*.c

What does this mean for your makefile? When you compile your object files, have a look what do they actually produce and match your target to that or (with -o $@ for instance) tell them to produce exactly the file matching your target.


I've extended the silly example a bit and there are now two files in test/:

.
├── Makefile
├── main.c
└── test
    ├── file.c
    └── other.c

And the Makefile can look something like this:

main: obj/file.o obj/other.o main.c

obj/%.o: test/%.c
    mkdir -p obj
    $(CC) $(CFLAGS) -c -o $@ $^

It now stores object files in obj/ and make still understand what needs what and can track changes. Of course your setup is more complex and will require more rules, perhaps also divining actual sources or intermediate targets from the directory tree and define few variables to work with that information, e.g.:

OBJS := $(patsubst test/%.c,obj/%.o,$(wildcard test/*.c))

main: $(OBJS) main.c

obj/%.o: test/%.c
        mkdir -p obj
        $(CC) $(CFLAGS) -c -o $@ $^

But the principles remain the same.



来源:https://stackoverflow.com/questions/54506722/makefile-compiles-all-the-files-everytime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!