Given this makefile:
foo_*.txt: foo_%.txt:
echo "Foo number $*" > $#
I was hoping to be able to run, say, "make foo_22.txt" to create a file called "foo_22.txt". Evidently, that is not the case: the result is the error "No rule to make target `foo_22.txt'." If the file foo_22.txt does exist, I can get close to the desired behavior by running "make -B foo_22.txt". How can I get to the desired behavior - namely, being able to run "make foo_22.txt" when the file "foo_22.txt" does not exist, resulting in creation of the file with the expected contents?
foo_%.txt:
echo "Foo number $*" > $#
Related
I am trying to simplify my makefile by allowing a target with a filter (%) to capture when I want other targets to have expanded actions
An example of this would be
1-check:
echo "1"
2-check:
echo "2"
run-check:
1-check-all 2-check-all
%-all:
echo $*
This is able to print the 2 targets names as 1-check and 2-check and I was expecting that if I remove the echo in %-all it would run the captured target
What I would expect when I call 1-check-all is that the modified %-all - seen below - it would invoke the method
%-all:
$*
Would have "1-check" as the value of $* and it would then run that target and I would see "echo 1" executed.
I am new to makefiles and haven't been able to find an example doing something similar to this.
A couple of problems. This:
%-all:
$*
does not appear in your makefile. The nearest thing to it is this:
%-all:
echo $*
But neither of these will invoke another rule. To get the effect I think you want, you must list % as a prerequisite of the rule:
%-all: %
echo $*
I want to check that an environment variable is set before executing some code in a Makefile. If it's not set I want to throw an error with a simple error message:
run:
[ -z "$(MY_APP)" ] && echo "MY_APP must be set" && exit 1
echo "MY_APP is set. Yay!"
echo "Let's continue on with the command..."
When MY_APP is not set I get the following error, which is desired:
[ -z "" ] && echo "MY_APP must be set" && exit 1
MY_APP must be set
make: *** [run] Error 1
However, when MY_APP is set I get the following error:
[ -z "EXAMPLE_NAME" ] && echo "MY_APP must be set" && exit 1
make: *** [run] Error 1
Any idea what I'm doing wrong? And is there a better way to do this?
Recall that the && condition require that all conditions must be TRUE to pass. Since the first condition fail, the whole command will return a status of 1 (-> false), effectively stopping the make
You can use the following, so that the test will fail only when MY_APP is missing.
Note that I'm using false instead of exit 1. Also better to use "${MY_APP}", which make it easier to copy/paste from Make to shell prompt/script.
run:
{ [ -z "$(MY_APP)" ] && echo "MY_APP must be set" && false } || true
...
# Or just if-Then-Else
if [ -z "${MY_APP}" ] ; then echo "MY_APP must be set" ; false ; fi
...
You can test environment variables with Makefile conditional syntax, like this:
sometarget:
ifndef MY_APP
#echo "MY_APP environment variable missing"
exit 1
endif
somecommand to_run_if_my_app_is_set
Note that ifndef/ifdef operate on the name of the variable, not the variable itself.
It seems that you are trying to use a Makefile to run commands which are not building targets (the target name run is a giveaway). You already got bitten by one of Makefile and shells caveats. Makefile caveat: exit status is inspected after each line and if not zero abort immediately. Shell caveat: the test command ([) returns a non zero exit status so the entire line returns non zero.
The rule of thumb is: a recipe of a rule should create a filename named like the target of the rule.
Here is a rule (to clarify the terms):
target:
recipe command lines
should create file named target
There are some exceptions to this rule of thumb. Most notably make clean and make install. Both typically do not create files named clean or install. One can argue that make run maybe also be an exception to this rule of thumb.
If your run is as simple as a typical clean then I might agree about making an exception. But usually commands are run with command line arguments. Before long you will want make run accept arguments. And making make accept custom command line arguments is not fun at all.
You tried to manipulate the behaviour using environment variables which is somewhat less problematic than command line arguments. But still problematic enough to make you trip over a caveat.
My suggestion for a fix:
Put complex recipes in a shell script. There you have all the power and flexibility of a shell script without the awkwardness of makefiles. For example as explained here: Basic if else statement in Makefile
In case of a typical run target write a wrapper shell script around the makefile which lets the makefile rebuild the target and then run the target. For exampe as explained here: Passing arguments to "make run"
You can conditionally exit the Makefile using error control function, at least in the GNU version.
This snippet is a helpful condition to put into the head of the Makefile. It exits with a message of help, if make was not called from within the directory of the Makefile.
MAKEFILE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ifneq (${MAKEFILE_DIR}, $(shell pwd))
INVALID_LOCATION:=`make` must be called from within ${MAKEFILE_DIR} (or with option -C ${MAKEFILE_DIR})
$(error ERROR: $(INVALID_LOCATION))
endif
See: https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
Useful in case your paths are relative to the Makefile and you don't want them to prefix with a base.
I have a number of makefiles that build and run tests. I would like to create a script that makes each one and notes whether the tests passed or failed. Though I can determine test status within each make file, I am having trouble finding a way to communicate that status to the caller of the make command.
My first thought is to somehow affect the return value of the make command, though this does not seem possible. Can I do this? Is there some other form of communication I can use to express the test status to the bash script that will be calling make? Perhaps by using environment variables?
Thanks
Edit: It seems that I cannot set the return code for make, so for the time being I will have to make the tests, run them in the calling script instead of the makefile, note the results, and then manually run a make clean. I appreciate everyone's assistance.
Make will only return one of the following according to the source
#define MAKE_SUCCESS 0
#define MAKE_TROUBLE 1
#define MAKE_FAILURE 2
MAKE_SUCCESS and MAKE_FAILURE should be self-explanatory; MAKE_TROUBLE is only returned when running make with the -q option.
That's pretty much all you get from make, there doesn't seem to be any way to set the return code.
The default behavior of make is to return failure and abandon any remaining targets if something failed.
for directory in */; do
if ( cd "$directory" && make ); then
echo "$0: Make in $directory succeeded" >&2
else
echo "$0: Make in $directory failed" >&2
fi
done
Simply ensure each test leaves its result in a file unique to that test. Least friction will be to create test.pass if thes test passes, otherwise create test.fail. At the end of the test run gather up all the files and generate a report.
This scheme has two advantages that I can see:
You can run the tests in parallel (You do us the -jn flag, don't you? (hint: it's the whole point of make))
You can use the result files to record whether the test needs to be re-run (standard culling of work (hint: this is nearly the whole point of make))
Assuming the tests are called test-blah where blah is any string, and that you have a list of tests in ${tests} (after all, you have just built them, so it's not an unreasonable assumption).
A sketch:
fail = ${#:%.pass=%.fail}
test-passes := $(addsuffix .pass,${tests})
${test-passes}: test-%.pass: test-%
rm -f ${fail}
touch $#
$* || mv $# ${fail}
.PHONY: all
all: ${test-passes}
all:
# Count the .pass files, and the .fail files
echo '$(words $(wildcard *.pass)) passes'
echo '$(words $(wildcard *.fail)) failures'
In more detail:
test-passes := $(addsuffix .pass,${tests})
If ${tests} contains test-1 test-2 (say), then ${test-passes} will be test-1.pass test-2.pass
${test-passes}: test-%.pass: test-%
You've just gotta love static pattern rules.
This says that the file test-1.pass depends on the file test-1. Similarly for test-2.pass.
If test-1.pass does not exist, or is older than the executable test-1, then make will run the recipe.
rm -f ${fail}
${fail} expands to the target with pass replaced by fail, or test-1.fail in this case. The -f ensures the rm returns no error in the case that the file does not exist.
touch $# — create the .pass file
$< || mv $# ${fail}
Here we run the executable
If it returns success, our work is finished
If it fails, the output file is deleted, and test-1.fail is put in its place
Either way, make sees no error
.PHONY: all — The all target is symbolic and is not a file
all: ${test-passes}
Before we run the recipe for all, we build and run all the tests
echo '$(words $(wildcard *.pass)) passes'
Before passing the text to the shell, make expands $(wildcard) into a list of pass files, and then counts the files with $(words). The shell gets the command echo 4 passes (say)
You run this with
$ make -j9 all
Make will keep 9 jobs running at once — lovely if you have 8 CPUs.
I am trying to write a very simple Makefile which I can't make it to work as I expect.
target_a : file.txt
echo "this is target_a"
touch 0_$#
target_b : 0_target_a
echo "executing target_b"
touch 0_$#
Whenever I run make taget_b it gives out an error:
make: *** No rule to make target '0_target_a', needed by 'target_b'. Stop.
I can just change touch 0_$# to touch $#. But I really want a solution for touch 0_$# (a free choice of filename).
From GNU-man page ftp://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_2.html
A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out
I want to know how to build the Make dependency when the target name is an:
Action
I am afraid you cannot directly do just that and you'd have to help yourself with an intermediate target that makes the connection between target and its output clear (and hence gives make a chance to decide when it does or does not need to be remade:
0_target_a: file.txt
echo "this is target_a"
touch $#
target_b: 0_target_a
echo "executing target_b"
touch 0_$#
I.e. defining rule for target 0_target_a and updating touch accordingly will give you the behavior you wanted as make now understand the rule the connection between target and file 0_target_a and know when it does not need to be remade as a dependency of target_b. Now if you still want to also have a standalone target_a that would generate file 0_target_a, you can define it as follows:
target_a: 0_target_a
Since we know this target is not really creating a file itself, we can spare make a little effort looking for its result (target_a) and also prevent clashes should such file be created by declaring it as phony.
As a matter of fact you may want to give your target_b the same treatment, as otherwise (again make does not have enough information to understand the relation between target_b and 0_target_b) make target_b is always remade even though the file has already been generated.
The whole make file would look like this:
.PHONY: target_a target_b
target_a: 0_target_a
target_b: 0_target_b
0_target_a: file.txt
echo "this is target_a"
touch $#
0_target_b: 0_target_a
echo "executing target_b"
touch $#
If that is a reoccurring theme throughout the file, you could also express the relation on second and third line by defining a static pattern rule:
target_a target_b: %: 0_%
This defines a rule that a any target (first '%' without anything else) has a prerequisite of 0_ prefix followed by that target name (0_%, 0_ plus stem which in this case is a target name in its entirety as matched by previous %). and makes this rule applicable to targets target_a and target_a. This rule has no recipe and hence only describe target/prerequisite relation between the two.
In other words it means the same thing as the full example lines 2 and 3 combined.
The dependency of target_b should be a valid target itself, or a file which already exists
target_a : file.txt
echo "this is target_a"
touch 0_$#
target_b : target_a
echo "executing target_b"
touch 0_$#
if you want to "alias" creation of 0_target_a file to the target_a "action" you can add intermediate rule:
0_target_a : file.txt
echo "creating 0_target_a"
touch 0_$#
target_a : 0_target_a
target_b : target_a
echo "executing target_b"
touch 0_$#
Here is two targets in my Makefile.
.SECONDARY:
exp-%.ans:
echo $* > eval/$#
%.scores: %.ans
cat eval/$< > eval/$#
When I write make -n exp-40.scores output would be:
echo 40 > eval/exp-40.ans
cat eval/exp-40.ans > eval/exp-40.scores
which is good except one thing. It does not aware of the dependency is already hold. If I create eval/exp-40.scores (first time) then I expect that make will say it is already in there if I run the same command. So I try to change my Makefile like this:
.SECONDARY:
exp-%.ans:
echo $* > eval/$#
%.scores: eval/%.ans
cat eval/$< > $#
When I write make -n exp-40.scores again and output would be:
echo eval/40 > eval/eval/exp-40.ans
cat eval/eval/exp-40.ans > exp-40.scores
which is completely wrong because my parameter should be 40 not eval/40.
How can I achieve the best of the these two worlds? Namely, I want to create *.scores in eval/ folder. I also want make to check whether file is already exist or not. Then make should proceed according to that file existence.
Thanks.
One of the core rules of make is that you need to build the target your rule told make that you'd build. A rule like foo : bar tells make that if it runs that recipe, the recipe will create or update a file named foo. If the recipe creates or updates a file named biz/foo instead, then your makefile is wrong and things will not work.
Make always puts the target it expects the recipe to create into the $# automatic variable. Your recipe should create or update $# and exactly $#. Not something else.
So in your case, you need to write:
eval/exp-%.ans:
echo $* > $#
eval/%.scores: eval/%.ans
cat $< > $#
If you want to be able to run make exp-40.scores and have it actually create eval/exp-40.scores, then you can add a rule like this:
%.scores: eval/%.scores ; #:
(you have to provide some kind of recipe for pattern rules, they cannot have an empty recipe; the one above does nothing).