Allow space in target of GCC makefile - gcc

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

Related

Use of eval in a target recipe

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.

Specifying wild card patterns when invoking make targets

Suppose I define:
# makefile
.FORCE:
foo-bar: .FORCE
#echo "$#"
foo-biz: .FORCE
#echo "$#"
.PHONY: foo-bar foo-biz
And I write on the command line:
$: make foo-bar foo-biz
Then is there a way for me to, somehow, write:
$: make foo-*
for example?
This works but it only supports the one single % wildcard of native make pattern matching. Moreover I didn't analyze its impact on pattern rules etc.
PATTERN_GOALS := $(foreach g,$(MAKECMDGOALS),$(if $(findstring %,$(g)),$(g)))
force-on-pattern = $$(if $$(filter $(PATTERN_GOALS),$$#),$$(eval FORCE+=$$#))
# make FORCE a simple expanded variable
FORCE :=
.SECONDEXPANSION:
foo: $(force-on-pattern)
echo $#
fooa: $(force-on-pattern)
echo $#
fooab: $(force-on-pattern)
echo $#
foob: $(force-on-pattern)
echo $#
$(PATTERN_GOALS): $$(FORCE) ;
Test:
$ make foo%
fooab
fooa
foob
foo
$ make fooa%
fooab
fooa
$ make fooab%
fooab
$ make fooab
echo fooab
fooab
$ make foo%b
fooab
foob
You can have a look at the GNUmake table toolkit and specifically the glob-match function for a make-native glob matcher but this is, as #Beta noticed, the realm of 'fearsome beast' make programming.
Here's an idea. Since globbing happens in the shell before the make program even starts, you could have files in the repository named like the rules you want globbable:
touch foo-bar foo-biz
make foo-*
# executes 'make foo-bar foo-biz'
This works with real files too (not just .FORCE or PHONY), but only if the files already exist.

How to check a string whether contain a substring in Makefile commands

I want to check whether a string variable contains a specified substring in the Makefile. The purpose is to clean the sub folders.
I used the below code, but it did not work.
SERVICES_LIST = A_Service B_Service C_Service #example
SPECIFIC_SERVICE_LIST = A_Service B_Service
clean:
#list='$(SERVICES_LIST)';for subdir in $$list;do \
echo "clean in $$subdir";\
if [[ "*$$subdir*" == "$(SPECIFIC_SERVICE_LIST)" ]];then\
make $$subdir clean;\
fi;\
done;\
This hasn't much to do with make, because substantially all the logic involved is expressed in the shell language. In particular, you seem to be assuming bash.
The problem is here:
if [[ "*$$subdir*" == "$(SPECIFIC_SERVICE_LIST)" ]];then\
You seem to by trying to match (make's expansion of) "$(SPECIFIC_SERVICE_LIST)" against a glob pattern formed as (make's expansion of) "*$$subdir*". But the left-hand side is quoted, so it is not interpreted as a pattern, and the == operator performs (exact) string matching, not pattern matching.
One of the main ways to apply such pattern-matching tests in the shell language is with a case construct, because the selection expressions used with it are always interpreted as globs. That might look like so in your makefile:
case "$(SPECIFIC_SERVICE_LIST)" in *$$subdir*) make $$subdir clean ;; esac
But the whole thing seems pretty non-idiomatic. Generally speaking, a makefile is tuned to the project. Even if it is dynamically generated in part or in whole, it is reasonable and appropriate to design your build system so that the clean target can do something more like this:
clean:
for subdir in $(SPECIFIC_SERVICE_LIST); do make -C $$subdir clean; done
... or maybe like this:
clean: clean_services
...
clean_services:
for subdir in $(SPECIFIC_SERVICE_LIST); do make -C $$subdir clean; done
I would make it more make way by defining a target for cleaning up any supported service and then call all required clean targets as a prerequisite to clean. This has additional advantage to make clean in parallel when running with -j option as opposed to strictly sequential shell loop.
$ cat Makefile
SERVICES_LIST = A_Service B_Service C_Service #example
SPECIFIC_SERVICE_LIST = A_Service B_Service
.PHONY: $(addsuffix -clean, $(SERVICES_LIST))
$(addsuffix -clean, $(SERVICES_LIST)): %-clean:
$(MAKE) -C $* clean
.PHONY: clean
clean: $(addsuffix -clean, $(SPECIFIC_SERVICE_LIST))

Using bash variables in Makefile

I want to use the bash timing variables in my makefile
for example in my terminal I can do this and it works
MY_TIME=$SECONDS
echo $MY_TIME
but when I write this on my makefile it does not work
how can I use these two lines in my make file?
this is what I'm doing
.PHONY: myProg
myProg:
MY_TIME=$SECONDS
echo $MY_TIME
After Etan Reisner' answer
This is what I have now
.PHONY: myProg
myProg:
MY_TIME= date; echo $MY_TIME
but the result of my echo is an empty line, it does not look like it is storing the date
the dollar sign ($MY_TIME) refers to make variables, which are not the same as bash variables.
To access a bash variable you must escape the dollar using the double dollar notation ($$MY_TIME).
.PHONY: myProg
myProg:
MY_TIME=$$SECONDS ; echo $$MY_TIME
As already mentioned in Etan answer you can't split the code into multiple lines (unless you are using the backslash) since each command executes in a different subshell, making variables inaccessible to other lines.
In the following example the value of SECONDS will be always 0, since it get reset by the spawn of the shell for the second line.
.PHONY: myProg
myProg: # WRONG
MY_TIME=$$SECONDS
echo $$MY_TIME
By default make uses /bin/sh as the shell which executes recipe lines.
Presumably /bin/sh doesn't support the SECONDS variable.
You can tell make to use a different shell by assigning a value to the SHELL variable (i.e. SHELL := /bin/bash).
Doing that will make SECONDS available but will still not allow you to carry a variable value between recipe lines as each recipe line is run in its own shell.
So to do what you want you would need to write both of those lines on one line or continue the line over the newline.
.PHONY: myProg
myProg:
MY_TIME=$SECONDS; echo $MY_TIME
or
.PHONY: myProg
myProg:
MY_TIME=$SECONDS; \
echo $MY_TIME
That being said you would almost certainly be better off not doing this and instead using something like date invoked at the start/end of the recipe or time invoked on the command to be timed directly instead.
.PHONY: myProg
myProg:
date
# Do something
date
or
.PHONY: myProg
myProg:
time some_command
PROGRAM_NAME = myLittleProgram
...
$(PROGRAM_NAME) : myLittleProgram.o
I know the above works, as it is in my own makefile (program names and object names changed to protect the innocent).
"Variable references can be used in any context: targets, dependencies, commands, most directives, and new variable values. Here is an example of a common case, where a variable holds the names of all the object files in a program:"
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
http://web.mit.edu/gnu/doc/html/make_6.html

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