How to set a variable in Makefile without giving a value? - makefile

#### Makefile.header ####
ifndef Hello
C_LINK = -switchOne
else
C_LINK = $(Hello)
endif
#### Makefile ####
...
Hello=
...
include Makefile.header
...
Question> The file Makefile includes the file Makefile.header. I would like to provide an option to the user so that they can customize the C_LINK, disable the default C_LINK, or use the predefined C_LINK.
Case 1> Customize the switch
The user can provide a value for the Hello to customize the value of C_LINK.
For example,
Hello = -abc -def
Case 2> Use the default switch
The user can use the default value of C_LINK by not defining the Hello variable inside the Makefile.
Case 3> Disable the default switch
The user can define the variable Hello without value to disable the C_LINK. I tried to use the following method but it doesn't work.
Hello =
The compiler treats the variable Hello as undefined.
Question> Is there a way that I can define a variable with empty value?
Thank you

If you are using GNU Make you can use the ?= operator like this:
Default_Value ?= MainDefault
Value ?= $(Default_Value)
Then in the included makefile you can set your options in these different ways:
# option #1
Value := -abc -def
or
# option #2 (do nothing)
or
#option #3 (clear default)
Default_Value :=
Now # option #1 sets Value so the Makefile will not change it.
Also # option #2 does nothing so the Makefile will add its own default.
And # option #3 sets the default to blank so the Makefile will add blank to Value.

Related

Is there a way to print all variables defined/visible within a makefile?

