Export a function in Makefile - function

I'd like to use a function in a recursive call of make.
I have the following in the principal Makefile:
define my_func
Hello $(1) from $(2)
endef
export my_func
And further, in another one called later, I have :
$(error my_func is $(call my_func,StackOverFlow,Me))
It gives me this output Makefile_rec.mk:1: *** my_func is Hello from . Stop.
But what I'd like is Makefile_rec.mk:1: *** my_func is Hello StackOverFlow from Me. Stop.
Is there a way using make to export such a variable/function and get it to work when using call function ?
Thanks !
-- EDIT --
As pointed by #Renaud Pacalet here, I would like also to use this kind of macro either in the current Makefile and in sub-makes.
If possible, not by including a file each time I need the macro

If you want this to work you must double the $ signs in your definition:
define my_func
Hello $$(1) from $$(2)
endef
export my_func
From the manual:
To pass down, or export, a variable, make adds the variable and its
value to the environment for running each line of the recipe. The
sub-make, in turn, uses the environment to initialize its table of
variable values.
You must protect the $ from one extra expansion.
Of course, you cannot use the same macro in the top Makefile. If it is a problem, you must define a macro for the top Makefile and another one for the sub-make Makefile:
host> cat Makefile
define my_func
Hello $(1) from $(2)
endef
my_func_1 := $(call my_func,$$(1),$$(2))
export my_func_1
all:
$(MAKE) -f Makefile_rec.mk
$(info TOP: $(call my_func,StackOverFlow,Me))
host> cat Makefile_rec.mk
all:
$(info BOT: $(call my_func_1,StackOverFlow,Me))
host> make --no-print-directory
TOP: Hello StackOverFlow from Me
make -f Makefile_rec.mk
BOT: Hello StackOverFlow from Me
make[1]: 'all' is up to date.

Related

I can not understand the output of patsubst in a makefile

This is the guilty makefile :
$ cat -n example.mak
1 define this
2 $(patsubst $(1)/%.o,%.o,why_this_does/that.o)
3 $(patsubst butnot/%.o,%.o, butnot/but_not_that.o)
4 endef
5
6 why:
7 $(info $(call this, why_this_does) ?)
And this is my question :
$ make -f example.mak
why_this_does/that.o
but_not_that.o ?
make: 'why' is up to date.
The root cause is not in patsubst but in call. The manual has a note:
A final caution: be careful when adding whitespace to the arguments to call. As with other functions, any whitespace contained in the second and subsequent arguments is kept; this can cause strange effects. It’s generally safest to remove all extraneous whitespace when providing parameters to call.
and indeed, if you replace
$(info $(call this, why_this_does) ?)
by
$(info $(call this,why_this_does) ?)
you get what you want.

Local variable in makefile is not expanded correctly

