How to search for whole words in a Makefile string? - makefile

I need a method to search for a substring only if it is a whole word.
Ideally I want a function (say findwstring()) such that the two examples,
$(findwstring a, a b c)
$(findwstring a, angle b c)
should produce the values ‘a’ and ‘’ (the empty string), respectively.
I couldn't find anything here which could help me.

$(filter a, a b c)
Produces 'a'.
$(filter a, angle b c)
Produces ''.
Percent (%) in the search string is considered the wildcard character.
From the docs you linked (actually the function right below findstring), boldface mine:
$(filter pattern…,text)
Returns all whitespace-separated words in text that do match any of the pattern words, removing any words that do not match. The patterns are written using ‘%’, just like the patterns used in the patsubst function above.
The filter function can be used to separate out different types of strings (such as file names) in a variable. For example:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
says that foo depends of foo.c, bar.c, baz.s and ugh.h but only foo.c, bar.c and baz.s should be specified in the command to the compiler.

Related

How to extract a pattern from a makefile string variable

In a Makefile, I have a variable DPDK_CUSTOM_REPO_VERSION which is defined as follows:
DPDK_CUSTOM_REPO_VERSION="dpdk-19.08-devel"
How to Extract 19.08 from above string into another variable DPDK_VERSION?
The string processing abilities of the make-command are somewhat limited, but you can try the following:
Replace all occurrences of - with space:
$(subst -, ,$(DPDK_CUSTOM_REPO_VERSION)
resulting in dpdk 19.08 devel and take the second word:
$(word 2, $(subst -, ,$(DPDK_CUSTOM_REPO_VERSION)))
This should yield the correct result, if the pattern doesn't change significantly.
Put it together to:
DPDK_CUSTOM_REPO_VERSION="dpdk-19.08-devel"
DPDK_VERSION=$(word 2,$(subst -, ,$(DPDK_CUSTOM_REPO_VERSION)))
test:
echo $(DPDK_VERSION)

How to filter out a pattern with space in Makefile

I want to filter out a pattern as "-Wl, Bdynamic -lmylib" from a long LDFLAGS list. But filter-out function can only handle space-separated list, is there other method to do this in Makefile?
The only way to do it is by replacing spaces with some other character that you know doesn't appear in the variable value. For example suppose you know that the ^ character never appears in your variable value, then you can do something like this:
# Create a variable containing a space
E :=
S := $E $E
LDFLAGS := $(subst ^,$S,$(subst -Wl^Bdynamic^-lmylib,,$(subst $S,^,$(LDFLAGS))))

How do you define pattern-specific variables for makefile pattern rules containing a patterned prerequisite?

I understand that you can define pattern-specific variables for rules with no prerequisites, like this:
%.o: var = 2
This will set variable var to 2 for recipes that apply to all targets ending in .o. This is clearly stated in the GNU Make documentation. But how do you define a pattern-specific variable for pattern rules that contain a pattern prerequisite, like this:
%.o: %.c
Say I have this section of makefile:
%.o: %.c
(recipe here)
%.o: %.b
(recipe here)
I want to define a pattern-specific variable only for the %.o: %.b rule, but I don't see how to do it (if it's even possible). I'd like to do something like this, but of course it doesn't work:
%.o: %.c
(recipe here)
%.o: %.b: var = 2
(recipe here)
Is there a way to do this?
You can only set variable for targets, not rules. %.o: %b is a rule where %.o is a target pattern (hence the "pattern-specific" name).
The usual way to solve this is ether hard coding values in the recipes or using rule-specific flags (maybe CVAR and BVAR in your case).
EDIT: Scratch that. Came up with a workaround.
It can be done by leveraging variables' recursive evaluation.
all: a.o b.o c.o
$(shell touch a.xx b.yy c.zz)
##
# Create rule-specific variable... rules
#
# #param 1 Target.
# #param 2 Prerequisite.
# #param 3 Variable name.
# #param 4 Variable value.
#
rule-var = $(eval $(rule-var-body))
define rule-var-body
$1: private $3 = $$(if $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)
$2: $3 = $4
rule-var-$1-$3 = $$(if $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)
endef
VAR = $(VAR_DEFAULT)
# Declare couple of test values
$(call rule-var,%.o,%.x,VAR,x-value)
$(call rule-var,%.o,%.y,VAR,y-value)
VAR_DEFAULT := z-value
ECHO_RULE_RECIPE = #echo -e '$#: $^\t(VAR = $(VAR))'
%.o: %.x
$(ECHO_RULE_RECIPE)
%.o: %.y
$(ECHO_RULE_RECIPE)
%.o: %.z
$(ECHO_RULE_RECIPE)
%.x: %.xx
$(ECHO_RULE_RECIPE)
%.y: %.yy
$(ECHO_RULE_RECIPE)
%.z: %.zz
$(ECHO_RULE_RECIPE)
The output is:
a.x: a.xx (VAR = x-value)
a.o: a.x (VAR = x-value)
b.y: b.yy (VAR = y-value)
b.o: b.y (VAR = y-value)
c.z: c.zz (VAR = z-value)
c.o: c.z (VAR = z-value)
The brains of the operation is macro rule-var. It will wrap variable value in a prerequisite matching if-else expression. The expression is also saved in rule-var-$1-$3 variable for other rule-specific values.
$$(if $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4) deobfuscation:
$$(if $$(<:$2=), will test first prerequisite value ($<) by replacing it's pattern ($2) with empty string.
If pattern doesn't match, use $(or $(value rule-var-$1-$3),$(value $3)). This is a workaround for global variable shadowing. In your example %.o: %.c doesn't have var declared so it should use global value but both rules share the same target, it's not visible. Both are referenced by value and single $ expands the expression during variable substitution phase. So the result is neat and or free.
Use $(value rule-var-$1-$3) if it's nonzero. That is function has been called before for this target and variable name.
Otherwise use variable's global value ($(value $3)).
Otherwise use the value provided ($4).
Unfortunately, when inherited, this if-else monstrosity won't expand properly so it's declared as private and fixed with a straightforward second rule.
In this example the following 3 rules will be declared.
%.o: private VAR = $(if $(<:%.y=),$(if $(<:%.x=),$(VAR_DEFAULT),x-value),y-value)
%.y: VAR = y-value
%.x: VAR = x-value
Limitations
Even with a workaround, variable's global counterpart is still shadowed. If you need a default value, assign it before calling rule-var. Global value is copied as a part of the rule-specific variable but not expanded until use.
I found another solution, although it could get a little difficult to read depending on how many prerequisite comparisons are made. Perhaps there's a way to refactor it.
# Return 1 if prerequisite ends in `.c`.
# Return 2 if prerequisite ends in `.b`.
build-var = $(if $(filter %.c,$<),1,$(if $(filter %.b,$<),2))
%.o: private var = $(build-var)
%.o: %.c
(recipe here)
%.o: %.b
(recipe here)
The instruction var = $(build-var) will be invoked for any target that ends in .o. But when build-var is expanded, its value comes from examining the end of the prerequisite to see what type of file it is.
The if statement evaluates to true if its first argument is a non-empty string, and filter returns a non-empty string if, in my example, the prerequisite contains .c. So, walking through the build-var line, if the prerequisite (denoted by the automatic variable $<) ends in .c, then return the value 1. Else, start a second if statement that returns the value 2 if the prerequisite ends in .b. If the prerequisite doesn't end in either .c or .b then build-var is set to nothing (an empty string).

How to match occurance of word in list in makefile

I wonder how to match exact occurrence of a given word in the given list of words using only standard makefile operations. In the below example for WORD_TO_MATCH = a the result is positive and apparently wrong.
INPUT_LIST= aa bb
WORD_TO_MATCH = aa
#WORD_TO_MATCH = a
ifneq ($(findstring $(WORD_TO_MATCH),$(INPUT_LIST)),)
$(warning List contains "$(WORD_TO_MATCH)")
else
$(warning List doesnt contain "$(WORD_TO_MATCH)")
endif
Use filter instead of findstring:
...
ifneq ($(filter $(WORD_TO_MATCH),$(INPUT_LIST)),)
$(warning List contains "$(WORD_TO_MATCH)")
...

makefile: how to add a prefix to the basename?

I have a list of file path like that:
FILE_PATH := a1.so a2.so bla/a3.so bla/a3.so bla/blo/a4.so....
I need to add a prefix to the basename in order to get:
FILE_PATH_PREFIXED := liba1.so liba2.so bla/liba3.so bla/liba3.so bla/blo/liba4.so....
any idea?
Look at Make's addsuffix function.
Here is an example we use with `addsuffix` to place obj files one directory below
the source.
SOURCE += MainThread.cpp
SOURCE += Blah.cpp
OBJ=$(join $(addsuffix ../obj/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.o)))
From the make manual:
$(addprefix prefix,names...)
The argument names is regarded as a series of names, separated by
whitespace; prefix is used as a unit. The value of prefix is
prepended to the front of each individual name and the resulting
larger names are concatenated with single spaces between them. For
example,
$(addprefix src/,foo bar)
produces the result src/foo src/bar.

Resources