use environment variable if set otherwise use default value in makefile - bash

I can do this:
MY_VAR:=$(myvar)
But what I want is to also define a value for MY_VAR that is used if the environment variable myvar isn't defined. Is this possible?
Something like:
# pseudo code
MY_VAR:=if not $(myvar) then someDefaultValue

Assuming make is GNU Make, all the environment variable settings inherited by make are automatically registered
as make variable settings. See 6.10 Variables from the Environment. So you can just write, e.g.
Makefile (1)
ifdef myvar
MYVAR := $(myvar)
else
MYVAR := default
endif
.PHONY: all
all:
echo $(MYVAR)
Which runs like:
$ make
echo default
default
when myvar is not defined in the environment; and when it is defined,
runs like:
$ export myvar=notDefault
$ make
echo notDefault
notDefault
And in case the environment variable and the make variable are the same - and why not? - it is simpler still.
Makefile (2)
MYVAR ?= default
.PHONY: all
all:
echo $(MYVAR)
See 6.5 Setting Variables
Then:
$ make
echo default
default
$ export MYVAR=notDefault
$ make
echo notDefault
notDefault

you can try this code below.
MY_VAR=${HOSTNAME1}
if [ "$MY_VAR" = "" ]; then
MY_VAR="DEFAULT"
fi

Related

Setting a variable in Makefiles through the command line [duplicate]

I am trying to do a simple thing:
TMPDIR ?= /tmp
test:
#echo $(TMPDIR)
This works if I run:
$ make test
/tmp
It also works if I run:
$ make test -e TMPDIR=~/tmp
/home/user/tmp
What can I do to also have it works for:
$ TMPDIR=~/tmp make test
/home/user/tmp
To follow up on my comments above, here's an example:
T ?= foo
all:
: '$(T)'
Now if I run the Makefile in various ways, it behaves as we expect (I get foo only if I don't set T either on the command line or environment):
$ make
: 'foo'
$ make T=bar
: 'bar'
$ T=bar make
: 'bar'
Variables specified on make command line override the values assigned in makefile:
TMPDIR := "/tmp"
test:
#echo $(TMPDIR)
And then:
make TMPDIR=whatever
whatever
It is generally considered a bad practice for makefiles to depend on environment variables because that may lead to non-reproducible builds. This is why passing variable overrides in make command line explicitly is recommended.
Here is a simple solution:
SHELL := env TMPDIR=$(TMPDIR) $(SHELL)
TMPDIR ?= "/tmp"
all:
#echo $(TMPDIR)
which works for both scenarios: TMPDIR=new/path make and make TMPDIR=new/path.
One of the thing you could do is:
TMPDIR := "/tmp"
ifdef $$TMPDIR
TMPDIR := $$TMPDIR
endif
test:
echo $(TMPDIR)

How to Check Whether a Variable Used in a Target Defined in an External File Is Set

Here's my msbuild.mk...
# default build configuration
CONFIGURATION = Release
.PHONY: check-env build-app
check-env: ## Check build environment
[ -v APP_NAME ] && $(error APP_NAME not set)
[ -v APP_SRCDIR ] && $(error APP_SRCDIR not set)
[ -v APP_OUTRDIR ] && $(error APP_OUTDIR not set)
build-app: check-env ## Build .NET application
#msbuild ${APP_SRCDIR}/${APP_NAME}.sln -t:Build \
-p:BaseIntermediateOutputPath=${APP_OUTDIR}/obj/ \
-p:OutDir=${APP_OUTDIR}/bin/ \
-p:Configuration=${CONFIGURATION}
... and here I've included it into my actual Makefile:
include ./msbuild.mk
SRCDIR = src
APP_NAME = MyApp
APP_SRCDIR = ${SRCDIR}/code/${APP_NAME}
APP_OUTDIR = ${OUTDIR}/${APP_NAME}
.DEFAULT_GOAL = build-app
When I try this...
make build-app
... the check-env target in msbuild.mk aborts the script saying the variables are not set (even they are set in the actual Makefile). Furthermore, if I remove the check-env target, the build-app target succeeds as variables APP_NAME, APP_SRCDIR, and APP_OUTDIR seem to be set correctly.
I'm a bit lost... Am I missing something?
A few things.
First, the -v option to test is not a POSIX standard option; it's a bash extension. Make will always invoke /bin/sh as the shell which may or may not actually be bash, depending on your system. If all you care about is whether the variable has a non-empty value or not you can use [ -n "$VAR" ] instead.
Second, you can't intersperse make functions like $(error ...) with shell operations like test. If you think about it there's no way that can work: make doesn't have a shell interpreter embedded in it, it expands the command, runs a real shell process, and waits for the exit code to know if it worked. How can a condition that is handled by the shell control whether or not a make function like $(error ...) is run or not?
If you want to test shell variables you have to use shell commands to handle the error, something like this:
check-env: ## Check build environment
[ -n "$$APP_NAME" ] || { echo APP_NAME not set; exit 1; }
If you just want to check for make variables being set, you can do it more straightforwardly using make syntax. Something like:
check-env = $(foreach V,APP_NAME,APP_SOURCEDIR,APP_OUTDIR, \
$(if $($V),,$(error Variable $V is not set)))
build-app:
$(check-env)
...

Makefile - Function to check empty environment variables

I am trying to write a function to check if multiple environment variables are set. In this example, I've just tried to use a function which does not works probably because call opens up a subshell which does not has my exported variable.
What is a neat way to check multiple environment variables? I am trying to avoid multiple ifndef statements in my Makefile.
Makefile
define func_test
ifndef ${1}
$(error ${1} is not set - does not works)
endif
endef
test:
#$(call func_test, account_name)
ifndef account_name
$(error account_name is not set - works)
endif
Logs
~ $ export account_name=somename
~ $ make test
Makefile:8: *** account_name is not set - does not works. Stop.
~ $
Check if variable is empty
$(if $(some_var),,$(error some_var is not defined))

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

Define a Makefile variable using a ENV variable or a default value

I am trying to do a simple thing:
TMPDIR ?= /tmp
test:
#echo $(TMPDIR)
This works if I run:
$ make test
/tmp
It also works if I run:
$ make test -e TMPDIR=~/tmp
/home/user/tmp
What can I do to also have it works for:
$ TMPDIR=~/tmp make test
/home/user/tmp
To follow up on my comments above, here's an example:
T ?= foo
all:
: '$(T)'
Now if I run the Makefile in various ways, it behaves as we expect (I get foo only if I don't set T either on the command line or environment):
$ make
: 'foo'
$ make T=bar
: 'bar'
$ T=bar make
: 'bar'
Variables specified on make command line override the values assigned in makefile:
TMPDIR := "/tmp"
test:
#echo $(TMPDIR)
And then:
make TMPDIR=whatever
whatever
It is generally considered a bad practice for makefiles to depend on environment variables because that may lead to non-reproducible builds. This is why passing variable overrides in make command line explicitly is recommended.
Here is a simple solution:
SHELL := env TMPDIR=$(TMPDIR) $(SHELL)
TMPDIR ?= "/tmp"
all:
#echo $(TMPDIR)
which works for both scenarios: TMPDIR=new/path make and make TMPDIR=new/path.
One of the thing you could do is:
TMPDIR := "/tmp"
ifdef $$TMPDIR
TMPDIR := $$TMPDIR
endif
test:
echo $(TMPDIR)

Resources