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

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')

Related

Why is variable not getting set in 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 ...)

I can't get Makefile ifeq syntax to work (checking age of a file)

I am attempting to use an inheritance hack (which is already working) along with checking a file date to create a sort of reusable, auto-updating base makefile from which multiple projects can inherit; and which will update the parent Makefile using a git pull, but (and this is where I'm stuck) I only want it to try the git pull once per day, so that it doesn't add up to a lot of wasted time waiting on a git pull when the chance of updates being available is almost nothing.
The basic idea is this:
baseMakefilePath=../../baseMakefile
do_some_work: .check-for-update
#echo "working..."
.check-for-update:
# is the file > 24 hours old?
ifeq ( $(find . -mtime +24h -name '.check-makefile-update'), ./.check-makefile-update )
#make .update
else
# if the file doesn't exist at all yet, pull the update
ifeq ( $(find . -name '.check-makefile-update'), '' )
#make .update
else
#echo "last update was recent, not updating..."
endif
endif
.update:
cd $(baseMakefilePath) && git pull
touch .check-makefile-update
My theory is that by updating the modified timestamp on the .check-makefile-update file using touch it should only run the git pull once per day.
However, I can't even get a dirt simple ifeq() conditional to work:
test:
ifeq (a, a)
#echo "a basic test works"
else
#echo "idunno"
endif
With this basic Makefile (note: this is the ONLY contents of the Makefile when I test it), I get this error:
$ make test
ifeq (a, a)
/bin/sh: -c: line 0: syntax error near unexpected token `a,'
/bin/sh: -c: line 0: `ifeq (a, a)'
make: *** [test] Error 2
I get the same result if I try to run the first Makefile:
$ make do_some_work
# is the file > 24 hours old?
ifeq ( , ./.check-makefile-update )
/bin/sh: -c: line 0: syntax error near unexpected token `,'
/bin/sh: -c: line 0: `ifeq ( , ./.check-makefile-update )'
make: *** [.check-for-update] Error 2
I think the stripped down example shows that there's something funky going on (or my understanding of ifeq is fundamentally flawed) but for what it's worth, I also tried quoting various combinations of the ifeq arguments, with both single and double quotes.
I'm out of ideas, but I feel like I'm sooo close to a working solution! What am I doing wrong?
If it matters, I'm on OSX 10.14.5, and my primary shell is zsh. It is not an absolute requirement but would be good if the solution can also run on modern versions of Windows (with WSL), too.
Remember that there are Make conditionals, and shell conditionals.
Here:
test:
ifeq (a, a)
#echo "a basic test works"
else
#echo "idunno"
endif
I assume you're trying to use a Make conditional, but if those whitespace margins are TABs, then you're inadvertantly telling Make that those lines are shell commands which it should pass to the shell as they are. The shell tries to interpret ifeq (a, a) and complains of a syntax error.
Remove some of the TABs (leaving the ones in front of the actual shell commands):
test:
ifeq (a, a)
#echo "a basic test works"
else
#echo "idunno"
endif
and it works.
Your understanding of ifeq is fundamentally flawed :)
A makefile is a combination of two different syntaxes: the makefile itself is written in make syntax, and the recipes are written in shell syntax.
Those two syntaxes are not in any way compatible: you can't use make syntax when you are writing recipes and you can't use shell syntax when you are writing makefiles.
How do you know which is which? The simplest way to think about it is that if the first character on your line is indented with a TAB character, it's shell syntax and if it's not, it's make syntax. The reality is more subtle but that rule is always true.
I'm sure you can now see what's wrong with your makefile AND also why you get the errors you do:
test:
ifeq (a, a)
#echo "a basic test works"
else
#echo "idunno"
endif
you are trying to pass make syntax to the shell.

Makefile: exit on conditional

