Vasileios Trigonakis
Vasileios Trigonakis

Reputation: 11

Make: compile object file twice in the same target

I have the following simple problem in a Makefile:

%.o:: %.c
      gcc -o $@ -c $<

lib1.a: test.o
        ar -r $@ test.o
        rm *.o

lib2.a: test.o
        ar -r $@ test.o
        rm *.o

all: lib1.a lib2.a

make lib1.a or make lib2.a work properly. However, make all gives:

gcc -o test.o -c test.c
ar -r lib1.a test.o
rm *.o
ar -r lib2.a test.o
ar: test.o: No such file or directory
make: *** [lib2.a] Error 1

I need to do the rm *.o cause I want the object file to compile each time (in my real Makefile, I have a more complex use case where I compile with different flags).

How can I fix this problem? It seems that make compiles the object file only once.

I tried with .PHONY instead of doing the rm, but again, it compiles only once.

Upvotes: 0

Views: 2654

Answers (2)

Dettorer
Dettorer

Reputation: 1336

Your makefile is a bit against the make logic, this is why the result is not what you expect:

  • Here you define two targets (lib1.a and lib2.a) with a common dependency: test.o.
  • Then you define the rule all (which, by the way, should be .PHONY but this isn't a problem here) that depends on lib1.a and lib2.a.

So, in order to "do" all, make have to build lib1.a and lib2.a. They both depend on test.o, so make builds test.o once, then build lib1.a and lib2.a, expecting that the recipes you defined will just build those files, and nothing more.

The problem is that you delete test.o in the recipe for lib1.a and lib2.a, although this action is not needed to build them, this is something you want to do when cleaning, not building.

There are two solutions:

  1. Move the deletion operation in a rule that is meant to do that (a .PHONY rule named clean for example).
  2. The use of intermediate targets which will be deleted when they're not needed anymore. In fact, you can achieve that without even thinking about intermediate targets if you simply delete the first rule of your makefile (the %.o:: %.c one), because make already has an implicit rule that does that using intermediate targets.

Upvotes: 2

reinierpost
reinierpost

Reputation: 8591

Make is a rule-based system. Rules are declarative: you declare what you want built from what, you don't specify the order in which this happens (unless you can't avoid it). So a good Makefile is declarative: all results are declared like in a declarative programming language. They are like final variables in Java: you bind them to a value, you don't reassign them to a different value afterwards.

Make is also file-based: its "variables", targets and prerequisites are files.

So if you want to build two different things, don't call them by the same name! If you want two different test.o files, call them differently. The problem will go away without you needing to try and convince make that it should be like an imperative programming language, which it was specifically designed not to be. If you want an imperative build specification, use a shell script.

Upvotes: 0

Related Questions