gnu make: filter-out escaped whitespace separated words - makefile

I have the following example makefile:
EXTRA_OVERRIDES := $(filter-out GIT_VERSION=% BUILD_TARGET=% BUILD_FLAGS=%,$(MAKEOVERRIDES))
$(info MAKEOVERRIDES: $(MAKEOVERRIDES))
$(info EXTRA_OVERRIDES: $(EXTRA_OVERRIDES))
all:
#touch /dev/null
The idea is to filter out "known" MAKEOVERRIDES, leaving just the rest.
This works for overrides which consist of a single value:
example: single value known overrides, BUILD_TARGET, GIT_VERSION
$ make EXTRA_FLAG=1 BUILD_TARGET=foo GIT_VERSION=a298bde
MAKEOVERRIDES: GIT_VERSION=a298bde BUILD_TARGET=foo EXTRA_FLAG=1
EXTRA_OVERRIDES: EXTRA_FLAG=1
Where it doesn't work is when passing multiple values in a single override
example: passing multiple values for the BUILD_FLAGS override
$ make BUILD_FLAGS="-Wall -Wextra -Werror" EXTRA_FLAG=1 BUILD_TARGET=foo GIT_VERSION=a298bde
MAKEOVERRIDES: GIT_VERSION=a298bde BUILD_TARGET=foo EXTRA_FLAG=1 BUILD_FLAGS=-Wall\ -Wextra\ -Werror
EXTRA_OVERRIDES: EXTRA_FLAG=1 -Wextra\ -Werror
So even though I pass BUILD_FLAGS as a quoted list, and even though MAKEOVERRIDES shows BUILD_FLAGS having its whitespace escaped, filter-out doesn't respect the escaping.
ie:
BUILD_FLAGS=-Wall\ -Wextra\ -Werror: filter-out BUILD_FLAGS=% matches BUILD_FLAGS=-Wall instead of continuing past the escaped whitespace.
Question:
How can I get filter-out to include the escaped whitespace in its match?

Answering my own question because I've found a work-around - namely to preprocess MAKEOVERRIDES using sed, and then run the filter-out on the result.
I can't help but think there must be a better way to achieve this, using make functionality only, but I guess this will work for my particular use-case
QUOTED_OVERRIDES:= $(shell echo "$(MAKEOVERRIDES)" | sed 's|\\ |\\|g')
EXTRA_OVERRIDES := $(filter-out GIT_VERSION=% BUILD_TARGET=% BUILD_FLAGS=%,$(QUOTED_OVERRIDES))
$(info MAKEOVERRIDES : $(MAKEOVERRIDES))
$(info QUOTED_OVERRIDES: $(QUOTED_OVERRIDES))
$(info EXTRA_OVERRIDES : $(EXTRA_OVERRIDES))
all:
#touch /dev/null
example:
$ make BUILD_FLAGS="-Wall -Wextra -Werror" EXTRA_FLAG=1 BUILD_TARGET=foo
MAKEOVERRIDES : BUILD_TARGET=foo EXTRA_FLAG=1 BUILD_FLAGS=-Wall\ -Wextra\ -Werror
QUOTED_OVERRIDES: BUILD_TARGET=foo EXTRA_FLAG=1 BUILD_FLAGS=-Wall\-Wextra\-Werror
EXTRA_OVERRIDES : EXTRA_FLAG=1

Related

Remove a flag from CFLAGS in FreeBSD makefile

