Use of Makefile Patterns - makefile

I am trying to write a relatively simple Makefile, but I am not sure how to best condense the rules using patterns. I tried using %, but I ran into difficulties. Here is the Makefile in its expanded form:
all : ./115/combine_m115.root ./116/combine_m116.root ... ./180/combine_m180.root
./115/combine_m115.root : ./115/comb.root
bash make_ASCLS.sh -l 115 comb.root
./116/combine_m116.root : ./116/comb.root
bash make_ASCLS.sh -l 116 comb.root
...
./180/combine_m180.root : ./180/comb.root
bash make_ASCLS.sh -l 180 comb.root

Unfortunately, we don't have a clean way to do this in Make. This task combines a couple of things Make doesn't do well.
Make can't handle wildcards very well (or regular expressions at all), so constructions like ./%/combine_m%.root : ./%/comb.root won't work. I think the closest we can get is with a canned recipe:
define apply_script
./$(1)/combine_m$(1).root : ./$(1)/comb.root
bash make_ASCLS.sh -l $(1) comb.root
endef
$(eval $(call apply_script,115))
$(eval $(call apply_script,116))
...
$(eval $(call apply_script,180))
We can condense things a little more like this:
NUMBERS := 115 116 # ...180
TARGS := $(foreach n, $(NUMBERS), ./$(n)/combine_m$(n).root)
all : $(TARGS)
...
$(foreach n, $(NUMBERS), $(eval $(call apply_script,$(n))))
There's also a way to generate NUMBERS, but it's an even uglier hack.

Related

Use of eval in a target recipe

