Make: Setting target-specific variables in static pattern rules - makefile

I'm writing a Makefile using static pattern rules and I want for each element of TARGETS a variable assigned to the current target name (here the stem '%').
TARGETS = a b c d
all : $(TARGETS)
$(TARGETS) : % : DIR = %
$(TARGETS) : % : %_setup build
a_setup :
code for a
b_setup :
code for b
...
build
code using "DIR = XX" previously configured
but gnumake complains about the target-specific variable DIR:
make: *** No rule to make target 'DIR', needed by 'a'
Is it possible to mix static pattern rules and variable assignation? Thanks!

According to the GNU make manual you can't do it like that. However, you can use $#. In you example you can directly assign DIR=$# but more generally you can use $# in combination with patsubst:
TARGETS = a b c d
all : $(TARGETS)
$(TARGETS) : DIR = $(patsubst %,%,$#)
$(TARGETS) : % : %_setup build
echo $#: DIR:$(DIR)
%_setup :
echo $#
build:
echo $#

Related

Variable substitution in Makefile target dependency

I have a Makefile with targets that have associated dependencies. So I use lookup table like:
APPS = a b c
dependency.lookup.a := x
dependency.lookup.b := y
dependency.lookup.c := z
$(APPS): %: path/$(dependency.lookup.%).datafile
do something with $(dependency.lookup.$#)
This makefile gives me error.
*** No rule to make target 'path/.datafile'
Constraints: Only MinGW. can't use shell/MSYS. Also support FreeBSD.
This requires using Secondary Expansion feature:
.SECONDEXPANSION:
$(APPS): %: path/$$(dependency.loopup.$$*).datafile
#echo "$# depends on $^"
%.datafile : # Create one on demand for testing.
mkdir -p ${#D}
touch $#
Outputs:
mkdir -p path/
touch path/x.datafile
a depends on path/x.datafile
Alternatively, use regular dependencies:
a : path/x.datafile
b : path/y.datafile
c : path/z.datafile
$(APPS): % :
#echo "$# depends on $^"
The top 3 lines only add dependencies, the rule is specified separately. The output is the same.
I had the same problem but with rather long names. I didn't want to repeat them (in APPS = ...), so I used a generated auxiliary makefile.
Makefile:
all: build
include deps.mk
deps.mk: deps.txt deps.sh
./deps.sh <$< >$#
build: $(DEPS)
echo "DEPS are up-to-date." # or whatever
deps.sh:
#!/bin/bash
echo "# This file is generated by $0"
echo "DEPS ="
while read -r dst src; do
echo
echo "DEPS += $dst"
echo "$dst: $src"
echo -e '\t''cp $< $#' # or whatever
done
deps.txt (now only this file needs to be updated for new dependencies):
a x
b y
c z

Dynamically generating a list of targets

If a file exists, I want to add a target to build. If the file does not exist, I want the target to be skipped.
an example:
FILENAME = f
TARGETS := normal
ifneq($(shell stat test_$(FILENAME).c), "")
TARGETS += test
endif
all: $(TARGETS)
normal:
#echo normal
test:
#echo test
I'm not sure the $(shell stat ...) part even works, but the bigger problem is that make with any file test_f.c in the current folder gives:
Makefile:4: *** multiple target patterns. Stop.
Removing the ifneq ... endif block makes the target normal. How can I only run the target test if test_f.c exists?
What you can do is generate a string variable (let's call it OPTIONAL) such that when 'test_f.c' exists, OPTIONAL=test; otherwise, OPTIONAL=_nothing_. And then add OPTIONAL as a prerequisite of all. e.g.:
FILENAME = f
TARGETS = normal
OPTIONAL = $(if $(wildcard test_f.c), test, )
all: $(TARGETS) $(OPTIONAL)
normal:
#echo normal
test:
#echo test
You can also iterate over targets with for loop
.PHONY: all
RECIPES = one
all: RECIPES += $(if $(wildcard test_f.c), two, )
all:
for RECIPE in ${RECIPES} ; do \
$(MAKE) $${RECIPE} ; \
done
one:
$(warning "One")
two:
$(warning "Two")
> make
for RECIPE in one ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
> touch test_f.c
> make
for RECIPE in one two ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
makefile:14: "Two"
make[1]: `two' is up to date.

Makefile. Multidimensional list?

I need to write a pattern rule for the following case:
There are 2 folders: A and B
Running the command python gen.py --a=A/file1.foo --b=file2.bar --c=file3.bar generates B/file1.foo
file1, file2 and file3 are different strings
Is there a way to group those filenames in some kind of a multidimensional array, so that all files are written exactly once (I'll use python syntax):
files = [["a1.foo", "a2.bar", "a3.bar"],
#...200 other groups...
["b1.foo", "b2.bar", "b3.bar"]]
and then the rule looks like this:
$(files): B/{reference 1 elem}: A/{1 elem} {2 elem} {3 elem}
python gen.py --a=A/{1 elem} --b={2 elem} --c={3 elem}
Any ideas how to archive it?
You can use standard make syntax for that:
all :
targets :=
define add_target
B/${1}: A/${1} ${2} ${3}
targets += B/${1}
endef
# Build dependencies.
$(eval $(call add_target,a1.foo,a2.bar,a3.bar))
# ...
$(eval $(call add_target,b1.foo,b2.bar,b3.bar))
# One generic rule for all ${targets}
${targets} : % :
#echo Making $# from $^
all : ${targets}
.PHONY: all
Note that these $(eval $(call add_target,...) are white-space sensitive, do not insert spaces in there.
If you would like make to create the directory for outputs automatically do:
${targets} : % : | B
B :
mkdir $#
Sometimes a little repetition isn't so bad really
targets := B/a1.foo B/b1.foo
.PHONY: all
all: $(targets)
$(targets): B/%: A/%
python gen.py --a=$< --b=$(word 2,$^) --c=$(word 3,$^)
B/a1.foo: a2.bar a3.bar
B/b1.foo: b2.bar b3.bar

Using the string matched by '%' from a pattern rule in pattern matching functions

I have two sets of files $(Xs) and $(Ys). Each .x file depends on an arbitrary number of .y files based on its name. For each <name>.x file I have a number of <name>_*.y files.
I can write individual rules for the .x files and use a function to compute the dependencies for it.
.PHONY: build
Xs = a.x b.x
Ys = a_1.y a_2.y b_1.y b_2.y
build: $(Xs)
a.x: $(filter a%,$(Ys))
#echo $#" with dependencies: "$+
b.x: $(filter b%,$(Ys))
#echo $#" with dependencies: "$+
%.y:
#echo "y : "$#
... or I could write a pattern rule for all .x files and enumerate the dependencies
$(Xs) : %.x : %_1.y %_2.y
#echo $#" with dependencies: "$+
But can I do both at the same time? I don't know how to get the string matched by % in the rule and use it in $(filter).
$(Xs) : %.x : $(filter ???,$(Ys))
#echo $#" with dependencies: "$+
You can do this with Secondary Expansion and a Canned Recipe (needed to work around the double use of % in filter and the static pattern rule's prereq list.
.PHONY: build
Xs = a.x b.x
Ys = a_1.y a_2.y b_1.y b_2.y
build: $(Xs)
%.y:
#echo 'y : $#'
define yf
$(filter $(1)_%.y,$(Ys))
endef
.SECONDEXPANSION:
$(Xs) : %.x : $$(call yf,%)
#echo '$# with dependencies: $+'

Makefile: using variable with computed name as prerequisite

In Make it's possible to compute variable names in runtime using double-evaluation, like $($(var)).
I'm interested if it's possible somehow to make this work:
one.js_DEPS=a b
two.js_DEPS=c b
all: one.js two.js
%.js: $(%.js_DEPS)
cat $^ > $#
I can make this work by declaring two rules with explicit dependencies, like this:
one.js: $(one.js_DEPS)
But this seems a bit anti-DRY. So I'm wondering if I miss something because make doesn't seem to understand me. I use GNU Make 3.81.
% and implicit rules are somewhat second-class citizens in make, since variable expansion seems to happen before implicit rule expansion. So, in your above Makefile, $(%.js_DEPS) is expanded to the empty string before % is substituted.
Alternative:
%.js: %.js_DEPS
cat $^ > $#
%.js_DEPS :
cat $^ > $#
.INTERMEDIATE: one.js_DEPS two.js_DEPS
or simply:
one.js : a b
two.js : c d
%.js :
cat $^ > $#
This could be done using Secondary Expansion.
JS := one.js two.js
all: $(JS)
.SECONDEXPANSION:
one.js_DEPS := a b
two.js_DEPS := c b
$(JS) : $$($$(#F)_DEPS)
cat $^ > $#
But in fact, the goal can be achieved much simply, without using any GNU Make extensions, as thiton has suggested in his answer:
all: one.js two.js
one.js : a b
two.js : c b
%.js :
cat $^ > $#

Resources