GNU Make dynamically created variable name - gnu-make

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

Related

GNU make pattern rules with different file base names

I have a data processing job that I would like to automate with Make. Hundreds of files need to be processed, in several steps.
Unfortunately, the base name will change for at least one of the steps, but it would be easy to write these dependencies into a separate file that then is included.
However, I'd like to avoid also writing the build instructions (which are quite complicated) for all these files separately.
I envisage something along these lines:
# automatically generated rules, included into make file
dir1/test.bb: dir2/test_other_name.aa
# (many more rules like the above, linking xxx.bb to yyy.aa)
# pattern rule
%.bb: %.aa
# build step using $# $>
What I would like is the pattern rule to provide the rules, and the explicit rule defining the dependencies. Can something like this be achieved?
When make's noddy patterns don't cut the mustard,
just write out the rules explicitly.
(This has the happy side effect of not using pattern rules.)
Let's say you have a function src-to-target which will generate the target filename (i.e., $(call src-to-target,dir2/test_other_name.aa) expands to dir1/test.bb.
Also, you have a list of sources in ${srcs}, and ${recipe} is a list of shell commands using $#, $< etc.
define src-to-target = ... # $1:source
define recipe =
echo Building $# from $<
⋮
endef
define generate-rule = # $1:source
target := $(call src-to-taget,$1)
targets += $${target}
$${target}: $1 ; $${recipe}
endef
$(foreach _,${srcs},$(eval $(call generate-rule,$_)))
.PHONY: all
all: ${targets} ; : $# Success
The $(foreach ...) does all the work here.
So, looking at that in painful detail,
First expand ${srcs}
Set $_ to the first in the list (dir2/test_other_name.aa say)
Expand $(call generate-rule,$_)
Expand $(call generate-rule,dir2/test_other_name.aa)
$1 is set to dir2/test_other_name.aa, and the expansion of $(generate-rule) follows, leading to this block of text
target := dir1/test.bb
targets += ${target}
${target}: dir2/test_other_name.aa ; ${recipe}
As a side effect, $(eval) swallows the above text. The expansion of the $(eval) though is empty.
$_ is set to the next source file.
Wash, lather, rinse, repeat
Once the $(foreach) is complete,
${targets} contains the complete list of targets.
Parallel safe too.
What's not to like?

Set a variable inside prerequisites or eval function

I have this code, and it works, but as you see I do the substitute three times, I would like to set a variable to the value, but with no success
$($(PKG)-py-valgrind-tests-status): $($(PKG)-swig-dlib)
$($(PKG)-py-valgrind-tests-status): $(OBJ_OUTPUT_DIR)%.valgrind_passed: %.py
#echo env $(PKG-TEST-HELPER-ENV) valgrind $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v ; \
env $(PKG-TEST-HELPER-ENV) $(VALGRIND) $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v \
|| (cat $(subst valgrind_passed,valgrind.log,$#); exit 1)
#touch $#
the problematic line $(subst valgrind_passed,valgrind.log,$#)
I tried:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $(subst valgrind_passed,valgrind.log,$#)
and
.SECONDEXPANSION:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#)
and(inside the recipe)
$(eval LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#))
but for all, if I write
--log-file=$(LOG-FILE)
log file come's up empty.
I have no more ideas on how to go forward,
appreciate the help, thanks!
Are you sure you're using GNU make? What version are you using (run make --version)?
There's nothing wrong with your first attempt, using a target-specific variable. If this doesn't work then you've got something wrong or different about your makefile that you haven't explained. Maybe if you showed the actual complete section of the makefile with the target-specific variable being set and used we might see what's wrong.
Your second attempt can't work because secondary expansion applies only the prerequisites, not target-specific variables (but, as above, it's not needed anyway).
Your third attempt might work but again, without seeing exactly what you do with the eval we can't say for sure. You don't need to double the $ before the subst function; it can be expanded first and it will still work.

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

ifeq issue: compare 2 strings with a dot included

I am trying to implement a simple string comparison to get the type of a file (using its extension) like this:
extract_pkg: $(PKG)
$(eval EXT := $(suffix $(PKG)))
#echo $(EXT)
ifeq ($(EXT), .zip)
#echo "is zip file"
else
#echo "is not a zip file"
endif
extract_pkg : PKG = mypkg.zip
However, when I run it it goes into the else branch. My guess is, it has to do with the dot, but I dont find a solution. Thanks for your help !
Edit 1: the essential code would be actually somewhat like the following, and it works as expected:
test_cmp:
ifeq (.zip,.zip)
#echo ".zip==.zip"
endif
ifeq (zip,zip)
#echo "zip==zip"
endif
thus the problem is somewhere else !
One thing to be careful about -- spaces in if constructs are significant. So if you have something like:
ifeq ($(EXT), .zip)
it will only match if $(EXT) expands to exactly ".zip" -- including the space before the period. So your first example will always print is not a zip file, since $(EXT) will never contain the space.
You cannot use ifeq() etc. inside recipes. ifeq() are preprocessor statements: they are interpreted immediately as the makefile is read in. Recipes are not run until much later, after all makefiles are parsed and make decides that this target needs to be updated. So trying to set a variable in a recipe using eval, etc. then test that variable using ifeq() cannot work.
You have to use shell constructs for this; something like:
extract_pkg: $(PKG)
#EXT=$(suffix $<); \
echo $$EXT; \
if [ $$EXT = .zip ]; then \
echo "is zip file"; \
else \
echo "is not a zip file"; \
fi

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