make, variables depend on target - makefile

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.

Related

Put Very Long Dependency List at the END of the Makefile

I'm trying to put a very long list of objects at the END of my Makefile. I cannot get this code to work. I can see $(LIST) getting assigned in the ASSIGN_LIST macro, however, it seems that $(LIST) is blank for stuff.txt. I'm a newbie at Make so maybe I'm way off the mark. (Using Red Hat Linux, GNU Make v3.81.)
> cat makefile
define ASSIGN_LIST
LIST = $1
endef
stuff.txt: $(LIST)
echo $(LIST)
# MUCH MORE CODE HERE
# THIS CODE CONTAINING THE LIST IS AT THE VERY END OF MAKEFILE (i.e. OUT OF THE WAY)
define VERY_LONG_LIST_OF_OBJECTS
aaa \
bbb \
ccc \
ddd \
eee \
fff \
etc
endef
$(info $(call ASSIGN_LIST, $(VERY_LONG_LIST_OF_OBJECTS)))
When I run the makefile:
> make
make: `stuff.txt' is up to date.
I use 'touch' to make the prereqs newer than stuff.txt. If I swapout "eval" with "info" (last line), I can see this
> make
LIST = aaa bbb ccc ddd eee fff etc
make: `stuff.txt' is up to date.
Is this even possible? Thanks for the help.
Variables that appear in targets or prerequisites are expanded immediately when read by make during the parsing of the makefile. So, this cannot work:
stuff.txt: $(LIST)
echo $(LIST)
...
LIST = ...
Here, the prerequisite variable is expanded when make reads that line but the variable inside the recipe is not expanded until later when make wants to build the target. So the variable is not set yet when parsing the prerequisite, then it gets set, then the recipe is run.
If you want to see what make sees you should use automatic variables instead:
stuff.txt: $(LIST)
echo $^
The best way to do this is delay the prerequisites until after the variable is set. If you really, really don't want to do that you can use secondary expansion:
.SECONDEXPANSION:
stuff.txt: $$(LIST)
echo $^
Using eval on ASSIGN_LIST will only set LIST to the 1st file (because of LIST = $1). As a result, stuff.txt only depends on aaa.
As an alternative, add the dependency after setting the variable:
define VERY_LONG_LIST_OF_OBJECTS
aaa \
... \
etc
endef
stuff: $(VERY_LONG_LIST_OF_OBJECTS)

Makefile - Generate same file differently depending on the target

I know makefile won't allow using a target specific variable as a target prerequisite.
My question is slightly different : is there a way to generate the same file differently depending on what target was called ?
For instance, let's say I want to be able to generate file_to_generate using two different methods that I call using make example_target_1 or make example_target_2
As an example, the following code gives 2 different recipes for the same file :
example_target_1 : file_to_generate-receipe1
example_target_2 : file_to_generate-receipe2
file_to_generate-receipe1:
/* some shell code here that end up generating file_to_generate */
file_to_generate-receipe2:
/* some different shell code here that also generates file_to_generate*/
issuing make example_target_1 will generate the file using one recipe while issuing make example_target_2 will do the same using the other recipe.
The issue using this is both example_target_1 and example_target_2 are done without checking if file_to_generate is up-to-date as the name of the target isn't really a file.
Is their a way to get the same behavior and still check if the file is up-to-date ?
One way that it can be achieved may be with use of target-specific variables, like so:
$ cat Makefile
target1 target2: file_to_generate
cat $<
target1: RECIPE=recipe1
target2: RECIPE=recipe2
file_to_generate:
$(if $(filter recipe1,$(RECIPE)),$(recipe1))
$(if $(filter recipe2,$(RECIPE)),$(recipe2))
define recipe1
echo recipe1
echo foo > $#
endef
define recipe2
echo recipe2
echo bar > $#
endef
Even though it works, I would strongly advise against such design. Generating a file in a non-deterministic way may easily lead to non-trivial errors. For example, using this approach you will generate a file and it will be checked if it's up to date, but there is no way for make to guess whether it was generated with recipe1 or recipe2. Therefore the next time you call a different target, the file will not be regenerated (since it already exists), even though the recipe has changed:
$ make target1
echo recipe1
recipe1
echo foo > file_to_generate
cat file_to_generate
foo
$ make target2
cat file_to_generate
foo
When called with target2 first, the file will have different contents, which will be reused in target1 as well:
$ rm file_to_generate
$ make target2
echo recipe2
recipe2
echo bar > file_to_generate
cat file_to_generate
bar
$ make target1
cat file_to_generate
bar
This may or may not be desirable, you need to be aware of such behavior.

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 $#

