wildcard in Makefile (what does $(1) mean?) - makefile

In the following line,
selected = $(firstword $(foreach R,$(REPS),$(wildcard $(R)/$(1))))
what does $(1) mean?
I tried to see the value of $(1) by
$(info "dollar one is " $(1))
And it does not print anything. Does it mean it is some param. passed from somewhere and it is set NULL or is it a special variable used in wildcard?

This selected is an expression meant to be used by the call function. Like this:
$(call selected,foo)
Make will evaluate the expand the expression, assigning "foo" to $(1).
In this case it looks as if it might be meant for use with wildcards, like this:
SRCS := $(call selected,*.cc)

I am not pretty sure about how $(1) is being used in your make file but $(1) is the first argument passed in the terminal, say if you type make target then target will be assigned to $(1).

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)

Makefile dependencies based on target

I have a Makefile with user-specified input files in the variable INPUT_FILES.
For each input file, I need to create an input file prime.
Some notes:
Each input file can have an arbitrary file location
It is reasonable to assume there aren't duplicate filenames
Each output file needs to go into $(OUTPUT_DIR)
My basic strategy has been to generate the set of targets based INPUT_FILES and then try to determine which input file is the actual dependency of the target.
A few variations I've tried:
# Create a list of targets
OUTPUT_FILES = $(foreach file,$(notdir $(INPUT_FILES)),$(OUTPUT_DIR)/$(file))
# This doesn't work, because all input files are dependencies of each output file
$(OUTPUT_FILES): $(INPUT FILES)
program --input $^ --output $#
# This doesn't work because $# hasn't been resolved yet
$(OUTPUT_FILES): $(filter,$(notdir $#),$(INPUT FILES))
program --input $^ --output $#
# This doesn't work, I think because $# is evaluated too late
.SECONDEXPANSION:
$(OUTPUT_FILES): $(filter,$(notdir $$#),$(INPUT FILES))
program --input $^ --output $#
# This doesn't work either
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter,$(notdir $#),$(INPUT FILES))
program --input $^ --output $#
I've looked into static pattern rules as well, but I'm not sure if it can help with what I need.
In your case .SECONDEXPANSION: works because you can use make functions (filter) to compute the prerequisite of each output file. In other circumstances it could be impossible. But there is another GNU make feature that can be used in cases like yours: if you use GNU make you can programmatically instantiate make statements using foreach-eval-call. Just remember that the macro that is used as the statements pattern gets expanded twice, reason why you must double some $ signs (more on this later):
OUTPUT_DIR := dir
OUTPUT_FILES := $(addprefix $(OUTPUT_DIR)/,$(notdir $(INPUT_FILES)))
.PHONY: all
all: $(OUTPUT_FILES)
# The macro used as statements pattern where $(1) is the input file
define MY_RULE
$(1)-output-file := $(OUTPUT_DIR)/$$(notdir $(1))
$$($(1)-output-file): $(1)
#echo program --input $$^ --output $$#
endef
$(foreach i,$(INPUT_FILES),$(eval $(call MY_RULE,$(i))))
Demo:
$ mkdir -p a/a b
$ touch a/a/a b/b c
$ make INPUT_FILES="a/a/a b/b c"
program --input a/a/a --output dir/a
program --input b/b --output dir/b
program --input c --output dir/c
Explanation:
When make parses the Makefile it expands $(foreach ...): it iterates over all words of $(INPUT_FILES), for each it assigns the word to variable i and expands $(eval $(call MY_RULE,$(i))) in this context. So for word foo/bar/baz it expands $(eval $(call MY_RULE,$(i))) with i = foo/bar/baz.
$(eval PARAMETER) expands PARAMETER and instantiates the result as new make statements. So, for foo/bar/baz, make expands $(call MY_RULE,$(i)) with i = foo/bar/baz and considers the result as regular make statements. The expansion of $(eval ...) has no other effect, the result is the empty string. This is why in our case $(foreach ...) expands as the empty string. But it does something: create new make statements dynamically for each input file.
$(call NAME,PARAMETER) expands PARAMETER, assigns it to temporary variable 1 and expands the value of make variable NAME in this context. So, $(call MY_RULE,$(i)) with i = foo/bar/baz expands as the expanded value of variable MY_RULE with $(1) = foo/bar/baz:
foo/bar/baz-output-file := dir/$(notdir foo/bar/baz)
$(foo/bar/baz-output-file): foo/bar/baz
#echo program --input $^ --output $#
which is what is instantiated by eval as new make statements. Note that we had a first expansion here and the $$ became $. Note also that call can have more parameters: $(call NAME,P1,P2) will do the same with $(1) = P1 and $(2) = P2.
When make parses these new statements (as any other statements) it expands them (second expansion) and finally adds the following to its list of variables:
foo/bar/baz-output-file := dir/baz
and the following to its list of rules:
dir/baz: foo/bar/baz
#echo program --input $^ --output $#
This may look complicated but it is not if you remember that the make statements added by eval are expanded twice. First when $(eval ...) is parsed and expanded by make, and a second time when make parses and expands the added statements. This is why you frequently need to escape the first of these two expansions in your macro definition by using $$ instead of $.
And it is so powerful that it is good to know.
When asking for help please provide some kind of actual example names so we can understand more clearly what you have. It also helps us use terminology which is not confusing.
You really want to use $< in your recipes, not $^, I expect.
IF your "input files" are truly input-only (that is, they are not themselves generated by other make rules) then you can easily solve this problem with VPATH.
Just use this:
VPATH := $(sort $(dir $(INPUT_FILES)))
$(OUTPUT_DIR)/% : %
program --input $< --output $#
I finally found a permutation that works - I think the problem was forgetting that filter requires a % for matching patterns. The rule is:
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter %$$(#F),$(INPUT_FILES))
program --input $^ --output $#
I also realized I can use #F (equivalent to $$(notdir $$#)) for cleaner syntax.
The rule gets the target's filename on its second expansion ($$(#F)) and then gets the input file (with path) that corresponds to it on second expansion ($$(filter %$$(#F),$(INPUT_FILES))).
Of course, the rule only works if filenames are unique. If someone has a cleaner solution, feel free to post.

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)

Using eval with wildcard in a Makefile

Borne out of morbid curiosity and seeing CMake's ExternalProject, I've tried to hack up a cute little attempt at an automatic git-dependency manager for a C++ project, however I can't quite make Make dance the way I want it to.
# shortname, git address, configure, make (install), make clean
DEPENDENCIES:=\
catch,https://github.com/philsquared/Catch.git,true,true,true
, := ,
hit_subtree = git subtree $1 --prefix deps/$2 $2 master --squash
define get_or_update
$(if $(wildcard deps/$1/*),
git fetch $1 master && $(call hit_subtree,pull,$1),
$(if $(shell git ls-remote catch),
true,
git remote add -f $1 $2) && $(call hit_subtree,add,$1)
)
endef
update_cxx_flags = $$(if $$(wildcard deps/$1/include/*),$$(eval CXXFLAGS += -Ideps/$1/include),)
update_ld_flags = $$(if $$(wildcard deps/$1/lib/*),$$(eval LDFLAGS += -Ideps/$1/lib),)
define update_flags
$(eval $(call update_cxx_flags,$1))
export CXXFLAGS
$(eval $(call update_ld_flags,$1))
export LDFLAGS
endef
build_project = cd deps/$1 && $4 && $2 && $3
define git_dependency
$(call get_or_update,$1,$2)
$(call build_project,$1,$3,$4,$5)
$(call update_flags,$1)
endef
caller = $(call git_dependency,$(word 1,$1),$(word 2,$1),$(word 3,$1),$(word 4,$1),$(word 5,$1))
git_dependencies:
$(foreach dep,$(DEPENDENCIES),$(call caller,$(subst $(,), ,$(dep))))
#echo ${CXXFLAGS}
#echo ${LDFLAGS}
The problem lies in the update_flags function: specifically, update_flags tries to modify CXXFLAGS and LDFLAGS to account for new include/lib dirs however it seems that $(eval ...) isn't doing what I want it do. On the first run (i.e. when the directory is first being cloned) the $(wildcard ...) function sees no sub-directories of deps/$1 however if I invoke make a second time it then works fine. To me, this suggests that $(eval ...) isn't actually evaluating update_cxx_flags and instead the function is being non-lazily evaluated. What am I doing wrong?
Here is your SSCCE:
all:
touch foobar
echo $(wildcard foobar)
This 'does not work', as you observe, first time, but second time, it works. Why? Because, GNU Make first evaluates the whole recipe, before executing any lines of it. Then, after the recipe is evaluated (translated into the shell language), only then it is executed.
OK, you wanted to do it with $$, it still won't work, the double $ won't make it defer to the recipe execution, it will just evaluate twice during the processing of eval:
all:
touch foobar
$(eval $$(info $$(wildcard foobar)))
On the chat, I told you what is happening, but you are assuming some "caching".
You are a very knowledgeable person in certain areas, but you must remember when you learn something new, to start from the beginning and follow simple examples and manual. I am giving you simple examples, analyze them with the help of the manual and do not spin your own theories.
Mark's answer led me to google to work out why $(eval $$(wildcard foobar)) wouldn't behave as intended -- after all, at the very least GNU make promises to evaluate the argument as though it was 'typed' into your makefile.
It turns out that $(wildcard ...) is a little too smart for its own good: it caches directories and only updates the cache if a file is generated via a makefile rule. In this instance, the file is generated by dropping to shell and using git which violates the assumption that files are generated via makefile rules. Thus, the check in update_cxx_flags is incorrect (as well as update_ld_flags). Instead, it should be modified as so:
update_cxx_flags = $$(if `ls deps/$1/include/* 2>/dev/null`,$$(eval CXXFLAGS += -Ideps/$1/include),)
where the /dev/null clobber is so that an error message doesn't appear when the file doesn't exist. This makes the makefile behave as expected, which is what I wanted!

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