I want to check that an environment variable is set before executing some code in a Makefile. If it's not set I want to throw an error with a simple error message:
run:
[ -z "$(MY_APP)" ] && echo "MY_APP must be set" && exit 1
echo "MY_APP is set. Yay!"
echo "Let's continue on with the command..."
When MY_APP is not set I get the following error, which is desired:
[ -z "" ] && echo "MY_APP must be set" && exit 1
MY_APP must be set
make: *** [run] Error 1
However, when MY_APP is set I get the following error:
[ -z "EXAMPLE_NAME" ] && echo "MY_APP must be set" && exit 1
make: *** [run] Error 1
Any idea what I'm doing wrong? And is there a better way to do this?
Recall that the && condition require that all conditions must be TRUE to pass. Since the first condition fail, the whole command will return a status of 1 (-> false), effectively stopping the make
You can use the following, so that the test will fail only when MY_APP is missing.
Note that I'm using false instead of exit 1. Also better to use "${MY_APP}", which make it easier to copy/paste from Make to shell prompt/script.
run:
{ [ -z "$(MY_APP)" ] && echo "MY_APP must be set" && false } || true
...
# Or just if-Then-Else
if [ -z "${MY_APP}" ] ; then echo "MY_APP must be set" ; false ; fi
...
You can test environment variables with Makefile conditional syntax, like this:
sometarget:
ifndef MY_APP
#echo "MY_APP environment variable missing"
exit 1
endif
somecommand to_run_if_my_app_is_set
Note that ifndef/ifdef operate on the name of the variable, not the variable itself.
It seems that you are trying to use a Makefile to run commands which are not building targets (the target name run is a giveaway). You already got bitten by one of Makefile and shells caveats. Makefile caveat: exit status is inspected after each line and if not zero abort immediately. Shell caveat: the test command ([) returns a non zero exit status so the entire line returns non zero.
The rule of thumb is: a recipe of a rule should create a filename named like the target of the rule.
Here is a rule (to clarify the terms):
target:
recipe command lines
should create file named target
There are some exceptions to this rule of thumb. Most notably make clean and make install. Both typically do not create files named clean or install. One can argue that make run maybe also be an exception to this rule of thumb.
If your run is as simple as a typical clean then I might agree about making an exception. But usually commands are run with command line arguments. Before long you will want make run accept arguments. And making make accept custom command line arguments is not fun at all.
You tried to manipulate the behaviour using environment variables which is somewhat less problematic than command line arguments. But still problematic enough to make you trip over a caveat.
My suggestion for a fix:
Put complex recipes in a shell script. There you have all the power and flexibility of a shell script without the awkwardness of makefiles. For example as explained here: Basic if else statement in Makefile
In case of a typical run target write a wrapper shell script around the makefile which lets the makefile rebuild the target and then run the target. For exampe as explained here: Passing arguments to "make run"
You can conditionally exit the Makefile using error control function, at least in the GNU version.
This snippet is a helpful condition to put into the head of the Makefile. It exits with a message of help, if make was not called from within the directory of the Makefile.
MAKEFILE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ifneq (${MAKEFILE_DIR}, $(shell pwd))
INVALID_LOCATION:=`make` must be called from within ${MAKEFILE_DIR} (or with option -C ${MAKEFILE_DIR})
$(error ERROR: $(INVALID_LOCATION))
endif
See: https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
Useful in case your paths are relative to the Makefile and you don't want them to prefix with a base.

Make : Unexpected token error

I am hitting following error while building my code.
/bin/sh: -c: line 0: syntax error near unexpected token `testk,x86_64'
/bin/sh: -c: line 0: `ifeq (testk,x86_64)'
Code:
define temp
mkdir -p $(#D)
ifeq ($(MACH),x86_64)
env PERLLIB=/usr/lib/perl5/site_perl/5.10.0 --test $<
else
env PERLLIB=/usr/ex-lib/perl5/site_perl/5.10.0 --test $<
endif
endef
I found we need to remove indentation before 'if' statement in make files. I did the same and still facing the issue. Can you please correct me if I am doing anything wrong.
I am using 'temp' in following way.
$(TGT64)/$(SETUP): $(TGT64)/$(GEN_K)_gen
$(temp)
Here I am trying to check the platform and set the environment accordingly. in 'define temp' I am check MACH variable content which has platform information.
I play around code and make following changes.
define temp
mkdir -p $(#D)
#ifeq ($(MACH),x86_64); then env PERLLIB=/usr/lib/perl5/site_perl/5.10.0 $<; \
#else \
#env PERLLIB=/usr/ex-lib/perl5/site_perl/5.10.0 $<; \
#fi
endef
This time I am facing.
/bin/sh: -c: line 1: syntax error: unexpected end of file issue.
Before we can help you have to show us how you're using this variable temp you've defined. Just showing the contents of the variable won't help.
I expect that you're trying to use the variable inside a recipe, like this:
foo:
$(temp)
You can't do that: the contents of a recipe have to be a shell script; recipes are passed to the shell to be executed. The contents of temp as you've defined it contain makefile constructs which the shell doesn't know anything about, hence your syntax error from the shell.
Also, every line in a recipe is passed to a separate shell, so your line env FOO = bar has no effect: once that line has been invoked the shell exits and all changes to its environment are lost.
Your example here is not sufficient for us to recommend a workable strategy: you haven't told us what you want to accomplish.
ETA My answer above is still true. It looks like you tried to convert your variable to contain shell script syntax for an if-statement, but did not complete the job: ifeq is makefile syntax not shell syntax. In the shell, you have to use if. Like this:
#if [ '$(MACH)' = x86_64 ]; then ...
However, please re-read my comment above about setting an environment variable... the recipe you've shown here will have no effect at all, even once it no longer has syntax errors.

Makefile conditional error

I am attempting to do a make statement to check the architecture. I am very close to getting it to work:
test:
ifeq ("$(shell arch)", "armv7l")
echo "This is an arm system"
else
echo "This is not an arm system."
endif
I have one issue: although this seems to resolve to ifeq ("i386", "armv7l") which should be false, I get the following error:
$ make
ifeq ("i386", "armv7l")
/bin/sh: -c: line 0: syntax error near unexpected token `"i386",'
/bin/sh: -c: line 0: `ifeq ("i386", "armv7l")'
make: *** [test] Error 2
So, it is resolving to two strings comparing to each other, but there is a syntax error. What's wrong here?
You cannot use make statements like ifeq inside a recipe. Recipes (the lines that begin with TAB) are passed to the shell. The shell doesn't understand ifeq; that's a make construct.
You'll have to use shell if-statements inside a recipe. And, you don't have to use $(shell ...) in a recipe, because you're already in a shell.
test:
if [ `arch` = armv7l ]; then \
echo "This is an arm system"; \
else \
echo "This is not an arm system."; \
fi
This is likely not the best way to handle this, but since you didn't provide any info on what you're really trying to do with this it's all we can say.
As MadScientist said, make is passing the ifeq lines to the shell, but if you write it properly, you can definitely mix make constructs like ifeq with commands within a recipe. You just need to understand how make parses a Makefile:
If a line begins with a TAB, it is considered a command for the shell regardless of where the line is within the file.
If it doesn't begin with a TAB, make interprets it as part of its own language.
So, to fix your file, just avoid starting the make conditionals with a TAB:
test:
ifeq ("$(shell arch)", "armv7l")
echo "This is an arm system"
else
echo "This is not an arm system."
endif

Resources