Makefile variable as a prerequisite

The Makefile in the deploy recipe requires the ENV environment variable to be configured to run correctly, while others do not care, for example:

 ENV = .PHONY: deploy hello deploy: rsync . $(ENV).example.com:/var/www/myapp/ hello: echo "I don't care about ENV, just saying hello!" 

How can I make sure that this variable is set, for example: is there a way to declare this variable makefile as a necessary condition for deployment, for example:

 deploy: make-sure-ENV-variable-is-set 

?

Thank.

+120
makefile
Jan 18 '11 at 20:43
source share
8 answers

This will lead to a fatal error if ENV not defined and something needs it (in any case, GNUMake).

 .PHONY: deploy check-env

 deploy: check-env
	 ...

 other-thing-that-needs-env: check-env
	 ...

 check-env:
 ifndef ENV
	 $ (error ENV is undefined)
 endif

(Note that ifndef and endif are not indented β€” they control what make β€œsees” and takes effect before the Makefile runs. β€œ$ (Error” is indented with a tab, so it only starts in the context of the rule.)

+147
Jan 19 2018-11-11T00:
source share

You can create an implicit protection target that checks that the variable in the stem is defined, for example:

 guard-%: @ if [ "${${*}}" = "" ]; then \ echo "Environment variable $* not set"; \ exit 1; \ fi 

Then you add the guard-ENVVAR target anywhere you want to state that the variable is defined, for example:

 change-hostname: guard-HOSTNAME ./changeHostname.sh ${HOSTNAME} 

If you call "make change-hostname" without adding HONNAME = somehostname to the call, you will receive an error message and the assembly will fail.

+87
Sep 09 '11 at 22:00
source share

Built-in option

In my makefiles, I usually use an expression like:

 deploy: test -n "$(ENV)" # $$ENV rsync . $(ENV).example.com:/var/www/myapp/ 

Causes:

  • it's a simple one line
  • it compact
  • it is close to commands that use the variable

Do not forget the comment important for debugging:

 test -n "" Makefile:3: recipe for target 'deploy' failed make: *** [deploy] Error 1 

... makes you search for the Makefile, while ...

 test -n "" # $ENV Makefile:3: recipe for target 'deploy' failed make: *** [deploy] Error 1 

... directly explains what is wrong

Global option (for completeness, but not specified)

In addition to your Makefile, you can also write:

 ifeq ($(ENV),) $(error ENV is not set) endif 

Warnings:

  • do not use tab in this block
  • use with caution: even if the clean limit will not work if ENV is not set. Otherwise, see the Hudon answer, which is more complicated.
+39
Mar 07 '16 at 14:13
source share

As I can see, the ENV variable is needed for the command itself, so you can check it in the command itself:

 .PHONY: deploy check-env deploy: check-env rsync . $(ENV).example.com:/var/www/myapp/ check-env: if test "$(ENV)" = "" ; then \ echo "ENV not set"; \ exit 1; \ fi 
+5
Jan 18 2018-11-18T00:
source share

I found that the best answer cannot be used as a requirement, except for other PHONY goals. When used as a dependency for a target that is a real file, using check-env will force that target to rebuild.

Other answers are global (for example, a variable is required for all purposes in the Makefile) or use a shell, for example, if ENV was absent, make will complete regardless of the target.

The solution I found for both problems:

 ndef = $(if $(value $(1)),,$(error $(1) not set)) .PHONY: deploy deploy: $(call ndef,ENV) echo "deploying $(ENV)" .PHONY: build build: echo "building" 

The output looks like

 $ make build echo "building" building $ make deploy Makefile:5: *** ENV not set. Stop. $ make deploy ENV="env" echo "deploying env" deploying env $ 

value has some frightening warnings, but for this simple use, I think this is the best choice.

+5
Apr 02 '18 at 21:53
source share

One of the possible problems with these answers so far is that the order of dependencies in make is not defined. For example, launch:

 make -j target 

when target has several dependencies, it does not guarantee that they will work in any given order.

The solution for this (to ensure that the ENV will be checked before the recipes are selected), you need to check the ENV during the first pass of make outside the recipe:

 ## Are any of the user goals dependent on ENV? ifneq ($(filter deploy other-thing-that-needs-ENV,$(MAKECMDGOALS)),$()) ifndef ENV $(error ENV not defined) endif endif .PHONY: deploy deploy: foo bar ... other-thing-that-needs-ENV: bar baz bono ... 

You can read about the various functions / variables used here and $() is just a way to explicitly indicate that we are comparing "nothing."

+4
May 26 '13 at
source share

You can use ifdef instead of another purpose.

 .PHONY: deploy deploy: ifdef ENV rsync . $(ENV).example.com:/var/www/myapp/ else @echo 1>&2 "ENV must be set" false # Cause deploy to fail endif 
+2
Jan 18 '11 at 20:57
source share

I know that this is old, but I thought that I would share my own experience for future visitors, since this is a little more neat, IMHO.

As a rule, make will use sh as the default shell ( set via a special SHELL variable ). In sh and its derivatives, it is trivial to exit with an error message when retrieving an environment variable if it is not set or is zero by doing: ${VAR?Variable VAR was not set or null} .

Extending this, we can write a reusable make target that can be used to fail other targets if the environment variable has not been set:

 .check-env-vars: @test $${ENV?Please set environment variable ENV} deploy: .check-env-vars rsync . $(ENV).example.com:/var/www/myapp/ hello: echo "I don't care about ENV, just saying hello!" 

Things are notes:

  • A shielded dollar sign ( $$ ) is required to defer the extension to the shell, not inside make
  • Using test simply prevents the shell from trying to execute the contents of the VAR (it serves no other significant purpose)
  • .check-env-vars can be trivially expanded to check for more environment variables, each of which adds only one line (for example, @test $${NEWENV?Please set environment variable NEWENV} )
0
Jul 24 '19 at 14:00
source share



All Articles