Getting the list of dependencies of a target - makefile

Is it possible to read the dependencies of a target inside a Makefile?
I would like to do something like the following:
.INTERMEDIATE: temp1.txt
.INTERMEDIATE: temp2.txt
print-intermediates:
#echo "Temp files: $(dependencies-of .INTERMEDIATE)"
The output of make print-intermediate would be
$ make print-intermediate
Temp files: temp1.txt temp2.txt
How can I access the list of dependencies of the .INTERMEDIATE target?

Recursive call of make.
There is no built-in function for this purpose. But there are some workarounds.
For example you can use recursive call of make with command line argument -p.
.INTERMEDIATE: temp1.txt
.INTERMEDIATE: temp2.txt
deps = $(shell $(MAKE) -qp none | sed -n "/$(strip $(1)):/ p;" | \
sed -e "s/$(strip $(1)): //g")
print-intermediates:
#echo "Temp files: " $(call deps,.INTERMEDIATE)
.PHONY: none
none:;

Related

addprefix in MAKE doesn't work correctly when -e is used as prefix

I encounter a strange behavior of addprefix with -e. The expected -e for the first word is not added. Here's the makefile to reproduce it.
a := file1 file2 file3
b := $(addprefix -s , $(a))
c := $(addprefix -e , $(a))
all:
#echo $(b)
#echo $(c)
Result:
-s file1 -s file2 -s file3
file1 -e file2 -e file3
Is it a bug or something is missing?
You should never use echo to print any string that is not just simple text that you know contains no starting dash characters. The echo command is not really standardized and there are a lot of versions, some of which accept options and some of which don't.
On your system, the echo command accepts a -e option so when you use echo -e file -e file2 -e file3 echo eats the first -e.
You can use:
all:
#printf '%s\n' '$(b)'
#printf '%s\n' '$(c)'
to be portable. Or you can use make's built-in functions:
all:
$(info $(b))
$(info $(c))

Makefile: how to use a command which randomly generates a file or leaves it unchanged

I have a command which sometimes updates a target file, sometimes it leaves it unchanged, but I cannot tell which one of those will happen. I would like to create a dependency that should run ONLY if the first file is updated. I could not do this. Here is a simple Makefile that reproduces this problem:
all: file2.txt
file1.txt:
#if [ ! -f file1.txt -o $(shell bash -c "expr \$$RANDOM \% 2") = 1 ]; \
then \
echo "update" >> file1.txt; \
echo "file1.txt overwritten"; \
else \
echo "file1.txt is unchanged"; \
fi
.PHONY: file1.txt
file2.txt: file1.txt
cp file1.txt file2.txt
The test in the rule for file1.txt will generate file1.txt if it does not exists or if a random number modulo 2 is 1. I would like to see cp file1.txt file2.txt executed if and only if file1.txt is overwritten.
By adding the .PHONY setting you are specifically telling make that no matter what that target is always considered out of date, so you're directly contradicting what you want to do.
You can use another method to handle this via a "force target", like this:
FORCE: ;
file1.txt: FORCE
...
and remove the .PHONY setting.

Makefile split string and pipe it to different target

