gmake: eval inside a recipe in function - makefile

Is there a way to do such thing in a makefile for gmake:
GOALS := g1
define fun_one
#echo "blabla" #this causes an error - maybe can't be recognized as a recipe
endef
define fun_two
$(1):
$(eval $(call fun_one,$(1)))
endef
$(forech goal, $(GOALS), $(eval $(call fun_two,$(goal))))
all: ${GOALS}
As far as I understand, I can't define a part of a recipe outside a function that defines a rule, am I write?

There's no need for the first eval; it just tells Make to enact that line while it's still parsing the definition of fun_one. Eliminate the eval and the makefile will work:
define fun_two
$(1):
$(call fun_one,$(1))
endef

Related

GNU make: foreach behave different in defined and outside

I am trying to write a custom "function" in GNU Makefile. However, The "$(foreach" function behaves differently between inside "defined" and outside
Makefile steps:
Define current folder is "."
Get all the sub-folders
For each sub-folder, add the suffix "-subdir"
define get_folders
dirs:=$(dir $(wildcard $(1)/*/))
test_function:=$(foreach subdir,$$(dirs), $(subdir)"-subdir")
endef
CURRENT_FOLDER:=.
dirs:=$(dir $(wildcard $(CURRENT_FOLDER)/*/))
test_outside:=$(foreach subdir,$(dirs), $(subdir)"-subdir")
.PHONNY:test_function
test_function:
$(eval $(call get_folders,$(CURRENT_FOLDER)))
#echo "in-function" $(test_function)
#echo "outside" $(test_outside)
when I run the make, following is printed out:
in-function ./header/ ./Tools/-subdir
outside ./header/-subdir ./Tools/-subdir
As you can see, the result of commands outside are as I expected but for the in-function, it seems that all sub-folders are considered one string so only one suffix was added
Can you please kindly explain me why it happened this way and how can I change it?
Your problem comes from the way make expands your recipe. $(eval ...) first expands its parameter ($(call get_folders,$(CURRENT_FOLDER))). If you try to imagine the result you'll understand that the first line of your macro expands as:
dirs:=./header/ ./Tools/
But consider the second line:
test_function:=$(foreach subdir,$$(dirs), $(subdir)"-subdir")
The foreach will iterate only once, on the $(dirs) token, leading to:
test_function:=$(dirs)"-subdir"
Thus the result you observe. Try this, instead:
define get_folders
dirs:=$(dir $(wildcard $(1)/*/))
test_function:=$(foreach subdir,$(dirs), $(subdir)"-subdir")
endef
Note that mixing make and shell programming in your recipes is not ideal. It is difficult to understand, maintain and debug. You could as well separate the two worlds:
define get_folders
dirs:=$$(dir $$(wildcard $(1)/*/))
test_function:=$$(foreach subdir,$$(dirs), $$(subdir)"-subdir")
endef
CURRENT_FOLDER:=.
$(eval $(call get_folders,$(CURRENT_FOLDER)))
.PHONY:test_function
test_function:
#echo "in-function" $(test_function)
The $$ in the macro are used to escape the first expansion by eval, leading to:
dirs:=$(dir $(wildcard ./*/))
test_function:=$(foreach subdir,$(dirs), $(subdir)"-subdir")
If you plan to use this macro with several CURRENT_FOLDER values you can pass it a second parameter to personalize the variable names and avoid conflicts:
# $(1): current folder
# $(2): variable names prefix
define get_folders
$(2)-dirs:=$$(dir $$(wildcard $(1)/*/))
$(2)-subdirs:=$$(foreach subdir,$$($(2)-dirs), $$(subdir)"-subdir")
endef
CURRENT_FOLDER:=.
$(eval $(call get_folders,$(CURRENT_FOLDER),test_function))
.PHONY:test_function
test_function:
#echo "in-function" $($#-subdirs)
Even better, you could turn CURRENT_FOLDER into a target-specific variable:
# $(1): current folder
# $(2): variable names prefix
define get_folders
$(2)-dirs = $$(dir $$(wildcard $(1)/*/))
$(2)-subdirs = $$(foreach subdir,$$($(2)-dirs),$$(subdir)"-subdir")
endef
$(eval $(call get_folders,$$(CURRENT_FOLDER),test_function))
.PHONY:test_function
test_function: CURRENT_FOLDER = .
test_function:
#echo "in-function" $($#-subdirs)

How ifeq from Makefile works?

I have a Makefile with content:
define SOME_FUNC
ifeq (n,y)
$(warning TRUE)
else
$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
After executing "make" command I've got following output:
$ make
Makefile:10: TRUE
Makefile:10: FALSE
make: Nothing to be done for 'all'.
I cannot explain why it happens.
From the documentation:
It’s important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for “$” characters when using eval.
You need to double the dollars to have those $(warning ...) functions evaluated on interpreting ifeq instead of expanding eval/call:
$ cat Makefile
define SOME_FUNC
ifeq (n,y)
$$(warning TRUE)
else
$$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
$ make
Makefile:11: FALSE
make: Nothing to be done for 'all'.

GNU make 4.1: Missing separator when $(if ...) is true in a defined function

I am trying to generate an error in a Makefile when a string is not found in the output of a shell command. The shell command depends on a parameter, therefore the whole thing is in a defined function. Here is a minimalist example:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))
$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)
all:
#echo Hello, world!
I would like this Makefile to output Hello, world! in this case, but I'd like it to output xyz not in abcdefg if I replace the call line with this one:
$(call check_in_abcdefg,xyz)
The problem is that with the def check I have this output:
Makefile:6: *** missing separator. Stop.
Where line 6 is $(call check_in_abcdefg,def)
Why does the syntax check fail when the $(if ...) condition is true since it's actually empty ?
Note that the echo command in the dummy target all is correctly preceded by a tab, not four spaces. I am running GNU make 4.1.90 built for Windows32, and it seems not to happen for newer version of GNU make. I am looking for any answer that could help me make it work with GNU make 4.1.90
I'm not sure why older make versions choke here, but you can make it work with one big $(eval ) like this:
define check_in_abcdefg
$(eval
TMP := $$(shell echo abcdefg)
ifeq ($$(findstring $$(1),$$(TMP)),)
$$(error $$(1) not in $$(TMP))
endif
)
endef
$(call check_in_abcdefg,def)
all:
#echo Hello, world!
To answer the question about why GNU make 4.1 is throwing this error: that version of GNU make is mishandling the newline. In your example:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))
$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)
The first line of the defined macro (the eval) expands to the empty string, and so does the second line (the if). So, the call expands to a single newline character.
That version of GNU make is not correctly ignoring this newline character and instead throws an error. You can change your makefile to work in those older versions by removing the newline:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)

Makefile: Targets for Crosscombinations

I have 2 types of files. Scripts and datasets. I want to write a makefile to run each script with each dataset.
My idea was to create a target for each output, where the target has a name containing the script and the model name. These targets would match a "target-pattern"-rule (of type: some_folder/%.eval). Now the "target-pattern"-rule would need to figure out from its name which files are needed. But this seems to be a hard job.
Is there a better, more elegant way?
Example:
Files: ScriptA, ScriptB, InputA, InputB
Targets/Outputfiles: InputA_ScriptA, InputA_ScriptB, InputB_ScriptA, InputB_ScriptB
# generate all combinations
RT_HW = $(foreach script,$(RT_SCRIPT_HW),$(foreach input, $(RT_INPUTS), $(input)_$(script)))
$(SANDBOX)%.eval: <requires Script X and input X>
You've run into one of the major shortcomings of Make: it isn't very good with wildcards.
You can get the effect you want by generating rules with eval, either by iterating over both variables:
define template
$(1)_$(2).eval: $(1) $(2)
#echo target is $$#
#echo running $(2) on $(1)
endef
$(foreach script,$(RT_SCRIPT_HW),$(foreach input, $(RT_INPUTS), $(eval $(call template,$(input),$(script)))))
or by generating pattern rules, iterating over only one, such as the input:
define template
$(1)_%.eval: $(1) %
#echo target is $$#
#echo running $$* on $(1)
endef
$(foreach input, $(RT_INPUTS), $(eval $(call template,$(input))))

Defining custom GNU make functions

What is the problem with the dep2 function in the sample code below?
dep1 = $(eval makefile_list_$1 := $(MAKEFILE_LIST))$(eval -include $1.mk)$(eval MAKEFILE_LIST := $(makefile_list_$1))
define dep2
$(eval makefile_list_$1 := $(MAKEFILE_LIST))
$(eval -include $1.mk)
$(eval MAKEFILE_LIST := $(makefile_list_$1))
endef
$(call dep1,test)
$(call dep2,test)
.DEFAULT_TARGET: all
.PHONY: all
all:
#echo $#
GNU make 3.81 and 3.82 produce Makefile:10: *** missing separator. Stop. which points to the dep2 call, dep1 is run without errors. The only difference between the two variants is the newlines in dep2 (and the whole point why I'd like to use define).
You forgot the =:
define dep2 =
EDIT:
Put a semicolon at the end of each line. I've tested this and it works (in GNUMake 3.81).
define dep2
$(eval makefile_list_$1 := $(MAKEFILE_LIST));
$(eval -include $1.mk);
$(eval MAKEFILE_LIST := $(makefile_list_$1));
endef
Why these semicolons are necessary I don't know, but in the documentation define seems to be used for multi-line "variables" only when defining sequences of shell commands to be used in recipes, not Make commands, so maybe the rules are a little different.
I would move the $(eval ...) calls outside of dep2. By doing it this way, there's no need for semicolons in dep2. This means doubling the $ signs of some expansions to avoid expansion being done too early. So:
define dep2
makefile_list_$1 := $$(MAKEFILE_LIST)
-include $1.mk
MAKEFILE_LIST := $$(makefile_list_$1)
endef
$(eval $(call dep2,test))
# Quick checks for testing, to be removed from the final code...
$(info $(makefile_list_test))
$(info $(MAKEFILE_LIST))
.DEFAULT_TARGET: all
.PHONY: all
all:
#echo $#
I've tested the code above and it works with Gnu Make 4.0. I would expect it to work back to Gnu Make 3.8x. The $(eval $(call ...)) pattern is what I always do to execute my custom functions, and I've used it for quite a while now.
You can do as the below line to kill the error:
FOO := $(call dep2, test)
I guess the reason is the early version of gcc (3.8.1/2) can only accept nothing as the return of expression.
eg $(info string) returns nothing, but $(call dep2, test) returns 2 newlines charaters.
There is much that can be improved in what you are doing. For one thing you really want to factor the eval calls to a single call at the top.
Your particular problem, however, stems from not understanding that the multiline recursive string the make's define command uses never includes the lady new line. The most natural convention for writing evalable functions is
define Foo
Line1
Line2
endef
You can look at the string eval is seeing and see what this does via the info command, e.g.
$(info $(call Foo,x) $(call Foo,y)).

Resources