I define a macro in gmake to set a variable before calling the underlying function, and reset it afterwards:
define InstallWithPermission
$(eval ORIG_INSTALL_FILE_MODE = $(INSTALL_FILE_MODE))
$(eval INSTALL_FILE_MODE = $(1))
$(InstallFile)
$(eval INSTALL_FILE_MODE = $(ORIG_INSTALL_FILE_MODE))
$(eval ORIG_INSTALL_FILE_MODE = )
endef
Here the $(InstallFile) will call install command with INSTALL_FILE_MODE as its file mode.
In order to set variables in a macro definition, I use eval function
In order to make sure ORIG_INSTALL_FILE_MODE is local, I reset it to empty at the end of the maro
Should this the right way to manipulate "local variables" inside a macro definition?
Thanks.
There is a cleaner way to do what you're trying. The trick is to keep careful track of expansions, and in this case use simply expanded variables to avoid a circular definition:
define InstallWithPermission
ORIG_INSTALL_FILE_MODE := $(INSTALL_FILE_MODE)
INSTALL_FILE_MODE := $(1)
$$(InstallFile)
INSTALL_FILE_MODE := $$(ORIG_INSTALL_FILE_MODE)
endef
But I'd advise you to do without these local variables entirely, and have InstallFile accept an argument.
Related
Make can't define multiple target-specific variables.
But there is a macros from this answer that can solve the problem.
assign-vars = $(foreach A,$2,$(eval $1: $A))
But probably it can't set variable that depends on another variable. For example:
assign-vars = $(foreach A,$2,$(eval $1: $A))
multiple_variable = a=10 \
b=$(shell echo $$(( $(a)-1 )) )
$(call assign-vars, print, $(multiple_variable))
print:
#echo a=$(a)
#echo b=$(b)
I expected to see b=9 as a result from print target but I get b=-1.
How can I fix it?
The problem is that the entire contents of multiple_variable is expanded first, before the for-loop runs. So, it's not possible to refer to earlier variables in the assignment of later variables: they won't be set yet.
When you run:
$(call assign-vars, print, $(multiple_variable))
the first thing make does, is expand the variable multiple_variable: That expands to this:
a=10 b=-1
because when make goes to invoke the shell, the make variable a has not been set yet and so it runs echo $(( -1 )).
I don't really know of a good way to do what you appear to want to do... although I'm not sure I understand what you want to do anyway. It seems like you should take a step back and reconsider your requirements.
If you rewrite it to:
define newline
endef
assign-vars = $(eval $1: $(subst $(newline),$(newline)$1 :,$2))
define print_vars :=
a=10
b=$(shell echo $$(( $(a)-1 )) )
endef
that should do the trick.
EDIT: I only see now, that you are trying to calculate a target specific variable from anothen one. I don't think that will work, as I doubt that there is a specified order in which target specific variables are assigned. So a may or may not be defined when b receives its value.
I am trying to build a macro in Makefile which I can expand once but can still work as a macro after being expanded. This is useful to me as the first level expansion will fill in recursive parameters that won't last. Here's an example:
all: aperiod
TGT = hello
hello.TGT = world
world.TGT = period
define CREATE_TARGET
.SECONDARY: $(1)
$(3)$(1): $(4)$(2)
#echo $$$$(#)
$(foreach t,$($(1).TGT),$(call CREATE_TARGET,$(t),$(1),$$(1),$$(1)))
endef
define CREATE
$(call CREATE_TARGET,$(TGT),,$$(1),)
endef
CREATE_EXP := $(call CREATE)
TGT :=
$(eval $(call CREATE_EXP,a))
Error when running make:
make: *** No rule to make target aperiod', needed byall'. Stop.
TGT contains a changing set of values. I want CREATE_EXP to contain the full expanded creation method which accepts a parameter to give prefixes to the targets.
So optimally, I can call make aperiod and get hello world period, or call make bperiod after $(eval $(call CREATE_EXP,b)) and get the same thing
This is a highly reduced test case!
The value of CREATE_EXP is correct, but won't work for me as a macro anymore.
$(info $(value CREATE_EXP))
.SECONDARY: hello
$(1)hello:
#echo $$(#)
.SECONDARY: world
$(1)world: $(1)hello
#echo $$(#)
.SECONDARY: period
$(1)period: $(1)world
#echo $$(#)
I would like to know why Make behaves this way, as well as if there is a better way to accomplish the general gist of what I am trying to do.
EDIT: I found a solution to accomplish this, although I am still curious as to whether a call to $(call) can create a macro that still needs expansion.
define CREATE
define CREATE_EXP
$(call CREATE_TARGET,$(TGT),,$$(1),)
endef
endef
Use $(eval $(call CREATE))
The first time through, make will expand the variables inside. This allows for the recursive expansion as well as the creation of a function macro.
I would have to think more deeply about "a better way" and really understand what you're trying to do, but to answer "why make behaves this way": you are assigning CREATE_EXP as a simply-expanded variable, with :=:
CREATE_EXP := $(call CREATE)
That information is stored along with the variable and when make expands something like $(CREATE_EXP) it knows that the value of CREATE_EXP has already been expanded and it shouldn't be expanded again. That's the entire point, really, of using :=.
Here's an alternate model that might work for you:
$(foreach 1,a,$(eval $(CREATE_EXP)))
(I haven't tried this). The difference here is that first we set the variable 1 as the foreach variable, then we call eval in that context. Although the $(CREATE_EXP) expands to the value without further expansion, then eval will parse it as a makefile and expand it again, with 1=a set.
Just a note: this:
CREATE_EXP := $(call CREATE)
Is absolutely identical to this:
CREATE_EXP := $(CREATE)
If you pass no arguments to call it's the same as a simple macro expansion.
You might be interested to read the set of blog posts here: http://make.mad-scientist.net/category/metaprogramming/ (start from the oldest first).
I want to set a Global variable through a recipe then reference that variable in another independent recipe
The below code is an example code that sets the variable within the recipe but the variable stays with the initial value if referenced outside the recipe
ACTIVE = a
switch:
ifeq ($(ACTIVE),b)
ACTIVE=$(shell echo 'a')
else
ACTIVE=$(shell echo 'b')
endif
print:
$(info acitve = $(ACTIVE))
I know there are ways to broadcast the value of a target-specific variable to dependent targets, but that's not what I want.
You can use $(eval ...) for this, although it's almost always a bad idea. I have to assume that your real situation is much more complicated because there are many better ways to accomplish what you've actually provided in the sample makefile.
switch:
$(eval ACTIVE=$(if $(filter-out a,$(ACTIVE)),a,b))
Consider the following Makefile:
MAKEFLAGS += --warn-undefined-variables
define foobar
echo "$(1)"
endef
.PHONY: all
all:
$(foobar)
Is there a way to have macros with default parameters without producing undefined variable warnings?
I mean: sometimes I call "foobar" with a parameter, but sometimes not. In the latter case I'd like to have a default value for $(1).
You can't set a default value in the macro but you can easily add one when the parameter is expanded:
1:=
define foobar
echo "$(if $1,$1,default)"
endef
all:
$(foobar)
$(call foobar,biz)
$ make
echo "default"
default
echo "biz"
biz
It's a bit annoying if you use the parameter lots of times because you have to use the if for each use.
The GNU make syntax is very limited; it's not a full blown programming language, so many things are missing, like default parameters in make macros.
But the shell is a programming language! Why not implement your requirements in the commands of a target? It may be possible to use something like this:
all:
if test "$(SOMECONDITION)"; then \
do_one_thing; \
else \
do_something_else; \
fi
There is a decent solution for GNU make. Not sure how portable it is.
Use a global variable that embodies the rather messy logic, using a naming convention to avoid conflicts:
# foo.mk
MAKEFLAGS += --warn-undefined-variables
foobar_p1_default = default parameter
foobar_p1 = $(if $(filter undefined,$(origin 1)),$(foobar_p1_default),$1)
define foobar
#echo $#: "$(foobar_p1)"
endef
.PHONY: all defaulted global local
all : defaulted global local
defaulted:
$(foobar)
variable = global value
global local:
$(call foobar,parameter was $(variable))
local: variable = target specific value
The results:
$ make -f foo.mk
defaulted: default parameter
global: parameter was global value
local: parameter was target specific value
I would like create a function that takes a list as an argument, performs some operation and returns a list(or a scalar value). I would like to define the function using 'define'. The function:
1) Performs an operation on the input list
2) Checks if the resultant list is empty, if the list is empty raise an error.
3) Otherwise return the resultant list
This is easily possible in languages like C/C++. I am facing issues when I try to do this in MAKEFILE.
a) Can you point me to examples or post an example here?.
b) How does the MAKEFILE function returns value?
I checked the makefile documentation and few other places on the web but could not find anything useful. This would give me an idea on starting with functions. Thank you for your help!
define myfuntest
fnames := $(filter %pattern, $(1))
ifneq ($(fnames),)
$(error this is an error)
endif
endef
Caller function is something like:
abc := Documents Downloads
return_value := $(call mytestfun,$(abc))
I want 'fnames' to be returned in 'return_value'
User-defined macros must be a single "expression". The returned value is the result of expanding the expression. You definitely cannot use ifneq or variable assignments or other similar things in a user-defined macro.
You can create a makefile piece which is used alongside call, but it can only be used with eval, and that means it's a separate section of makefile, not really a "function" as normally defined.
So, if you can construct your user-defined macro with just make functions such that the result of the expansion is the result you want then you can do it as a macro; for example:
myfuntest = $(or $(filter %pattern,$(1)),$(error this is an error))
results := $(call myfuntest,foo barpattern biz baz)
If the result of the filter will either be a list of matching words and that will be assigned to results, or else it will run the error function.
However, if your function is more complex and cannot be expressed in an expression format, you will have to use eval, and pass in the name of the variable to be assigned, like this:
define myfuntest
... compute fnames from $(2) ...
$(1) := $$(fnames)
endef
You must be very careful with $ vs. $$, as always when using eval and call together. You then invoke this like:
$(eval $(call myfuntest,return_value,$(abc)))