0
votes

I know that I can pass variables to a Makefile in two ways:

make <target> FOO=bar

export FOO=bar
make <target>

and that both of these will make a variable FOO available in the makefile with value bar.

However, is there a way to require that the variable only comes from the command line? I want the passing of variables to be explicit in order to avoid certain potential overlaps of environment variables, so I want to ensure make only receives the variable if passed from the command line, and to disregard it if it's only set in the environment so that the value it uses must be defined by the user when calling make.

EDIT: I realize after researching it a bit more that environment variables are not actually accessed the way I thought they are, they're actually used within make as ${FOO} so as long as I don't define FOO at any point in the makefile, its only set value will be from the command line (as noted in this answer).

3

3 Answers

3
votes

These methods to pass variables to GNU make aren't equivalent.

  • Variables that come from the environment don't override the assignments in makefile, unless make is invoked with -e option. See variables from the environment. This is because depending on environment variables is poor practice in terms of build reproducibility (someone forgets to set the environment variable and the build is different):

    It is not wise for makefiles to depend for their functioning on environment variables set up outside their control, since this would cause different users to get different results from the same makefile. This is against the whole purpose of most makefiles.

  • Variables that come from make command line do override assignments in makefile, unless override is specified. See the override directive.

Hence, the recommended practice is to explicitly set all your variables to their default values in the makefile unconditionally, so that only the assignments from the command line override them.

2
votes

As far as I know, there's no difference between ${FOO} and $(FOO), regardless of the way FOO is defined.

If you're using GNU make, there's a function origin that allows you to make the distinction: it will return command line for a variable defined on the command line and environment for a variable exported by the environment (more info in the manual)

with the following Makefile:

foo ?= foo

default:
    @echo ${foo}, comes from $(origin foo)
  • make prints foo, comes from file
  • make foo=bla prints bla, comes from command line
  • (export foo=bar; make) prints bar, comes from environment
0
votes

expanding on @Virgile answer, you could add the following kind of check at the start of the makefile. It is a lot to repeat for each variable you wish to check, although all such checks could reside in a dedicated makefile that is then included from main makefile

foo ?= foo

# check origin
ifdef foo
ifneq "$(origin foo)" "command line"
$(error foo: must come from command line)
endif
else
$(error foo not defined)
endif

default:
    @echo ${foo}, comes from $(origin foo)