Makefile match wildcard prerequisites for a rule

I want to pass some recipes to a makefile, like comp1.mt comp2.mt comp3.mt and have the makefile agregate them into a single list of MTs (it could also be mt.compN) to run them in bulk.
%.mt:
#echo $* >> list_of_mts.txt
mt: %.mt
#cat list_of_mts.txt
I want to pass these recipes in any order to the makefile so the specific case of calling make comp1.mt comp2.mt mt is not desirable.
I do not understand why you need this implemented in makefile, but here you are:
MT_TARGETS:=$(filter %.mt,$(MAKECMDGOALS))
.PHONY: $(MT_TARGETS)
$(MT_TARGETS):
#echo $# | sed -e "s:.mt$$::" >> list_of_mts.txt
mt: $(MT_TARGETS)
#cat list_of_mts.txt
Testing:
$ make mt comp1.mt comp2.mt comp3.mt
comp1
comp2
comp3
make: `comp1.mt' is up to date.
make: `comp2.mt' is up to date.
make: `comp3.mt' is up to date.
This is using special variable MAKECMDGOALS.
Note: list_of_mts.txt will grow endlessly...
Note2: writing to list_of_mts.txt is unsafe in parallel execution (the list_of_mts.txt file may get corrupted).

Why does my Makefile pattern rule run its recipe multiple times?

As per the gnu make documentation, a pattern rule's "...recipe is executed only once to make all the targets." However, I have the following Makefile
.PHONY: entrypoint
entrypoint: test_1.cpp test_2.cpp
test_%.cpp:
echo $#
And running make produces:
echo test_1.cpp
test_1.cpp
echo test_2.cpp
test_2.cpp
I'm new to make, and I'm probably misunderstanding something, but the documentation seems misleading if clear.
$ make -v
GNU Make 4.0
...
You're misreading the documentation. It means, the recipe is run only one time assuming that all the target patterns in that rule will be created.
Since you have only one target pattern in your rule (test_%.cpp`) make knows that each time it runs that recipe it will create one output file matching that pattern. To create different targets that match that pattern it will run multiple instances of the recipe.
If you had a rule like this:
%.x %.y %.z :
dothings
then make would expect that a single invocation of the recipe dothings would create all the targets matching this pattern (e.g., foo.x, foo.y, and foo.z).
Contrast this with an explicit rule like this:
foo.x foo.y foo.z :
dothings
Make here treats this exactly as if you'd written this:
foo.x :
dothings
foo.y :
dothings
foo.z :
dothings
That is, to build all three of these targets it would run the recipe three times.
There's no way to tell make "please run this recipe one time and it will produce every single target that could possibly match the pattern foo_%.cpp".
The following functions construct a dynamic list of dependencies of your multiple-target where the non-existent files are named last. This is more or less the method named "Another attempt" in the link you gave, except that it doesn't trip over missing files and is able to make a missing file by giving it as target on the command line. What it does not: execute the multitarget recipe if one of the multitargets is out of date relative to the others, but I think this is more of wanted side effect than a problem. The only drawback is the syntactic ugliness as you have to write it into an eval expression which forces you to quote all variables in the recipe which shall be evaluated at execution time.
define newline :=
endef
list2rules = $(firstword $1) $(if $(word 2,$1),: $(word 2,$1)$(newline)$(call list2rules,$(wordlist 2,1000,$1)))
multitarget = $(call list2rules,$(wildcard $1) $(filter-out $(wildcard $1),$1))
.PHONY: all
targets = test1 test2 footest3
#$(info $(call multitarget,$(targets)))
all: somefile
somefile: $(targets)
touch somefile
# here we generate the dependency list on the spot. Only one recipe to update all targets.
$(eval $(call multitarget,\
$(targets)) : ; \
touch $(targets) \
)

Resources