This may be a very esoteric question, but it's a larger problem that boils down to this. There is a global variable keeping track of state (in this case it's NUM), that is changing. The problem is I need to do a deferred assignment (to LIST) but I need the current value of NUM to be expanded.
NUM := ONE
LIST = three four $(VAR_$(NUM))
NUM := TWO
VAR_ONE := SUCCESS
VAR_TWO := FAILURE
$(info LIST => $(LIST))
$(info LIST VALUE => $(value LIST) )
This results in:
LIST => three four FAILURE
LIST VALUE => three four $(VAR_$(NUM))
What I would like to see:
LIST => three four SUCCESS
LIST VALUE => three four $(VAR_ONE)
I have a very cumbersome solution, which I'll post below, but if there is a simpler solution I would be glad to hear it.
Thank you.
My (pretty ugly) solution is to use a macro to construct the assignment, to force one round of expansion:
define append_deferred
$(1) += $(2)
endef
NUM := ONE
LIST = three four
$(eval $(call append_deferred,LIST,$$(VAR_$(NUM))))
NUM := TWO
VAR_ONE := SUCCESS
VAR_TWO := FAILURE
$(info LIST == $(LIST))
$(info LIST VALUE == $(value LIST) )
This prints the desired result:
LIST => three four SUCCESS
LIST VALUE => three four $(VAR_ONE)
But I'm wondering if there is a better way.
Related
I have a question about variable assignments in makefile.
given 2 lists of the same size as below :
(there are no any characters or string match between 2 lists)
index := 1 2 3 4 5
objects := one_obj two_obj three_obj four_obj five_obj
and I need some index to be active, for example, 1 and 3
act_idx := 1 3
all :
#echo act_obj = $(act_obj)
how should I assign $(act_obj) to get the output as "act_obj = one_obj three_obj" after I type "make all" in shell ?
Something like this should do the trick:
act_obj := $(foreach I,$(act_idx),$(filter $I_%,$(objects)))
Based on the adjusted question you can do something like this:
act_obj := $(foreach I,$(act_idx),$(word $I,$(objects)))
You can probably work this out yourself from the set of functions described in the GNU make manual.
The GNUmake library at https://github.com/markpiffer/gmtt was originally written with this exact use case in mind (it grew into a everything-but-the-kitchen-sink, but oh well).
The formulation for the selection process which you are basically needing here is done with the relational table idiom. You produce a table as GNUmake string list and execute a select statement which mimicks a simple SQL select.
include gmtt/gmtt.mk
# define a table with 2 columns (no empty cells allowed!)
define object_tbl :=
2
key1 one_obj
key2 two_obj
key3 three_obj
key4 four_obj
key5 five_obj
endef
act_keys := key1 key3
# SELECT column 2 FROM object_tbl WHERE column 1 is found in act_keys
objects := $(call select,2,$(object_tbl),$$(filter $$1,$(act_keys)))
$(info $(objects))
Output:
one_obj three_obj
make: *** No targets. Stop.
More complex clauses (using arithmetic e.g.) are also possible.
What are the scenarios , where = is preferred over := ?
What are the scenarios, where := is preferred over = ?
I read from gnu site, that usage of = makes make run slower. I just wonder, when do we use = in makefile?
To answer your question, = is used when you want to delay expansion of the right side until the variable is used.
This allows you to define variables in any order. It also allows you to create variables with values that refer to automatic variables (remember automatic variables have no value until the rule is being run). So for example:
my_FLAGS = -a
your_FLAGS = -b
FLAGS = $($#_FLAGS)
my your : ; #echo $(FLAGS)
this cannot work if you use := because when the FLAGS variable is defined, $# has no value.
They are also useful when defining user-defined functions that can later be invoked with $(call ...); you don't want those to be expanded until they are called.
With simply defined variables you can do things like recursively use the variable:
ITEMS := one two three
ITEMS := $(addsuffix $(ITEMS))
This is because the simple assignment (:=) happens in the order you read them
Non simple assignment (=) is recursively expanded so if you assign it to other variables they are in turn expanded until you end up with the final result that contains all the expanded parts. Note that the makefile first parses the file so that the order you do the assignment is not so important, examples to follow:
i.e. this is not allowed:
ITEMS = one two three
ITEMS = $(addsuffix $(ITEMS))
So this affects when you want to use each type. With non simple you can do:
ITEMS1 = a b c
ITEMS_all = $(ITEMS1) $(ITEMS2)
ITEMS2 = d e f
And now ITEMS_ALL will contain a b c d e f - even though they are not defined in order, this can be very useful. So if you just want to assign a simply value - stick with := if you want to keep adding things to a variable you might want to use =...
Is it possible to add multiple commands in a foreach loop? I'm trying to add a simple if-else conditional but unfortunately I cannot find a good explanation for that.
Foreach lib in TARGET_LIBS, I would like to check if lib contains the word "test". If yes, call TEST_COPY_RULE. Otherwise, call LIB_COPY_RULE.
Thank you
This Makefile answers your question positively:
TARGET_LIBS := a testb ctest d e
TEST_COPY_RULE = $(info TEST_COPY $1)
LIB_COPY_RULE = $(info LIB_COPY $1)
IS_TEST = $(findstring test, $L)
$(foreach L, $(TARGET_LIBS), $(if $(call IS_TEST,$L),$(call TEST_COPY_RULE, $L),$(call LIB_COPY_RULE, $L)) )
I want to replace a string like this
OntVeip -> ManagedObjects/OntVeip/OntVeipConfigDef.xml
so logically to me this is like :
% -> ManagedObjects/%/%ConfigDef.xml
If I try to do this with pathsubst, the % is only replaced once
ie
ManagedObjects := OntVeip OntMoca
XMLSOURCES := $(patsubst %,ManagedObjects/%/%ConfigDef.xml,$(ManagedObjects))
does not work as I was hoping for.
How can I achieve the result I am after ?
Liberally stolen adapted from here
XMLSOURCES := $(foreach obj,$(ManagedObjects),ManagedObjects/$(obj)/$(obj)ConfigDef.xml)
How can we use a conditional or pattern test to make our function accept any symbols as input except for lists?
Use Except:
f[x : Except[_List]] := doSomethingTo[x]
expr /. x : Except[_List] :> doSomethingElseTo[x]
You can combine that with Alternatives (infix operator |) to exclude several things:
g[x : Except[_List | _Rational]] := etc[x]
Edit: Consolidating answers from the comments too:
ListQ[expr] will return True if expr is a list (has head List) and False otherwise. MatchQ[expr, _List] and Head[expr]===List are equivalent ways to accomplish the same thing.