make file substantial command - makefile

I am learning make and ran across a few commands that I do not understand. Can anyone help me understand these commands please?
I have the following few lines from a larger make file.
blank :=
blank +=
type_switcher = $(or\
$(if $(subst TEMP_WEIGHT,,$(firstword $(subst _,$(blank),$(1)))),,$(blank)),\
$(if $(subst TEMP_SPEED,,$(firstword $(subst _,$(blank),$(1)))),,-temp_speed),\
$(if $(subst TEMP_MASSN,,$(firstword $(subst _,$(blank),$(1)))),,-temp_mass -temp_massn),\
$(error No known type defined in command $(2)))
What is $(1) in this code?
Is there a way to print out all the variables fully expanded but without actually running make?

Related

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

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!

Get include tree of GNU makefiles

In an existing GNU Make build system, I'd like to see a tree of the makefile includes. How may I do this? Like Do you know tool building tree of include files in project\file? but for GNU Make rather than C and C++.
A related but slightly different capability I'd be interested in: being able to put $(info ...) statements in a makefile and have it print out a backtrace of includes for that info statement.
If you just want a list of included makefiles, a $(info Included files: ${MAKEFILE_LIST}) at the bottom of the main makefile fits the bill.
However, if you do want a tree, then you'll have to replace all include <file> with $(call include,<file>). The include function would be something like:
space :=
space +=
${space} := ${space}
stack := $(firstword ${MAKEFILE_LIST})
define includefunc
stack := $(subst ${ },|,$(strip ${MAKEFILE_LIST} $1)) ${stack}
$(info INCLUDES $(firstword ${stack}))
include $1
stack := $(wordlist 2,$(words ${stack}),${stack})
MAKEFILE_LIST := $(subst |, ,${stack})
endef
include = $(eval $(value includefunc))
$(call include,1.mak)
$(call include,_1/1.mak)
$(call include,folder/Makefile)
.PHONY: all
all: ; echo $# success

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