I am attempting to do the following. There is a program, call it foo-bin
, that takes in a single input file and generates two output files. A dumb Makefile rule for this would be:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
However, this does not tell make
in any way that both targets will be generated simultaneously. That is fine when running make
in serial, but will likely cause trouble if one tries make -j16
or something equally crazy.
The question is whether there exists a way to write a proper Makefile rule for such a case? Clearly, it would generate a DAG, but somehow the GNU make manual does not specify how this case could be handled.
Running the same code twice and generating only one result is out of the question, because the computation takes time (think: hours). Outputting only one file would also be rather difficult, because frequently it is used as an input to GNUPLOT which doesn't know how to handle only a fraction of a data file.
The trick is to use a pattern rule with multiple targets. In that case make will assume that both targets are created by a single invocation of the command.
This difference in interpretation between pattern rules and normal rules doesn't exactly make sense, but it's useful for cases like this, and it is documented in the manual.
This trick can be used for any number of output files as long as their names have some common substring for the
%
to match. (In this case the common substring is ".")I would solve it as follows :
In this case parallel make will 'serialize' creating a and b but since creating b does not do anything it takes no time.
This is based on @deemer's second answer which does not rely on pattern rules, and it fixes an issue I was experiencing with nested uses of the workaround.
I would have added this as a comment to @deemer's answer, but I can't because I just created this account and don't have any reputation.
Explanation: The empty recipe is needed in order to allow Make to do the proper bookkeeping to mark
file-a.out
andfile-b.out
as having been rebuilt. If you have yet another intermediate target which depends onfile-a.out
, then Make will choose to not build the outer intermediate, claiming:This is how I do it. First I always separate pre-requesits from the recipes. Then in this case a new target to do the recipe.
The first time:
1. We try to build file-a.out, but dummy-a-and-b.out needs doing first so make runs the dummy-a-and-b.out recipe.
2. We try to build file-b.out, dummy-a-and-b.out is up to date.
The second and subsequent time:
1. We try to build file-a.out: make looks at prerequisites, normal prerequisites are up to date, secondary prerequisites are missing so ignored.
2. We try to build file-b.out: make looks at prerequisites, normal prerequisites are up to date, secondary prerequisites are missing so ignored.
As an extension to @deemer's answer, I have generalised it into a function.
Breakdown:
Strip any leading/trailing whitespace from the first argument
Create a variable target to a unique value to use as the intermediate target. For this case, the value will be .inter.file-a.out_file-b.out.
Create an empty recipe for the outputs with the unique target as a depedency.
Declare the unique target as an intermediate.
Finish with a reference to the unique target so this function can be used directly in a recipe.
Also note the use of eval here is because eval expands to nothing so the full expansion of the function is just the unique target.
Must give credit to Atomic Rules in GNU Make which this function is inspired from.
Make doesn't have any intuitive way to do this, but there are two decent workarounds.
First, if the targets involved have a common stem, you can use a prefix rule (with GNU make). That is, if you wanted to fix the following rule:
You could write it this way:
(Using the pattern-rule variable $*, which stands for the matched part of the pattern)
If you want to be portable to non-GNU Make implementations or if your files can't be named to match a pattern rule, there is another way:
This tells make that input.in.intermediate won't exist before make is run, so its absence (or its timestamp) won't cause foo-bin to be run spuriously. And whether either file-a.out or file-b.out or both are out-of-date (relative to input.in), foo-bin will be only run once. You can use .SECONDARY instead of .INTERMEDIATE, which will instruct make NOT to delete a hypothetical file name input.in.intermediate. This method is also safe for parallel make builds.
The semicolon on the first line is important. It creates an empty recipe for that rule, so that Make knows we will really be updating file-a.out and file-b.out (thanks @siulkiulki and others who pointed this out)