I am trying to write a simple Makefile to build .expected files and compare them but I am failing.
APSSCHED=../../bin/apssched
BASE=.:../../base:../../examples
FLAGS=-DCOT
EXAMPLES=../../examples/
CASES=simple-binding1 simple-binding2
# skipping lines doesn't work ...
# run command and skip the first line
%.aps:
${APSSCHED} ${FLAGS} -p ${BASE} ${EXAMPLES}/$* | tail -n +2
# get all cases as an array to pipe it to different make targets
# maybe overcomplicating
cases:
echo ${CASES} | \
awk '{split($$0,numbers," ")} END {for(n in numbers){ print numbers[n] }}'
# create all .expected files from ${CASES}
build.expected:
$(MAKE) cases | xargs -n1 -I file /bin/bash -c '$(MAKE) file.build.expected'
# create single .expected file
%.build.expected:
$(MAKE) $*.aps > $*.expected
# compare result with
%.compare:
$(MAKE) $*.aps | diff $*.expected -
# run command for all cases and diff the result with corresponding expected
all:
$(MAKE) cases | xargs -n1 -I file /bin/bash -c '$(MAKE) file.compare'
clean.expected:
rm *.expected
Running make without any target and nothing happens.
echo simple-binding1 simple-binding2 | \
awk '{split($0,numbers," ")} END {for(n in numbers){ print numbers[n] }}'
simple-binding1
simple-binding2
I think the issue is with my cases target. I am not sure if I am on the right track.
I appreciate any help or hint.
I would avoid re-running make just to call a different target - it's a performance hit and may be unreliable (depending on rest of the Makefile) since separate calls may not be able to track dependencies correctly.
Moreover, I would avoid using | - every time a command is concatenated with pipe, exit code of piped command would be exit code of the last command. So a call like command | tail would return the exit code of tail (which would almost always succeed). Even if the command has failed, it would be covered with exit code 0 from tail and make will not detect the error and will not stop.
Thus said, I tried to rewrite your approach by just creating dependencies between the targets, like so:
$ cat Makefile
APSSCHED=../../bin/apssched
EXAMPLES=../../examples
BASE=.:../../base:$(EXAMPLES)
FLAGS=-DCOT
CASES=simple-binding1 simple-binding2
# Just for reproducing
$(EXAMPLES)/%.aps: ;
# Generate output and store it in a file
%.output: $(EXAMPLES)/%.aps
# echo is only for reproducing
echo $(APSSCHED) $(FLAGS) -p $(BASE) $< > $#
# Copy actual output as expected
%.expected: %.output
cp -f $< $#
# Compare actual output with expected
.PHONY: %.compare
%.compare: %.output | %.expected
diff $| $<
# Generate and verify all outputs
.PHONY: all
all: $(addsuffix .compare,$(CASES))
# Regenerate expected output
.PHONY: build.expected
build.expected: $(addsuffix .expected,$(CASES))
.PHONY: clean.expected
clean.expected:
-rm -f *.expected
Now the make build.expected will create expected output files, while make all or make will check the actual output against expected:
$ make build.expected
echo ../../bin/apssched -DCOT -p .:../../base:../../examples ../../examples/simple-binding1.aps > simple-binding1.output
cp -f simple-binding1.output simple-binding1.expected
echo ../../bin/apssched -DCOT -p .:../../base:../../examples ../../examples/simple-binding2.aps > simple-binding2.output
cp -f simple-binding2.output simple-binding2.expected
rm simple-binding1.output simple-binding2.output
$ make
echo ../../bin/apssched -DCOT -p .:../../base:../../examples ../../examples/simple-binding1.aps > simple-binding1.output
diff simple-binding1.expected simple-binding1.output
echo ../../bin/apssched -DCOT -p .:../../base:../../examples ../../examples/simple-binding2.aps > simple-binding2.output
diff simple-binding2.expected simple-binding2.output
rm simple-binding1.output simple-binding2.output

pipe out of rule in make recipe?

Suppose I have the following situation:
.SECONDEXPANSION:
rule-%: etc/$$*
some other things
here
something-something -c "cat $< | envsubst | ..." \
> $#
And I'd like to rewrite this rule, as it is a common pattern across n items, without getting clever:
.SECONDEXPANSION:
rule-%: etc/$$*
some other things
here
make something-something-$< > $#
something-something-%:
something-something -c "cat $* | envsubst | ..."
But I can't find any guarantees that this will work if, suppose, I call the outer rule, rule-% as a submake from a different directory (make, in my experience, sometimes prints directory position into the pipeline -- which can be suppressed by executing make silently, but I would never assume a user to know that).
Will this work in all cases? Alternatively, I know I can:
etc...
make something-something arg1=... arg2=...
something-something:
something-something -c "cat ${arg1} | envsubst | ..." > ${arg2}
But I'd like to avoid this.
I don't think I understand your question, but: first you should never use raw make when invoking make recursively: you must always use the $(MAKE) variable.
Second, if you want the recursive make to not print directory info, or even to run silently, you can just add those options to the submake: it won't matter what options the person who invoked the outer make specified:
$(MAKE) -s --no-print-directory something-something-$< > $#
If you just want to keep your code DRY, you can just make use of a template, like so:
$ cat Makefile
.SECONDEXPANSION:
define something
bash -c "cat $(1) | envsubst"
endef
rule-%: etc/$$*
$(call something,$<) > $#
another-rule-%: etc/$$*
$(call something,$<) > $#
Output:
$ make rule-foo another-rule-foo rule-bar another-rule-bar
bash -c "cat etc/foo | envsubst" > rule-foo
bash -c "cat etc/foo | envsubst" > another-rule-foo
bash -c "cat etc/bar | envsubst" > rule-bar
bash -c "cat etc/bar | envsubst" > another-rule-bar

Makefile with dependency graph not known in advance

