Makefile: Conditionals in foreach - makefile

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)) )

Related

When do we use := and when do we use = in GNU makefile?

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 =...

Define custom gmake function equivalent to multiple piped functions

I'm trying to define a function that would to the work of $(patsubst %,$(OBJDIR)/%.o,$(basename $(ARG1)))
something like;
define getobj
$(1): $(patsubst %,$(OBJDIR)/%.o,$(basename $(1)))
endef
the context I'm trying to improve is:
OBJDIR = objects
TESTHNF_CPPS = testhnf.cpp timings.cpp LongModular.cpp VeryLong.cpp VeryLongModular.cpp squfof.cpp
TESTHNF_CS = mt19937int.c lip.c
TESTHNF_OBJS = $(patsubst %,$(OBJDIR)/%.o,$(basename $(TESTHNF_CPPS) $(TESTHNF_CS)))
TESTHNF_OBJS2 = $(getobj $(TESTHNF_CPPS) $(TESTHNF_CS))
Here, $(TESTHNF_OBJS2) remains empty. How should I write the function ?
Your getobj macro looks like you want to declare a rule (target: prerequisites). But the use you try to make of it looks like you try to assign the result of its expansion to a variable, which does not really make sense.
So, let's ignore the rule look and concentrate on your goal (as far as I understand it): define a macro that gets the basename of each word it is passed and substitutes it for $(OBJDIR)/<basename>.o. You were almost there:
getobj = $(patsubst %,$(OBJDIR)/%.o,$(basename $(1)))
should make it, where $(1) is the macro's parameter, a list of space-separated words. And you call it with the call make function:
TESTHNF_OBJS2 = $(call getobj,$(TESTHNF_CPPS) $(TESTHNF_CS))
The call make function substitutes $(1) for the $(TESTHNF_CPPS) $(TESTHNF_CS) parameter in the definition of getobj and returns the result. In your case it is equivalent to:
TESTHNF_OBJS2 = $(patsubst %,$(OBJDIR)/%.o,$(basename $(TESTHNF_CPPS) $(TESTHNF_CS)))
When TESTHNF_OBJS2 will be expanded the result will be:
objects/testhnf.o objects/timings.o ... objects/lip.o
Back to the rule look. If what you want instead is instantiate a make rule:
<basename>: $(OBJDIR)/<basename>.o
for each <basename> in your list, plus define a TESTHNF_OBJS variable listing all corresponding object files, what you can use is a combination of foreach, eval and call. But things are a bit more tricky because make expands the eval parameter once, instantiates the result as a make construct and expands it once more when parsing it as any regular make constructs. Double expansion must thus sometimes be escaped by doubling $ signs; this is not your case but it is better to keep this in mind when using such things.
OBJDIR = objects
TESTHNF_CPPS = testhnf.cpp timings.cpp LongModular.cpp VeryLong.cpp VeryLongModular.cpp squfof.cpp
TESTHNF_CS = mt19937int.c lip.c
TESTHNF_OBJS :=
# $(1): basename of one source file
define getobj
$(1): $(OBJDIR)/$(1).o
TESTHNF_OBJS += $(OBJDIR)/$(1).o
endef
$(foreach b,$(basename $(TESTHNF_CPPS) $(TESTHNF_CS)),$(eval $(call $(b))))
clean:
rm -f $(TESTHNF_OBJS)
As all this is rather complicated, and the benefits are quite low, it is probably better to stick with simpler constructs, easier to understand and maintain:
OBJDIR := objects
TESTHNF_CPPS := testhnf.cpp timings.cpp LongModular.cpp VeryLong.cpp VeryLongModular.cpp squfof.cpp
TESTHNF_CS := mt19937int.c lip.c
BASENAMES := $(basename $(TESTHNF_CPPS) $(TESTHNF_CS))
TESTHNF_OBJS := $(addprefix $(OBJDIR)/,$(addsuffix .o,$(BASENAMES)))
$(BASENAMES): %: $(OBJDIR)/%.o
clean:
rm -f $(TESTHNF_OBJS)
The rule is a static pattern rule.

makefile patsubst multiple expressions

