I have a Makefile question and I can’t seem to figure this out.
I have the following variable in my Makefile
AA := word1 word2 word3 word4 word5
AAA = $(patsubst word2% $(word 1, %), word22, $(AA))
What I want AAA to be is
AAA := word1 word22 word4 word5
I want to search for a known pattern word2 and the unknown word following it and replace it with word22. Unfortunately I don’t know word3 before hand all I know is I need to replace word2 and the next word following it. Is there a clever way to do this?. I thought of using $filter and $filter-out Makefile functions but don't know how to extract the pattern and the word(s) following it.
I would also like a variation where I would replace word2 with word22 and insert word33 after the word following word2. So AAA would look like:
AAA := word1 word22 word3 word33 word4 word5
Thanks for your help. I was trying to do this without invoking shell and sed
The built-in functions of make do not allow for complex string process. In particular, the patterns are limited using '%' as wildcard. Given this limitations, few choices:
Use external program (sed, etc) to perform the translation
Use the built-in guile (if compiled into make).
Complex: use foreach to find the match, the and remove the following word. Unfortunately, this very challenging as make does not ahve any arithmetic/logic.
The simplest approach is to use external program (sed, awk, perl) to perform translation. Solution using 'sed'
AA := word1 word2 word3 word4 word5
P := word2
AAA = $(shell echo ${AA} | sed -e 's/$P *[^ ]*/word22/')
t:
echo "AAA=${AAA}"
The output is: AAA=word1 word22 word4 word5
For make-only solution, consider the following, which is using foreach to perform the substitution/token removal as needed
AA := word1 word2 word3 word4 word5
P := word2
R := word22
PREV=
# Loop with X over items
# Replace P with R
# Ignore word following P
# Set PREV=$X
DDD := $(foreach X, ${AA}, $(if $(findstring $X,$P),$R,$(if $(findstring $(PREV),$P),,$X)) $(eval PREV=$X))
x:
echo 'C=${DDD}'
I take your comment
Based on a command line switch to make I am switching compilers so the arguments passed to compiler 1 will need to be altered for compiler 2.
as the true problem you want to solve. You can try to solve it with ad-hoc programming, either in make or with the help of command line tools like sed. The former has the problem of being quite hard to comprehend after some time or with the code in the hands of a different programmer, the latter can be equally involved and needs those tools installed. The third option is to use gmtt which is a GNUmake library written for such build configuration tasks:
include gmtt/gmtt.mk
# Define a table which selects the executable depending on cmd-line parameter 1
define COMPILER_EXECUTABLE
2
foo gcc
bar clang
baz arm-none-eabi-gcc
endef
# Additional command line args depending on compiler
define ADDITIONAL_CFLAGS
2
gcc -E
gcc -O2
clang -DUSE_CLANG
arm-none-eabi-gcc -mcpu=cortex-m7
arm-none-eabi-gcc -mthumb
endef
# Select column 2 from COMPILER_EXECUTABLE where cmd-line parameter 1 == first column
CC := $(call select,2,$(COMPILER_EXECUTABLE),$$(call str-eq,$(CMD_LINE_PARAM_1),$$1))
# Fetch additional CFLAGS
CFLAGS += $(call select,2,$(ADDITIONAL_CFLAGS),$$(call str-eq,$(CC),$$1))
$(info compiler: $(CC))
$(info CFLAGS: $(CFLAGS))
Testing it:
make CMD_LINE_PARAM_1=foo
Output:
compiler: gcc
CFLAGS: -E -O2
The rationale is to simply save the time for your first attempt - if you lay down your information in tabular format, nothing is lost if you later want to switch to a build method better fitting your software process. The interface to the select (and most other gmtt functions) aren't particularly complicated, neither to comprehend nor to replicate in another language.
Related
I'm trying to put a very long list of objects at the END of my Makefile. I cannot get this code to work. I can see $(LIST) getting assigned in the ASSIGN_LIST macro, however, it seems that $(LIST) is blank for stuff.txt. I'm a newbie at Make so maybe I'm way off the mark. (Using Red Hat Linux, GNU Make v3.81.)
> cat makefile
define ASSIGN_LIST
LIST = $1
endef
stuff.txt: $(LIST)
echo $(LIST)
# MUCH MORE CODE HERE
# THIS CODE CONTAINING THE LIST IS AT THE VERY END OF MAKEFILE (i.e. OUT OF THE WAY)
define VERY_LONG_LIST_OF_OBJECTS
aaa \
bbb \
ccc \
ddd \
eee \
fff \
etc
endef
$(info $(call ASSIGN_LIST, $(VERY_LONG_LIST_OF_OBJECTS)))
When I run the makefile:
> make
make: `stuff.txt' is up to date.
I use 'touch' to make the prereqs newer than stuff.txt. If I swapout "eval" with "info" (last line), I can see this
> make
LIST = aaa bbb ccc ddd eee fff etc
make: `stuff.txt' is up to date.
Is this even possible? Thanks for the help.
Variables that appear in targets or prerequisites are expanded immediately when read by make during the parsing of the makefile. So, this cannot work:
stuff.txt: $(LIST)
echo $(LIST)
...
LIST = ...
Here, the prerequisite variable is expanded when make reads that line but the variable inside the recipe is not expanded until later when make wants to build the target. So the variable is not set yet when parsing the prerequisite, then it gets set, then the recipe is run.
If you want to see what make sees you should use automatic variables instead:
stuff.txt: $(LIST)
echo $^
The best way to do this is delay the prerequisites until after the variable is set. If you really, really don't want to do that you can use secondary expansion:
.SECONDEXPANSION:
stuff.txt: $$(LIST)
echo $^
Using eval on ASSIGN_LIST will only set LIST to the 1st file (because of LIST = $1). As a result, stuff.txt only depends on aaa.
As an alternative, add the dependency after setting the variable:
define VERY_LONG_LIST_OF_OBJECTS
aaa \
... \
etc
endef
stuff: $(VERY_LONG_LIST_OF_OBJECTS)
If I have a list of files:
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
Is there any way of removing everything up to the last underscore from the list to get foo1.c foo2.c bar1.c bar2.c?
I was looking into using patsubst, but I would need two%'s -- one for the first part to be ignored, and one for the last part to be kept.
It can be done but it's a little gross. You want something like this:
final := $(foreach F,$(files),$(word $(words $(subst _, ,$F)),$(subst _, ,$F)))
This says, for each element in files we convert the _ to a space, now we can use our per-word functions to manipulate it: extract the last word in the list of words.
ETA
ReAl points out below that this can be simplified using lastword:
final := $(foreach F,$(files),$(lastword $(subst _, ,$F)))
As I see it you are using the underscore as separating character between hierarchical names. GNUmake is well equipped to work with such a scheme if the character is /: file name functions.
So your example should simply boil down to
$(notdir $(subst _,/,$(files))
Use the external program — sed — and enjoy all its power
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
f := `echo $(files) | sed -e "s/[[:alnum:]]*_//g"`
all:
echo $(f)
I need to extract the word before the last from $(MAKEFILE_LIST).
So far I could not come up with anything better than this kind of monstrosity:
LIST := a b c
LAST_WORD_INDEX = $(words $(LIST))
BEFORE_LAST := $(word $(shell echo $(LAST_WORD_INDEX) - 1 | bc),$(LIST))
$(info word before last is $(BEFORE_LAST))
When I run it:
word before last is b
make: *** No targets. Stop.
The result is correct, but is there more elegant and sane way to achieve the same?
$(words ${LIST}) will give you the index of the last-but-one word if you prepend an element to LIST.
BEFORE_LAST := $(word $(words ${LIST}),1st ${LIST})
Notice that 1st in there.
I ended up using GMSL library, which makes things quite a bit more coherent:
include gmsl-1.1.6/gmsl
$(lastword $(call chop,$(MAKEFILE_LIST)))
I am writing a Makefile and I get stuck on a filter function limitation.
Indeed, filter takes only one wildcard.
What I would like to do is:
I have a list a files, some matching the regexp blabla, some not. But for this I need 2 wildcards, thus i cannot use filter function.
I would like to split my original list in 2 lists, one containing all the element containing the blabla string (filter equivalent) and the other one containing the not matching one (filter-out equivalent).
thanks for your help.
You can do this without running any external commands. Define the two macros
containing = $(foreach v,$2,$(if $(findstring $1,$v),$v))
not-containing = $(foreach v,$2,$(if $(findstring $1,$v),,$v))
Now you can do
LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_OLD := $(call containing,old,$(LIST))
LIST_NOT_OLD := $(call not-containing,old,$(LIST))
One of Make's greatest shortcomings is its poor ability to handle regular expressions. The functions filter and filter-out can't find "old" in the middle of a word. I'd suggest this hack:
NOT_OLD = $(shell echo $(LIST) | sed 's/[^ ]*old[^ ]* *//g')
OLD = $(filter-out $(NOT_OLD), $(LIST))
You could take advantage of your shell's more advanced string handling capabilities. Assuming that you have bash, you could use the following in your makefile:
LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_NOT_OLD := $(shell l=($(LIST)); echo $${l[#]//*old*})
LIST_OLD := $(filter-out $(LIST_NOT_OLD),$(LIST))
You can find an explanation of the bash string replacement mechanism in how to delete elements from an array based on a pattern. The double $ is required to keep the $ sign in the shell invocation.
Let's say you have a variable in a makefile fragment like the following:
MY_LIST=a b c d
How do I then reverse the order of that list? I need:
$(warning MY_LIST=${MY_LIST})
to show
MY_LIST=d c b a
Edit: the real problem is that
ld -r some_object.o ${MY_LIST}
produces an a.out with undefined symbols because the items in MY_LIST are actually archives, but in the wrong order. If the order of MY_LIST is reversed, it will link correctly (I think). If you know a smarter way to get the link order right, clue me in.
A solution in pure GNU make:
default: all
foo = please reverse me
reverse = $(if $(1),$(call
reverse,$(wordlist 2,$(words
$(1)),$(1)))) $(firstword $(1))
all : #echo $(call reverse,$(foo))
Gives:
$ make
me reverse please
An improvement to the GNU make solution:
reverse = $(if $(wordlist 2,2,$(1)),$(call reverse,$(wordlist 2,$(words $(1)),$(1))) $(firstword $(1)),$(1))
better stopping condition, original uses the empty string wasting a function call
doesn't add a leading space to the reversed list, unlike the original
Doh! I could have just used a shell script-let:
(for d in ${MY_LIST}; do echo $$d; done) | tac
You can also define search groups with ld:
ld -r foo.o -( a.a b.a c.a -)
Will iterate through a.a, b.a, and c.a until no new unresolved symbols can be satisfied by any object in the group.
If you're using gnu ld, you can also do:
ld -r -o foo.o --whole-archive bar.a
Which is slightly stronger, in that it will include every object from bar.a regardless of whether it satisfies an unresolved symbol from foo.o.
Playing off of both Ben Collins' and elmarco's answers, here's a punt to bash which handles whitespace "properly"1
reverse = $(shell printf "%s\n" $(strip $1) | tac)
which does the right thing, thanks to $(shell) automatically cleaning whitespace and printf automatically formatting each word in its arg list:
$(info [ $(call reverse, one two three four ) ] )
yields:
[ four three two one ]
1...according to my limited test case (i.e., the $(info ...) line, above).