I'm trying to create a makefile (GNU make) that does the following:
A script generates a bunch of files--filenames not known in advance.
Each one of these files is converted to a different file.
After all are converted, all of these files are combined into a single output file.
How do I create a makefile with a "bellcurve"-patterned dependency graph, where the intermediate source and target files are not known in advance?
Conceptually I'm doing the following:
combined.pdf: $(filter combined.pdf, $(wildcard *.pdf))
cat *.pdf > combined.pdf
%.pdf: %.svg
cp $^ $#
$(wildcard *.svg):
# recipe is for simple example
# actually the *.svg files are not known in advance
echo a > a.svg
echo b > b.svg
echo c > c.svg
.PHONY: clean
clean:
${RM} *.svg *.pdf *.d
Of course this doesn't work: Make evaluates the targets and sources before it runs the target that actually creates the svg. Also, there's no way to make sure all svgs are converted before they are combined.
I realized I could create dependencies and include them into the makefile, but I had trouble getting this to work too:
.PHONY: clean
include deps.d
combined.pdf: deps.d
cat *.pdf > combined.pdf
%.pdf: %.svg
cp $^ $#
deps.d:
## recipe is for simple example
## actually the *.svg files are not known in advance
echo a > a.svg
echo b > b.svg
echo c > c.svg
## we know what files exist now, so we can establish dependencies
## "a.pdf : a.svg"
echo *.svg : > deps.d
## combined.pdf: a.pdf b.pdf c.pdf
ls *.svg \
| awk '{targetfn=$$0; sub(/\.svg$$/, ".pdf", targetfn); print targetfn, ":", $$0;}' \
>> deps.d
## combined.pdf: a.pdf b.pdf c.pdf
echo combined.pdf : $$(echo *.svg | sed -e 's/\.svg/\.pdf/g') >> deps.d
clean:
${RM} *.pdf *.svg *.d
However this still isn't connecting the dependency graph properly. When I run this, make quits as follows:
Makefile:3: deps.d: No such file or directory
echo a > a.svg
echo b > b.svg
echo c > c.svg
echo *.svg : > deps.d
ls *.svg \
| awk '{targetfn=$0; sub(/\.svg$/, ".pdf", targetfn); print targetfn, ":", $0;}' \
>> deps.d
echo combined.pdf : $(echo *.svg | sed -e 's/\.svg/\.pdf/g') >> deps.d
make: Nothing to be done for `a.svg'.
I still seem to have the problem that the make doesn't know about the rules in deps.d.
Also, this still doesn't solve the problem of building all the dependencies. I thought of using a marker file like this:
%.pdf: %.svg
cp $^ $#
## if all svgs are converted, touch a target allpdfs
if [ $(ls -1 *.svg | wc -l) -eq $(ls -1 *.pdf | grep -v combined\.pdf | wc -l) ]; touch allpdfs; fi
But there's no way to inform make that "allpdfs" may be created by this rule.
I'm surprised that moving the include directive makes a difference (what version of Make are you using?), but there is a simpler way. Your use of deps.d is in effect a recursive use of Make -- Make is arranging to execute itself a second time -- so we might as well make it official:
combined.pdf: ALL_SVGS
$(MAKE) ALL_PDFS
rm -f $# # just in case it exists already
cat *.pdf > $#
.PHONY: ALL_SVGS
ALL_SVGS:
# recipe is for simple example
# actually the *.svg files are not known in advance
echo a > a.svg
echo b > b.svg
echo c > c.svg
# These variables will be empty in the first execution of Make
SVGS = $(wildcard *.svg)
PDFS = $(patsubst %.svg,%.pdf,$(SVGS))
.PHONY: ALL_PDFS
ALL_PDFS: $(PDFS))
%.pdf: %.svg
cp $^ $#
This isn't an answer exactly, because I don't know why this works, but I discovered that if I move the include directive after the target that creates the included file, everything works.
I.e. do this:
deps.d:
....
include deps.d
Because my deps.d includes enough dependency information, there's no need to have an intermediate target allpdfs file. Everything Just Works, even with make -j.
However, I don't know why this works. The include documentation isn't enlightening me.
UPDATE
I noticed the following note at the very bottom of the make manual discussing Automatic Prerequisites:
Note that the ā€˜.dā€™ files contain target definitions; you should be sure to place the include directive after the first, default goal in your makefiles or run the risk of having a random object file become the default goal. See How Make Works.
So what happened is that the first rule inside the generated deps.d became the default target, causing the mysterious premature completion of the build. So the solution is just to make sure include directives are not before your intended default target.
I was just working on this exact problem in a slightly different setting. Here is a clean solution - no need for recursion and such (and you can tweak the sed if you like):
include deps.d
combined.pdf:
cat *.pdf > combined.pdf
%.pdf: %.svg
cp $^ $#
deps.d:
echo a > a.svg
echo b > b.svg
echo c > c.svg
echo 'combined.pdf:' *.svg | sed 's/\.svg/\.pdf/g' > deps.d
Enjoy!

Resources