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.
Related
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).
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) \
)
This is easily a duplicate of this question, but it has not been answered, for what I can see.
Here is my goal: to be able to print the prerequisites to a target.
I have some kind of a solution but it feels like a hack to me.
Say the target is all, and it only has prerequisites, in a file named makefile-1:
all: foo
all: bar baz
I can use another makefile named makefile-2:
all:
ifeq ($(SHOW),yes)
echo $^
else
cat $^
endif
This kind of gives me what I need, when invoked properly:
$ make -f makefile-1 -f makefile-2
cat foo bar baz
$ make -s SHOW=yes -f makefile-1 -f makefile-2
foo bar baz
But I am not sure what to do if the prerequisite actually has a recipe in the original makefile, or if there is a better/cleaner way.
An easier way to show the prerequisites to a target without building the target would probably be to use the -p option (as suggested here) and -q options together, as suggested on the GNU-Make manual page (long name for the option is --print-data-base):
make -qp | grep '^all:'
This will print the database and select only the line which has the target all and its prerequisites. If there is a rule for that target, it would be at the end of the same paragraph (if I am understanding the format of the output correctly).
If multiple makefiles are used, one can specify all of them with the -f option:
make -f makefile-1 -f makefile-2 -qp
This will of course collect prerequisites for the same target from all makefiles, but the target cannot have a rule specified in more than one file.
I've been learning make from the software carpentry tutorial (make patterns) and it says that we can use $^ to get the dependency list for our rule but have extra dependencies for our target by writing extra empty rules. For example
all:
touch f1.txt f2.txt a.txt
result: a.txt
result: f*.txt
#echo $^
I thought that this would print f1.txt f2.txt but instead I see f1.txt f2.txt a.txt. Am I missing something or is the tutorial wrong?
Clearly, I should have been explicit, in the example I run make to create the files for the test, then I run make result to get the result shown.
Not sure I understand your problem and what you would like to do but if you type just make it is like if you were typing make all because all is the first target and thus the default goal. It should create the 3 files (or update their last modification date if they already exist), echo the recipe and you should see:
touch f1.txt f2.txt a.txt
If you then type make result you should see the result of #echo $^, that is the list of all dependencies of result:
f1.txt f2.txt a.txt
It is absolutely normal as you declared them all as dependencies of result.
Finally, if you type make result before make or make all and if a.txt does not exist or if there is no file matching f*.txt, you will get an error because make needs a.txt and at least one file matching f*.txt to make result and it does not know how to make them.
I cannot imagine a use case where the behaviour you expected ($^ being expanded as dependencies of the current rule only) would be useful, but if really you need this feature you can use the rather obscure Double-Colon Rules (DCR):
all:
touch f1.txt f2.txt a.txt
result:: a.txt
#echo $^
result:: f*.txt
#echo $^
Note that if there is a DCR for a target, all rules of this target must also be DCR. Note also that all DCR must have a recipe. A DCR recipe is applied if one of the dependencies of this rule is newer than the target. If several DCR apply, they are executed in order of appearance in the Makefile. And finally, what you were interested in: the $^ automatic variable logically expands as the dependencies of the particular DCR.
No, $^ refers to the prerequisites for the current target, not the current rule.
Also, the results you are reporting are not correct; for make result I get make: *** No rule to make target `f*.txt', needed by `result'. Stop. like I very much expected.
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.