patsubst in GNU Makefile using variable replacements - makefile

Given the following GNU Makefile code.
ENDING = '\.cpp'
OBJ = $(SOURCES:$(ENDING)=.o) # (does not work)
This does replace nothing, however,
OBJ = $(SOURCES:.cpp=.o)
does (for cpp files). Is there a way to tell patsubst specific endings? Or at least to replace all kind of endings, like below?
OBJ = $(SOURCES:.*=.o) # (does not work)

This works:
ENDING = .cpp
OBJ = $(SOURCES:$(ENDING)=.o)
And this will replace all endings:
OBJ = $(addsuffix .o,$(basename $(SOURCES)))

Related

Replacing a string with underscore character in GNU Make

We are facing a slightly weird problem in GNU Make.
In one of the part Makefile, we try to modify a string in order to get right filename.
So "dummy_1_.pl" is to converted to "dummy_1.pl".
We tried to use following way :-
MY_STRING := dummy_1_.pl
UNDPL := _.pl
DPL := .pl
$(subst $(UNDPL), $(DPL), $(MY_STRING) )
Surprisingly it doesn't work. We can replace ".", "pl", ".pl" etc all this way. However just "" or "." or "_.pl" etc replacement strings starting with an underscore doesn't seem to work.
Is underscore a special character in Make. Are we missing something basic here... We are at GNU Make 3.81
Any help/thought is highly appreciated. Thanks in advance!.
EDIT :-
The problem was posted in short for focused discussion. It seems, the details are necessary. This applies to a pattern rule as below. There's a OUT_V_FILES target that contains *blah_cpu.v *foo_gpu.v etc various target files. (The special string "cpu", "gpu" etc are part of a list.) We want to derive blah.pl, foo.pl respectively as input file for the rule.
DEVICES := cpu gpu memc dram
MY_STRING := $$(foreach dev,$(DEVICES),$$(subst $$(dev),$(NOTHING),$$(notdir %.pl)))
NOTHING :=
UDOT := _.
DOT := .
$(OUT_V_FILES) : %.v : $(subst $(UDOT),$(DOT),S(MY_STRING)) Makefile
#Body of rule+++++++++
There are two problems here. 1) you never assign the result of the $(subst ... invocation and 2) you have to be aware that whitespace is significant within the context of string manipulation functions. With that in mind your makefile code should be something like...
MY_STRING := $(subst $(UNDPL),$(DPL),$(MY_STRING))
In your updated example, it looks like you have some issues when setting MY_STRING. If you correct it to have the proper filename(s), you get:
MY_STRING := $$(foreach dev,$(DEVICES),$$(subst $$(dev),$(NOTHING),$$(notdir %.pl)))
$(info 1. MY_STRING=$(MY_STRING))
MY_STRING := dummy_1_.pl
$(info 2. MY_STRING=$(MY_STRING))
NOTHING :=
UDOT := _.
DOT := .
MY_NEW_STRING=$(subst $(UDOT),$(DOT),$(MY_STRING))
$(info MY_NEW_STRING=$(MY_NEW_STRING))
gives
1. MY_STRING=$(foreach dev,cpu gpu memc,$(subst $(dev),,$(notdir %.pl)))
2. MY_STRING=dummy_1_.pl
MY_NEW_STRING=dummy_1.pl

GNU make: text replacement at two places in the same string?

In a GNU makefile, I need to rename
/infiles/signal_*.wav
to
/outfiles/out_signal_*.wav
This works (using subst):
SIGNALIN += $(wildcard /infiles/signal_*.wav)
TEMP += $(subst infiles,outfiles, $(SIGNALIN) )
SIGNALOUT += $(subst signal,out_signal, $(TEMP) )
Is there a better, one-line way that would not include the TEMP line?
You can wrap function calls:
SIGNALOUT += $(subst signal,out_signal,$(subst infiles,outfiles,$(SIGNALIN)))
In your case there are not really replacements at two places. What you want is replace /infiles/ at the beginning of each word by /outfiles/out_. And that's one place. Try:
SIGNALOUT := $(patsubst /infiles/%,/outfiles/out_%,$(SIGNALIN))

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