1

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 $@.$$$$
MUT
  • 13
  • 6
  • What's your question? What are you doing, what are you expecting to happen, and what is actually happening? – jhnc Feb 03 '19 at 20:05
  • Sorry I thought the title was clear. My problem is that my Makefile complies all the files everytime I run it. And I do not want that to happen. Also, as mentioned in the last line of my post, I want all the .o files to be saved under the folder obj. So how do I do these 2 things? – MUT Feb 03 '19 at 20:19

2 Answers2

2

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
jhnc
  • 11,310
  • 1
  • 9
  • 26
  • I understand I need to somehow create dependencies. But none of the solutions provided online seems to work for my Makefile. The first option provided by you would mean I should manually look at each c file and look for its header file right? Is there a simpler option? Because I have too many c files. And the link on the second option says it is not applicable for Windows OS. And speaks only about compiling rules and nothing about how to create dependencies. – MUT Feb 03 '19 at 20:34
  • No, it says the rules available may be different. What did `make -p` output for `%o: %.c` ? Nothing stops you adding (literally) `gen/display/*.c` as a dependency of `display.o:`, for example. – jhnc Feb 03 '19 at 20:59
  • Did not find that particular option. But found this:# Not a target: .C.o: # Implicit rule search has not been done. # Modification time never checked. # File has not been updated. # commands to execute (built-in): $(COMPILE.C) $(OUTPUT_OPTION) $ – MUT Feb 03 '19 at 21:05
  • I just noticed your `.o` rules aren't actual files. GNU make has the .PHONY` target - https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html - for this – jhnc Feb 03 '19 at 21:06
  • So I tried adding gen/display/*.c as a dependency of display.o: as the following: display.o: gen/display/*.c gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c this did not help either – MUT Feb 03 '19 at 21:07
  • Actually, make checks `target` exists. If not, it check if all `prerequisites` are available (if not it goes and makes those first) and when all `prerequisites` are met, it will run the `recipe`. – Ondrej K. Feb 03 '19 at 21:07
  • @jhnc: If I make it a PHONY, would it not run it everytime? And that is exactly what I do not want. – MUT Feb 03 '19 at 21:28
  • Thanks @OndrejK. My sloppy thinking. I have amended answer. – jhnc Feb 03 '19 at 21:33
  • @jhnc Yeah, I think that is probably a way to do give a "quick and dirty" solution to not having to actually describe the build. List sources as prerequisites and leave some marker lying around. It'll appear to work as desired, but... :( – Ondrej K. Feb 03 '19 at 21:38
  • @jhnc: Thanks a TON!!!! This worked like magic! Thank you so much again! Also, how do I make all the .o files generate under the folder 'obj' as mentioned in the folder structure? – MUT Feb 03 '19 at 21:51
  • I can think of Q&D ways to do this with your existing Makefile but you're be better to try to wrap your head around the ideas in OndrejK's answer to do it right. cf. https://stackoverflow.com/questions/13552575/gnu-make-pattern-to-build-output-in-different-directory-than-src and https://stackoverflow.com/questions/41568508/makefile-compile-multiple-c-file-at-once/41924169#41924169 – jhnc Feb 03 '19 at 22:30
1

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.

Ondrej K.
  • 8,841
  • 11
  • 24
  • 39
  • So how do I avoid the Makefile compiling everything everytime? I understand I need to create dependencies. Have tried many solutions but nothing seem to help. – MUT Feb 03 '19 at 20:41
  • I've added a bit more practical explanation and the last paragraph should hopeful help translating that to your situation. – Ondrej K. Feb 03 '19 at 20:50
  • But I have many c files under many different folders as mentioned in my original post. So trying the above solution would mean me writing the rule for every c file, which is tedious. Is there a different solution to automate the dependencies? – MUT Feb 03 '19 at 20:52
  • So I tried- display: gen/display/*.c gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -o $@ -c but got the following error: gcc.exe: fatal error: cannot specify -o with -c, -S or -E with multiple files compilation terminated. make: *** [display] Error 1 – MUT Feb 03 '19 at 21:25
  • Yes, you won't be able to summarily build your targets from all prerequisites in single compile goes anyways. When there are multiple targets listed before `:`, it does not mean they are all built at once, but that the same rule applies to all of them and will we invoked each time of of them is needed. – Ondrej K. Feb 03 '19 at 21:33