Use of eval in a target recipe - makefile

In a Makefile I'm writing I had an interest in cleaning up some of the CC prints and centralizing some of the build preparations (like creating directories in the build tree). I figured macros would be a good fit for this task. This is effectively what im trying to do, used all over various Makefiles:
define func
#mkdir -p $$(dir $(1))
#printf "%-5s $(2)\n" $(3)
endef
test:
#echo Run
$(eval $(call func,a,b,c))
My thought was that after first expansion I'd get something like (less any tabs maybe, I'm not exactly sure how the expansion works within eval):
test:
#echo Run
$(eval #mkdir -p $(dir a)\n#printf "%-5s b\n" c
and of course finally the commands would be executed. However, what I get is this:
# make
Makefile:7: *** recipe commences before first target. Stop.
I changed eval to info and got this:
#mkdir -p $(dir a)
#printf "%-5s b\n" c
Run
So I thought maybe my explicit tabs in the macro definition were causing trouble, so I removed them and tried again:
# make
Makefile:7: *** missing separator. Stop.
So it still does not quite work. If it is indeed possible at all, it seems some function of indentions in the macro, or maybe I'm defining the macros incorrectly. I thought perhaps the two commands in the macro was causing trouble (since the complaint is regarding a separator), but reducing the macro to a single line did not help either.

You don't want eval here. Eval is used to evaluate makefile syntax. That is, the thing you're evaluation has to be a valid, complete makefile. You can see that what info prints is not a valid makefile. If you put that into a file and ran make -f <file>, you'd get a syntax error.
You are just trying to expand a variable for shell syntax. Just remove the eval.

Related

How to avoid overriding targets, or suppress the warning

I want to create a makefile to be included in others, kind of as a "library", let's call it library.mak:
TARGET_FILES = a.txt b.txt c.txt d.txt
SRC_FOLDER = somewhere/else
.PHONY: all
all:
for target in ${TARGET_FILES} ; do \
echo -e "\nMaking $${target}:" ;\
${MAKE} ${MFLAGS} --no-print-directory $${target} ;\
done
.PHONY: ${TARGET_FILES}
${TARGET_FILES}:
cp ${SRC_FOLDER}/$# $#
The two rules are there to make all, as well as one specific target, respectively; using a default rule (which is the purpose of library.mak).
In my "user makefile" called Makefile, I want to then do this:
include library.mak
# special handling of c.txt
c.txt:
grep -v 'all except this' ${SRC_FOLDER}/$# > $#
As you can see, the user wants to be able to override the behaviour for some special cases. While this works, it always greets the user with the dreaded warning: overriding recipe for target and warning: ignoring old commands for target messages, even though the behaviour is as intended.
So here's the question: Can this be done in a different way that avoids these warnings, or is there a means to suppress them?
The warning says you overwrite a recipe for an explicit rule. And this is really wrong. It makes sense to use a pattern rule instead, like: %.txt: ${SRC_FOLDER}/%.txt. Then it's ok to have some explicit rule (c.txt: ${SRC_FOLDER}/c.txt) overwriting a recipe. Of course, it's not 100% the same, but, I think that should not be a problem. Anyway, putting an explicit rule into a reusable file is a crime.
Next, you constantly use "phonies" and rules w/o prerequisites even when you definitely should have them. This is bad. You're trying to make "make" work as a non-branching shell script. That's not only inefficient but also is a misuse. Basically, make can be viewed as "shell extension" capable of "branching on file timestamps in a sophisticated way". If you don't need it, don't use make at all.
Next, I see absolutely no reason to go into recursive make. Whatever, you're going to do, I believe, you can do without it. And even if you really need recursion, then write simply $(MAKE). $(MFLAGS) shoud not be used anymore (read this).
P.S. There's yet another (more flexible) option to define parameterized variables (macros). For example:
define nl :=
endef
define myrule.text
$1: $2/$1
#cp $$< $$#
endef
myrule = $(eval $(foreach foo$0,$1,$(call myrule.text,$(foo$0),$2)$(nl)))
...
# user makefile
# for everything except c.txt call predefined macro
$(call myrule,$(filter-out c.txt,$(TARGET_FILES)),$(SRC_FOLDER))
# for c.txt only
c.txt: $(SRC_FOLDER)/c.txt
grep -v 'all except this' $< >$#
But as you see, it's a bit of a "cryptic" stuff, and I don't feel like recommending it for a beginner.

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) \
)