In a Makefile I'm writing I had an interest in cleaning up some of the CC prints and centralizing some of the build preparations (like creating directories in the build tree). I figured macros would be a good fit for this task. This is effectively what im trying to do, used all over various Makefiles:
define func
#mkdir -p $$(dir $(1))
#printf "%-5s $(2)\n" $(3)
endef
test:
#echo Run
$(eval $(call func,a,b,c))
My thought was that after first expansion I'd get something like (less any tabs maybe, I'm not exactly sure how the expansion works within eval):
test:
#echo Run
$(eval #mkdir -p $(dir a)\n#printf "%-5s b\n" c
and of course finally the commands would be executed. However, what I get is this:
# make
Makefile:7: *** recipe commences before first target. Stop.
I changed eval to info and got this:
#mkdir -p $(dir a)
#printf "%-5s b\n" c
Run
So I thought maybe my explicit tabs in the macro definition were causing trouble, so I removed them and tried again:
# make
Makefile:7: *** missing separator. Stop.
So it still does not quite work. If it is indeed possible at all, it seems some function of indentions in the macro, or maybe I'm defining the macros incorrectly. I thought perhaps the two commands in the macro was causing trouble (since the complaint is regarding a separator), but reducing the macro to a single line did not help either.
You don't want eval here. Eval is used to evaluate makefile syntax. That is, the thing you're evaluation has to be a valid, complete makefile. You can see that what info prints is not a valid makefile. If you put that into a file and ran make -f <file>, you'd get a syntax error.
You are just trying to expand a variable for shell syntax. Just remove the eval.

Can I call a user defined function outside of a target

I want to know the following:
Q1: Can I call a user-defined function outside of a target?
Q2: Would the changes happen inside like exporting a variable would take effect?
DOT_ENV_FILE ?= $(CURDIR)/.env
define importEnvFile
VARS = $(shell sed -ne 's/ *\#.*$$//; /./ s/=/?=/ p' ${1})
$(foreach v,${VARS},$(eval export ${v}))
endef
$(call importEnvFile,$(DOT_ENV_FILE))
A1: Yes, if you use the eval function:
define defThings
SKY := blue
endef
$(eval $(call defThings))
$(info sky is $(SKY))
A2: Yes:
makefile:
define defThings
export SKY := blue
endef
$(eval $(call defThings))
delegate:
$(MAKE) -f Makefile.other
Makefile.other:
report:
#echo sub-make reports sky is $(SKY)
The other complications of reading and parsing a file and iterating over a list can wait until you have this much working. When you implement a new functionality, keep it as simple as possible until you have it working.

code management: generate source files with slight variations of various rules

I have a source file in a declarative language (twolc, actually) that I need to write many variations on: a normative version and many non-normative versions, each with one or more variations from the norm. For example, say the normative file has three rules:
Rule A:
Do something A-ish
Rule B:
Do something B-ish
Rule C:
Do something C-ish
Then one variation might have the exact same rules as the norm for A and C, but a different rule for B, which I will call B-1:
Rule A:
Do something A-ish
Rule B-1:
Do something B-ish, but with a flourish
Rule C:
Do something C-ish
Imagine that you have many different subtle variations on many different rules, and you have my situation. The problem I am worried about is code maintainability. If, later on, I decide that Rule A needs to be refactored somehow, then I will have 50+ files that need to have the exact same rule edited by hand.
My idea is to have separate files for each rule and concatenate them into variations using cat: cat A.twolc B.twolc C.twolc > norm.twolc, cat A.twolc B-1.twolc C.twolc > not-norm.twolc, etc.
Are there any tools designed to manage this kind of problem? Is there a better approach than the one I have in mind? Does my proposed solution have weaknesses I should watch out for?
As you added the makefile tag, here is a GNU-make-based (and Gnu make only) solution:
# Edit this
RULES := A B B-1 C
VARIATIONS := norm not-norm
norm-rules := A B C
not-norm-rules := A B-1 C
# Do not edit below this line
VARIATIONSTWOLC := $(patsubst %,%.twolc,$(VARIATIONS))
all: $(VARIATIONSTWOLC)
define GEN_rules
$(1).twolc: $$(patsubst %,%.twolc,$$($(1)-rules))
cat $$^ > $$#
endef
$(foreach v,$(VARIATIONS),$(eval $(call GEN_rules,$(v))))
clean:
rm -f $(VARIATIONSTWOLC)
patsubst is straightforward. The foreach-eval-call is a bit more tricky. Long story short: it loops over all variations (foreach). For each variation v, it expands (call) GEN_rules by replacing $(1) by $(v) (the current variation) and $$ by $. Each expansion result is then instantiated (eval) as a normal make rule. Example: for v=norm, the GEN_rules expansion produces:
norm.twolc: $(patsubst %,%.twolc,$(norm-rules))
cat $^ > $#
which is in turn expanded as (step-by-step):
step1:
norm.twolc: $(patsubst %,%.twolc,A B C)
cat $^ > $#
step2:
norm.twolc: A.twolc B.twolc C.twolc
cat $^ > $#
step3:
norm.twolc: A.twolc B.twolc C.twolc
cat A.twolc B.twolc C.twolc > norm.twolc
which does what you want: if norm.twolc does not exist or if any of A.twolc, B.twolc, C.twolc is more recent than norm.twolc, the recipe is executed.

Makefile for many LaTeX files and dependencies in increasing order

I have the following question on writing a reasonable Makefile for compiling a lot of LaTeX exercise sheets for my lectures. The exercises are organised in files file1.tex, file2.tex, ... fileXXX.tex where XXX is the total number (varies from course to course). Now the catch is that I use crossrefs from later files to earlier ones: the dependence of fileY.tex is on all the aux-files of the files fileZ.tex with Z < Y: so those have to be generated before.
So instead of hardcoding all the XXX files with their dependencies into a Makefile I'm looking for a more efficient way to do that. It would be somehow nice to specify just the total number XXX of files.
Like this?
N := 100 # can override on the command line, etc.
SRCS := $(foreach n,$(shell seq $(N)),file$(n).tex)
define dep
$(call dep-rec,$1,,)
endef
# $(call dep-rec,.tex files,accumulated aux files)
define dep-rec
$(if $1,$(eval $(word 1,$1): $2)$(call dep-rec,$(wordlist 2,$(words $1),$1),$2 $(call aux-files-of,$(word 1,$1))))
endef
# return the aux files of $1 (which is a .tex file) -- define as appropriate
define aux-files-of
$(basename $1).aux
endef
$(call dep,$(SRCS))

multi-wildcard pattern rules of GNU Make

I want to write something like regex:
SRC:="a.dat.1 a.dat.2"
$(SRC): %.dat.%: (\\1).rlt.(\\2)
dat2rlt $^ $#
so that a.dat.1 and a.dat.2 will give a.rlt.1 and a.rlt.2.
In GNU Make info page, it says "the % can be used only once".
Is there some trick to achieve this in GNU Make?
I'm afraid what you are trying to do is not possible the way you suggest to do it, since - as you already mention - (GNU) make only allows a single stem '%', see http://www.gnu.org/software/make/manual/make.html#Pattern-Rules:
A pattern rule looks like an ordinary rule, except that its target
contains the character ‘%’ (exactly one of them).
Without it, creating such 'multi-dimensional' targets is cumbersome.
One way around this is by rebuilding the name of the dependency in the command (rather than in the dependency list):
SRC := a.dat.1 a.dat.2
all : $(SRC:%=%.dat2rlt)
%.dat2rlt :
dat2rtl $(word 1,$(subst ., ,$*)).rlt.$(word 2,$(subst ., ,$*)) $*
Of course, however, this way you would lose the dependency, it will not rebuild once the rlt has been updated.
The only way I can see to address that is by generating the rules explicitly:
SRC := a.dat.1 a.dat.2
all : $(SRC)
define GEN_RULE
$1.dat.$2 : $1.rlt.$2
dat2rtl $$< $$#
endef
$(foreach src,$(SRC),$(eval $(call GEN_RULE,$(word 1,$(subst ., ,$(src))),$(word 3,$(subst ., ,$(src))))))
Using named variables, we can write more readable code (based on answer of Paljas):
letters:=a b c
numbers:=1 2 3 4
define GEN_RULE
$(letter).dat.$(number) : $(letter).rlt.$(number)
./rlt2dat $$< $$#
endef
$(foreach number,$(numbers), \
$(foreach letter,$(letters), \
$(eval $(GEN_RULE)) \
) \
)
We can generate SRC in a similar way. Note that using that method SRC will contain all the combinations. That may or may not be beneficial.
Building on the answer of Erzsébet Frigó, you might additionally choose to:
in the inner loop, eval not the macro itself but the result of calling it
name the macro after program you're calling, dat2rtl
in combination, allowing you to
refer to the program name using make's ${0}
define a target, ${0}s (expanding to dat2rts - note the pluralization) with preconditions of all combinations of letters and numbers on which dat2r2 was called
Like this:
letters:=a b c
numbers:=1 2 3 4
define rlt2dat
${0}s::$(letter).dat.$(number)
$(letter).dat.$(number): $(letter).rlt.$(number)
./${0} $$< $$#
endef
$(foreach number,$(numbers), \
$(foreach letter,$(letters), \
$(eval $(call rlt2dat))))
allowing you to build all rlt2dat targets as:
make rlt2dats
For the limited example you gave, you can use a pattern with one %.
SRC := a.dat.1 a.dat.2
${SRC}: a.dat.%: a.rlt.%
dat2rlt $^ $#
$* in the recipe will expand to whatever the % matched.
Note that the "s around your original macro are definitely wrong.
Have a look at .SECONDEXPANSION in the manual for more complicated stuff (or over here).

Resources