GNU Makefile generate multiple targets from multiple prerequisites using single recipe execution - makefile

I have a tool foo that can take in one or more inputs and generate corresponding outputs. For example, foo a.in produces a.out and foo a.in b.in produces a.out and b.out.
a.out only depends on a.in, and b.out only depends on b.in.
While the tool can be run on single inputs, it is more efficient to run with multiple inputs, so while I can use the rule
%.out: %.in
foo $?
to generate each out file individually, I would like to batch them into a single call to foo.
With pattern rules, I can generate multiple targets using a single execution of a recipe:
a%out b%out: a%in b%in
foo $?
However, a.out now unnecessarily depends on b.in and b.out now unnecessarily depends on a.in.
stamp: a.in b.in a.out b.out
cat
How can I generate multiple targets from multiple inputs using a single recipe execution when there is only a one-to-one dependency between the prerequisites and targets?

A .INTERMEDIATE staging file and a timestamp file can do this
.INTERMEDIATE: list
stamp: list
foo `cat list`
touch stamp
list: a.out b.out a.in b.in
#:
%out: %in
echo $? >> list

Related

Make: run code before a dependency is checked

I have a target that depends on an external library:
output.txt: library/build.txt main.txt
cat library/build.txt main.txt >output.txt
(This is just an example, my actual makefile is much more complicated)
However, library/build.txt is generated by a separate makefile.
I need to run the library makefile (to potentially update build.txt) before make checks if library/build.txt is newer than output.txt
I could just have it run $(MAKE) -C library, every time, but I'm hoping to only call that when it is needed (meaning, when you try to make a target which depends on library/build.txt)
Is this possible? (maybe there's a way to get a list of dependencies and then run something if that list contains library/build.txt?)
You can try things like :
FORCE:
library/build.txt: FORCE
#echo make to build $#
output.txt: library/build.txt main.txt
cat $^ > $#

makefile rule to update a variable number of targets with a batch compile

We have a proprietary compiler that can take a number of input files and process them at once:
compiler a.in
# produces a.out
compiler a.in b.in c.in
# produces a.out b.out c.out
The reason to do that is that is saves a lot of time for initialization. For thousands of files the batch version is orders of magnitude faster than compiling files individually. We also run a post-processor on the files.
Now, I have this in the (GNU) makefile, which is not taking advantage of the batch processing capabilities and updates files one by one. I want to update it to use batch compilation:
.INTERMEDIATE: $(TMP)
$(TMP): $(TMPDIR)/%.tmp: $(SRCDIR)/%.in |$(TMPDIR)
compiler $< -o $#
$(RESULT): $(RESDIR)/%.out: $(TMPDIR)/%.tmp $(SRCDIR)/%.in
post-process $< -o $#
How would I rewrite the first rule to recompile all files that have been modified with a single command, perhaps, using $?? The second rule needs to stay there and work the same.
If you are able to require GNU make 4.3+, then your life is quite simple, you can take advantage of grouped targets, like this (note the &:):
a.out b.out c.out &: a.in b.in c.in
compiler $^
If you can't require a recent version of GNU make, you're relegated to using "sentinel files", like this:
a.out b.out c.out : .sentinal ;
.sentinal: a.in b.in c.in
compiler $^
#touch $#
(be sure to include the trailing semicolon on the first rule...)

How to determine if Make target is a PHONY?

I have a make target that depends on a variable, which contains both PHONY and real targets.
This target needs to depend only on the real targets in the variable.
How can I test a variable to determine if it is a PHONY or not, so I can filter them out?
(I can test for a file's existence inside the recipe, but I don't want my target to be triggered by execution of any of the PHONY targets.)
Thanks!
There is a way to do it, but I would strongly recommend against it. First of, phony targets can be also file targets. And there is no way to tell a phony file target from a non-phony file target.
It looks like the question implies that the phony targets the author wants to ignore are all non-file targets. In this case see the example below.
.PHONY: phony_target .FORCE
.FORCE:
ALL_TARGETS = phony_target file_target undetermined_target
-include detect_phony.inc
all: final_target
# All done
final_target: $(REAL_TARGETS)
# create $# triggered by $?
#touch $#
ifeq (,$(MAKE_RESTARTS))
# Generate the list of real file targets in make include file
detect_phony.inc: .FORCE
#echo 'REAL_TARGETS = ' `ls $(ALL_TARGETS) 2>/dev/null` > $# |:
endif
file_target:
touch $#
undetermined_target phony_target:
# process $#
clean:
rm -f file_target final_target
Here are the test results:
$make clean
rm -f file_target final_target
$ make
# create final_target triggered by
# All done
$ touch file_target
$ make
# create final_target triggered by file_target
# All done
$ make
# All done
As you can see it only triggers the final target when the file target is updated.
Before you criticize - Here are the flaws of this implementation:
make is always called twice, updating the generated detect_phony.inc include file at every run
if detect_phony.inc gets corrupted somehow, make execution will be locked by syntax errors, until you manually delete it.
it can't handle phony file targets as I mentioned before
if another generated include is added in this makefile that requires another restart before detect_phony.inc this functionality will break.
So it this method is hacky and has several gotchas. I would not use it in production environment. I would insist on changing the top level Makefile first.

makefile pattern with multiple variants

I tried to make a makefile with patterns.
I want to have two variants:
when i write make it should just compile the list of files.
when i write make run it should run the respective files.
this is my current makefile:
files = test test1 test2
all: $(files)
$(files): % : %.scala
scalac $<
run: $(files)
$(files): % : %.scala
scala $<
now, regardless of whether i do make or make run it always executes the scala command and never scalac
You have a duplicate target:
$(files): % : %.scala
remove one of the targets (probably the second line was added by you)

Simple inference rule in makefile

I'm currently learning how to use makefiles. But I'm struggling with % pattern rules. I've boiled down my failing makefile to this very simple example:
I fill an empty directory with:
echo aaa > a.in && echo bbb > b.in
A first makefile like this works very well:
a.out : a.in
cat $< > $#
as
make && echo *.out && cat *.out
returns
cat a.in > a.out
a.out
aaa
but when I try to use a pattern rule modifying the makefile as follows:
%.out : %.in
cat $< > $#
make then returns me:
make: *** No targets. Stop.
It seems like a very simple problem but I can't get to what I am missing...
If you have a makefile with no targets lists (only patterns), and you just type make, then you haven't told make that it should build anything specific, so it won't do anything.
A pattern rule doesn't mean "go find all the files that match this pattern and build them". A pattern rule tells make "if you need to find a way to build a file that matches this target pattern, then here's how you do it".
If you type make a.out so make knows that you want to build a target a.out, then make will use your pattern rule to build it.
Alternatively, you can add the target to your makefile, something like this:
.PHONY: all
all: a.out
%.out : %.in
cat $< > $#

Resources