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.
Related
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 =...
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.
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)) )
This is a quote from make's docs about the call function:
The call function can be nested. Each recursive invocation gets its own local values for $(1), etc. that mask the values of higher-level call. Here is an implementation of a map function:
map = $(foreach a,$(2),$(call $(1),$(a)))
Now it can map a function that normally takes only one argument, such as origin, to multiple values in one step:
o = $(call map,origin,o map MAKE)
and end up with o containing something like ‘file file default’.
Can somebody take the pain to explain what are they trying to convey and explain the code:
o = $(call map,origin,o map MAKE)
I find it useful sometimes to be able to make a local module (inside a parent module) with its own local symbols, which does a small task to be used only by the parent module. This is useful when the module becomes large, and there is no good reason to make smaller helper functions OUTSIDE the module as those helper functions are really needed and used by only that one parent module.
Here is a silly example with one module, and a helper module inside it to something
foo[x_] := Module[{r},
r = Module[{y, sol},
sol = First#Solve[y^2 + 3 y + 2 == 0];
y /. sol
];
x^r
]
But the problem in the above, is that the local variables for the inner module, could conflict with local variables with the parent module, because M notebook makes the inner module local variables red when I do the following, which tells me I am doing something I am not supposed to do: (I do not want to worry all the time with checking if I am using a unique local symbol for the inner module which is different from one used as local symbols for the parent Module, after all, it is supposed to be local. And also having to come up with a different symbol name when this is the case)
foo[x_] := Module[{r, y=0},
r = Module[{y, sol},
sol = First#Solve[y^2 + 3 y + 2 == 0];
y /. sol
];
x^r
]
Notice the red coloring, which according to the help, it is local scope conflict or shadowing in multiple contexts.
(M needs to use better colors, hard to make a difference between many colors, all shades of red).
(I think it is a shadowing warniong) Either way, it tells me I am not supposed to do this, even though I did not see any problem with such construct when I used it.
Value of parent module local variable 'y' in this example did not get over-written by the call to the inner module 'r' which is good.
Again, I did not want to make a function outside foo, because this small task is only used by foo[] and no need to move it to the Global context.
Ofcourse, I could always just write:
foo[x_] := Module[{r, y, sol},
sol = First#Solve[y^2 + 3 y + 2 == 0];
r = y /. sol;
x^r
]
But I am just giving an example, and this is for large module, where it helps to break the tasks inside the module itself into even few smaller tasks. Internal functions, inside functions is something I used before in other languages such as Ada/Pascal and such which has this construct and can be useful for large programs.
My question is: Just want to check with the experts here if it is safe for me to use the above, even though M gives me this red coloring warning? and if there is something I need to worry about doing this
thanks,
Yes, it is safe to use the same variable in nested Modules as long as you don't lose track of them. Mathematica treats each variable defined in a Module as local to that module. Local variables are Temporary, and are numbered as variable$ModuleNumber. You can check this for yourself with the following example:
Module[{x = 1},
Print[HoldForm#x, " = ", x]
Module[{x = 2},
Print[HoldForm#x, " = ", x]
Module[{x = 3},
Print[HoldForm#x, " = ", x]
];
Print[HoldForm#x, " = ", x]
];
Print[HoldForm#x, " = ", x]
]
(*Output
x$4576 = 1
x$4577 = 2
x$4578 = 3
x$4577 = 2
x$4576 = 1
*)
To the best of my knowledge this is a small issue with the detection.