How do you make setup and teardown routines in Make - makefile

I've searched everywhere, and I can't find this feature. It might just not be possible. I want something the run before and after the recipes are executed. I expected it would look something like this:
.BEFORE:
echo "Hello world"
.AFTER:
echo "Goodbye cruel world"
foo.txt:
touch foo.txt
Outputting thusly:
Hello World
touch foo.txt
Goodbye cruel world
Is this doable?

targets := foo.txt
.PHONY: after before
after: $(targets)
#echo "Goodbye cruel world"
$(targets): before
before:
#echo "Hello world"
foo.txt:
touch foo.txt
My previous suggestion ran the recipe for foo.txt each time as it depended on a .PHONY target. If you're ok with that then you might as well just use double colon rules which will also fix the issue about having multiple targets.
targets := foo.txt
$(targets)::
#echo "Hello world"
foo.txt::
touch $#
$(targets)::
#echo "Goodbye cruel world"
If foo.txt ends up having prerequisites of its own then this issue should resolve on its own, of course the issue now is that the before and after rules will be executed one for each target.

Related

Customizing make target based on from where it was entered

Is there a variable like $# which would contain instead of current target the target which required current target? Below is a simple example of what kind of functionality I am looking for. Basically "make foo" would create a directory foo, "make bar" would create bar and "make both" both. So far I haven't found anything which would enable this kind of customization.
foo: foobar
do stuff
bar: foobar
do other stuff
both: foo bar
....
foobar:
mkdir <foo or bar>
Found one solution, pattern rules.
foo: from_foo
#echo "This is foo"
from_%:
#echo "Called from $(lastword $(subst _, ,$#))"
#echo "Simpler version... $*"

Specifying wild card patterns when invoking make targets