let's assume the following Makefile:
FOO=1
BAR?=test
BLAH:=$(BAR)
target:
#echo "helloworld"
Is there a way it can show me variables like this (\n or \s) ?
FOO
BAR
BLAH
bonus question:
if it is possible, is there a way to also get external variables?
make target BLABLAH=toto or export BLABLAH=toto; make target
FOO
BAR
BLAH
BLABLAH
You don't say so specifically but because of the syntax I'll assume you're using GNU make. In that case you can use the .VARIABLES special variable:
$(info Variables: $(.VARIABLES))
Note, that make target BLABLBAH=toto does not create an "external variable" (that term is not well-defined so I'm just guessing that by this you mean variables that are inherited from the environment and not set in the makefile). Setting a variable on the command line creates a real make variable and it will be shown in the list above.
Variables obtained from the environment are imported into make as makefile variables and will also appear in the above list.

Makefile: macros with default parameters

Consider the following Makefile:
MAKEFLAGS += --warn-undefined-variables
define foobar
echo "$(1)"
endef
.PHONY: all
all:
$(foobar)
Is there a way to have macros with default parameters without producing undefined variable warnings?
I mean: sometimes I call "foobar" with a parameter, but sometimes not. In the latter case I'd like to have a default value for $(1).
You can't set a default value in the macro but you can easily add one when the parameter is expanded:
1:=
define foobar
echo "$(if $1,$1,default)"
endef
all:
$(foobar)
$(call foobar,biz)
$ make
echo "default"
default
echo "biz"
biz
It's a bit annoying if you use the parameter lots of times because you have to use the if for each use.
The GNU make syntax is very limited; it's not a full blown programming language, so many things are missing, like default parameters in make macros.
But the shell is a programming language! Why not implement your requirements in the commands of a target? It may be possible to use something like this:
all:
if test "$(SOMECONDITION)"; then \
do_one_thing; \
else \
do_something_else; \
fi
There is a decent solution for GNU make. Not sure how portable it is.
Use a global variable that embodies the rather messy logic, using a naming convention to avoid conflicts:
# foo.mk
MAKEFLAGS += --warn-undefined-variables
foobar_p1_default = default parameter
foobar_p1 = $(if $(filter undefined,$(origin 1)),$(foobar_p1_default),$1)
define foobar
#echo $#: "$(foobar_p1)"
endef
.PHONY: all defaulted global local
all : defaulted global local
defaulted:
$(foobar)
variable = global value
global local:
$(call foobar,parameter was $(variable))
local: variable = target specific value
The results:
$ make -f foo.mk
defaulted: default parameter
global: parameter was global value
local: parameter was target specific value

Why doesn't gnu make's "override" pass through to sub-makes?

Note: This question was originally posted as a rant by a now-deleted user, but there was a valid question behind the rant; this is my attempt to provide an answer.
Given the Makefile:
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
export foo
all::
#echo outer: foo is "$(foo)"
#$(MAKE)
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
The expectation is that export foo will cause make to export the value defined in the override declaration. But it doesn't:
$ make -s foo=original
outer: foo is replaced
inner: foo is original
The expectation is probably reasonable, but it turns out that this is not the way Gnu make works. It could well be that the make documentation could be improved to clarify the process, but the hints all seem to be there.
How variables get their values
A variable can be set by the programmer in three ways:
On the command line with a var=value command-line argument
Explicitly in the make file
From the environment
The above list is the normal priority order; the first definition found in the list "wins". However, you can use the override directive to swap the priorities of the first two methods. (You can also use the -e flag to make to swap the priorities of the last two methods. The -e flag is required by Posix, but its use is discouraged and it does not interact will with override.)
How variables are passed to sub-makes
make is very similar to a shell in that the environment is used to pass variable values. If a variable is marked as exported, then its value is placed into the environment for any processes initiated by make, including sub-makes. As with the shell, a variable is marked as exported if its definition came from the environment or if it is explicitly marked as exported with the export directive. Variables are also exported if they were set on the command line.
However, there is another mechanism by which variables on the command-line are passed to subprocesses: the MAKEFLAGS exported variable.. MAKEFLAGS contains (most) command-line options as well as all of the command-line variable overrides. If make finds MAKEFLAGS in the environment, it merges the settings in that variable with the ones actually specified on its command line. That means that command-line variable settings in a make will also take priority in a sub-make.
Since command-line variable settings are passed through the MAKEFLAGS variable, they are not subject to any changes in the makefile. A makefile can unexport or override a variable set on the command-line, but that will only affect the value (or presence) of the variable in the environment; it does not remove or change the value from MAKEFLAGS.
Resolution
So if the intent is to override (or modify) a command-line variable both in the make itself and in the environment of sub-makes, it is necessary to use both an override and an explicit modification of MAKEFLAGS. (As explained in the make manual, MAKEFLAGS is actually recursively composed using the MAKEOVERRIDES variable, so we actually modify that variable.)
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
MAKEOVERRIDES += foo=replaced
all::
#echo outer: foo is "$(foo)"
#$(MAKE) -s
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
And now we get the expected result:
$ make -s foo=original
outer: foo is replaced
inner: foo is replaced
Real-life application: dealing with whitespace
The primary intention of overrides is to allow the makefile to append words to a variable possibly provided on the command line. The example provided in the gnu make manual is insisting that CFLAGS always includes the -g flag, even if it were specified on the make command line:
override CFLAGS += -g
Passing the append through to a sub-make needs a little caution; in particular, the obvious:
MAKEOVERRIDES += CFLAGS=$(CFLAGS) # Don't do this
won't work because the whitespace inside the CFLAGS variable will not be escaped when it is added to MAKEFLAGS; the result will be that MAKEFLAGS will look something like this:
-- CFLAGS=-O3 CFLAGS=-O3 -g
instead of the desired
-- CFLAGS=-O3 CFLAGS=-O3\ -g
If the value assigned to CFLAGS on the command line included whitespace, the whitespace is escaped in MAKEFLAGS. The particular escaping mechanism used is not documented, and Posix only requires that there be some mechanism; apparently, Gnu make uses backslash. It would be possible to manually backslash escape the whitespace, resulting in something like this:
# Don't do this either
MAKEOVERRIDES += CFLAGS=$(subst $(space),\ ,$(CFLAGS))
(The definition and use of space is based on an example in the gnu make manual.)
But it is actually easier to just use an append assignment in MAKEOVERRIDES, which is undocumented but appears to work. It works on the command line, too.
override CFLAGS+=-g
MAKEOVERRIDES += CFLAGS+=-g
Important Note as of make v4.1: A bit of testing revealed that the above stanza will only work if CFLAGS (or some other variable) is actually set on the command-line. I reported this bug as Savannah issue 46013, with a very simple fix in the bug report. In the meantime, if you really want to do this, use the following workaround:
override CFLAGS+=-g
MAKEOVERRIDES += CFLAGS+=-g
# This line is necessary in case there were no command-line overrides.
# In effect, it produces a command-line override, although that value
# will not be passed on to sub-makes.
MAKEFLAGS += dummy=dummy
Update May 19, 2019: Today I was informed that a fix for the bug referenced above has been committed, so it should be fixed in the next gmake release.
First of all, I want to point out that your suggestion to add to MAKEOVERRIDES, is dangerous!
And SHOULD NEVER BE DONE!!
You simply turn a recursive variable into a simple one, you will always get false results, if recursive expansion is done.
I can not believe that you got up-voted for this clearly wrong "suggestion".
And note this:
You can not even fix it with a quoted assignment, like MAKEOVERRIDES += foo=$$(bar)!!!
But, let me return to the main point of your post.
And, with which, I couldn't disagree more.
One simple example would be, if you run the very same makefile, that you have:
This one is copied verbatim, from your post:
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
export foo
all::
#echo outer: foo is "$(foo)"
#$(MAKE) -s
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
And running in any modern version, 4.0 and up:
# Sub-make does NOT get the value from the root-Make's command-line.
# Instead, it "inherits" the value from the root-Make's definition in the Makefile.
$ make -s foo=original -e
outer: foo is replaced
inner: foo is replaced
Now, given your assertion above:
However, there is another mechanism by which variables on the command-line are passed to subprocesses: the [MAKEFLAGS exported variable.][3]. MAKEFLAGS contains (most) command-line options as well as all of the command-line variable overrides. If make finds MAKEFLAGS in the environment, it merges the settings in that variable with the ones actually specified on its command line. That means that command-line variable settings in a make will also take priority in a sub-make.
Since command-line variable settings are passed through the MAKEFLAGS variable, they are not subject to any changes in the makefile. A makefile can unexport or override a variable set on the command-line, but that will only affect the value (or presence) of the variable in the environment; it does not remove or change the value from MAKEFLAGS.
You should get:
outer: foo is replaced
inner: foo is original
In other words, we should get for the sub-make, the value defined on the command-line (original)!
Because, you said yourself:
A makefile can't unexport or override a variable set on the command-line.
So, here, when we empower the environment over the makefile, which means that the makefile has less "power" in the total scheme of things. Right?
Sure, for such a case, you assertion will hold even stronger.

Makefile adding spaces to variables

I am trying to use a makefile in order to compile my story in LaTex. I am trying to use a variable in order to complete the file name for my story. If I simply run make, it works. However, I need to be able to run a specific command from make.
pdflatex "\\def\\isdraft{1} \\input{FMPGC.tex}"
How would I create a variable from PROJ + OBJS so that I could do something like what I am trying to do below. If I run the below code for make draft, it fails out and APPEARS that it is adding numerous spaces between FMPGC and tex.
How could I combine two variables with a "." symbol between the pair so I can compile my story in the below command. I have also experimented with not escaping the \ symbols and that seems to have no effect.
# This makefile compiles my story using LaTex
# Author:
#
# VARS - Variables to be changed for reuse of my script
PROJ = "FMPGC" # The name of the project
OBJS = "tex" # The extension for the content
AUXS = "aux" # The aux extensions
CHAP = "chapters/" # The chapters
FOO = $(PROJ) += "."
F002 = $(FOO) += $(OBJS)
# Configuration:
CC = pdflatex # The compiler
# Rules
all:
$(CC) $(PROJ).$(OBJS)
draft:
$(CC) "\\def\\isdraft{1} \\input{$(FOO2)}"
The current error comes from it not inputting anything into the variable as of right now -
pdflatex "\\def\\isdraft{1} \\input{}"
Below seems to be the exact issue.
<*> \def\isdraft{1} F
MPGC.tex
---------------- Updated Make File
# This makefile compiles my story using LaTex
# Author:
#
# VARS - Variables to be changed for reuse of my script
# The name of the project
PROJ:=FMPGC
# The extension for the content
OBJS:=tex
# The aux extensions
AUXS:=aux
# The chapters
CHAP:=chapters/
# Configuration:
# The compiler
CC=pdflatex
# Rules
all:
$(CC) $(PROJ).$(OBJS);
draft:
$(CC) "\def\isdraft{1} $(PROJ).$(OBJS)";
Updated Error ----
pdflatex "\def\isdraft{1} FMPGC.tex";
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
LaTeX2e <2014/05/01>
Babel <3.9k> and hyphenation patterns for 21 languages loaded.
! LaTeX Error: Missing \begin{document}.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
<*> \def\isdraft{1} F
MPGC.tex
?
! Emergency stop.
...
<*> \def\isdraft{1} F
MPGC.tex
! ==> Fatal error occurred, no output PDF file produced!
Transcript written on texput.log.
In the The Two Flavors of Variables section of the GNU make Manual we find:
If you put whitespace at the end of a variable value, it is a good idea to put a comment like that at the end of the line to make your intent clear. Conversely, if you do not want any whitespace characters at the end of your variable value, you must remember not to put a random comment on the end of the line after some whitespace, such as this:
dir := /foo/bar # directory to put the frobs in
Here the value of the variable dir is ‘/foo/bar ’ (with four trailing spaces), which was probably not the intention. (Imagine something like ‘$(dir)/file’ with this definition!)
Thanks to #Etan Reisner I was able to fix it after reviewing the documentation.
My make file now looks like this -
# This makefile compiles my story using LaTex
# Author:
#
# VARS - Variables to be changed for reuse of my script
# The name of the project
PROJ:=FMPGC
# The extension for the content
OBJS:=tex
# The aux extensions
AUXS:=aux
# The chapters
CHAP:=chapters/
# Configuration:
# The compiler
CC=pdflatex
# Rules
all:
$(CC) $(PROJ).$(OBJS);
draft:
$(CC) "\def\isdraft{1} \input{$(PROJ).$(OBJS)}";
I can now compile my document and it adds my variable for use inside of LaTex.

"heritage" in Makefile

I'm looking a way to overload parts of a Makefile A to another one B, hence extending A.
For instance we have the following makefile A:
TEXT="AHAHA"
default: after-default
before-default:
echo "BEFORE DEFAULT"
default: before-default
echo ${TEXT}
after-default: default
echo "AFTER DEFAULT"
I want to reuse it in a new Makefile B like this:
TEXT="HIHIHI"
before-default:
echo "NEW BEFORE DEFAULT"
The new makefile will print:
NEW BEFORE DEFAULT
HIHIHI
AFTER DEFAULT
This example is a bit absurd it is not possible like this way, but I want to know if it is possible to make such Makefile composition close to this idea.
You can do this:
File Makefile:
# Define default TEXT
TEXT := HAHAHA
# Define default target
after:
#echo AFTER
run:
#echo $(TEXT)
before:
#echo BEFORE
# Define dependencies
run: before
after: run
# Include the new makefile
include inheritance.mk
File inheritance.mk:
# Redefine TEXT
TEXT := HIHIHI
# Redefine before target
before:
# echo NEW BEFORE
When you run make you'll have some warning but it'll work as expected:
inheritance.mk:4: warning: overriding commands for target `before'
Makefile:10: warning: ignoring old commands for target `before'
NEW BEFORE
HIHIHI
AFTER
Your example will be trivially fulfilled by adding include A at the start of B. The new before-default target will override the old one.
vnix$ tail *
==> A <==
TEXT="AHAHA"
before-default: default
echo "BEFORE DEFAULT"
default: after-default
echo ${TEXT}
after-default:
echo "AFTER DEFAULT"
==> B <==
include A
TEXT="HIHIHI"
before-default: default
echo "NEW BEFORE DEFAULT"
vnix$ make -sf A
AFTER DEFAULT
AHAHA
BEFORE DEFAULT
vnix$ make -sf B
B:6: warning: overriding commands for target `before-default'
A:4: warning: ignoring old commands for target `before-default'
AFTER DEFAULT
HIHIHI
NEW BEFORE DEFAULT
This isn't a very good design, though; parametrizing things like you already do with TEXT comes with fewer surprises than having code that is being overridden elsewhere.
(See my comment above re: why the output is in the opposite order from what you were hoping.)

Resources