Split a string in gnu make with a separator - makefile

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.

Related

$$ in shell , Linux

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

How to use eval Makefile variable from recipe to conditionally execute some commands in recipe

Goal is to apply patch ONLY if patch is not present. If patch is present don't do anything.
I used below makefile rule.
C_FILE_PATCH_SIG=###MAGIC_CODE;
C_FILE_CODE=~/code/file.c
C_PATCH_FILE=~/test.patch
.tmp/patch_c:
cp ${C_PATCH_FILE} ${SDK}
ifneq ($(PATCH_DONE), 1)
$(MAKE) applypatch || $(MAKE) helppatch
endif
#echo DONE > .tmp/patch_c
applypatch:
#echo "Patching ${C_FILE_CODE}"
if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE} ; \
then \
echo 1 > .tmp/PATCH_PRESENT_file; \
else \
echo 0 > .tmp/PATCH_PRESENT_file;\
fi
cat .tmp/PATCH_PRESENT_file
# $(eval PATCH_PRESENT := `cat .tmp/PATCH_PRESENT_file`)
$(eval PATCH_PRESENT := $(shell cat .tmp/PATCH_PRESENT_file))
#echo "WWWWWW PATCH_PRESENT=[$(PATCH_PRESENT)] WWWWWWW"
ifeq ($(PATCH_PRESENT), 0)
#echo "Applying the patch $(PATCH_PRESENT)"
cd ~/code && git apply -v ${C_PATCH_FILE}
else
#echo "NOT Applying the patch $(PATCH_PRESENT)"
endif
helppatch:
#echo -e "\n\n\n"
#echo -e "++++++++++ Apply below patch manually then run 'make build PATCH_DONE=1' ++++++++++\n\n"
#echo -e "+++++++++++++++++++++++++++++++++++++"
#cat ${C_PATCH_FILE}
#echo -e "+++++++++++++++++++++++++++++++++++++"
#echo -e "\n\n\n"
false
But it always evaluates to the else part of ifeq.
Where am I doing wrong?
If I use the patch command of git withing the shell multiline I loose the error code returned by the git patch.
Thanks in advance...
Your ifeq will be evaluated when the makefile is first read (as opposed to when the recipe is run). The eval, on the other hand, will not be executed until the recipe is run (afterwards). Thus, PATCH_PRESENT is not equal to 0 at parse time, and make will expand the else portion of the clause. By the time the eval is run, the if statement is already evaluated and gone from memory.
BTW, a cleaner way to do this is to do everything in bash:
applypatch:
#echo "Patching ${C_FILE_CODE}"
#if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE}; then \
echo "NOT Applying the patch"; \
else \
echo "Applying the patch"; \
cd ~/code && git apply -v ${C_PATCH_FILE}; \
fi

How To test the exit status, and do something in Makefile

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

GNUMAKE: find if a string is present in a file before updating it

I am trying to add "description" to $(#)/etc/release file only if its not there already
$(PROJECTDIR)/projectroot:
if [ grep -q "description" "$(#)/etc/release" ]; then \
echo "description :" $(PLATFORM) >> $(#)/etc/release; \
fi
but it throws error "/bin/sh: line 0: [: too many arguments" and not doing anything with release file. can you please help. Thanks a lot.
As per my comment (and an edit I was about to make but couldn't). What you want is:
$(PROJECTDIR)/projectroot:
if ! grep -q description '$(#)/etc/release'; then \
echo 'description :' $(PLATFORM) >> '$(#)/etc/release'; \
fi
or (assuming you don't have whitespace you want echo to normalize for you inside the value of $(PLATFORM)):
$(PROJECTDIR)/projectroot:
if ! grep -q description '$(#)/etc/release'; then \
echo 'description : $(PLATFORM)' >> '$(#)/etc/release'; \
fi

makefile setting a variable in if statement in a target

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.

Resources