Why is variable not getting set in Makefile? - makefile

This is my Makefile.
ifeq ($(BRANCH), )
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
endif
ifeq ($(COMMIT), )
COMMIT := $(shell git rev-parse --abbrev-ref HEAD)
endif
all:
ifeq ($(BRANCH), )
$(error Please pass a valid branch)
endif
ifeq ($(COMMIT), )
$(error Please pass a valid commit)
endif
echo $(BRANCH) $(COMMIT)
rm -f server-test
I want user to be able to pass the arguments BRANCH and COMMIT using the make command and set it to the default value if the user doesn't pass those arguments. This works if I use
make or make BRANCH=master. But if the user uses make BRANCH= or make BRANCH="", it gives an error. The part I don't understand is I am using the same check for setting the variable and throwing the error. Why is it going inside the if block in one place and not the other?
I am trying to conditionally set a variable to default if the user didn't pass it to the Makefile

The trouble is that this:
BRANCH := $(shell ...)
Doesn't work as you expect. From the manual page:
If a variable has been set with a command argument,
then ordinary assignments in the makefile are ignored.
So if you run make BRANCH= or make BRANCH="", then you have set the value of that variable -- to an empty string. It remains empty when you try to assign a new value to it, and triggers the error. The solution is on the same page:
override BRANCH := $(shell ...)

Related

Using $(call ....) in a makefile recipe

