Makefile target-based dependencies with a wildcard (compact) - makefile

I would like to apply the following code in a more compact way:
hello1:
#echo "Hello1"
hello2:
#echo "Hello2"
hello3:
#echo "Hello3"
hello_all: hello1 hello2 hello3
Is there a way of coding hello_all dependencies with a wildcard, e.g. hello* or something?

If your targets are very similar, go with #TimF's solution as it avoids repetition. If they are different and can't be generalized, you can do this with a small helper:
hello-add = $(eval HELLO_TARGETS += $1)$1
$(call hello-add,hello1):
#echo "Hello1"
$(call hello-add,hello2):
#echo "Hello2"
$(call hello-add,hello3):
#echo "Hello3"
hello_all: $(HELLO_TARGETS)
hello-add accepts an argument, adds it to the HELLO_TARGETS variable, and expands to exactly that argument (because eval expands to empty). It behaves like:
HELLO_TARGETS += hello1
hello1:
...
But avoids having to write the target name twice.

I don't think there's a native way to do it. Find below a solution specific to your usecase. I think however that it cannot easily be used in a generic way.
# The number list that will be used to generate the targets
NUMBERS = 1 2 3 4
# Function that will expand to a custom helloX target, with X the number given as parameter
# Note the strip that removes the spaces in the parameter
define createHelloTargets
HELLO_TARGETS += hello$(strip $(1))
hello$(strip $(1)):
#echo Hello$(strip $(1))
endef
# Generate one Hello target for each number in NUMBERS
$(foreach nb, $(NUMBERS), $(eval $(call createHelloTargets, $(nb))))
all: $(HELLO_TARGETS)
This will output :
Hello1
Hello2
Hello3
Hello4
The advantage is that you don't have to write each target, just fill the NUMBERS var and it does the trick.
Basically, this Makefile will create for each number X in NUMBERS a target that looks like this :
helloX:
#echo HelloX
It will also add helloX to HELLO_TARGETS which is a list of all the existing helloX targets. This list is expanded in the all target prerequisites.

Related

Make $* check for string

I am trying to check if $* matches hello . But the following is not working
build: build-hello
build-%:
ifeq ($*, hello)
echo Hello
else
echo World
endif
The conditions in the ifeq's are processed at makefile read time -- when $* is still blank. There's a couple of workarounds to this: First, you could do a build-hello: rule, which would override the build-% rule for build-hello. If, on the other hand you wanted to minimize rules, you could use the $(if) function as so:
build-%:
#echo $(if $(filter $*,hello),Hello,World)
Or, you could just use shell logic to accomplish this as well.

How to know if a makefile variable is a string of char or numbers?

It probably sounds very elementary but I am unable to find a way to classify a makefile variable into text or number. My pseudocode is like this:
ifeq ($N, 'numeric')
CFLAGS+=-D$N
endif
How to do this? I am using the GNU Make (in cygwin/Windows). I read the make.pdf that comes with it but could not find a way.
Thanks in Advance
EDIT: adopted a suggestion by bobbogo that does not depend on the number of characters to purge.
I assume you use GNU make. Here is a make-only solution, without calling the shell. For performance reasons, depending on your use of it, it can be preferable. Moreover, it does not depend on which shell make uses. Last but not least, it uses recursion and I like recursion:
define PURGE
$(if $(2),$(call PURGE,$(subst $(firstword $(2)),,$(1)),$(filter-out $(firstword $(2)),$(2))),$(1))
endef
DIGITS := 0 1 2 3 4 5 6 7 8 9
define IS_NOT_A_NUMBER
$(call PURGE,$(1),$(DIGITS))
endef
CFLAGS += $(if $(call IS_NOT_A_NUMBER,$(N)),,-D$(N))
all:
$(info N=$(N) => CFLAGS=$(CFLAGS))
Demo:
host> make N=12345
N=12345 => CFLAGS=-D12345
make: 'all' is up to date.
host> make N=foobar
N=foobar => CFLAGS=
make: 'all' is up to date.
Explanation: PURGE is a recursive macro that takes two arguments. The first one ($(1)) is a string to test, the second one ($(2)) is a list of words to match. If $(2) is the empty list PURGE returns $(1). Else, it calls itself with two new parameters:
the value of $(1) where the first word of $(2) has been substituted by nothing,
$(2) from which the first word has been removed
and returns the result. So, if you call PURGE with a string and the list of all digits, it returns the empty string if and only if the string contained only digits.
All make variables are strings. To find out whether a string is in fact a number, you need some elementary text analysis functions. GNU make itself does not offer anything convenient in this area, but you could run a shell command to do the job, perhaps like this:
define is_number
$(shell test '$(1)' -eq '$(1)' 2>/dev/null && echo yes || echo no)
endef
ifeq ($(call is_number, $(N)),yes)
default:
#echo N is a number
else
default:
#echo N is not a number
endif
This results in:
$ make N=5
N is a number
$ make N=string
N is not a number
However, such string processing can be quite unreliable if the string contains special characters.

