Makefile rule causing unnecessary rebuild - makefile

I've got a rule that checks if a certain environment variable has been set:
check-env:
ifndef NODE_ENV
$(error NODE_ENV is undefined)
endif
I then have stuff that depend on it (which should fail if NODE_ENV isn't set):
sql/schema.js: sql/schema.sql check-env
...
My issue is that check-env always causes a rebuild, what should I actually be doing to achieve what I want in a reasonably modular way?

Why are you doing this in the recipe of a target? Why not just put it out in the main part of your makefile?
ifndef NODE_ENV
$(error NODE_ENV is undefined)
endif
If you really want to have this done through rules, your best bet (assuming you're using a "new-enough" version of GNU make) is to use order-only prerequisites like this:
check-env:
$(if $(NODE_ENV),,$(error NODE_ENV is undefined))
sql/schema.js: sql/schema.sql | check-env
...
(note the pipe symbol |). See the manual for details.

Related

Cause makefile to error if an environment variable is not set?

I'd like my makefile to crash if an environment variable is not set. This is what I have so far:
ifneq ($(shell echo $${VIRTUAL_ENV:+True}),True)
$(error Looks like no virtualenv is active)
endif
and it works!
I'm wondering if there's a more elegant way to do this, perhaps with make directly instead of calling $(shell ...).
Thanks for your help!
You can make use of the origin function...
ifeq ($(origin VIRTUAL_ENV),undefined)
$(error Looks like no virtualenv is active)
endif
This could be simplest option, but isn't as fine tuned as using origin.
ifndef VIRTUAL_ENV
$(error Looks like no virtualenv is active)
endif
One caveat here is that you can't distinguish if VIRTUAL_ENV defined in the Makefile or in the environment.
As #KurtisRader pointed out in a comment, this is possible because environment vars are implicitly "imported" into make's namespace https://www.gnu.org/software/make/manual/html_node/Environment.html.

Makefile ifndef variable in target

Please believe me: I searched and tested a lot... but I don't get whats wrong here:
VERSION := 123
all:
ifndef VERSION
$(error VERSION not set)
else
$(info Start deploy $(VERSION))
endif
outputs VERSION not set
what I really wanted was to call make like VERSION=1.2.3 make but not even setting the variable in the Makefile worked
What am I missing?
For me it outputs Start deploy 123.
Note that ifndef and $(error) and $(info) are part of makefile syntax, not the recipe syntax belonging to all target. So, your code is equivalent to:
VERSION := 123
ifndef VERSION
$(error VERSION not set)
else
$(info Start deploy $(VERSION))
endif

How can I add a directory to the search path of GNU Make?

I have a makefile that looks something like this:
include anotherFile.mk
all:
someStuff
The file anotherFile.mk is like this:
include yetAnotherFile.mk
export SOME_VAR = 93
The problem is that anotherFile.mk and yetAnotherFile.mk are in a different directory from my Makefile. So my makefile can't just be changed to this:
include $(OTHER_PROJECT_PATH)/anotherFile.mk
all:
someStuff
The problem with this approach is that the include statement in anotherFile.mk will fail because it will be searching in the current directory.
A partial solution that I found is to pass the --include-dir=$OTHER_PROJECT_PATH flag to the invocation of make, but that's a bit user-unfriendly.
So my question is: Is there something I can put inside my makefile that will add to the directories that make searches for when executing an include? Something like MAKE_INCLUDE_DIRS += $(OTHER_PROJECT_PATH)
Surprisingly there doesn't seem to be a good answer to that question. Forcing .INCLUDE_DIR doesn't help and there doesn't seem to be any way around invoking make with --include-dir=$OTHER_PROJECT_PATH.
It is however possible to put the appropriate recursive make invocation inside the makefile but, in order to get it to work for all reasonable cases it quickly becomes too complicated to be worth it. In summary it requires:
a top level condition to check if the OTHER_PROJECT_PATH is in .INCLUDE_DIR
the appropriate target with the recipe invoking make recursively
possibly additional targets if there are multiple command goals
the real make file enclosed in the else part of the conditional
You Makefile would look like this:
OTHER_PROJECT_PATH := other
ifeq (,$(filter $(OTHER_PROJECT_PATH), $(.INCLUDE_DIRS)))
# this is the mechanism to add the include dir in a recursive make
$(or $(firstword $(MAKECMDGOALS)),all):
$(MAKE) -I$(OTHER_PROJECT_PATH) $(MAKECMDGOALS)
# add empty targets for additional goals if needed
ifneq (,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)):
endif
else
# this is where the real makefile starts
all more:
echo $#: $< $^
include a.mak
endif
It still does not seem possible from a makefile, but if you have a script that sets up environment variables, you can use MAKEFLAGS (e.g. export MAKEFLAGS=I/your/path ordentlich on Linux, or SET on Windows)

ifndef include guard in Gnu Make breaks on nested conditional

I’m trying to implement include guards in Gnu Make. In this Makefile, the first inclusion is OK, while the second one fails with an error.
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
define inner_macro
macro content...
endef
else
define inner_macro
endef
endif
endef
endif
The same effect can be simulated by explicitly giving INCLUDED = 1 before the inclusion, e.g. on command line.
Gnu Make 4.1 under Gentoo says Makefile:14: *** missing separator. Stop., while Gnu Make 3.81 under Debian Wheezy says Makefile:14: *** extraneous `endef'. Stop.. On the first inclusion, they both say:
Including
make: *** No targets. Stop.
If I try $(eval $(call macro,whatever)) after the first inclusion, it defines inner_macro as expected.
I used make INCLUDED=1 and make commands respectively to get the described behavior.
The same happens when I clear the environment and disable built-in rules and variables: env -i make -rR INCLUDE=1. When I use -p to dump the database, without INCLUDED=1, the macro is defined as it should be, but with INCLUDED=1, empty inner_macro is defined. This is consistent across both the versions of Make. This hints me that when the condition is false, Make parses the Makefile differently and thinks the else inside macro’s definition belongs to the ifndef. Other condition types behave all the same.
If I remove both the definitions of inner_macro, the problem goes away.
I read the manual pages info make conditional\ syntax and info make multi-line (formerly defining), but I found no caveat there and I still think I am doing nothing wrong.
Am I correct with my conclusions?
Is this a bug in Make, or am I invoking undefined behavior?
How should I implement include guards in Gnu Make?
That's a bug. Report it on Savannah.
There's something wrong with the tracking of nested define/endef inside a not-taken ifdef/ifndef condition. If you don't use nested define/endef then it works; for example (obviously you may not be able to do this in your environment):
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
inner_macro = macro content...
else
inner_macro =
endif
endef
endif

How to use ifeq inside of a define in GNU Make?

I'm trying to do an ifeq inside of a define within a Makefile, but I seem to be running into some errors, and I'm wondering if I'm missing something. I have the following Makefile:
$(info ---- start ----)
ifeq ("X","Y")
$(info DOES not appear_1)
endif
define TESTDEF
ifeq ("X","Y")
$(info SHOULD not appear)
# $(error DEFINITELY SHOULD not error...)
endif
endef
$(eval $(call TESTDEF, 1,2,3))
I'm getting the following error:
---- start ----
SHOULD not appear
Makefile:14: *** DEFINITELY SHOULD not error.... Stop.
Is there some trick that I'm missing? Is it possible to do ifeq's inside define? (note: this happens on both my native GNU 3.81 make, and on my mips uclibc cross-compiler)
When you call this function, Make evaluates the definition, using whatever parameters you provide (irrelevant in this case). So if the definition includes something like $(info ...) or $(error ...), even in a comment, Make will evaluate it and you'll see the result (see documentation; I've tested it in GNUMake 3.81).
To get the behavior you want, add a couple of dollar signs:
define TESTDEF
ifeq ("X","Y")
$$(info SHALL not appear)
# $$(info DEFINITELY SHALL not error...)
endif
endef
$(eval $(call TESTDEF))

Resources