I have the following function in makefile:
define INSTALL_SCRIPT
SRC_DIR = $(ROOT)\src
cd $(SRC_DIR)
$(SRC_DIR)\stage.bat
endef
I also echo the steps, so here's the output of the above snippet:
$SRC_DIR = C:\project_root\src
'SRC_DIR' is not recognized as an internal or external command,
operable program or batch file.
$cd
C:\project_root
\stage.bat
'\stage.bat' is not recognized as an internal or external command,
operable program or batch file.
It seems that in assignment statement the value is expanded correctly but then $(SRC_DIR) gives an error. Then cd goes to one directory up (and not src), then when I need to execute the batch file, $(SRC_DIR)'s value seems to be empty.
Assuming you're trying to do this from a recipe context, you would need to do it as follows:
define INSTALL_SCRIPT
set SRC_DIR=$(ROOT)\\src & \
cd %SRC_DIR% & \
%SRC_DIR%\\stage.bat
endef
sometarget:
#$(INSTALL_SCRIPT)
You need the \ at the end of each line to concatinate them into a single recipe line (other wise the variable you set will fall out of context when the first recipe line's shell terminates). You seem to be using windows so I believe you need to use the %varname% syntax to refer to the variables. Notice that $(ROOT) is a makefile variable in this case, so it still uses the $ syntax. (Note that if you were in bash you would need to use $$ to refer to shell variables). You also need to double the \\ in directory names, as make will interpret the first slash as an escape, and then pass a single slash to cmd.
Note that my windows machine doesn't have make installed on it, so I couldn't test the above, so it's quite possible I missed something.

gmake function / ifneq/else/endif

I am trying to create a function that will determine if a directory exist, and if it does, add a target to the all list. but something is wrong. Here is the Makefile code snippet:
define buildMQ
$(info **** Checking to see if the MQ series directory exist *****)
ifneq "$(wildcard $(MQ_DIR) )" ""
$(info /opt/mqm was found)
MQ_APPS=MQSAPP
else
$(error $n$n**** ERROR - The MQ Series direcory: "$(MQ_DIR)" does not exist ******$n$n)
endif
endef
ifeq ('$(FN)' , 'TEST')
TEST_APPS=
endif
ifeq ('$(FN)' , 'ONSITE_TEST')
ONSITE_TEST_APPS= # insert ONSITE_TEST only apps here
$(call buildMQ)
endif
ifeq ('$(FN)' , 'ACCOUNT')
ACCOUNT_APPS=
$(call buildMQ)
endif
all:$(COMMON_APPS) $(TEST_APPS) $(ONSITE_TEST_APPS) $(ACCOUNT_APPS) $(MQ_APPS) makexit
and when I run it with FN = ONSITE_TEST:
**** Checking to see if the MQ series directory exist *****
/opt/mqm was found
Makefile:128: ***
**** ERROR - The MQ Series direcory: "/opt/mqm" does not exist ******
How can both print statements get printed? What am I missing?
The directory does exist
There's a lot of misunderstanding here about how call works. The call function takes a variable (name), plus zero or more arguments. It assigns the arguments to $1, $2, etc. and then it expands the variable.
Note that by "expands" here we don't mean "interprets the variable value as if it were a makefile". We mean very simply, go through the value of the variable and locate all make variables and functions and replace them with their appropriate values.
So, you invoke $(call buildMQ). This will not assign any values to $1, etc. since you didn't provide any arguments: in effect this is exactly the same as just using $(buildMQ); the call function has no impact here.
So make expands the value of the buildMQ variable... basically it takes the value as one long string:
$(info **** Checking to see if the MQ series directory exist *****) ifneq "$(wildcard $(MQ_DIR) )" "" $(info /opt/mqm was found) MQ_APPS=MQSAPP else $(error $n$n**** ERROR - The MQ Series direcory: "$(MQ_DIR)" does not exist ******$n$n) endif
and expands it. So first it expands the $(info ... Checking ... function and prints that. Then it expands the $(wildcard ..) and replaces that. Then it expands the $(info /opt/mqm ...) and prints that. Then it expands the $(error ...) and shows the message and exits.
If it hadn't exited, then you'd have a syntax error because a function like call cannot expand to a multi-line statement; as above it's not evaluated like a set of makefile lines. It has to expand to a single value makefile line.
You need to use the eval function if you want make to parse the contents of a variable as if it were a makefile; eval doesn't take a variable name it takes a string to parse, so it would be:
$(eval $(buildMQ))
However, this won't do what you want for the same reason: it expands the buildMQ variable and that causes all the functions to be expanded first, before eval even sees them.
One option would be to escape all the variables and functions reference in buildMQ. But in your situation a simpler solution is to use the value function to prevent expansion before eval sees the value:
$(eval $(value buildMQ))

Encountered an issue setting the value of a variable inside a Makefile function

Directory structure (to run this example) looks like this:
example
|-- Makefile
|-- foo
| |-- foo.h (empty file)
|-- bar
| |-- bar.h (empty file)
Makefile looks like this:
TARGETS=foo bar
define GEN_HEADER_RULE
$(1)_INCS = $(wildcard $(1)/*.h)
$(info $$($(1)_INCS) is [$($(1)_INCS)])
$(info $$(wildcard $$(1)/*) is: [$(wildcard $(1)/*)])
endef
$(foreach t,$(TARGETS),$(eval $(call GEN_HEADER_RULE,$t)))
all:
#echo "DONE"
Actual output is:
$(foo_INCS) is []
$(wildcard $(1)/*) is: [foo/foo.h]
$(bar_INCS) is []
$(wildcard $(1)/*) is: [bar/bar.h]
DONE
Expected output is:
$(foo_INCS) is [foo/foo.h]
$(bar_INCS) is [bar/bar.h]
I'm failing to understand why the value of the variable $(1)_INCS is not being set to be the output of the wildcard function? The wildcard function is clearly working without issue - as shown by the $info command dumping the output directly from it.
I have constructed this simple Makefile to demonstrate the same issue I have encountered in a much larger build system.
You forgot to protect variables and functions from double expansion. Double all $ except $(1) and you should see almost what you want (except for the second $(info...) that will produce more output than what you expect).
host> cat Makefile
TARGETS=foo bar
define GEN_HEADER_RULE
$(1)_INCS = $$(wildcard $(1)/*.h)
$$(info $$$$($(1)_INCS) is [$$($(1)_INCS)])
$$(info $$$$(wildcard $(1)/*) is: [$$(wildcard $(1)/*)])
endef
$(foreach t,$(TARGETS),$(eval $(call GEN_HEADER_RULE,$t)))
all:
#echo "DONE"
host> make
$(foo_INCS) is [foo/foo.h]
$(wildcard foo/*) is: [foo/foo.h]
$(bar_INCS) is [bar/bar.h]
$(wildcard bar/*) is: [bar/bar.h]
DONE
When using foreach-eval-call remember that there is a first expansion by eval-call and a second expansion by make when it parses the result as make syntax. You frequently need to escape the first expansion ($$) and even sometimes the second one ($$$$). See the GNU make manual for a detailed explanation.

GNU make call function with multiple arguments and multiple commands

I am trying to write a GNU make call function (example below) which has multiple shell commands to execute, such that it can be called with different arguments.
shell_commands = $(shell echo $(1); ls -ltr $(2))
try:
$(call shell_commands,$(FILE1),$(FILE2))
1) Is above the correct way to write a call function with multiple commands? By using a semi-colon to separate them? To make it readable, I write my targets as shown below. Is there a similar way to write a call function?
shell_commands:
echo $(1)
ls -ltr $(2)
2) I get this error from make when I execute make -B try. It looks like it is trying to execute /home/user/file1. But why?
make: execvp: /home/user/file1: Permission denied
make: *** [try] Error 127
3) Is it possible to pass variable number of parameters to a call function? Like pass in just the second parameter and not the first one.
$(call shell_commands,,$(FILE2))
I tried google-ing, searching on SO, and looking on gnu.org but I did not get any solutions. Will appreciate any answers or pointers to any resources which document call function with multiple optional arguments and commands.
Question 1: No, this is not right. The shell make function should NEVER be used inside a recipe: the recipe is already running in the shell, so why would you run another shell? It's just confusing. Second, it's generally recommended to use && between multiple commands in a recipe, so that if the first command fails the entire command will immediately fail, rather than continuing on and perhaps succeeding. Of course, that is not always correct either, it depends on what you're trying to do.
Question 2: This happens because the shell make function is like backticks in the shell: it expands to the output printed by the shell command it runs. Your shell command that make runs is:
echo $(1); ls -ltr $(2)
(where, one assumes, $1 expands to /home/user/file1) which prints the string /home/user/file1. After the expansion, that string is substituted into the recipe and make tries to run that recipe, giving the error you see above.
You want this, most likely:
shell_commands = echo $(1) && ls -ltr $(2)
try:
$(call shell_commands,$(FILE1),$(FILE2))
Now the call expands to the actual text, not an invocation of make's shell function, and then that text is run as the recipe.
Question 3: Sure, just using empty parameters means that the variable $1 (in this case) expands to the empty string.