Makefile if statement causing some weird behavior - makefile

In my makefile, the user supplies an argument called EXEC (make target EXEC=something). I want this to happen:
if EXEC equals "server"
make the variable NOT equal to "client"
if EXEC equals "client"
make the variable NOT equal to "server"
I tried doing this:
ifeq ($(EXEC),server)
NOT := client
endif
ifeq ($(EXEC),client)
NOT := server
endif
I run this by saying make -f build.mk EXEC=server
the output is:
NOT := client
make[2]: NOT: No such file or directory
Why is this error happening?

It seems you've indented the variable assignment with a TAB character. That means that line is considered part of the recipe for the previous target.
Since you haven't provided the entire makefile, or at least the section of the makefile before/after this, we can't say more than that.
However, in general in a makefile you should never indent any lines with TAB characters unless they are intended to be a part of a recipe.

Related

How to execute a bash script into a makefile

I have a simple bash script, that install and zip a NodeJS application. It's worked great local. Now I want to execute this script from a Makefile. So I wrote this code
.FOO: install_and_zip
install_and_zip: ./install_and_zip_foo.sh
So if I run this Makefile with make foo I'm getting this error message:
make: *** No rule to make target `foo'. Stop.
Makefile and bash script are in the same directory.
What I'm doing wrong? Can you help me to fix this Makefile?
Different errors. This will work:
foo: install_and_zip
install_and_zip:
# next line must start with a real TAB (ASCII 9)
./install_and_zip_foo.sh
If you want to make foo, then call the rule foo, but not FOO or .FOO.
Code goes always below the rule. All command lines must start with a real TAB (ASCII 9, but not a series of spaces).
Rule foo calls rule install_and_zip. Is this your whish? Otherwise this will do it too:
foo:
# next line must start with a real TAB (ASCII 9)
./install_and_zip_foo.sh
You should write "make .FOO" as you have defined it to be that way. However, you have to properly write your Makefile (tab identations and so on). If you do, you can just write 'make' and the tool will identify "install_and_zip" as the main target, thus executing your script.
PD: targets with a leading '.' (and without slashes) will be ignored by make when trying to identify the main goal (i.e. they won't run just executing 'make'). So, unless you know what you are doing, just rename your target to 'foo'.

What is the meaning of $(Q)#: in Makefile

I've read in linux Makefile:
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
$(Q)#:
What is the meaning of $(Q)#:?
I'm trying to google it, but google always crappy if the search using some weird character. So in the end i can't found any Manual about it.
After looking in the code. Q is defined somewhere after those line. Since makefile have peculiar concept of variable (which is expandable), it can be implement in anywhere. Q is used to whether show message or not (Q maybe for Quiet).
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = #
endif
And for the last #: this means do-nothing-output-nothing.
So the conclusion $(Q)#: simply do-nothing-output-nothing.
To reinforce and expand on what nafsaka found:
Sometimes Makefiles are written like this:
target:
rm -rf $(DIRECTORY)
$(Q)$(MAKE) all
And Q will be defined as # or nothing for example:
V ?= 0
ifeq ($(V), 0)
Q = #
else
Q =
endif
If a target action is preceded by # then make won't display it when run. Here's the GNU make documentation on that subject: Recipe Echoing
In this case you need to define V=1 before running make to see commands as they're run (This is very common).
Another wrinkle: Look for "include file.mk" statements in your Makefile, which is where V and Q were defined in my case. Here's the GNU make documentation on include: Including Other Makefiles
From the Make manual:
5.2 Recipe Echoing
Normally make prints each line of the recipe before it is executed. We call this echoing because it gives the appearance that you are typing the lines yourself.
When a line starts with ‘#’, the echoing of that line is suppressed. The ‘#’ is discarded before the line is passed to the shell. Typically you would use this for a command whose only effect is to print something, such as an echo command to indicate progress through the makefile:
#echo About to make distribution files
When make is given the flag ‘-n’ or ‘--just-print’ it only echoes most recipes, without executing them. See Summary of Options. In this case even the recipe lines starting with ‘#’ are printed. This flag is useful for finding out which recipes make thinks are necessary without actually doing them.
The ‘-s’ or ‘--silent’ flag to make prevents all echoing, as if all recipes started with ‘#’. A rule in the makefile for the special target .SILENT without prerequisites has the same effect (see Special Built-in Target Names).

Make runs target with variable name regardless of whether file exists

I'm using GNU make to work with some data. When I try to run a target with a variable name, make will run that target, regardless of whether the target file already exists. Consider the following Makefile:
include config.mk
.PHONY : all
all : $(PG_DB).db
$(PG_DB).db :
createdb $(PG_DB) -U $(PG_USER)
touch $#
where config.mk contains:
MAKEFLAGS += --warn-undefined-variables
SHELL := bash
.SHELLFLAGS := -eu -o pipefail
.DEFAULT_GOAL := all
.DELETE_ON_ERROR:
.SUFFIXES:
PG_USER="foo"
PG_DB="foo"
When I run make, make creates the Postgres database, and then touches the file foo.db. However, when I run make again, the output is:
createdb "foo" -U foo
createdb: database creation failed: ERROR: database "foo" already exists
make : *** ["foo".db] Error 1
This shouldn't have happened! I would expect make, in this situation, to check the prerequisites for the phony target all, see that foo.db already exists, and exit immediately without doing anything.
Strangely, this is exactly what happens when I get rid of the variables in the target names:
include config.mk
.PHONY : all
all : foo.db
foo.db :
createdb $(PG_DB) -U $(PG_USER)
touch $#
When I run make with this modified Makefile, I get:
make: Nothing to be done for `all`.
Which is exactly what I expect.
What's going on here?
The problem does not come from the variable, it comes from the quotation mark in the variable value. make does not remove the quotation mark before checking the dependency. So it is checking for the file "foo".db with the quotation mark included. While the command touch "foo".db gets interpreted by the shell that removes the quotation marks. So for make, the file will never be there and you will always have the same problem. When you put the dependency explicitly, you put it as foo.db that does not include the quotation mark. It makes all the difference.
in config.mk, turn
PG_DB="foo"
into
PG_DB=foo
it should work. I guess createdb is a normal shell command that will not care about the quotation marks. Otherwise you will need to add it before calling the command.

How to prevent make from communicating any variable to a submake?

I am unable to prevent make from communicating any variables to a submake. I've read the manual and I've followed their advice (resetting MAKEOVERRIDES and MAKEFLAGS) but it's still not working has I think it should.
Consider the following prototype Makefile:
${warning $(MAKEOVERRIDES)}
${warning $(MAKEFLAGS)}
${warning $(VAR)}
none:
$(MAKE) -f Makefile MAKEOVERRIDES= MAKEFLAGS= all
all:
echo done!
If I make VAR=10 none, I get the following:
Makefile:2: VAR=10
Makefile:3:
Makefile:4: 10
make -f Makefile MAKEOVERRIDES= MAKEFLAGS= all
make[1]: Entering directory `/home/adriano/sandbox/makes'
Makefile:2:
Makefile:3:
Makefile:4: 10
echo done!
done!
make[1]: Leaving directory `/home/adriano/sandbox/makes'
Meaning that make is communication VAR to the submake. Is this the correct behaviour?
I've tried unexport VAR and bash -c make ... without any luck.
EDIT: I've modified none's recipe to: bash -c "echo $$MAKEOVERRIDES $$MAKEFLAGS $$VAR" ; make ...
This way I found out that VAR is actually being passed through the environment that make creates for the commands to be executed and not through the other variables (the other variables are also passed this way to make).
I think my question now is: how can I create a fresh shell/environment to run my sub make?
EDIT: Someone asked why am I trying to this; I'll try to answer to that here.
I have a "module" which uses a variable named CONFIG. In order to build this module I need to build another partially unrelated "module" which also uses CONFIG, but with a different value. The problem is that when I try to build the "sub-module" CONFIG contains the value of the "super-module." I could specify CONFIG when making the "sub-module" however both modules use many variables with the same name and trying to specify them all would make the modules tightly coupled which is something I cannot afford.
How can this be so difficult...
This is wrong:
none:
$(MAKE) -f Makefile MAKEOVERRIDES= MAKEFLAGS= all
These variables (MAKEOVERRIDES and MAKEFLAGS) are set in the environment by the parent make to be passed down to the sub-makes. Setting overrides on these values inside the recipe won't help, because make has to set the environment for the recipe before it actually starts the commands in the recipe (of course).
You have to override/remove these values in the parent makefile, so that those changes are seen by the parent make before it constructs the sub-make's environment:
MAKEOVERRIDES =
none:
$(MAKE) -f Makefile all
There's no perfect way to do this. However, you can play a trick that will work most of the time:
unexport $(shell echo '$(MAKEOVERRIDES)' | sed 's/=[^ ]*//g')
MAKEOVERRIDES =
The first line tries to unexport all the variables in MAKEOVERRIDES and the second line resets MAKEOVERRIDES. There are a few issues with this. One is that if MAKEOVERRIDES is empty, it will use "unexport" by itself which unexports everything. That can be easily worked around by sticking some bogus variable before the shell function. The other is that if any variable's value contains whitespace, the expansion will consider it a variable to be unexported. That's probably OK, but it's odd.
I can't think of any better way to do it.
You don't really say why you want to do this. Have you considered doing something different, such as running the commands where you want to have a "vanilla" environment using env; for example if you want to run a command with a limited and specific set of env vars, you can run:
test:
env -i PATH='$(PATH)' LANG='$(LANG)' runMyCommand --with --my arguments
Unfortunately some versions of env use - instead of -i; check your man page.
Alternatively, you can try to start a login shell which will re-read the user's shell setup environment from scratch:
test:
/bin/sh -lc 'runMyCommand --with --my arguments'
EDIT: It's difficult because what you're asking to do (restrict the environment of the sub-make) is tricky.
Luckily based on your description, it doesn't seem necessary. Make has a hierarchy of importance for finding variable values. The command line is the highest level (well, there's override but we'll ignore that). After that comes variables set in the makefile itself. And last and lowest comes variables imported from the environment (well, default variables are even lower but we'll ignore that too).
So if your goal is to allow the variables in the sub-makes to not be affected by command line variables given to the upper-level makes, then all this rigmarole of getting the variables out of the environment is not necessary. Variables set in the sub-makefiles will take precedence over the values in the environment. So all you have to do is get rid of the variables set on the command line, which I've already shown how to do above, by setting MAKEOVERRIDES.

Get exit code 1 on Makefile if statement

I'm trying to get the exit code on the ifdef statement if the statement is not true, but I tried by using exit 1 and $(call exit 1)
when using the first on the following code I get "Makefile:11: * missing separator. Stop."
...
ifdef PACKAGE
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
exit 1
endif
...
By using $(call exit 1) I get no error but the makefile still keeps executing.
What I'm trying to accomplish is to exit the Makefile on the else with the error code 1
Thanks
As geekosaur says you can't put a shell command like exit 1 as a makefile operation. Makefiles are not shell scripts, although they can contain shell scripts. Shell commands can only appear within a target recipe, and nowhere else.
If you have a sufficiently new version of GNU make you can use the $(error ...) function, like this:
ifdef PACKAGE
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
$(error You must define the PACKAGE variable)
endif
Also note that ifdef will be true if the variable is defined, even if it's defined to be the empty string. You may prefer:
ifneq ($(PACKAGE),)
PACKAGEDIR = $(HOME)/$(PACKAGE)
else
$(error You must define the PACKAGE variable)
endif
to ensure the variable is set to a non-empty value.
And, it's possible your version of GNU make is too old to support the $(error ...) function, although it's been around for a long time now.
You can't simply have bare code sticking somewhere in a Makefile; code (such as exit 1) is always associated with a build rule of some kind.
In this case you want the $(error) function. It may not be sufficient to drop it in the else, though, for the same reason that exit 1 itself won't work there; you may need to rephrase the whole thing as
PACKAGEDIR := $(if $(flavor PACKAGE),undefined,$(error PACKAGE must be defined!),$(HOME)/$(PACKAGE))

Resources