using variables in multiline define in gnumake - gnu-make

The Makefile:
define t1
a = $1
$(info $(a) -- $(1))
endef
list = x y z
$(foreach v,$(list),$(eval $(call t1,$(v))))
The output
-- x
x -- y
y -- z
The problem is delaying of value of 'a' by one invocation. Even a := $1 shows the same issue.
Any quick fixes ?
This is gnu make 3.81 .

This is one of the confusing things about using call and eval together: when to add extra quoting. The problem is that the call function is invoked first, and that will expand the argument once. Then eval is called and that evaluates its argument (the output from call).
In your example, the info function is being evaluated by call, the first time, and so in that situation a has not been set yet (because it's not set until the eval function runs). So you need to escape that function, and the expansion of a:
define t1
a = $1
$$(info $$(a) -- $(1))
endef

Related

zsh - how does the indexing for this associative array work?

The code below seems to be working correctly:
#!/bin/zsh
zparseopts -D -E -A opts f -foo
if [[ -n ${opts[(ie)-f]} || -n ${opts[(ie)--foo]} ]]; then
echo "foo is set."
else
echo "foo is not set."
fi
~/tmp > ./args.sh
foo is not set.
~/tmp > ./args.sh -f
foo is set.
~/tmp > ./args.sh --foo
foo is set.
What does the syntax for the index of opts mean i.e. (ie)-f? Is there some documentation where I can learn more about this? I don't even know what to search for to learn more about this kind of indexing.
My bad - I found it in the zsh manual here. It's explained in section 5.4.2 Using Associative Arrays.
To explain, it seems like this is a part of parameter substitutions in zsh. I don't know if it applies to bash as well.
This allows you to use certain "parameters" to perform some functionalities.
The syntax is to include the parameters within parentheses and prefix the specific part of the object that you want to substitute, modify etc.
For example, taking opts in my question,
echo ${opts}
prints the values of the associative array.
We have the parameter k which signifies the keys and v which signifies values. This can be used as follows:
echo ${(k)opts} # print only the keys
echo ${(kv)opts} # print the keys and values
To answer the main part of my question, what does (ie)-f mean, these are parameters that apply to the index of the associative array. Looking at the manual I had linked to, here is what i does - it searches for the key and returns the key instead of the value.
Explanation from the manual:
If instead of an ordinary subscript you use a subscript preceded by the flag (i), the shell will search for a matching key (not value) with the pattern given and return that. This is deliberately the same as searching an ordinary array to get its key (which in that case is just a number, the index), but note this time it doesn't match on the value, it really does match, as well as return, the key
And with regards to e - this seems a bit more complicated. But reading through the manual, it seems like this further evaluates the value if necessary i.e. in the event that it's not a constant.
Here is an example:
bar=4
foo='$bar'
> echo $foo
$bar
> echo ${(e)foo}
4
So combining the two together (ie) in my question returns the key and also expands it if necessary.

zsh command line processing - separating the last arguments from the previous ones

I am writing a zsh script, which is invoked with a variable number of arguments, such as
scriptname a b c d filename
Inside the script, I want first to loop over the arguments (except the last one) and process them, and finally do something with the processed data and the last argument (filename).
I got this working, but am not entirely happy with my solution. Here is what I came up with (where process and apply are some other scripts not relevant to my problem):
#!/bin/zsh
set -u
x=""
filename=$#[-1]
# Process initial arguments
for ((i=1; i<$#; i++))
do
x+=$(process ${#[$i]})
done
apply $x $filename
I find the counting loop too cumbersome. If filename where the first argument, I would do a shift and then could simply loop over the arguments, after having saved the filename. However I want to keep the filename as the last argument (for consistency with other tools).
Any ideas how to write this neatly without counting loop?
You can slice off the last argument from the original list and save them into an array, if thats an option
args=("${#:1:$# -1}")
for arg in "${args[#]}"; do # iterate over all, except the last
printf '%s\n' "$arg"
done
Using the array as a placeholder is optional as you can iterate over the arguments slice directly i.e. for arg in "${#:1:$# -1}"; do. The syntax is even available in bash also.
As pointed out by chepner's comment, you could use a zsh specifc syntax as
for arg in $#[1,-2]; do
printf '%s\n' "$arg"
done

GNU Make dynamically created variable name

This is my make recipe, I'm passing nonle value *, although bamboo_nonle_password variable is having password, still function is failing
validatevar.%: $(if $(IS_BAMBOO),)
#echo "\n################################################################################"
#echo ">>> validatevar"
#echo ">>> Start: Validate bamboo variables "
ifndef bamboo_$*_password
#echo "Bamboo variable bamboo_$*_password is empty"
exit 1
endif
ifndef bamboo_$*_server
#echo "Bamboo variable bamboo_$*_server is empty"
exit 1
endif
#echo "<<< Done: validatevar"
#echo "################################################################################\n\n"
You crazy person :)
Make will expand the recipe just before it wants to execute it.
At this point make knows what the % corresponds to,
and so can ensure $* is set appropriately.
Unfortunately, the ifndef…endif pair is evaluated as the makefile is read,
not as the recipe is expanded.
This suggests the answer:
use macros.
Maybe $(origin …) or $(flavor …) in something like
validatevar.%:
#echo
$(if $(filter undefined,$(origin bamboo_$*_password)),#echo "Bamboo variable bamboo_$*_password is undefined")
$(if $(filter undefined,$(origin bamboo_$*_password)),exit 1)
Naturally you can tidy this up a bit.
Maybe:
die-if-undefined = $(if $(filter undefined,$(origin $1)),$(error "$1" is undefined))
validatevar.%:
$(call die-if-undefined,bamboo_$*_password)
$(call die-if-undefined,bamboo_$*_server)
#echo
#echo
Two things to note:
The whole of the recipe is expanded in one go, before any of the lines are executed.
The upshot of this is that it doesn't matter where you expand the $(error) assertions.
I have put them at the top.
ifndef also detects empty variables, as well as undefined ones. You may wish to adjust die-if-undefined to take account of this (and rename it to die-if-empty).
(This would all be a lot easier if make would only take a --error-undefined-variables parameter,
just to match the existing --warn-undefined-variables parameter.)

How does gmake parse and execute template definitions

So, I have a make template. I invoke it like:
$(eval $(call PRIVATE_LIBRARY_TEMPLATE,privatelib1,64))
And it is defined like:
define PRIVATE_LIBRARY_TEMPLATE
# Evaluate the condition multiple times because of way make processes templates
$(if $(2)=='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),32)))
$(if $(2)=='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),64)))
$(if $(2)!='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),$(2))))
# More stuff that doesn't matter here
endef
or like:
define PRIVATE_LIBRARY_TEMPLATE
# Evaluate the condition multiple times because of way make processes templates
$(if $(2)=='',$(call LIBRARYBUILD_TEMPLATE,$(1),32))
$(if $(2)=='',$(call LIBRARYBUILD_TEMPLATE,$(1),64))
$(if $(2)!='',$(call LIBRARYBUILD_TEMPLATE,$(1),$(2)))
# More stuff that doesn't matter here
endef
previously it was defined as:
define PRIVATE_LIBRARY_TEMPLATE
$$(eval $$(call LIBRARYBUILD_TEMPLATE,$(1),32))
$$(eval $$(call LIBRARYBUILD_TEMPLATE,$(1),64))
before I add the $(if, $(1) is passed to the LIBRARYBUILD_TEMPLATE intact, but once I add the $(if, $(1) becomes an empty string.
I've tried various combinations of $$ $(eval $$eval etc, but there is something fundamental I'm just not understanding about the way gmake is parsing this template definition.
What I am trying to do is to make $(2) optional in this template, and use it if provided, or if not provided build both 32 and 64 bit libraries.
How is the template definition being initially parsed, and then evaluated.
First off the function $(if condition,then-part,else-part) is not the same as the other conditionals like ifeq. The $(if) function condition is simply checked for empty string or not (c.f. manual):
If it expands to any non-empty string, then the condition is considered to be true. If it expands to an empty string, the condition is considered to be false.
When it comes to $(eval $(call ...), things can become tricky in which order things must be evaluated. I usually think like this:
If the evaluated result of an operation depends on an argument (like $1), then the operation needs to be delayed with secondary expansion.
So in your case I think this is what you want:
.SECONDEXPANSION:
define LIBRARYBUILD_TEMPLATE
$$(info >> LIBRARYBUILD_TEMPLATE: $1 $2)
endef
define PRIVATE_LIBRARY_TEMPLATE
$$(info > PRIVATE_LIBRARY_TEMPLATE $1 $2)
ifeq ($2,)
$$(eval $$(call LIBRARYBUILD_TEMPLATE,$1,32))
$$(eval $$(call LIBRARYBUILD_TEMPLATE,$1,64))
else
$$(eval $$(call LIBRARYBUILD_TEMPLATE,$1,$2))
endif
endef
$(eval $(call PRIVATE_LIBRARY_TEMPLATE,privatelib1,64))
$(eval $(call PRIVATE_LIBRARY_TEMPLATE,privatelib2,))
Which gives the output:
> PRIVATE_LIBRARY_TEMPLATE privatelib1 64
>> LIBRARYBUILD_TEMPLATE: privatelib1 64
> PRIVATE_LIBRARY_TEMPLATE privatelib2
>> LIBRARYBUILD_TEMPLATE: privatelib2 32
>> LIBRARYBUILD_TEMPLATE: privatelib2 64
Let's use this definition of PRIVATE_LIBRARY_TEMPLATE
define PRIVATE_LIBRARY_TEMPLATE
# Evaluate the condition multiple times because of way make processes templates
$(if $(2)=='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),32)))
$(if $(2)=='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),64)))
$(if $(2)!='',$(eval $(call LIBRARYBUILD_TEMPLATE,$(1),$(2))))
endef
It is instructive to look in detail at what make does when it encounters
$(eval $(call PRIVATE_LIBRARY_TEMPLATE,privatelib1,64))
Clearly, before it can expand the $eval, make must first expand the $call:
1 is set to privatelib1
2 is set to 64
PRIVATE_LIBRARY_TEMPLATE is now expanded.
First off, a $(if …) needs expanding:
Make looks at the condition in $(if $(2)=='',$(call LIBRARYBUILD_TEMPLATE,$(1),32)), and so expands $(2)==''. You will note that 64=='' not an empty string, and so is considered true. To complete the expansion of the $(if …) , make thus chooses the true branch and goes on to expand $(call LIBRARYBUILD_TEMPLATE,$(1),32)
1 becomes privatelib1
2 becomes 32
$(call LIBRARYBUILD_TEMPLATE,privatelib1,32) becomes some text. No idea what, but since it will eventually be passed to $eval it must be valid make syntax. Let's assume it is something simple like LIB_privatelib1_32 := 1.
A second $(if …) is similarly expanded.
A third $(if …) is similarly expanded.
For the sake of argument, let's say the final expansion of the $(call PRIVATE_LIBRARY_TEMPLATE,…) is this text:
LIB_privatelib1_32 := 1
LIB_privatelib1_64 := 1
LIB_privatelib1_64 := 1
These three lines are passed to $eval.
As a side-effect, three new simple variables are defined.
The expansion of the $(eval …) though is empty.
Phew.
One obvious mistake here is that == is not valid make syntax.
Truthyness is merely whether a string has characters in it.
You probably want something like:
$(if $2,$(eval $(call LIBRARYBUILD_TEMPLATE,$1,32)))
$(if $2,$(eval $(call LIBRARYBUILD_TEMPLATE,$1,64)))
$(if $2,,$(eval $(call LIBRARYBUILD_TEMPLATE,$1,$2)))
(check out that last one for negations.)

Call command in Unix Make

I'm trying to understand a makefile, can anyone tell me what the following line does:
#echo cp -f --preserve=mode,timestamps $(call $1,$<) $(call $1,$#)
Especially I don't get what is the significance of $1 and call here.
There's no way to know what this does, since it's completely out of context.
It looks to me like this value is supposed to be passed to another instance of $(call ...). So for example if your makefile has:
QUOTE = '$1'
COPY = #echo cp -f --preserve=mode,timestamps $(call $1,$<) $(call $1,$#)
then later you would see something like:
foo: bar ; $(call COPY,QUOTE)
The first call would expand to the COPY value with $1 replaced with QUOTE, so it would be:
#echo cp -f --preserve=mode,timestamps $(call QUOTE,bar) $(call QUOTE,foo)
then that gets expanded, and you end up with:
#echo cp -f --preserve=mode,timestamps 'bar' 'foo'
But without more information we can't say more.
The 'call' command is a GNU extension in GNUmake; it is not supported in POSIX make or most other makes. It basically expands a macro with arguments. Something like
$(call A,b,c,d)
will expand the macro A with the arguments b, c, and d. The arguments are assigned to the temporary macros $(1), $(2), ... which may be present in the definition of A
See the GNUmake documentation

Resources