Reputation: 9133
I have pairs of input/output files. I generate the name of the output file from a script: output=$(generate input)
. For example the pairs could be:
in1.c out1.o
in2.txt data.txt
in3 config.sh
sub/in1.c sub/out1.o
All those pairs obey the same set of rules in the makefile:
$(out): $(in) $(common)
$(run) $< > $@
What is a concise and efficient way to write such a Makefile?
I would rather avoid generating the Makefile from another script.
Upvotes: 3
Views: 5044
Reputation: 136286
What is a concise and efficient way to write such a Makefile?
It is possible given a list of inputs and a shell script that generates output file name to generate targets, dependencies and rules using GNU make features:
all :
inputs := in1.c in2.txt in3 sub/in1.c
outputs :=
define make_dependency
${1} : ${2}
outputs += ${1}
endef
# replace $(shell echo ${in}.out) with your $(shell generate ${in})
$(foreach in,${inputs},$(eval $(call make_dependency,$(shell echo ${in}.out),${in})))
# generic rule for all outputs, and the common dependency
# replace "echo ..." with a real rule
${outputs} : % : ${common}
@echo "making $@ from $<"
all : ${outputs}
.PHONY : all
Output:
$ make
making in1.c.out from in1.c
making in2.txt.out from in2.txt
making in3.out from in3
making sub/in1.c.out from sub/in1.c
In the above makefile one little used by powerful GNU make construct is used: $(eval $(call ...))
. It requests make to expand the macro to produce a piece of text and then evaluate that piece of text as a piece of makefile, i.e. make generates makefile of the fly.
Upvotes: 1
Reputation: 11696
If you include
a file gmake will try to generate and include it before any other targets. Combining this with a default rule should get you close to what you want
# makefile
gen=./generate.sh
source=a b c
run=echo
# Phony so the default rule doesn't match all
.PHONY:all
all:
# Update targets when makefile changes
targets.mk:makefile
rm -f $@
# Generate rules like $(target):$(source)
for s in $(source); do echo "$$($(gen) $$s):$$s" >> $@; done
# Generate rules like all:$(target)
for s in $(source); do echo "all:$$($(gen) $$s)" >> $@; done
-include targets.mk
# Default pattern match rule
%:
$(run) $< > $@
Testing with generate.sh
like
#!/bin/bash
echo $1 | md5sum | awk '{print $1}'
give me
$ make
rm -f targets.mk
for s in a b c; do echo "$(./generate.sh $s):$s" >> targets.mk; done
for s in a b c; do echo "all:$(./generate.sh $s)" >> targets.mk; done
echo a > 60b725f10c9c85c70d97880dfe8191b3
echo b > 3b5d5c3712955042212316173ccf37be
echo c > 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
Upvotes: 3
Reputation: 18667
I wouldn't generate Makefile fragments from a script, but you could use an include
:
INS := in1.c in2.txt in3 sub/in1.c
include rules.mk
rules.mk: Makefile
rm -f $@
for f in $(INS); do \
out=`generate "$$f"`; \
echo -e "$$out: $$f\n\t\$$(run) \$$<> > \$$@\n\n" >> $@; \
done
Upvotes: 3