Makefile secondary expansion: execution of escaped function?

Consider the following makefile:
.SECONDEXPANSION:
%.result: jobs/% $$(call get-job,$$<)
echo $^
define get-job
$(shell head -n 1 $(1))
$(shell tail -n +2 $(1))
endef
The idea is that each file under jobs/ contains a list of filenames, which should be appended to the prerequisite list.
However, if I want to create xyz.result from an existing file jobs/xyz, I get the following error message:
$ make -n xyz.result
head: cannot open 'xyz.result' for reading: No such file or directory
tail: cannot open 'xyz.result' for reading: No such file or directory
head: cannot open 'xyz.result' for reading: No such file or directory
tail: cannot open 'xyz.result' for reading: No such file or directory
make: *** No rule to make target 'xyz.result'. Stop.
I am aware that $$< isn't set to what I want, as it reflects the prerequisite list of any previous rule.
What I don't understand is the following:
In my understanding, $$< should evaluate to the empty string (as shown in the example in the official doc, under the second sub-heading). However, it seems to be expanded to the value of the target here (xyz.result). Why is that?
It seems that the get-job function is called twice (head and tail both bark twice). I understand that the prerequisite list is expanded twice. But in the first run, the call is still escaped, so this isn't what I expect.
(Maybe the whole approach is flawed, and I shouldn't be (ab)using Makefile for this kind of task in the first place.)
I don't know why $$< expands that way.
You can make your configuration work by using $$* instead, though:
%.result: jobs/% $$(strip $$(call get-job,jobs/$$*))
echo $^
You need to add the strip call so that the newlines embedded in the results of the call will be turned into spaces.

Allow space in target of GCC makefile

Is there a way to get spaces inside target names working when using make.exe? It seems to be impossible if this really ancient bug report is correct:
http://savannah.gnu.org/bugs/?712
For reference, the big problem is that pieces of makefile commands like:
"foo bar baz": $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
... seem to get treated as three separate commands: one to build "foo (note the included "), one to build bar, and lastly, one to build baz" (again, including "). This is because make.exe seems to be using space as a delimiter.
However, it's reasonable to assume that one might want to build "Hello World.exe" for example. This doesn't seem to be possible. Double quotes don't work, and neither does escaping the separate words (I've read that somewhere, don't remember the link):
"foo\\ bar\\ baz": $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
Is there any other way to fix this? The official manual only confirms the tokenize-by-spaces stuff, but doesn't provide a way to use space for any other purpose:
http://www.gnu.org/software/make/manual/make.html#Rule-Syntax
Edit: as suggested, I've tried single slashes too, but these have the exact same effect as double slashes. Make complains it can't find rules for the first word:
mingw32-make.exe: *** No rule to make target `foo', needed by `all'. Stop.
The executable "foo bar baz.exe" is correctly produced though, but linking is done each time per word.
as Matthias said, it's a matter of "\ ", but of double quote too. Here is how I succeded into this :
EXECUTABLE=foo\ bar\ baz
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(OBJECTS) -o "$#" $(LDFLAGS)
Note the double quotes around the $#
It seems to me that when make reach the target "$(EXECUTABLE)" it expands the "\ ", so the command line becomes
gcc file.o -o foo bar baz -LFlags
which is not what you want, you want double quotes around the name of the file.
Now you are on windows and I don't remember how it deals with spaces in names, so as Matthias said, first check how "cmd.exe" deals with spaces (except by surrounding name with double quotes...)
Instead of double backslash use single ones. The following Makefile works (at least for gnu make):
goal: foo\ bar
foo\ bar:
gcc -o "foo bar" "foo bar.c"
Maybe easier to consider a simpler Makefile, which ignores the dependencies in the standard targetRule: dependencies invocation. The OP is for Windows, and the below is done on Linux in the bash shell; but it should probably be applicable to Windows using the bash shell via cygwin, I guess. I have the following in my Makefile (note, there should be a TAB before the echo commands, which SO converts to spaces):
testA:
echo "testA"
unrelated:
echo "unrelated"
testA\ clean:
echo "testA clean"
clean:
echo "clean"
If I call make with targets clean or testA in the bash shell terminal, I get the expected results:
$ make testA
echo "testA"
testA
$ make clean
echo "clean"
clean
Now, if I call make testA clean just written as is, the bash shell will split the arguments at spaces, so make will receive two arguments, and will run them separately:
$ make testA clean
echo "testA"
testA
echo "clean"
clean
... but if I wrap the target supplied to make in quotes - or if I escape the space - the shell will understand that it is supposed to be a single argument with a space inside, and will propagate it as such to make, which will proceed to execute what is written as the testA\ clean target rule:
$ make "testA clean"
echo "testA clean"
testA clean
$ make testA\ clean
echo "testA clean"
testA clean
One consequence of this, is that unfortunately you cannot "TAB" at the command line for autocompletion of testA\ clean; if you type make teTAB at the command line, only testA will be autocompleted automatically (and the testA\ clean will not be shown as an autocomplete option).

