why makefile function also works in recipe part? - makefile

According to the doc, in the recipe part, the code after a tab is send to shell. Then shell should not know makefile function. For example
a:
<tab>$(info ===)
if I run make a, it outputs
===
make: `a' is up to date.
Why? Shell doesn't know $(info ===), why it can output ===?

$(info ...) isn't being interpreted by shell, it is actually a special function in make. The info function prints the expanded arguments, which in this case are just a constant === back to the console. Here is an excerpt from the GNU documentation:
This function does nothing more than print its (expanded) argument(s) to standard output... The result of the expansion of this function is the empty string.
You can read more about it here.

Related

GNU make use variable value to call a "function"

I want to iterate through the list, LIST, and want to call a function which writes something into a text file using the name of the extracted list elements as name of the call to the "function".
.PHONY: GEN_BAT
GEN_BAT:
for comp in $(basename $(notdir $(LIST))); do \
echo comp: $$comp ;\
$(call $$comp)
done; \
If I write the line $(call $$comp) without a ;\ I'll receive "syntax error: unexpected end of file".
If I write the line with the original function e.g. $(call FUNCTION);\ with a ;\ it calls the existing function as expected. So, something is wrong with the used variable?
How can I persuade make to use the value of $$comp as a call to "function"?
It's not clear at all what you want to do; providing a complete, minimal example would help a lot.
However, it's not possible to use a shell loop then try to utilize the shell loop variable in a make function. If you think about it that doesn't make any sense: the shell is a completely different program than make, make can't know what the shell's variable are set to, and the shell can't call make functions.
Make runs a command like this: first the entire recipe is expanded so that ALL make variables and functions are resolved. Then make takes that expanded command string and gives it to the shell and the shell process it as a shell script. Make waits for the shell to complete, then reports the exit code as success (0) or failure (not 0).
Also, a statement like $(call xyz) makes no real sense in make: calling a function with no arguments is identical to simply expanding the variable as in $(xyz). The only time call makes sense is if you pass arguments to it.

Escaping # in gmake $(shell) function

I have an environment variable GITHUB_REFS that I want to perform some bashism on and capture the result in another variable GITHUB_BRANCH from a GNU makefile. My naive approach looks like this:
SHELL:=/bin/bash
GITHUB_BRANCH:=$(shell echo "${GITHUB_REF#refs/heads/}")
If I run the bashism by itself, it works fine however when running the makefile above it fails with:
Makefile:2: *** unterminated call to function 'shell': missing ')'. Stop.
I tried escaping the # as \#, since it is a plausible culprit, and indeed then the Makefile works however the bashism does not. Double escaping it gives the same error again.
So how can I pull this off?
You also need to double the dollar sign to pass it through to the shell.
GITHUB_BRANCH:=$(shell echo "$${GITHUB_REF\#refs/heads/}")
For what it's worth, this simple parameter expansion is portable to any reasonably modern sh, so not at all an exclusive Bash feature.
Of course, make is perfectly capable of performing the same substitution, without invoking an external process.
GITHUB_BRANCH := $(patsubst refs/heads/%,%,${GITHUB_REF})
The # can be escaped using \, but you also forgot to escape the $.
This Makefile works:
SHELL:=/bin/bash
foo := $(shell echo "$${SHELL\#/bin/}")
all:
echo $(foo)
Just to note that this (the need to escape the #) is a bug, and will be fixed in an upcoming version of GNU make. If you want to allow your makefile to be portable before/after the bug is fixed, you should hide it in a variable like this:
HASH := \#
foo := $(shell echo "${GITHUB_REF$(HASH)refs/heads/}")
This will work in all versions of GNU make.

How to comment a line within a Makefile define directive?

I want to comment one or more line(s) within a define directive in a Makefile so as the line is ignored when the directive is expanded. The goal is to place the commented line as a hint for the users of my Makefile to show an example of what could be into the define directive. The directive is expanded into a target.
In other words, I want that Makefile
define ECHO_FOO =
# #echo foo
endef
all:
#echo Before call
$(ECHO_FOO)
#echo After call
.PHONY: all
to have the same behavior than this one :
define ECHO_FOO =
endef
all:
#echo Before call
$(ECHO_FOO)
#echo After call
.PHONY: all
The issue is that the first Makefile gives me the following error :
process_begin: CreateProcess(NULL, ##echo foo, ...) failed.
make (e=2): The system cannot find the file specified.
Makefile:6: recipe for target 'all' failed
make: *** [all] Error 2
The GNU make:Makefile contents page states that :
Within a define directive, comments are not ignored during the definition of the variable, but rather kept intact in the value of the variable. When the variable is expanded they will either be treated as make comments or as recipe text, depending on the context in which the variable is evaluated.
But this doesn't explain in which specific case the # symbol is treated as a make comment or as a recipe text (which seems to be the problem I meet).
Can someone tell me how to have the # symbol treated as a comment mark in a define function ?
I have already tried all of the following lines with the idea of escaping the # symbol or changing the indentation but none of them gave me a correct output :
##echo foo
##echo foo
###echo foo
###echo foo
\##echo foo
\##echo foo
/##echo foo
/##echo foo
I'm running MinGW make 3.82 on Windows but I have already tried other implementations of make v3.82.90 and 4.1.
There's no way to do what you're asking for directly. The contents of the variable are expanded in a recipe context, so no matter what the variable expands to it will be considered part of the recipe and whatever characters are there will be passed to the shell.
Note you can use : in UNIX shells as well as Windows command.com, because : is the shell no-op operator. You have to add a space after it though otherwise it will try to run the command :echo which is not a valid command. However, further note that the shell will still expand the line! This means that if you use backquotes etc. then those still are expanded. Also note that since it's a statement, semicolon will stop it. So for example:
define ECHO_FOO
: echo hi `echo there 1>&2` ; echo bye
endef
all: ; #$(ECHO_FOO)
Here, the hi won't be printed because the echo command is not run, but the backticks are still expanded so there will be printed (to stderr) and the semicolon ends the "no-op" command so bye will also be printed.
If your commands are simple enough then : will work, but if they're that simple one wonders why you're using define...
Another option is just to override the variable, rather than comment it out:
define ECHO_FOO =
#echo foo
endef
ECHO_FOO =
ETA:
In the comments you affirm that the command is simple. I don't quite know what you mean by could be expanded by the final user or why that makes a difference.
But what I was alluding to is that if you have a simple command you can just write:
ECHO_FOO = echo hi
and not use define. define is only needed for complicated commands: really it's only required for commands that contain un-escaped newlines.
And, if you write:
ECHO_FOO =# echo hi
then you ARE commenting out the content of the variable using make comments, not shell comments, so it will work everywhere.
On Windows, you can use : as a comment character. The traditional comment keyword in MS-DOS is REM (as in "remark").

Multi-line define in GNU make

I'm trying to make sense out of the multi-line define directive of GNU make and I cannot. Example:
define A
1
2
endef
all:
#echo W=$(word 1,$(A))
Running make produces a result I have expected the least:
W=1
make: 2: Command not found
make: *** [all] Error 127
It appears that part of $(A) has spilled outside the $(word) function.
Is it a bug or intended behavior? If the "spill" is intentional, how does it really works?
P.S. GNU make v3.81 on Linux/x64
The thing to remember here is that make stores each recipe as a single recursive variable. At the point that make decides that it must run your recipe, it expands that variable. Make then passes each line in the resulting expansion to a separate shell, stopping if any of those shell executions return an error.
In your example, before running anything make expands #echo W=$(word 1,$(A)).
$(A) becomes 1¶2 (dunno what this looks like on your browser, but I'm using ¶ to represent a newline character)
Now, 1¶2 is a single word as far as make is concerned, so $(word 1,1¶2) naturally expands to 1¶2 (can you see where this is going yet?)
This leaves make with the string #echo W=1¶2. Make dutifully passes the first line of this to the shell (without the # as that is special to make). The shell executes echo W=1.
make executes 2 in a new shell.
The second shell complains that it can't find the command 2.
So, yes, expected behaviour.
[Warning: slight simplification in the above where I gloss over the bit where make is able to elide the shell and invoke the command itself if the string has no shell metacharacters in it]
The $(word) function is splitting on spaces. Not whitespace, spaces.
There are no spaces in your A macro so nothing gets split.
Add a trailing space on the 1 line or a leading space on the 2 line and you get your expected behaviour.
This is consistent across GNU make 3.81, 3.82, 4.0, and 4.1 in some quick testing here.
The reason you see the "spill" as you called it is because of how the define is expanded. It is expanded literally, newline and all. (Think template expansion.)
So make expands the define into the call to $(word 1,...) then expands that result (the whole define including the newline) into the recipe template and ends up with two lines that it executes as the recipe.
Consider a macro like this:
define somecommands
echo foo
echo bar
echo baz
endef
all:
$(somecommands)
What would you expect to happen here? How many lines is the body of all? How many shells are run here? What commands are executed? The answer is three lines, three shells and three echo commands.
If the newlines weren't counted then you would effectively run echo foo echo bar echo baz in one command and get foo echo bar echo baz as output instead of the expected (and far more useful) foo, bar, and baz on three different lines.

Printing content of Variables before a target is called

I currently have a makefile that looks like this
ifndef __makefile_env_setup_included_1__
export __makefile_env_setup_included_1__ := true
export ENV_ROOT := $(abspath $(lastword $(MAKEFILE_LIST))/../..)
export ENV_TOOLS := $(ENV_ROOT)/tools
endif # __makefile_env_setup_included_1__
include $(ENV_TOOLS)/Makefile.utils.function
include $(ENV_TOOLS)/Makefile.utils.variable
include $(ENV_TOOLS)/Makefile.utils.action
include $(ENV_TOOLS)/Makefile.utils.rule
I wanted to echo the value of the variables being used like
$ENV_ROOT
From what I have read so far , I can only insert a command in the commands section of a processed target. My question is is there a way for me to echo out a certain variables before reaching the include file ?
You can use the Functions That Control Make to do this. Specifically $(info) and $(warning).
$(warning text…)
This function works similarly to the error function, above, except that make doesn’t exit. Instead, text is expanded and the resulting message is displayed, but processing of the makefile continues.
The result of the expansion of this function is the empty string.
$(info text…)
This function does nothing more than print its (expanded) argument(s) to standard output. No makefile name or line number is added. The result of the expansion of this function is the empty string.

Resources