Remove a flag from CFLAGS in FreeBSD makefile - 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

Related

gnu make: filter-out escaped whitespace separated words

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

Makefile: Parametrizable recepie command in pattern rule

Assume the following makefile
objects = $(wildcard *.in)
outputs := $(objects:.in=.txt)
%.txt: %.in
some-command $# $<
compile: $(outputs)
This works as expected.
Now I want to add another target called (for example) upgrade that should do the same thing as compile but pass additional options to some-command (possibly depending on environment variables, but that is out of scope to this question).
The only 'solution' I've found so far has been recursively invoking the same makefile and passing the additional options via env variables. But that seems like a pretty ugly hack.
Is what I'm after possible in make (GNU is fine, this doesn't have to be portable) or am I just going about this the wrong way?
Target-specific variable value:
%.txt: %.in
#echo some-command $(SOME_OPTIONS) $# $<
compile: $(outputs)
upgrade: SOME_OPTIONS:=whatever
upgrade: compile

why make behaves different with quotes?

I know quotes are not supposed to be used within Makefile, but just out of curiosity, why make behaves differently with make foobar and make. See detailed code below.
Makefile:
TARGET = 'foobar'
$(TARGET): foobar.cpp
g++ -g $^ -o $#
clean:
rm foobar
output:
$ make
g++ -g foobar.cpp -o 'foobar' <-- correct
$ make clean
rm foobar
$ make foobar
g++ foobar.cpp -o foobar <-- incorrect but works. Why?
$ make clean
rm foobar
$ make baz <-- doesn't work, which is normal
make: *** No rule to make target 'baz'. Stop.
$
This only "works" because the shell is stripping the single quotes from your first example for you.
The quotes are literally in the value of the $(TARGET) make variable. make doesn't dequote the right-hand side of the TARGET = 'foobar' assignment.
You can see this by using $(info $(TARGET)) in your makefile.
So your target line:
$(TARGET): foobar.cpp
is creating a target with the name 'foobar' and not foobar like you expect.
This is why running make does the "right" thing and make foobar does something else.
make foobar is running the make built in rule for %: %.cpp.
The fact that your default 'foobar' target works to create foobar is because the shell sees the single quotes and strips them.
You'll notice that if you make make; make make will build your 'foobar' target twice but make foobar; make foobar will tell you there is nothing to be done the second time. That's because the first target creates a file different from what make is expecting.
If you were to quote $# in your recipe line you would see different behavior.
$(TARGET): foobar.cpp
g++ -g $^ -o '$#'
for example would have make run g++ -g foobar.cpp -o ''foobar'' and generate a foobar file while
$(TARGET): foobar.cpp
g++ -g $^ -o "$#"
would have make run g++ -g foobar.cpp -o "'foobar'" and generate a 'foobar' file (which would cause make; make to report nothing to be done for the second make run).
You want the quotes in the recipe line not in the variable here.
TARGET = foobar
$(TARGET): foobar.cpp
g++ -g $^ -o '$#'
clean:
rm foobar
That being said since you can't have spaces in make target names (not reliably at least) the need for those single quotes (or any quoting) is diminished since you only need it if the filename contains shell metacharacters.
It's because make uses a default rule when it does not find specific rules to build a target.
You can compile program from program.cpp even without or with an empty Makefile. Try
make -f /dev/null foobar
The default rules are specified by POSIX and your make implementation has probably some of its own.
Trying to build baz fails, because none of the default rules knows how to build a baz.c or baz.cpp or any of the other built-in source files that could be used to build baz.

Makefile, declare variable in executable

This is a simple question for a starter like me, but what can I do to do like the following
all: run
run:
DIR=bin/
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $(DIR)$#
Thanks.
Why not go like this?
DIR=bin/
all: $(DIR)/run
$(DIR)/run:
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $#
As written, you have an assignment to the shell variable DIR on one command line. On the next line, you have the expansion of a make variable DIR. This doesn't work because the two lines are executed by different shells, and in any case, make expands $(DIR) before running the shell and DIR is not a make variable.
You could make it work like this:
all: run
run:
DIR=bin/; \
$(CC) $(LIBRARY) $(INCLUDE) run.o -o $${DIR}$#
The backslash means the two lines are treated as one (so much so that the semicolon is needed). The $${DIR} notation is expanded by make to ${DIR} (more precisely, $$ expands to $ and make ignores the {DIR}), and then the shell expands ${DIR} from the value set previously. You could, of course, omit the braces.
However, the answer by BeSerK is probably what you're looking for.

How to specify --no-print-directory within the Makefile itself

I'd like to run my makefile without the -w flag turned on by the recursive make calls.
The flag to do that is --no-print-directory on the make command line.
Is it possible to specify that flag within the makefile itself?
I plan to make this flag dependent on a VERBOSE mode, perhaps something like
$(if $(VERBOSE),,MAKEFLAGS += no-print-directory))
Thanks,
Dan
Yes, just appending --no-print-directory to MAKEFLAGS should be enough, but you have to do that with conditional directives, not with conditional functions:
ifndef VERBOSE
MAKEFLAGS += --no-print-directory
endif
You can include the .SILENT: special target in the calling makefile. For example, here's your toplevel makefile:
all:
$(MAKE) -f sub.mk foo
.SILENT:
and the submake makefile, sub.mk:
foo:
#echo done
Note that .SILENT is considered obsolete, so it may not be around forever, and also note that including that in your makefile also has the effect of suppressing command echo, just as if you had put # before every command in the makefile.

Resources