Conditional inclusion/execution of Makefile commands in a variable? - makefile

There is a Makefile that I am using, which I got from somewhere, and which is quite big. I have also found some things that I'd like changed occasionally in the makefile - and the easiest way to do that for me is to define (or not) a (switch) variable (say, OVWRCHOICE) at the start of the makefile; and then later on in the makefile code, do something like:
ifdef OVWRCHOICE
MYOPT = override
....
endif
... which is all dandy and fine.
The thing is, eventually I also need to change parts in the "override" part as well, so I'd like to have it at the start of the file. So, as this "override" part contains several make commands -- I tried to use define, to have a variable which will contain the commands (which would be executed at the ifdef OVWRCHOICE... part).
So I arrived at this simple example:
# uncomment as needed;
OVWRCHOICE = YES
define SET_OVWRCHOICE
MYOPT = override
endef
export SET_OVWRCHOICE
# ... many lines of code ...
MYOPT = default
# ... many lines of code...
# without indent: Makefile:18: *** missing separator. Stop.
# with tab indent: Makefile:18: *** commands commence before first target. Stop.
ifdef OVWRCHOICE
$(SET_OVWRCHOICE)
endif
all:
#echo $(MYOPT)
... which fails with the errors noted. Of course, if I use the first snippet in the post instead, all runs fine, and make prints out the expected result: "override".
How would I go about in achieving something like this? Not sure if "inclusion" or "execution" of "Makefile commands" are even the right terms in this context; so I have a hard time in finding a starting point for a search :)

Got it - it is described in Eval Function - GNU `make'; the right construct is:
ifdef OVWRCHOICE
$(eval $(call SET_OVWRCHOICE))
endif
Hope this helps someone,
Cheers!

Oh well, didn't really know where to archive this snippet, so back to this old question of mine :) this is off topic for OP; but here goes:
To test how environment variables are processed by a makefile, here is a simple example:
Foo=something
all :
ifdef DEBUG
#echo "Debug defined"
else
#echo "Debug NOT defined"
endif
... and here is the test for it:
$ make
Debug NOT defined
$ make DEBUG
make: *** No rule to make target `DEBUG'. Stop.
$ DEBUG make
DEBUG: command not found
$ DEBUG= make
Debug NOT defined
$ DEBUG=1 make
Debug defined
... so obviously, the right syntax to set that variable inside the makefile from the command line is: "DEBUG=1 make"

Related

Make: evaluating result of call: recipe commences before first target

