I'm looking at the following toy Makefile to track a bug:
all: hey bye
hey bye: hi
cat hi
touch hey bye
In this scenario, I've created the hi
file beforehand.
I then run make -j 2 all
and get the following output:
cat hi
cat hi
hey
touch hey bye
hey
touch hey bye
Whereas if I run it with make -j 1 all
, the commands get executed only once. The debug output is pretty verbose, but from what I understand, it's executing it twice because it tried to satisfy hey
and bye
with two different jobs.
How can I prevent this from happening? Is this correct behaviour? Why does it think running a command twice at once will make it finish faster?
The behavior is correct, because you're misunderstanding what you are telling make.
You can read the details in the GNU make manual.
This rule:
hey bye: hi
...
Does not tell make that one invocation of the recipe will build both targets. Instead, make interprets it exactly the same way as if you'd written:
hey: hi
...
bye: hi
...
That is, you've declared two completely different targets where each one would be created by a separate invocation of ...
.
When you run make without -j
it sees that hey
is out of date and runs the recipe. Then it looks at bye
, and now (since the previous recipe just updated it) it's up to date so it seems to work.
But if you run with -j2
, make sees hey
is out of date and starts that job, then looks for more work. Now when it checks bye
, the recipe for hey
didn't update it yet so make thinks it's still out of date and starts another instance of the recipe.
The best way to fix this depends on the details of your actual usage, which you haven't given.
If your inputs and outputs are related by the names of the targets and prerequisite, you can write a pattern rule. Unlike explicit rules, multiple target patterns tell make that one invocation of the recipe builds all the targets:
%.x %.y : %.z
...
If that won't work and you're willing to restrict your builds to using GNU make 4.3 or better you can take advantage of the "grouped targets" feature:
hey bye &: hi
...
If that isn't feasible, you'll have to use a sentinel target, something like:
all: hey bye
hey bye: .sentinel ;
.sentinel: hi
...
@touch $@