How do computed variables passed to included make files' functions get expanded?

To reduce the amount of repeated boiler plate in a top level makefile, I created an included make file that uses computed variable names. Where I'm having difficulty is in the excerpt from the included makefile: ftp-files.mk:
...
$($(FNMPFX)_FTP_CFG): $(CFG_MAKE_FILE) | $($(FNMPFX)_FTP_CFG_DIR)
$(call ftp_helper, $#, $($(FNMPFX)_FTP_DIR), $($(FNMPFX)_CACHE_DIR), $($(FNMPFX)_FTP_NAME))
...
In the main makefile, I was hoping to do something akin to:
CFG_MAKE_FILE := Makefile
define ftp_helper
echo "quote USER anonymous" > $(1)
echo "quote PASS" >> $(1)
echo "cd $(2)" >> $(1)
echo "lcd $(3)" >> $(1)
echo "binary" >> $(1)
echo "get $(4)" >> $(1)
echo "quit" >> $(1)
endef
FNMPFX := FILE_A
include ftp-files.mk
...
FNMPFX := FILE_Z
include ftp-files.mk
...
The trouble is that the order only prerequisite (also tried it as a normal prerequisite) expands to the last ... instanciation (?) of the included file.
What appears to be happening is a first expansion of the two targets that behave as though they were written:
# Point of confusion V
# |
$(FILE_A_FTP_CFG): $(CFG_MAKE_FILE) | $(FILE_Z_FTP_CFG_DIR)
$(call ftp_helper, $#, $(FILE_A_FTP_DIR), $(FILE_A_CACHE_DIR), $(FILE_A_FTP_NAME))
$(FILE_Z_FTP_CFG): $(CFG_MAKE_FILE) | $(FILE_Z_FTP_CFG_DIR)
$(call ftp_helper, $#, $(FILE_Z_FTP_DIR), $(FILE_Z_CACHE_DIR), $(FILE_Z_FTP_NAME))
Is this possible?
My work around was to include that simple target rule in the main Makefile (ftp-file.mk is reasonably wordy at 100 lines), so having those two lines (repeated) throughout the main Makefile isn't too burdensome.
Can somebody suggest a working alternative?
In general you can't read a whole Makefile like a shell script. It's read in multiple phases, and certain things are done with the entire contents of the Makefile before proceeding to the next step. The rules are rather complex, but suffice it to say that it looks like the last assigned value is used:
$ cat Makefile
variable := original
first:
echo $(variable)
variable := other
second:
echo $(variable)
$ make first
echo other
other
$ make second
echo other
other

make functions in the prerequisites

I'm in the process of learning make and am getting stuck on whether it is possible to use functions in the prereq's list for a target. Say I have the following...
FILES = folder1/file folder2/file folder3/file
$(FILES) : code/my_script.bash $(subst folder,dir,$#)
bash code/my_script.bash $(subst folder,dir,$#)
In this case my_script.bash will run, even if folder1/file does not exist. I would anticipate that it would execute another rule to build that target first. When I replace the bash line with echo $^, all that is outputted is code/myscript.bash. It doesn't seem to be doing the replacement on $# to build the prereq. I see now that I can get the desired goal by using % in the target and prereq, but is it possible to use a function in the prereqs?
To use functions that depend on the target of the rule, you need to enable secondary expansion. To enable secondary expansion you need to use .SECONDEXPANSION: besides doubling your $ in the depdnencies. So:
FILES = folder1/file folder2/file folder3/file
all: $(FILES)
.SECONDEXPANSION:
$(FILES): code/my_script.bash $$(subst folder,dir,$$#)
bash code/my_script.bash $(subst folder,dir,$#)
What the doubling of the $ does is prevent the code from being evaluated during the first expansion.
You do not need secondary expansion if the functions you use in your prerequisites do not depend on variable that exist only when the rule is being evaluated (like $#).
FILES = folder1/file folder2/file folder3/file
$(FILES) : code/my_script.bash $$(subst folder,dir,$$#)
bash code/my_script.bash $(subst folder,dir,$#)

multi-wildcard pattern rules of GNU Make

I want to write something like regex:
SRC:="a.dat.1 a.dat.2"
$(SRC): %.dat.%: (\\1).rlt.(\\2)
dat2rlt $^ $#
so that a.dat.1 and a.dat.2 will give a.rlt.1 and a.rlt.2.
In GNU Make info page, it says "the % can be used only once".
Is there some trick to achieve this in GNU Make?
I'm afraid what you are trying to do is not possible the way you suggest to do it, since - as you already mention - (GNU) make only allows a single stem '%', see http://www.gnu.org/software/make/manual/make.html#Pattern-Rules:
A pattern rule looks like an ordinary rule, except that its target
contains the character ‘%’ (exactly one of them).
Without it, creating such 'multi-dimensional' targets is cumbersome.
One way around this is by rebuilding the name of the dependency in the command (rather than in the dependency list):
SRC := a.dat.1 a.dat.2
all : $(SRC:%=%.dat2rlt)
%.dat2rlt :
dat2rtl $(word 1,$(subst ., ,$*)).rlt.$(word 2,$(subst ., ,$*)) $*
Of course, however, this way you would lose the dependency, it will not rebuild once the rlt has been updated.
The only way I can see to address that is by generating the rules explicitly:
SRC := a.dat.1 a.dat.2
all : $(SRC)
define GEN_RULE
$1.dat.$2 : $1.rlt.$2
dat2rtl $$< $$#
endef
$(foreach src,$(SRC),$(eval $(call GEN_RULE,$(word 1,$(subst ., ,$(src))),$(word 3,$(subst ., ,$(src))))))
Using named variables, we can write more readable code (based on answer of Paljas):
letters:=a b c
numbers:=1 2 3 4
define GEN_RULE
$(letter).dat.$(number) : $(letter).rlt.$(number)
./rlt2dat $$< $$#
endef
$(foreach number,$(numbers), \
$(foreach letter,$(letters), \
$(eval $(GEN_RULE)) \
) \
)
We can generate SRC in a similar way. Note that using that method SRC will contain all the combinations. That may or may not be beneficial.
Building on the answer of Erzsébet Frigó, you might additionally choose to:
in the inner loop, eval not the macro itself but the result of calling it
name the macro after program you're calling, dat2rtl
in combination, allowing you to
refer to the program name using make's ${0}
define a target, ${0}s (expanding to dat2rts - note the pluralization) with preconditions of all combinations of letters and numbers on which dat2r2 was called
Like this:
letters:=a b c
numbers:=1 2 3 4
define rlt2dat
${0}s::$(letter).dat.$(number)
$(letter).dat.$(number): $(letter).rlt.$(number)
./${0} $$< $$#
endef
$(foreach number,$(numbers), \
$(foreach letter,$(letters), \
$(eval $(call rlt2dat))))
allowing you to build all rlt2dat targets as:
make rlt2dats
For the limited example you gave, you can use a pattern with one %.
SRC := a.dat.1 a.dat.2
${SRC}: a.dat.%: a.rlt.%
dat2rlt $^ $#
$* in the recipe will expand to whatever the % matched.
Note that the "s around your original macro are definitely wrong.
Have a look at .SECONDEXPANSION in the manual for more complicated stuff (or over here).

Resources