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).
Related
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)
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.
When I execute Make, I'd like to know what shell commands / recipes are executed (and perhaps which line in which Makefile invoked these). Is there a way of doing this (print onto the stdout or write to a file) without modifying the Makefiles?
The commands will be echoed to stdout by default, unless you disabled that by prefixing them with #.
A few options you can pass to make:
-n: echos commands that would be run, but without running them. This will also echo commands that are prefixed with #.
-d: print debug output. This will output the process that make goes through in attempting to find rules that match the targets that it's trying to make. There will be a lot of useless information in here, as it will try many default build rules (like how to build .o files from .c).
There are a few ways to cut down on the noise:
-r: disable implicit rules. If you don't depend on any of the default rules , this can be used in conjunction with -d to just print the parts you (mostly) care about
--debug=b: basic debug mode. prints what it's trying to make, but not any information about implicit rules.
None of these print the line numbers of the commands, but make -n --debug=b will print both the targets being built and the commands being run, so it's almost as good. Example below.
$ cat makefile:
c: a b
cat $^ > $#
a:
echo 'foo' > $#
b: a
cat $^ > $#
echo 'bar' >> $#
$ make -n --debug=b:
Reading makefiles...
Updating goal targets....
File 'a' does not exist.
Must remake target 'a'.
echo 'foo' > a
Successfully remade target file 'a'.
File 'b' does not exist.
Must remake target 'b'.
cat a > b
echo 'bar' >> b
Successfully remade target file 'b'.
Prerequisite 'a' is newer than target 'c'.
Prerequisite 'b' is newer than target 'c'.
Must remake target 'c'.
cat a b > c
Successfully remade target file 'c'.
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) \
)
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.