Printing in Makefiles: #echo vs $(info ) - makefile

What is the difference between these two commands in makefiles:
#echo "Hello World"
$(info Hello World)
As it seems, echo and info print the same output, so where is the difference?
And when to use which one?

Well, echo is a shell command. So if you put it in a recipe, a shell will be invoked to run it and the shell command will generate the output:
foo: ; #echo "Hello World"
runs /bin/sh -c 'echo "Hello World"'. It can only be used in a recipe. It will work in any version of make, and with any POSIX shell. Because it invokes a shell, you may need to be concerned with quoting issues, etc. (not in this simple example of course).
info is a GNU make function. It is handled directly by make: no shell is invoked. It can appear anywhere in a makefile, not just in a recipe. It is not portable to other versions of make. Because no shell is invoked, there are no quoting issues.
However, because info is a make function it is parsed by make before the shell is invoked: that means it can't show shell variables that are set within a recipe; for example:
foo: ; #for i in a b c d; do $(info $$i); done
cannot work; you must use echo here.

Related

Makefile FILES command will not execute

I am trying to learn Make syntax, I have instructed it to compile a file and it does this correctly, when I tell it to execute a shell command to create a file using touch it does not do this, it returns no error to indicate anything is wrong.
Commands
HelloWorld: HelloWorld.c
gcc -o HelloWorld HelloWorld.c
FILES="$(shell touch > file1.txt)"
As pointed out already your syntax is wrong. But beyond that your shell function is never even executed.
Why not? Because in your makefile, you define a variable which contains the shell variable invocation but then you never use the variable anywhere. Since you don't use it, the variable is never expanded. Since the variable is never expanded, the function in it is never invoked.
If you want the variable to be expanded you either need to make it a simple variable (using :=) so that the right-hand side is expanded when the variable is assigned, or you have to use the function in a context where it's expanded immediately; the other answer has an example of using that (outside of any variable assignment)
Here's an example of using a simple variable:
HelloWorld: HelloWorld.c
gcc -o HelloWorld HelloWorld.c
FILES := $(shell touch file1.txt)
(note you don't need quotes when setting makefile variables, unlike shell variables).
As for the manual, it's written to be a user guide: that is you're supposed to read it starting at the beginning, rather than a reference manual where you jump around. In particular you won't get anywhere writing makefiles until you read carefully and fully understand the initial two chapter "Introduction to Makefiles" and "Writing Makefiles". For fluency you need to understand the section How make Reads a Makefile.
I think you want:
$(shell touch file1.txt)
(Notice the lack of a > redirect in there). Also touch does not output anything to stdout, therefore FILES would be blank no matter what, so the assignment is useless.
If you're going to do things like this be aware that the $(shell ...) commands (in the global scope) are executed at parse time, before any of the recipes are run.
------------ EDIT -------------
In regards to your question about creating a blank file:
all:
$(info output $(shell echo "hello world"))
$(shell touch file1.txt)
should produce your file:
> ls file1.txt; make; ls file1.txt
file1.txt
output hello world
make-3.81: Nothing to be done for `all'.
file1.txt

make debug option that echoes (prints) shell function arguments

Is there make debug option that prints all executions of shell function?
For example if I have makefile
contents := $(shell echo foo)
all:
I want it to print "echo foo".
You can supply your own program as a shell, that will echo the commands and do whatever else you need.
https://www.gnu.org/software/make/manual/html_node/Choosing-the-Shell.html:
5.3.2 Choosing the Shell
The program used as the shell is taken from the variable SHELL. If this variable is not set in your makefile, the program /bin/sh is used as the shell. The argument(s) passed to the shell are taken from the variable .SHELLFLAGS. The default value of .SHELLFLAGS is -c normally, or -ec in POSIX-conforming mode.
So, run:
SHELL=mysneakyprogram make all
Each command will be passed to mysneakyprogram with a -c argument.
The catch is: this will also apply to normal commands that build targets, not only $(shell ...); I don't think you can distinguish the two cases.

GNU Make and using ; to execute multiple shell commands in the same command shell

I have a very basic problem using GNU Make 3.81 on Windows, I must be doing something very silly and I'm sure someone here will point it out in milliseconds. My problem is with using ";" to run multiple commands in the same shell.
As I understand it, make runs each line in its own command shell and so if you want to run two commands, one after the other, you must put them on the same line separated by a semicolon. In it's simplest form:
all:
echo hello; echo hello
...should produce the output:
hello
hello
But for me it produces the output:
hello; echo hello
In other words, the semicolon is being passed straight through to the shell, which doesn't make too much sense for cmd.exe.
I'm now ready to be embarrassed by everyone pointing out where I've gone wrong...
FYI, the reason I need this is that I'm using a $(foreach) loop which must execute two shell commands for each iteration.
You are be under the impression that ; is a GNU-make operator for executing multiple
commands in the same shell within a recipe. Not so. It is linux shell operator
for punctuating a sequence of commands on the same line. It is not an operator for
the Windows shell, cmd, so when the recipe:
echo hello; echo hello
is executed by make on Linux, it has the output you expect, but when executed by make
on Windows it just means echo this:
hello; echo hello
So, the answer is that your shell is the thing that has to understand that ; separates multiple commands on the same line, it's nothing to do with make. This is not the case for Windows cmd.exe but is presumably the case for the shells that normally arrive with environments that use make (Linux, msys etc.). In my case, a good workaround was this:
define useDef
echo hello
echo hello
endef
all:
$(call useDef)
With this form of "single-lined" definition I can invoke a multiline shell command inside $(foreach). Make still does each "hello" in its own shell but in my case that's OK because I'm appending outputs to a file. If you need two commands to be run in the same shell for some reason then, on Windows, you would need to write a separate batch file (which I suppose you could create from inside the makefile).
I know this question is relatively old, but I've stumbled across the same problem recently. The solution (for me) was quite simple. I replaced ; with &.
Basically
all:
echo hello & echo hello
will produce
hello
hello
in cmd.exe.
And it works with $(foreach) loops as well.
UPD: You also can use && instead of & if you don't want your commands to fail silently.

Is it possible to enforce recipe echoing with GNU Make?

According to the GNU make manual, echoing of shell commands can be suppressed by prefixing the according lines with #.
Using the option -n or --just-print, one can make a dry run and print all those prefixed lines without actually doing them.
Is it possible to execute the make recipes and print the shell commands at the same time? Put differently, can I enforce echoing for all recipes, no matter whether they have a #at the beginning, or not?
With GNU Make 4.0 there's the option --trace. (I don't know how what is the earliest version that supports --trace. I just know that 4.0 supports it.)
'--trace'
Show tracing information for 'make' execution. Prints the entire
recipe to be executed, even for recipes that are normally silent
(due to '.SILENT' or '#'). Also prints the makefile name and line
number where the recipe was defined, and information on why the
target is being rebuilt.
With this Makefile:
all:
#echo foo
echo blah
A regular run:
$ make
foo
echo blah
blah
With --trace:
$ make --trace
Makefile:2: target 'all' does not exist
echo foo
foo
echo blah
blah
echo foo is output even though it begins with #.

How to handle setting up environment in makefile?

So, to compile my executable, I need to have the library locations set up correctly. The problem is, the setup comes from a bunch of scripts that do the env variable exporting, and what needs to be set up may change (beyond my control) so I need to use those scripts instead of copying their functionality. To compile in regular command line, I need to do something like:
setup library1
setup library2
source some_other_setup_script.bash
g++ blah.c
# setup is a executable on my system that run some scripts
How would I write a makefile that accomplishes that? As far as I tried, the env variable exporting does not carry over (i.e. "export VAR=remember; echo $VAR" won't work)
You can also add environment variables properly with the machinery of GNU make, like so:
export TEST:="Something Good!"
test:
echo $$TEST
This (I think) has different semantics from:
TEST2:="Something not quite so useful?"
test2:
echo ${TEST2}
Which (again, I think) does the substitution within make before passing along to the shell. Note that the export command doesn't work within a target block, just unindented as an immediately executed command.
If variable exporting is not working the way it does on your command line, that suggests that Make is choosing a shell different from the one you're using, with different syntax for handling variables (export VAR=remember; echo $VAR works fine for me). Make uses /bin/sh by default, but you can override this with the SHELL variable, which Make does not import from the environment. I suggest setting SHELL (in the Makefile) to whatever you're using in your environment and trying the export VAR=remember experiment again.
Ultimately you will need to define the variable and execute the compiler in a shell list or even a script, rather than in separate make commands. There are a couple of refinements you could add, however. You could tell make about the script:
maintarget: script.sh blah.c
source script.sh; g++ blah.c
script.sh:
setup include script here
Another thing would be to just execute all that stuff in the same shell
maintarget: blah.c
run this; run that; run the other thing; g++ blah.c
I believe all make versions will run a ; list in the same shell, but you can always force a subshell with (list) or by calling specifically a shell script as a compiler command wrapper.
Don't forget to have the appropriate targets depend on your scripts themselves. BTW, some make versions (pmake aka bsd make) can execute a command when defining a make variable, and all versions of make then exports those. But I don't think gmake can do that.
You could write another shell script that executes all those commands, then prints out variable assignments that make can use. Run the script, pipe its output to a file, then include that file from your Makefile. For example:
Makefile:
all:
echo $(FOO)
test.mk: test.sh
./$< > $#
include test.mk
test.sh
echo "FOO=1"
Running "make" in the directory containing this Makefile produces:
make: Entering directory `/home/luser/build/mktest'
Makefile:7: test.mk: No such file or directory
./test.sh > test.mk
make: Leaving directory `/home/luser/build/mktest'
make: Entering directory `/home/luser/build/mktest'
echo 1
1
make: Leaving directory `/home/luser/build/mktest'
make creates test.mk by running the shell script, then includes it. test.mk contains the output of test.sh, and is parsed as a Makefile. See http://www.gnu.org/software/make/manual/make.html#Include for more details.
We use a variant of this in Mozilla's client.mk to let you define options in a "mozconfig" file:
http://mxr.mozilla.org/mozilla-central/source/client.mk#138
Restatement: How do I get a shell variable into a make file?
Something like:
MYVAR := $(shell echo $(MYVAR)) <any_makefile_additions_here>
So, this defines MYVAR inside a MAKEFILE when an environment variable named MYVAR is also set.
It might be of interest, that, in order to override an option that is already defined in a makefile, make supports (I am referring to GNU Make 3.82, but other version probably too) the option -e.
Example:
Makefile:
CC=gcc
...
Run make:
CC=gcc-4.7
make -e
will use gcc-4.7 instead of gcc.

Resources