So I have a really nice make macro that creates me a pretty message during compilation that is done in such a way that the destination of the message can be implemented externally.
It uses the $(shell ) make function something like this.
send_msg = $(shell $do_send_msg "$1")
Where do_send_msg can be expanded to be echo, wall, email, etc.
I use this lots in the makefiles.
I'd like to be able to use the same macro in recipes as the result of the recipe may change what is needed to be sent, dependent on some test made to the result of an external program.
However variables in recipes are expanded by make entirely before the lines in the recipe are called one at a time.
So if I write (e.g.)
if test_prog; then \
$(call send_msg,PASS);\
else \
$(call send_msg,FAIL);\
fi
Effectively this becomes (not strictly true syntax, but close enough):
(bash -c 'echo "PASS"') &
(bash -c 'echo "FAIL"') &
(bash -c 'if test_prog; then \
\
else \
\
fi)'
This of course will not work, it will run test_prog and, separately, regardless of the exit status both send_msg lines will expand and be executed.
So it will print both PASS and FAIL as the two subshells are run independently and in parallel by make.
I don't want to 'cheat' and use another variable in the $(call ) function or even worse a global that, ok, would allow an if some_var ... in the send_msg implementation but would reduce it's flexability as the implementation would have to understand that variable in all cases.
Another way would be to just have two different send_msg macros, one with and one without the $(shell ) function. Simple, but not elegant.
Right now I am using a 'hack' and calling the same makefile with a variable. If that variable is set then it sends it's contents otherwise does nothing. This works fine but to me it seems clunky and wrong, there must be a better way.....
e.g.
if test_prog; then \
MESSAGE=PASS $(MAKE) message;\
else \
MESSAGE=FAIL $(MAKE) message;\
fi
Where the Makefile says (and this is an abreviated version to convey the idea)
ifneq ($(MESSAGE),)
message:
$(call send_msg,$(MESSAGE))
else:
message:
endif
Question:
How would I make (make) detect if the macro is being expanded inside a Makefile recipe or inside the Makefile body and effectively keep or remove the $(shell ) call that wraps how the work is done?
e.g. (if make_or_shell existed)
ifdef some_test
$(make_or_shell send_msg,"Message from Make")
endif
goal:
$(make_or_shell send_msg,"Message from Recipe")

Calling one target from another and also pass a variable or value in makefile

I need to call "target_1" from "target_2" plus also pass on a variable to the "target_1".
.PHONY:target_1
target_1:
ifeq ($(RQM_SETUP),ci)
mkdir /tmp/$1
chown 1000:1000 /tmp/$1
else
#echo "Nothing to do"
endif
.PHONY:target_2
target_2:
$(MAKE) target_1(dags)
So like in this eg, target_2 would call target_1 and also pass on value as "dags" to it.
So that it would create a "/tmp/dags" folder

Makefile - Function to check empty environment variables

I am trying to write a function to check if multiple environment variables are set. In this example, I've just tried to use a function which does not works probably because call opens up a subshell which does not has my exported variable.
What is a neat way to check multiple environment variables? I am trying to avoid multiple ifndef statements in my Makefile.
Makefile
define func_test
ifndef ${1}
$(error ${1} is not set - does not works)
endif
endef
test:
#$(call func_test, account_name)
ifndef account_name
$(error account_name is not set - works)
endif
Logs
~ $ export account_name=somename
~ $ make test
Makefile:8: *** account_name is not set - does not works. Stop.
~ $
Check if variable is empty
$(if $(some_var),,$(error some_var is not defined))

How to change PATH for Makefile $(shell ...) commands?

When I run
export PATH := mypath
$(error $(shell echo "$${PATH}"))
it seems my PATH isn't changed on the call to shell.
Why is this and how do I actually change the PATH for shell calls?
Is this with GNU make? There is a long-standing GNU make feature request to honor exported variables with $(shell …). This is not specific to PATH at all, it affects (or does not affect) all export variables.
According to the GNU make sources, this is tricky to implement:
/* Using a target environment for 'shell' loses in cases like:
export var = $(shell echo foobie)
bad := $(var)
because target_environment hits a loop trying to expand $(var) to put it
in the environment. This is even more confusing when 'var' was not
explicitly exported, but just appeared in the calling environment.
See Savannah bug #10593.
envp = target_environment (NULL);
*/
The solution is simple: never ever use $(shell) or export.
Environment variables should be part of the recipe that needs them.
For $(shell) invocations that are supposed to fill a makefile variable you can use instead.
it also has the advantage to be more flexible, because you can fill more than one variable with one recipe
you can also define proper dependencies, whereas $(shell) is always executed, either when the makefile is parsed or the recursively expanded variable gets expanded.
you get build errors and recipes are logged, whereas $(shell) can make the DevOp engineers life a living h...
PATH := mypath
Makefile.variables:
#PATH=$(PATH) echo "This my path '$${PATH}'"
echo >$# "MY_DYNAMIC_CONTENT := abcd"
include Makefile.variables
$(info MY_DYNAMIC_CONTENT '$(MY_DYNAMIC_CONTENT)')
Example run:
$ make
MY_DYNAMIC_CONTENT ''
This my path 'mypath'
echo >Makefile.variables "MY_DYNAMIC_CONTENT := abcd"
MY_DYNAMIC_CONTENT 'abcd'
make: 'Makefile.variables' is up to date.

makefile not running commands and "eating" first letter of variable

I have a function like this:
build:
git_branch="$(`git rev-parse --abbrev-ref HEAD`)"
ifeq("$git_branch", 'development')
tag="development"
else("$git_branch", 'staging')
tag="staging"
else("$git_branch", 'master')
tag="production"
endif
echo "tag is $(tag)"
when I run make build this is the output
git_branch=""
ifeq("it_branch", 'development')
/bin/sh: -c: line 0: syntax error near unexpected token `"it_branch",'
/bin/sh: -c: line 0: `ifeq("it_branch", 'development')'
make: *** [build] Error 2
so first, the git branch is empty, when it shouldn't. if I run this command on console, I get the branch name correctly, second, whats up with the variable being evaluated as it_branch instead git_branch?
Well, I'm kinda new to makefiles, I went through the docs and couldn't catch anything I'm possibly doing wrong
Edit
I changed to this:
git_branch:= $(shell git rev-parse --abbrev-ref HEAD)
ifeq ($(git_branch), "development")
tag:= "development"
else($(git_branch), "staging")
tag:= "staging"
else($(git_branch), "master")
tag:= "production"
endif
build:
echo "tag is $(tag)"
echo "branch is $(git_branch)"
the tag is empty, but git_branch is right
Something like this should work:
git_branch:= $(shell git rev-parse --abbrev-ref HEAD)
ifeq ($(git_branch), development)
tag:=development
endif
ifeq ($(git_branch), staging)
tag:=staging
endif
ifeq ($(git_branch), master)
tag:=production
endif
build:
echo "tag is $(tag)"
Short explanation: You're confusing shell variables with make variables. Your logic for the make variables doesn't have to be inside a recipe, in fact, this doesn't make much sense because these things are interpreted by make (while the recipe is executed by a shell make launches).
In make, you have to enclose variable names in parantheses, otherwise it is assumed they only have one character.
If a makefile variable contains multiple characters you have to enclose the name with parentheses not quotes. Evaluate shell commands with shell:
git_branch=$(shell git rev-parse --abbrev-ref HEAD)
ifeq($(git_branch), 'development')

Resources