Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order-only prerequisites not working correctly in GNU make?

Tags:

I have a problem with order-only prerequisites. These do not execute first at all. Am I mis-understanding the way order-only prerequisites work?

The following make script:

.PHONY: mefirst mefirst2  mefirst:     @echo "I'm first!"  mefirst2:     @echo "I'm first too!"  normaltarget: normaltarget2 | mefirst2     @echo "normaltarget done"  normaltarget2: a b c      @echo "normaltarget2 done"  helloworld: normaltarget | mefirst     @echo "helloworld done"  .DEFAULT_GOAL := go go: helloworld     @echo "go done"  a:     @echo a b:     @echo b c:     @echo c 

...prints out the following:

a b c normaltarget2 done I'm first too! normaltarget done I'm first! helloworld done go done 

...instead of what I would expect:

I'm first! I'm first too! a b c normaltarget2 done normaltarget done helloworld done go done 

What am I doing wrong?

like image 907
George André Avatar asked Jul 18 '14 09:07

George André


People also ask

What is prerequisites in Makefile?

The prerequisites or dependents are those files that must exist before the target can be successfully created. And the commands are those shell commands that will create the target from the prerequisites.

What are order only prerequisites?

Order-only prerequisites is not about the order in which they are processed. They can be processed in any order, and can be processed even in parallel. Order-only means that updating prerequisite doesn't make updating the target.

What is $@ in Makefile?

$@ is the name of the target being generated, and $< the first prerequisite (usually a source file). You can find a list of all these special variables in the GNU Make manual.

How do I call a target in Makefile?

When you type make or make [target] , the Make will look through your current directory for a Makefile. This file must be called makefile or Makefile . Make will then look for the corresponding target in the makefile. If you don't provide a target, Make will just run the first target it finds.


1 Answers

Am I mis-understanding the way order-only prerequisites work?

Yes, that is what it looks like.

The name "order-only" is somewhat confusing. The prerequisites behind the | are called "order-only prerequisites" not because they change the order of recipe execution inside the list of prerequisites for a single target, but because their only purpose is to have certain targets created before others, like a bootstrap. As accurately explained by user bobbogo below ( -- thanks for correcting): if make decides to rebuild a prerequisite of a target, it will run the recipe for that prerequisite. Now, for an ordinary prerequisite this update implies that the target is now out-of-date, and make will have to run the target's recipe. For an order-only prerequisite on the other hand, make does not mark the target as needing an update.

For example see the section Types of Prerequisites for a use case where a directory should be created before the objects in that directory are created.

Take this example makefile:

a: b     touch a  b: c     touch b  c:     touch c  x: | y      touch x  y: | z      touch y  z:     touch z 

As you can see, b and c are normal prerequisites of a and b, whereas y and z are order-only prerequisites of x and y. Starting from a clean slate, they look the same:

:~$ make a touch c touch b touch a :~$ make x touch z touch y touch x :~$ make a make: `a' is up to date. :~$ make x make: `x' is up to date. 

However, if we now manually "update" the prerequisites at the end of the chain (c and z), we see the difference:

:~$ touch c :~$ make a touch b touch a :~$ touch z :~$ make x make: `x' is up to date. 

This shows how order-only prerequisites that exist do not invalidate any targets, independent of their time-stamp. Deleting the order-only target does result in rebuilding though (but only rebuilding of that missing file):

:~$ rm c :~$ make a touch c touch b touch a :~$ rm z :~$ make x touch z 

Having that said, the correct way to change the order in which your recipes are run is by correcting the dependencies between the targets. For example, if you want mefirst to be built before a, then you need to make mefirst a prerequisite for a, as in

a: mefirst     @echo a 

It is not possible to give the entire solution to your question since you did not describe in detail in which order you expect recipes to run.


There is a shortcut to your answer which is not the solution but still interesting to know. Although not documented, it seems that the prerequisites of a single target are processed in the order that they appear. The | sign does not change that. In your simple case, you can take advantage of that to achieve the output that you are looking for:

normaltarget: mefirst2 normaltarget2     @echo "normaltarget done" 

and

helloworld: mefirst normaltarget     @echo "helloworld done" 

However, as pointed out by yourself, this "solution" breaks as soon as the -j flag is used to run recipes in parallel. Also, as pointed out by user bobbogo, relying on this ordering mechanism is bad practice. Introducing new dependencies can interfere with the ordering. So don't do this :-)

like image 139
Reinier Torenbeek Avatar answered Oct 03 '22 15:10

Reinier Torenbeek