Customizing make target based on from where it was entered - makefile

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... $*"

Related

How to make conditional expansion without subshell?

I wrote following function in GNU Make, that checks whether first argument belongs is found in some list, and expands to second or third argument accordingly:
FLAGS := foo bar
use = $(shell { echo $(FLAGS) | grep -qw $(1) ; } && echo $(2) || echo $(3))
all:
» $(info $(call use, foo, have-foo, no-foo))
» $(info $(call use, baz, have-baz, no-baz))
It behaves as I want:
$ make all
have-foo
no-baz
make: 'all' is up to date.
Is there any way to implement same functionality only with GNU Make,
without subshell?
Is there any way to add more syntax sugar at call sites?
I need it work on GNU Make built without Guile support.
I'm not sure I fully understand but why not this?
use = $(if $(filter $1,$(FLAGS)),$2,$3)
??
While MadScientists answer may be all you need, it looks like you are doing the configuration management in make. While this is IMO the place where it should happen, the heritage has prevented make from becoming a full-flegded tool for that purpose. However, there is a library which helps in this case: The GNUmake Table Toolkit lets you select from a table those lines which fulfill certain freely defined condition (read the documentation to select).
include gmtt/gmtt.mk
# original definition of the flag table
define FLAG-TABLE
2
foo have-foo-1
foo have-foo-2
bar have-bar-1
endef
# GNUmake even allows to write an addendum to a define:
define FLAG-TABLE +=
bar have-bar-2
foo have-foo-3
endef
# the $1 is where the parameter of the call to MY-SELECTION is placed; the $$1 is
# where the first column of the table is put before calling `str-eq`
MY-SELECTION = $(call select,2,$(FLAG-TABLE),$$(call str-eq,$$1,$1))
$(info $(FLAG-TABLE))
$(info ------------------)
$(info $(call MY-SELECTION,foo))
$(info ------------------)
$(info $(call MY-SELECTION,bar))
Output:
$ make
2
foo have-foo-1
foo have-foo-2
bar have-bar-1
bar have-bar-2
foo have-foo-3
------------------
have-foo-1 have-foo-2 have-foo-3
------------------
have-bar-1 have-bar-2

Why $(shell ls) is expanded at the start of the makefile task?

I have the following code:
foo:
touch foo
$(foreach f, $(shell ls | grep foo), \
echo $f; \
)
it will not list the file foo created by the touch foo above, will list if the foo file already exists before the task starts, like this:
$ make foo # first time call, file 'foo' doesn't exists yet
$ make foo # second time call, file 'foo' already exists
foo
Is there a way to evaluate the ls after all the commands above are executed?
That's how Make works. The Makefile is parsed and any Makefile functions are called, then one or more recipes are evaluated.
Is there a reason you're not simply using a shell loop?
foo:
touch foo
for f in *foo*; do \
echo "$$f"; \
done
Notice how the dollar sign needs to be doubled to escape it from being evaluated by make, and also how shell variables should generally be double-quoted unless you specifically require the shell to perform whitespace tokenization and wildcard expansion on the value.
On the other hand, a more "make-ish" approach is to explicitly document any dependencies.
.PHONY: all
all: foo
printf '%s\n' $^
foo:
touch $#
Now all depends on foo, so Make knows it must create foo before it can perform the all recipe if foo doesn't exist, or is out of date in relation to its own dependencies (of which of course there are currently none).
The make variable $^ refers to the dependencies of the current target, and $# expands to the current recipe target. The printf shell script is just a more economical way to print one thing per line without a loop.

Questions about Automatic variables in makefile

According to https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
I don't understand the difference between $? and $^. The document says $? returns the names of all prerequisites that are newer than the target whereas $^ returns all prerequisites.
However, I tried to test it with many examples and none of them worked? Could anyone provide me an example?
Also: What are order-only prerequisites? I don't understand what 'order-only' means. This is from the following definition:
$| :
The names of all the order-only prerequisites, with spaces between them.
Thanks
If you show us an example that you tried that didn't work, we can be more help. Here's a simple example:
$ cat Makefile
all: foo bar
#echo '$$? = $?'
#echo '$$^ = $^'
$ touch foo; sleep 1; touch all; sleep 1; touch bar
$ make
$? = bar
$^ = foo bar
You can find information on order-only prerequisites in the GNU make manual.

Avoid running GNU make recipe when prerequisite is updated

I have a Makefile that looks like this:
foo: bar
touch foo
sleep 2
echo UPDATED > bar
bar: baz
cp baz bar
sleep 2
bar does not initially exist so it is copied from baz. However, a side-effect of building foo is that bar is also modified such that its timestamp might be newer than that of foo. Is there a way around this? Would order-only dependencies help somehow?
In addition, if baz is later modified, bar and foo would need rebuilding.
SOLUTION 1
It is enough to touch foo just after bar has been updated. In this way, foo is always newer than bar.
foo: bar
sleep 2
echo UPDATED > bar
touch foo
bar: baz
cp baz bar
sleep 2
SOLUTION 2
An order-only prerequisites, such as
foo: | bar
touch foo
sleep 2
echo UPDATED > bar
bar: baz
cp baz bar
sleep 2
will do, too; but keep in mind that order-only prerequisites are a GNU extension to the syntax of Makefile, hence not fully portable.
EDIT
If, in addition, you want bar and foo to be rebuilt when baz is modified, then the only feasible solution is the first one.
Yes, if you need bar to exist for foo but don't need to rebuild foo just because bar changes then an order-only prerequisite sounds like what you want.
You could also possibly just touch foo at the end of the rule instead and avoid this 'foo causes itself to get rebuilt next time' problem entirely, assuming that is workable in this environment.

make, variables depend on target

Makefile
abc:
TEST=$# /* TEST=abc */
def:
TEST=$# /* TEST=def */
xxx:
#echo $(TEST)
If I run make abc xxx, I
expect output abc.
If I run make def xxx, I
expect output def.
But it doesn't work like that. It seems like make won't let me define a variable in target. My question is how could I define a variable and its value which depend on which target is built?
Using GNU Make, you can duplicate the intended result of your Makefile as follows:
echo.%:
#echo $*
or simply:
%:
#echo $*
These would allow you to specify make echo.abc or make abc, respectively, and see abc as output.
That's all well and good, but it sounds like you would be more interested in target-specific variables. If the variable value is dependent on, but not a substring of, the target name, you can do something like this:
%:
#echo $(TEST)
abc: TEST=xyzzy
def: TEST=plugh
This allows you to specify make abc and see xyzzy as output.
If you want to go beyond this, so that the behavior of a target changes based on what other targets are specified on the command line, we have two options: either have .PHONY targets that update a file used by your primary target, or do some processing of the MAKECMDGOALS variable:
Option 1:
xxx: xxx.temp
#cat xxx.temp
#rm xxx.temp
abc def:
#echo $# > xxx.temp
.PHONY: xxx abc def
This relies on the fact that the command line goals are processed in order (If you specify several goals, make processes each of them in turn, in the order you name them. -GNU Make Manual, section 9.2), so you must specify make abc xxx and not make xxx abc.
Option 2:
ifneq (,$(findstring abc,$(MAKECMDGOALS)))
VAR=abc
else ifneq (,$(findstring def,$(MAKECMDGOALS)))
VAR=def
endif
xxx:
#echo $(VAR)
abc def:
#echo -n > /dev/null
.PHONY: xxx abc def
The #echo -n > /dev/null suppresses the make: Nothing to be done for 'abc'. message. This seems much clumsier to me, and I would recommend Option 1 if you can tolerate the temp file.

Resources