I have several files for my GNU make setup. In this.mk, I have
define this_template
THIS = $(1)
THIS_DIR = $(2)
THIS_LIBNAME = $(3)
THIS_EXTERNAL_DIRS = $(4)
...
endef
In my Makefile, I have
include this.mk
... # define VAR1 and VAR2
include util/make.mk
...
util/make.mk contains one line:
$(eval $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
However, when I run make, I get
util/make.mk:1: *** recipe commences before first target. Stop.
Reading up on other questions that relate to this error message, what I'm understanding is that this error is caused by evaluating a string which begins in a way that looks like it's inside of a recipe. However, what I'm evaluating does not.
This error means that (a) the line begins with a TAB (or more specifically, with the character defined as .RECIPE_PREFIX if your version of GNU make supports it), and (b) it is not recognized as any sort of make command such as a rule introduction, etc.
Given what you've shared with us here, that cannot happen. So there must be something going on that you haven't shared with us. Maybe one of the other included makefiles is modifying the this_template variable to contain something else.
The way to debug eval problems is always the same no matter what they are: change the eval to info so that make will print out what it will evaluate. This usually makes it pretty obvious what the problem is. So use:
$(info $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
$(eval $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
and see what make shows you.

Makefile recursive install?

Suppose I have the following: A chain of dependencies, each of which may or may not be present, and each of which installs in the exact same way (with the exception being the string name of the dependency). In order to get rid of repetitive makefile code, I prototype the following function:
define install_utility =
$(1):
# recursion
$(foreach bar, $(1)_dependencies,$(eval $(call install_utility,$(bar)))
ifeq(`which $(1)`,) # check for existence of dependency
echo will install $(1) # show me make is executing expected commands
endif
endef
foo_dependencies=A B
foo=foo
eval $(call install_utility,$(foo))
# Expected results:
will install A
will install B
will install foo
# Actual result: no error message, just:
... (infinite loop that prints nothing)
And when I run, I get the following error: an infinite loop.
This seems like really simple functionality. However, I am having trouble getting it to work in make. Is there a manner in which I am "expected" to do this in make?
Have tinkered a bit... and getting a variety of errors depending on whether I tab out the recursion:
$(1):
$(foreach bar, ...
ifeq(`which $(1)`,)
...
# error messages:
make: *** no rule to make target '$(foo_dependencies)
ifeq(`which', needed by 'foo'. Stop.
I don't really understand what you're trying to do... it looks extremely non-make-like to me.
But, this definitely will not work:
ifeq(`which $(1)`,)
If you want this to be part of the recipe, you have to indent it with a TAB character and write it in shell syntax, not make syntax.
If you want this to be part of the makefile (not the recipe) you need to write it in correct make syntax: first you have to include a space between the ifeq and the (. Second, make does not support backquotes. If you want to run a shell command you have to use the $(shell ...) function.

Using ifeq to test the variables

I tried to write this dynamic target to check the variable before running the actual target:
.PHONY: check-env-%
check-env-%:
ifeq ($(${*}),)
$(error not found ${*})
endif
so that I can use it like:
build: check-env-VERSION
But looks like it cannot compare it and even when I supply the required variable, it errors: Makefile:16: *** not found VERSION. Stop.
I believe I'm using the ifeq correctly but not sure why it cannot compare it?
From the docs: "Conditionals control what make actually “sees” in the makefile, so they cannot be used to control recipes at the time of execution." So your access to $* always yields an empty string at the time of makefile analysis, leaving your $(error) as recipe instruction.
Vroomfondel is right. What you can do instead is this:
check-env-%:
test $($*) || (echo $* not found; exit 1;)
...
test shall stop when there is no variable defined.

Makefile conditional not respecting target-specific variables

Is there a way to use the native Makefile if-else conditional and also have it respects target-specific variables re-assignments?
Example Makefile:
#!/usr/bin/make
CONFIG = Debug
.PHONY: test printme
test: override CONFIG=Release
test: printme
#echo "Done."
printme:
ifeq "$(CONFIG)" "Debug"
#echo "should be DEBUG -> $(CONFIG)"
else
#echo "should be RELEASE -> $(CONFIG)"
endif
Running make test prints the following output:
should be DEBUG -> Release
Done.
The output I'm looking for is should be RELEASE -> Release how can I achieve that? Do I need to use shell conditionals instead?
This behavior seems logical to me: At the time of parsing the Makefile, CONFIG is defined as Debug. The ifeq conditional uses the value of CONFIG it knows at that time. Therefore it chooses the ifeq branch that outputs "Should be DEBUG".
The target-specific variable is only defined as Release with the target test. Its prerequisite printme also knows the target-specific variable and ouputs Release.
May I suggest that you set variable to make on the command line for the purpose you want. It's not many more characters to write when invoking make but brings all you seem to be willing.
make CONFIG=Release

Changing value of a variable according to a condition inside a target in Makefile

In a makefile which I have ,I want to assign value to a variable based on a condition.
I have:
CMAKE=cmake ../
I tried doing:
if test condition;
then $(eval CMAKE := $(cmake -DCMAKE_BUILD_TYPE=Release ../));\
fi;
But this does not work.Is there any other way to do this?
P.S The error which is getting reported is :
Syntax error: ";" unexpected
When I removed ";" ,it showed another error :
Syntax error: "fi" unexpected
This kind of "back and forth" between the shell and make is not possible. It's important to understand the relationship between them: make does not implement a shell parser: it just runs the shell for that. All make gets back from the shell is a single exit code that determines whether the command succeeded or not.
Make runs a recipe by first expanding all the variables and functions in the recipe, then passing the resulting command to the shell to be invoked.
Thus it should be clear why your example doesn't work.
Before we can give you good advice we'd need higher-level information about exactly what you're trying to do and why. One way to do what you want is to use $(shell ...) to compute the conditional, all outside of any rule:
ifeq ($(shell test condition && echo true),true)
CMAKE := cmake -DCMAKE_BUILD_TYPE=Release ../
endif
However, this looks pretty strange to me. If you described what you really want to do we can probably give you better help.
ETA:
Based on your description below, the best option is target-specific variables:
CMAKE_BUILD_FLAG = -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
debug: CMAKE_BUILD_TYPE = Debug
release: CMAKE_BUILD_TYPE = Release
setup: CMAKE_BUILD_FLAG =
debug release setup: all
all:
cmake $(CMAKE_BUILD_FLAG) ../

Resources