Can a rule be declared in a define in gnu-make? - gnu-make

I thought that I could use the define directive in make to make multiple alike rules
define TESTPRG
${1}:
echo Build ${1}
endef
$(call TESTPRG,x)
But I get this output
$ make
make: *** No rule to make target 'echo', needed by '
x'. Stop.
It looks like the newline in my define is lost
$ make -n -p | grep echo
make: *** No rule to make target 'echo', needed by '
x'. Stop.
echo Build ${1}
x: echo Build x
echo:
Am I just trying to do something that the define is not supposed to be used for.
Kjeld

This is exactly what define is supposed to be used for. You just need to remember to $(eval) it as a part of Makefile:
$ cat Makefile
define TESTPRG
${1}:
echo Build ${1}
endef
$(eval $(call TESTPRG,x))
$ make
echo Build x
Build x

Related

Makefile, iterate over dirs and exclude some

In a toplevel (unix, GNU) Makefile I want to iterate over a list of subdirs and execute tasks in there but exclude some of them. In this case exclude all subdirs starting with an underscore (_)
The following is working in iterating but ignores the exclusion regex. It still visits dir "_exclude".
In particular I think it ignores the "start of string" ^ (or it takes it literally). I have tried other regexes and they do work. Any idea how to fix that? Or indeed if you have any idea how to eliminate the shelling out or not using GNU makefile extensions?
SHELL=/bin/bash
EXCLUDE_DIRS_REGEX=^_
# later addition:
ALLMAKEFILES = $(shell find . -maxdepth 2 -type f -name Makefile)
SUBDIRS = $(filter-out ./,$(dir $(ALLMAKEFILES)))
all:
for dir in $(SUBDIRS); do \
if [[ "$$dir" =~ $(EXCLUDE_DIRS_REGEX) ]]; then continue; fi; \
make -C $$dir all; \
done
You have two problems to solve:
compute the list of target directories
call make in each of them
For the first one a mixture of make and shell functions or built-ins could be something like:
REGEX := ^_
ALLMAKEDIRS := $(patsubst %/,%,$(dir $(wildcard */Makefile)))
SUBDIRS := $(shell for d in $(ALLMAKEDIRS); \
do ! [[ "$$d" =~ $(REGEX) ]] && echo "$$d"; done)
I kept the REGEX just in case you have more complex regular expressions or you would like it to be easily modified. But of course if it is exactly ^_ and you never change it the following is simpler:
SUBDIRS := $(filter-out _%,$(patsubst %/,%,$(dir $(wildcard */Makefile))))
For the second one your approach is not very make-ish. A better approach would be to have one rule per subdirectory. The following is an example:
.PHONY: all $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# all
It has several advantages:
there is no need to loop over subdirectories in a complicated recipe, make will do it for you,
make can launch several jobs in parallel (see the -j option) and this will speed-up your build.
Note: calling make in a Makefile is not recommended. Prefer $(MAKE). See this section of the manual for the details.

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

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

Unix bash scripting and using the test -z script

So I wrote a script called MYSCRIPT with this code:
if test -z $1 ; then
echo "rm: missing operand"
echo "'try rm --help'" for more information.
fi
From my understanding it means: "If the $1 parameter does not exist, then echo: "rm: missing operand".
Yet if type "sh MYSCRIPT -i" then it still echoes this. Surely the $1 parameter is now equal to something (it is -i) so it should run?
This is one way but maybe not the best way to check if the argument given is empty.
I suggest you to use something like this, that counts the number of parameters given:
#!/bin/bash
[ $# -eq 0 ] && echo "no arguments given" && exit
echo "$1 is the input"
Test
$ ./test
no arguments given
$ ./test a
a is the input

How can I create make pattern rule that can expand the target as a dependency?

I'm trying to create a makefile that has rule something like:
~/$ cat > Makefile << EOF
FILES=a b c
$(FILES): % : src/%/%
#echo "$# $<"
EOF
Running the command gives:
~/$ make a
make: *** No rule to make target `src/a/%', needed by `a'. Stop.
And what I'd like to see is:
~/$ make a
a src/a/a
How do a create rule to expand the second %?
In this case,
secondary expansion
might help.
For example:
FILES=a b c
.SECONDEXPANSION:
$(FILES): % : src/$$*/$$*
#echo "$# $<"
If your GNU make's version is 3.80 or lower, $* might not work.
In that case, $# and some text manipulation might be needed instead.

Resources