Suppose I define:
# makefile
.FORCE:
foo-bar: .FORCE
#echo "$#"
foo-biz: .FORCE
#echo "$#"
.PHONY: foo-bar foo-biz
And I write on the command line:
$: make foo-bar foo-biz
Then is there a way for me to, somehow, write:
$: make foo-*
for example?
This works but it only supports the one single % wildcard of native make pattern matching. Moreover I didn't analyze its impact on pattern rules etc.
PATTERN_GOALS := $(foreach g,$(MAKECMDGOALS),$(if $(findstring %,$(g)),$(g)))
force-on-pattern = $$(if $$(filter $(PATTERN_GOALS),$$#),$$(eval FORCE+=$$#))
# make FORCE a simple expanded variable
FORCE :=
.SECONDEXPANSION:
foo: $(force-on-pattern)
echo $#
fooa: $(force-on-pattern)
echo $#
fooab: $(force-on-pattern)
echo $#
foob: $(force-on-pattern)
echo $#
$(PATTERN_GOALS): $$(FORCE) ;
Test:
$ make foo%
fooab
fooa
foob
foo
$ make fooa%
fooab
fooa
$ make fooab%
fooab
$ make fooab
echo fooab
fooab
$ make foo%b
fooab
foob
You can have a look at the GNUmake table toolkit and specifically the glob-match function for a make-native glob matcher but this is, as #Beta noticed, the realm of 'fearsome beast' make programming.
Here's an idea. Since globbing happens in the shell before the make program even starts, you could have files in the repository named like the rules you want globbable:
touch foo-bar foo-biz
make foo-*
# executes 'make foo-bar foo-biz'
This works with real files too (not just .FORCE or PHONY), but only if the files already exist.

Make $* check for string

I am trying to check if $* matches hello . But the following is not working
build: build-hello
build-%:
ifeq ($*, hello)
echo Hello
else
echo World
endif
The conditions in the ifeq's are processed at makefile read time -- when $* is still blank. There's a couple of workarounds to this: First, you could do a build-hello: rule, which would override the build-% rule for build-hello. If, on the other hand you wanted to minimize rules, you could use the $(if) function as so:
build-%:
#echo $(if $(filter $*,hello),Hello,World)
Or, you could just use shell logic to accomplish this as well.

Define a target that depends on a value/variable that need to be resolved

Updated my question as it seemed to be not clear enough!
I was listing when to use make over bash. One thing I like about make is its declarative way of describing necessary steps; we can write a rule by relying on other rules knowing how to provide necessary files (or other external states).
I'm wondering how I can get the same benefit for a value not a file, without changing outer world (like leaving a temporary file).
hello.txt: (here, tell that it needs to resolve person's name)
# Here, person's name is available.
echo Hello $(var_name) > $#
We can imperatively prepare a necessary value with $(call prepare_name, ...) at the beginning of a command in a rule, but that's not what I'm after here.
I posted my attempts as an answer when I opened this question. Hopefully that adds more info on what I'm trying to achieve.
It's not overly clear what you're after, however to clarify a few concepts:
A target must be dependent on other targets. It cannot be dependent on a variable name. It can be dependent on the value of a variable, if that variable resolves to a target name.
So you could do:
VAR=some_target
hello.txt: $(VAR)
echo "hello $^" > $#
some_target:
touch $#
You CANNOT do:
VAR=some_target
hello.txt: VAR
and expect it to work (make would try to build VAR which likely doesn't exist and it would fail).
I'm assuming from the question that you want make to request the variable name of a person, and put that into hello.txt. In that case you would likely want to store the name in a temporary file and use that for the output:
.getname.txt:
#read -p "enter name" name > $#
hello.txt: .getname.txt
#echo "hello $$(cat $$<)" > $#
This will update .getname.txt if it didn't previously exist (so it will not necessarily ask on every invokation of make...). You could make .getname.txt be a .PHONY target, and it will run every time.
If you do want to run every time, then you can simply do:
hello.txt:
#read -p "enter name: " name && echo "hello $$name" > $#
.PHONY: hello.txt
Which will invoke the hello.txt rule regardless of whether hello.txt already exists, and will always prompt the user for a name and rebuild hello.txt.
I can think of a way using eval function. Below suppose foo is a value obtained by a complex calculation.
hello.txt: var_name
echo Hello $($<) > $#
.PHONY: var_name
var_name:
$(eval $# = foo)
Or with .INTERMEDIATE target, this also works, but I feel it's more complicated.
var_name = var-name.txt
hello.txt: $(var_name)
echo Hello $$(< $<) > $#
.PHONY: $(var_name)
.INTERMEDIATE: $(var_name)
$(var_name):
rm -f $# # In case the var file already exists
echo bar > $#
Another way could be to use a target-specific variable. It's not listing a variable as a prerequisite, but I still don't need to think about how to get var_name when writing echo Hello ....
define get_name
echo foo
endef
hello.txt: var_name = $(call get_name)
hello.txt:
echo Hello $(var_name) > $#
As noted in other answers, make track dependencies between files, using timestamps. The regular solution for handling a value will be to store it in a file (or to generate it into a file). Assuming that there is significant work to do whenever the data is changing, you can follow one of the patterns below to implement dependency check on the file value.
The following makefile snapshot will trigger rebuild of complex-result, only when the content of var-value is modified. This is useful when the content of var-value is continuously regenerated, but does not change very frequently.
all: complex-result
last-value.txt: var-value.txt
cmp -s $< $# || cat <$^ > $#
complex-result: last-value.txt
echo Buildig for "$$(cat var-value.txt)"
touch $#
Or more realistic example: trigger a build if the value (content) of any file was modified, using md5 checksum,
all: complex-result
last-value.txt: $((wildcard *.data)
md5sum $^ > $#
last-value.txt: var-value.txt
cmp -s $< $# || cat <$^ > $#
complex-result: last-value.txt
echo Building for "$$(cat var-value.txt)"
touch $#

Pairing same-index items in two lists as target and dependency in makefile

I have the example makefile below:
list1 = foo.txt bar.txt
list2 = foo bar
.PHONY: ${list2}
all: ${list1} ${list2}
${list1}: ${list2}
#echo $# $<
This produces the result
foo.txt foo
bar.txt foo
But what I want is
foo.txt foo
bar.txt bar
How can I do this? Just stripping .txt would work in this case and ignore list2, but not in the real file I am trying to work with. I need to pair members of lists across a large number of targets and substitutions.
As Etan points out, after you expand the variables make sees:
foo.txt bar.txt : foo bar
Make is simply not going to somehow infer that you really wanted to say foo.txt depends on foo and bar.txt depends on bar: there are many ways make could interpret this. The way it DOES interpret it, is if you had written:
foo.txt : foo bar
bar.txt : foo bar
which is why you get the results you do.
Your example is not very specific, so it's hard to help you. For example you show your two lists of files as being related by filename. If that's really true, then you have no problem. Just use this:
list1 = foo.txt bar.txt
.PHONY: ${list2}
all: ${list1}
%.txt: %
#echo $# $<
If the files you're building and using for prerequisites have no relationship (by name) to each other that you can describe with a pattern, then you have larger problems.

Resources