I'm trying to simplify some of my makefiles, and i started doing this:
TEMP_OBJECTS_C = $(patsubst %.c,%.c.o,$(C_SOURCES))
TEMP_OBJECTS_CPP = $(patsubst %.cpp,%.cpp.o,$(CPP_SOURCES))
but what i really wanted to do, is simply append .o to anything that ends in .c or .cpp. This will only work for .c, if i want to add cpp files, i have to make a completely separate substitution.
I tried this:
TEMP_OBJECTS = $(patsubst %.c,%.c.o,$(SOURCES)) $(patsubst %.cpp,%.cpp.o,$(SOURCES))
with SOURCES being all cpp and c files, but then i obviously got duplicate files, and the original source filenames in it...
So, i went with this:
TEMP_OBJECTS = $(patsubst %.c,%.c.o,$(SOURCES))
TEMP_OBJECTS = $(patsubst %.cpp,%.cpp.o,$(TEMP_OBJECTS))
but then it complained about recursivity.
Is there any way to make what i need?
Thanks
To paraphrase from my answer here.
Use $(filter) to include only the entries you that you want.
OBJS = $(filter %.o,$(patsubst %.cpp,%.o,$(CPP_SOURCES)) \
$(patsubst %.c,%.o,$(C_SOURCES)))
That said you could also use Substitution References instead of explicit calls to $(patsubst) to shorten that a bit.
OBJS = $(filter %.o,$(CPP_SOURCES:.cpp=.o) $(C_SOURCES:.c=.o))
If you wanted to keep your original final attempt then you just need to make your variable simply-expanded instead of recursively-expanded (see The Two Flavors of Variables for more explanation of this difference).
TEMP_OBJECTS := $(patsubst %.c,%.c.o,$(SOURCES))
TEMP_OBJECTS = $(patsubst %.cpp,%.cpp.o,$(TEMP_OBJECTS))
I would try
TEMP_OBJECTS = $(patsubst %.c,%.c.o,$(patsubst %.cpp,%.cpp.o,$(SOURCES)))
or something like:
TEMP_OBJECTS_C = $(patsubst %.c,%.c.o,$(SOURCES))
TEMP_OBJECTS = $(patsubst %.cpp,%.cpp.o,$(TEMP_OBJECTS_C))
Both are pretty the same and will filter the sources twice.
or the anser which was given already:
TEMP_OBJECTS := $(patsubst %.c,%.c.o,$(SOURCES))
TEMP_OBJECTS = $(patsubst %.cpp,%.cpp.o,$(TEMP_OBJECTS))
The := will immediately evaluate the expression thus avoiding recursion.

Double "foreach" in makepp

I am trying to do something similar to nested loops with makepp, and can't figure out how to do it.
Here's basically what I am trying to do.
MODULES= A B C
TEMPLATES = X Y Z
#I'd like to make a target which needs both the MODULE and TEMPLATE:
$(MODULE)/$(TEMPLATE) :
#echo The Module is $(MODULE) and the Template is $(TEMPLATE)
#I've tried foreach, and can do something like:
$(MODULE)/$(foreach) : : foreach $(TEMPLATES)
#echo The Module is $(MODULE) and the Template is $(foreach)
#Or I can do:
$(foreach)/$(TEMPLATE) : : foreach $(MODULES)
#echo The Module is $(foreach) and the Template is $(TEMPLATE)
How can I create a set of rules that can work for any MODULE/TEMPLATE target?
I'd like the user to be able to have a target like:
makepp A/Z
not
makepp MODULE=A TEMPLATE=Z
Then how to make a target which would do the cross-product of all MODULES and TEMPLATES:
makepp all
The Module is A and the Template is X
The Module is A and the Template is Y
The Module is A and the Template is Z
...
The Module is C and the Template is X
The Module is C and the Template is Y
The Module is C and the Template is Z
This one is tricky. I am not a makepp expert but if it is sufficiently compatible with GNU make, as it claims to be, the following should do something close from what you want:
MODULES := A B C
TEMPLATES := X Y Z
ALL :=
define MODULE_TEMPLATE_rule
$(1)/$(2):
#echo The Module is $(1) and the Template is $(2)
ALL += $(1)/$(2)
endef
define MODULE_rule
$(foreach template,$(TEMPLATES),$(eval $(call MODULE_TEMPLATE_rule,$(1),$(template))))
endef
$(foreach module,$(MODULES),$(eval $(call MODULE_rule,$(module))))
all: $(ALL)
The magic wands, here, are this mixture of foreach, call and eval. The first argument of call is a variable name. In my example these variables are defined with the define-endef construct but it does not make any difference. call expands the variable, assigning its next arguments to the $(1), $(2)... local variables. So:
$(call MODULE_TEMPLATE_rule,A,X)
for instance, will return:
A/X:
#echo The Module is A and the Template is X
ALL += A/X
But returning is not instantiating. This is where eval enters the scene: it expands its argument and the result is parsed as any make statement. The foreach is there to iterate over the modules and templates, but you know this already.
Note that the ALL variable is progressively built by the iterators over modules and templates. So, if you type make all make will build all words in ALL, that is, print all combinations:
The Module is A and the Template is X
The Module is A and the Template is Y
The Module is A and the Template is Z
The Module is B and the Template is X
The Module is B and the Template is Y
The Module is B and the Template is Z
The Module is C and the Template is X
The Module is C and the Template is Y
The Module is C and the Template is Z
That's all. But be warned: in order to use this efficiently you must understand how make works, what it does and in what order. And here, the manual is mandatory.

Deferred assignment in makefile with one round of expansion

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.

Resources