stop on error when target of makefile rule is a foreach function

I have a makefile that defines several rules where the target is a foreach function.
$(foreach var,$(list), $($(var)_stuff) $($(var)_more_stuff)):
#echo Building $# from $^...
$(CC) $(FLAGS) ...
Is there any way to get make to quit when encountering an error without going through the entire list.
One workaround is to "manually" invoke exit on failure.
For example, assume we have a directory called scripts with a number of shell scripts (with filenames that end with .sh) that we want to execute.
Then a variable declaration like this:
LIST_OF_SCRIPTS ?= $(wildcard scripts/*.sh)
will give us a list of those scripts, and a target like this:
run-all-scripts
#$(foreach scriptfile,$(LIST_OF_SCRIPTS),$(scriptfile);)
will run all of those scripts, but as you note, the foreach loop will keep going whether or not one of the scripts returns an error code. Adding a || exit to the command will force the subcommand to exit on error, which Make will then treat as a failure.
E.g.,
run-all-scripts
#$(foreach scriptfile,$(LIST_OF_SCRIPTS),$(scriptfile) || exit;)
will do what you want (I believe).
Specifically, using your pseudo-code example, I think you want something like this:
$(foreach var,$(list), $($(var)_stuff) $($(var)_more_stuff)):
#echo Building $# from $^...
($(CC) $(FLAGS) ...) || exit
(where all I've changed is wrapping the (CC) $(FLAGS) ... bit in parens and appending || exit to make it fail on error).
The foreach is completely evaluated and substituted before any of the rules are executed. So the behaviour of this should be identical to as if you had hardcoded the rule without using the foreach. In other words, it's not directly relevant to the problem.
There are only a few possible explanations for what you're seeing, mostly described in the manual here:
You are running Make with -k or --keep-going
You are running Make with -i or --ignore-errors
Your targets is defined as prerequisites of the special .IGNORE target
Your recipe starts with a -
Your recipe isn't actually returning a non-zero exit status
Not sure about your example, but maybe problem is in ; - look at Makefile : show and execute:
dirs = $(shell ls)
clean:
$(foreach dir,$(dirs),echo $(dir);)
produce:
$ make clean
echo bin; echo install.sh; echo Makefile; echo README.md; echo utils;
So make check exit code only for last command: echo utils.

Resources