In a GNU makefile, it is possible to use filter-out to remove a flag from CFLAG like this :
CFLAGS:=$(filter-out -flag,$(CFLAGS))
However, I can't make it work with a FreeBSD makefile.
Is filter-out supported by FreeBSD ? Otherwise, what can I do to remove a specific flag from CFLAGS in a makefile ?
Yes, there is filter-out-like feature in FreeBSD's Makefile but with different syntax:
:Npattern
This is identical to `:M', but selects all words which do not match
pattern.
From man make.
Usage example:
CFLAGS= -foo -bar -flag
all:
#echo ${CFLAGS}
#echo ${CFLAGS:N-flag}
The output:
$ make
-foo -bar -flag
-foo -bar

Makefile For Loop Pattern Substitution

In the following makefile for loop, how can I edit the string, that the i variable represents, with a pattern substitution in the middle of the string? In my case, I wish to replace any / character in the string with a _ character.
for i in $(MODULES:%.cpp=%); do \
g++ -c Sources/$$i.cpp -o Build/$$i.o; \
done
For example if MODULES = Directory/File.cpp then the inner line should expand to
g++ -c Sources/Directory/File.cpp -o Build/Directory_File.o
This answer is valid only with GNU make and bash.
Simple bash substitution (${parameter/pattern/string}) in the context of a make recipe (double $):
for i in $(MODULES:%.cpp=%); do \
g++ -c Sources/$$i.cpp -o Build/$${i//\//_}.o; \
done
Warning: this works only if the shell used by make is bash. So, add maybe a:
SHELL := bash
at the beginning of your Makefile.
Explanation:
${i/X/_} expands as the value of variable i in which the first occurrence of X is replaced by _.
${i//X/_} expands as the value of variable i in which all occurrences of X are replaced by _.
In your case X is the / character and it must be escaped (\/): ${i//\//_}.
Note that there is probably a less bash and more make way to do the same. Something like:
SRCS := $(shell find Sources -type f -name *.cpp)
OBJS :=
define OBJ_rule
obj := Build/$$(subst /,_,$$(patsubst Sources/%.cpp,%,$(1))).o
OBJS += $$(obj)
$$(obj): $(1)
g++ -c $$< -o $$#
endef
$(foreach s,$(SRCS),$(eval $(call OBJ_rule,$(s))))
.PHONY: objs
objs: $(OBJS)
Which instantiates one rule per module and should do the same... with the significant advantage that, when you type make objs, only the outdated object files are rebuilt. But it's a bit more tricky.

Makefile implicit rule matching - prefix length not affecting match

In my gnu-make-3.81 Makefile, I wish to define two implicit rules, such that the more specific first one takes precedence over the more general second one:
src/%_bar.o : src/%_bar.c
$(CC) -Wall -Wno-unused-but-set-variable -c $^ -o $#
src/%.o : src/%.c
$(CC) -Wall -c $^ -o $#
The issue I have is that foo_bar.c represents autogenerated code that triggers a warning with -Wall that I'd prefer to suppress, so the first rule is meant to catch this special case.
According to make's manual:
It is possible that more than one pattern rule will meet these criteria. In that case, make will choose the rule with the shortest stem (that is, the pattern that matches most specifically).
I thought that for a filename of src/foo_bar.o, the first rule would generate the stem foo, and the second would generate foo_bar. The former is the shortest so I'd expect it to apply. However this doesn't seem to be the case, the second rule is selected by make and executed. Running make -d doesn't even show an attempt to match with the stem foo - only foo_bar is considered.
If I make the following change, by making the second rule's stem deliberately longer by shortening the prefix from src/ to sr, the second rule is still selected:
src/%_bar.o : src/%_bar.c
$(CC) -Wall -Wno-unused-but-set-variable -c $^ -o $#
sr%.o : sr%.c
$(CC) -Wall -c $^ -o $#
I can't reconcile this with the make documentation.
In addition, if I remove the src/ prefix entirely, the second rule is selected:
src/%_bar.o : src/%_bar.c
$(CC) -Wall -Wno-unused-but-set-variable -c $^ -o $#
%.o : %.c
$(CC) -Wall -c $^ -o $#
Although this solves my problem, it's actually not suitable because it overrides/interacts with another implicit rule on the current directory that I need to keep:
%.o : %.c
# do something else
My question is why does make behave this way in this case, and is this consistent with the documentation? If so, is there a better way to specify a more specialised implicit rule?
First, be aware that the "shortest stem" method of pattern matching was introduced in GNU make 3.82. If you're using GNU make 3.81 then your version of make uses the old method which was "first match". It's always best to read the documentation that comes with your distribution, if possible, rather than the web documentation because the web documentation is for the latest version of GNU make. GNU make 3.81 was released in April 2006... that's pretty old.
However, the example you provided actually DOES work the way you wanted it to:
src/%_bar.o : src/%_bar.c ; #echo shorter: $*
src/%.o : src/%.c ; #echo longer: $*
all: src/foo_bar.o
$ make-3.81
shorter: foo
$ make-3.82
shorter: foo
I suspect that when you asked your question here you didn't use the same code you're using in your real environment. In that environment you must have the shorter pattern after the longer pattern, like this:
src/%.o : src/%.c ; #echo longer: $*
src/%_bar.o : src/%_bar.c ; #echo shorter: $*
all: src/foo_bar.o
$ make-3.81
longer: foo_bar
$ make-3.82
shorter: foo
When asking questions it's very important to be sure that the simplified examples in the question accurately reflect the real situation.

% not matching zero or more characters in rule?

According to the manual on Defining and Redefining Pattern Rules (and if I am reading it correctly):
‘%’ character matching any sequence of zero or more characters...
But the following is not matching both bench.cpp and bench2.cpp:
bench%.o : bench%.cpp
$(CXX) $(CXXFLAGS) -DCRYPTOPP_DATA_DIR='"$(PREFIX)/share/cryptopp"' -c $<
%.o : %.cpp
$(CXX) $(CXXFLAGS) -c $<
Here's what I see when running make:
$ rm bench*.o
$ make static dynamic cryptest.exe PREFIX=/usr/local
make: Nothing to be done for `static'.
make: Nothing to be done for `dynamic'.
g++ -DNDEBUG -g -O2 -fPIC -march=native -pipe -c bench.cpp
g++ -DNDEBUG -g -O2 -fPIC -march=native -pipe -DCRYPTOPP_DATA_DIR='"/usr/local/share/cryptopp"' -c bench2.cpp
Above, both bench.cpp and bench2.cpp should have -DCRYPTOPP_DATA_DIR='"/usr/local/share/cryptopp"'. I also tried using the asterisk (*) with no joy.
How do I craft a rule that matches both bench.cpp and bench2.cpp?
According to the link you provided
A pattern rule contains the character ‘%’ (exactly one of them) in the
target; otherwise, it looks exactly like an ordinary rule. The target
is a pattern for matching file names; the ‘%’ matches any nonempty
substring, while other characters match only themselves.
So % doesn't match empty strings.
‘%’ character matching any sequence of zero or more characters...
refers to the definition of vpath which is totally different.
I'm afraid you'll have to use bench1 instead of bench. Alternatively you can use macro to defines 2 rules but write it only once.
Well, I happened to stumble across the proper section of the documentation on this.
According to 4.4 Using Wildcard Characters in File Names, I should probably use an asterisk in this case.
And according to Stallman and the GNUMake manual, % is not a wildcard for specifying file names:
A single file name can specify many files using wildcard characters.
The wildcard characters in make are ‘*’, ‘?’ and ‘[…]’ ...

WINAVR not finding file in include path with whitespace

When I supply an path for EXTRAINCDIRS (in the Makefile, following the sample provided by WINAVR) without whitespace, the compiler is able to find my header file, but when I use a path containing whitespace (enclosed in quotation marks, as the comments in the Makefile direct), it raises: error: No such file or directory.
"d:/dev/avr/atmega/shared/" # will search files in this dir
"d:/dev/avr/atmega/sha ed/" # will not search this dir for files
I mean, the comments say:
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
Any idea how to get WINAVR to handle this correctly?
I'm using Programmer's Notepad (WINAVR) on Windows XP. Here's the command line command:
avr-g++ -c -mmcu=atmega328p -I. -gdwarf-2 -DF_CPU=UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst -I"d:/dev/avr/atmega/shared/" -I"d:/dev/avr/atmega/sha -Ied/" -std=gnu99 -MMD -MP -MF .dep/main.o.d main.c -o main.o
What is happening is that I'm guessing somewhere else in the makefile there is a line that does something like the following:
INCLUDES = $(addprefix -I, $(INCDIRS))
When that happens addprefix treats any spaces in the $(INCDIRS) variable as a seperator to the next variable and will add the -I there. What you could do is use a special character for spaces say '\\' and then before the command is generated call a substitute function to resubstitute spaces. Something like the below example:
SPACE = \\
INCDIRS = /home/posey/test$(SPACE)dir
INCLUDES = $(addprefix -I, $(INCDIRS))
REAL_INCLUDES = $(subst $(SPACE), ,$(INCLUDES))
.PHONY : all
all:
$(info $(REAL_INCLUDES))
If this doesn't make sense you can post the entire makefile and we can show you exactly what's going on. Once the space has been substituted back into the variable you cannot run it through any further make functions that work with space delimiters without the same behavior occurring.

Resources