I have a Makefile with the following:
AVAR=""
all :
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
fi
#echo $(AVAR)
The idea is that depending on the presence of directory "../old" i will or not
have information in AVAR (available for use later), however, the if is evaluating to true, and the Ping! is echoed, but nothing is assigned to AVAR, either inside or outside the if statement.
Output is as follows:
$ make all
if [ -d ../old ]; then \
(echo "all Ping!"; AVAR="../old"; echo "") \
fi
all Ping!
Any Insight appreciated.
1) Each command in a makefile recipe runs in its own subshell. You define AVAR in the first command (the "if" statement), so it's not available to the second command (#echo $(AVAR)).
2) Within the first command, you define AVAR, but you don't call it right. The term $(AVAR) is Make syntax; Make expands it before executing the command, and since Make doesn't know anything about such a variable, it expands to nothing. You have to use the shell to expand it, using shell syntax: $AVAR. But if you try that, Make will expand the $A to nothing, and you'll get "VAR". So you "escape" the $ with another $:
all :
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $$AVAR) \
fi
Try
(echo "$# Ping!"; AVAR="../old"; echo $$AVAR) \
instead of
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
That may be a bit late, but in case someone else has the same question.
The way I handle it is by evaluating the if in a shell function and extract the stdout to a variable that's usable in the next command.
Method 1: appending the echo to the AVAR setting
if [ -d ../old ]; then \
(echo "$# Ping!") \
fi
AVAR=$(shell if [ -d ../old ]; then \
(AVAR="../old"; echo $${AVAR}) \
fi;) && \
echo $${AVAR}
or Method 2: evaluating the variable so that it could be used throughout the target without appending the echo ${AVAR} to the same shell.
if [ -d ../old ]; then \
(echo "$# Ping!") \
fi
$(eval AVAR=$(shell if [ -d ../old ]; then \
(AVAR="../old"; echo $${AVAR}) \
fi;))
#echo ${AVAR}
instead of
if [ -d ../old ]; then \
(echo "$# Ping!"; AVAR="../old"; echo $(AVAR)) \
fi
#echo $(AVAR)
Notes:
- AVAR being set by the $(shell ... ) output is a different variable from the one inside the if condition
- If you want to use a single if, you'll have to remove the stdout of "$# ping" from the beginning of AVAR content
Problem solved, Did the very bad recursive make option to redefine stuff and reinterpret.
Related
I have the following code from a make file, I know this creates bin folder in Home if that doesn't exist... but I couldn't understand what $$HOME/bin mean...
I googled and found $$ is to get the processid of the bash... but couldn't understand what $$HOME/bin mean... can someone please explain ?
.PHONY: home_bin
home_bin: ## Create home bin if not created
# if [[ ! -d "$$HOME/bin" ]]; then \
echo "Creating $$HOME/bin"; \
mkdir $$HOME/bin; \
echo "✔︎ $$HOME/bin created"; \
else \
echo "✔︎ $$HOME/bin already created"; \
fi
Thank you.
make itself performs expansion of $-prefixed characters; the $$ is expanded to a single literal $ to pass to the shell.
Consider a simple Makefile:
x=f
all:
xoo=3 && echo $xoo
which will output foo, because
make expands $x to the single character f.
make passes the string xoo=3 && echo foo to the shell for execution
Compare with
x=f
all:
xoo=3 && echo $$xoo
which outputs 3, because
make expands $$ to $
make passes the string xoo=3 && echo $xoo to the shell for execution
I want to do something like:
#!/bin/sh
[ -f "/tmp/nodes" ]
[[ $? -eq 0 ]] && VAL=$? ||
geth --datadir /root/.ethereum \
${VAL+"--nodekey \"/root/nodekey.txt\""} \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0" \
I want the option --nodekey "/root/nodekey.txt" to be passed if the file /tmp/nodes exists. How can that be done more elegantly than an if with two nearly identical commands?
--EDIT--
This is the best I've been able to get working so far:
if [ $VAL -eq 0 ]; then
/geth --datadir /root/.ethereum \
--nodekey "/root/nodekey.txt" \
# No dice
# Would be nice if this worked so I didn't need the if
# ${VAL+ --nodekey "/root/nodekey.txt" } \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0"
else
/geth --datadir /root/.ethereum \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0" \
fi
This is another line in the file and works fine:
ENODE_URL=$(/geth --datadir /root/.ethereum ${VAL+ --nodekey "/root/nodekey.txt"} --exec "${JS}" console 2>/dev/null | sed -e 's/^"\(.*\)"$/\1/')
There's a bashism here, but it's [[ $? -eq 0 ]], as [[ is a ksh extension adopted by bash. There's no point to using $? at all here, since you can just directly perform your assignment based on whether the test -f succeeds:
touch /tmp/nodes # set us up for the truthy path
if test -f /tmp/nodes; then tmp_nodes_exists=1; else unset tmp_nodes_exists; fi
printf '%s\n' /tmp/nodes ${tmp_nodes_exists+"REALLY EXISTS" "(yes, really)"}
...properly emits as output (as run with dash, perhaps the most common minimal /bin/sh interpreter):
/tmp/nodes
REALLY EXISTS
(yes, really)
By contrast, to demonstrate that the other path fails as it should:
rm -f -- /tmp/nodes # set us up for the falsey path
if test -f /tmp/nodes; then tmp_nodes_exists=1; else unset tmp_nodes_exists; fi
printf '%s\n' /tmp/nodes ${tmp_nodes_exists+"REALLY EXISTS" "(yes, really)"}
emits as output only:
/tmp/nodes
So I'm trying to hack one of my makefiles to be simpler (simpler, as if, not defining a lot of rules how to transform subdirectory into .deb).
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
$(eval STATUS_REBUILD=1) \
echo "component: file not found: 1"; exit;\
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
$(eval STATUS_REBUILD=1) echo "component: newer files exists: 1"; exit;\
else \
$(eval STATUS_REBUILD=0) echo "component: no newer files: 0"; \
fi;\
fi
#echo "status $(STATUS_REBUILD)"
actual-target: build-if-need-status-vars
ifeq ($(STATUS_REBUILD), 1)
#echo first status: 1
else
#echo second status: 0
#echo different action
endif
all: actual-target
.PHONY: actual-target
Test with:
mkdir -p test/{sources,debs}; touch test/debs/1.deb; sleep 2; touch test/sources/1.src; (create makefile there and run)
Result:
component: file not found: 1
status 0
second status: 0
Regardless of what conditional block is executed, STATUS_REBUILD will always be 0 (last evaluated value), try it: touch test/debs/1.deb
So it seems that last $(eval) is always used.. How to avoid this behaviour and keep the correct assigned value (from first match in build-if-need-status-var)?
$(eval) is a make-level function. It is expanded in your recipe during recipe the recipe expansion stage.
The contents of a recipe are expanded in the second phase of makefile parsing (discussed briefly in the manual here).
I believe, but cannot say for sure (without testing), that recipes are not expanded until they are about to be run (but for the purposes here that doesn't change anything either way).
So your problem here is that all the $(eval) calls are expanded by the time make goes to run your shell script so you always see the last value in effect when the last line is run.
That all being said you don't actually need a make-level variable here. Your recipe is already only two shell executions.
You can simply include the last line in the same execution as the first (split) line and use a shell variable.
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
STATUS_REBUILD=1; \
echo "component: file not found"; \
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
STATUS_REBUILD=1; echo "component: newer files exists"; \
else \
STATUS_REBUILD=0; echo "component: no newer files"; \
fi;\
fi; \
echo "status $$STATUS_REBUILD"
Note that I needed to remove the exit pieces to make this work. If those are necessary in the real makefile (because this is a stripped down sample) then you can keep them by wrapping the if in a sub-shell and/or by rewriting the recipe.
I'm trying to do this ..... and this is how my Makefile look like
.PHONY: run
SHELL := /bin/tcsh
run:
md5sum -c md; \
if ($$?==0) then \
echo "PASS" \
else \
echo "FAIL" \
endif
But i got this error.
if: Badly formed number.
make: *** [run] Error 1
Is what I'm doing correct? Or is there a better way of doing that in a Makefile?
Basically, you should simply not, ever, use csh (or tcsh) for writing makefile rules. Write the rule using POSIX shell:
.PHONY: run
run:
md5sum -c md; \
if [ $$? -eq 0 ]; then \
echo "PASS"; \
else \
echo "FAIL"; \
fi
I have makefile with below code:
.PHONY: $(PROJECTDIR)/projectroot
$(PROJECTDIR)/projectroot:
if [ -f $(#)/etc/release ]; then \
#$(RELEASE_VERSION) will have value something like 00.01.02.03
IFS=. read major minor micro build <<<"${RELEASE_VERSION}" \
echo major=${major} > $(#)/etc/release \
echo major=${minor} >> $(#)/etc/release; \
echo major=${micro} >> $(#)/etc/release; \
echo major=${build} >> $(#)/etc/release; \
fi
I am expecting the $(#)/etc/release to get content like below:
major=00
minor=01
micro=02
build=03
I need to replace IFS=. with some equivalent command in gnu make, can you please help me with this? thanks.
The problem is that you're readin the values into shell variables called major, minor etc. but then you are printing Make variables with the same names. The shell cannot set the Make variables.
To fix your existing code you need to repeat the $ characters as $$ so Make outputs a $ to the shell, which will interpret them (also you're saying "major" every time, and are missing some semi-colons):
IFS=. read major minor micro build <<<"${RELEASE_VERSION}" ; \
echo major=$${major} > $(#)/etc/release ;\
echo minor=$${minor} >> $(#)/etc/release; \
echo micro=$${micro} >> $(#)/etc/release; \
echo build=$${build} >> $(#)/etc/release; \
But to do it within make you can use the subst and word functions:
REL_WORDS := $(subst ., ,${RELEASE_VERSION})
...
echo major=$(word 1,${REL_WORDS}) > $(#)/etc/release ;\
echo minor=$(word 2,${REL_WORDS}) >> $(#)/etc/release ;\
echo micro=$(word 3,${REL_WORDS}) >> $(#)/etc/release ;\
echo build=$(word 4,${REL_WORDS}) >> $(#)/etc/release ;\
.PHONY: $(PROJECTDIR)/projectroot
$(PROJECTDIR)/projectroot: $(PROJECTDIR)/projectroot/etc/release
$(PROJECTDIR)/projectroot/etc/release:
echo "${RELEASE_VERSION}" | \
sed 's/^/major=/; s/\./\nminor=/; s/\./\nmicro=/; s/\./\nbuild=/' >$#
Add dependencies to the release file.