how to break GNU Make shell function call to multiple line - makefile

Here is the correct code, but it is very long
$(shell if [ ! -f .oldmodel ] || [ "$(MODEL)" != `cat .oldmodel` ] ; then echo $(MODEL) > .oldmodel ; fi )
I tried
$(shell if [ ! -f .oldmodel ] || [ "$(MODEL)" != `cat .oldmodel` ] ; \
then echo $(MODEL) > .oldmodel ; fi )
But There is a error
common.mak:21: *** missing separator. Stop.
the strange thing is that the .oldmodel is generated, and it's content is right, the next time call make, no error anymore.
Can anyone explain this.

Looks like you are trying to achieve something in a wrong way. Consider the following solution:
ifeq ($(MODEL),)
MODEL := $(shell cat .oldmodel 2>/dev/null)
endif
stuff_to_do: \.oldmodel
mkdir -p $(MODEL)
\.oldmodel: $(if $(shell echo '$(MODEL)' | cmp $# 2>/dev/null), phony,)
#if [ -z "$(MODEL)" ]; then echo "error: MODEL is not set" && exit 1; fi
echo '$(MODEL)' > $#
clean::
rm -rf $(MODEL)
rm -f .oldmodel
The stuff_to_do is your rule where you use the $(MODEL) variable.
Note, the prerequisites for .oldmodel rule is either a phony or empty depending on equality of .oldmodel file contents and $(MODEL). This phony is taken as some non-existent file/rule, so when phony is there it causes the .oldmodel to be rebuilt.

Related

How to use bash conditionals correctly

I want to only do some code if $# equals 1 and $1 is a readable existing file.
I have following code:
if [[ $# -eq 1 ] -a [ test -r $1 ]]
I have tried many different solutions for an and statement.
Like
[ $# -eq 1 ] $$ [ test -r $1 ]
[ $# -eq 1 -a test -r $1 ]
and many more...
Nothing seems to work. I think its because of the test command.
Sometimes I get an error like test not found or too many arguments or smth else
My whole code:
#!/bin/bash
if [[ $# -eq 1 ] -a [ test -r $1 ]]
then
groupadd -f "TAI12A"
IFS=:
while read nachname vorname klasse
do
nutzername=$nachname$vorname
groupadd -f $klasse
useradd -g "TAI12A" -G $klasse -s /bin/bash -m -p "taipasswd $nutzername
done < $1
else
echo "Uebergabewerte fehlerhaft"
fi
if you want to use test, then no brackets:
if [ $# -eq 1 ] && test -r "$1" ; then
do this ...
fi
Note: [ is a command, it's an alias for test with one exception: when using [ instead of test, ] must be the last argument.
To illustrate this: even if bash nowadays includes [ as a builtin command, the command /usr/bin/[ should still exist on your server.
PS: I would just use:
if [ $# -eq 1 ] && [ -r "$1" ] ; then
do this ...
fi
which is the same as
if test $# -eq 1 && test -r "${1}" ; then
do this ...
fi
Further confusion might be added by the fact that bash also has an extended conditional expressions in the form [[ ... ]]. I recommend to read https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html#Bash-Conditional-Expressions

Unable to call function from a Makefile

I need to call a function from a make target, this function would be called multiple time ,
define generate_file
if [ "${RQM_SETUP}" = "ci" ]; then
echo "$1" > $(2).txt
else
echo "It is Not Setup";
fi
endef
all:
$(call generate_file,John Doe,101)
$(call generate_file,Peter Pan,102)
right now i am stuck at this Error:
bash-5.0# make
if [ "" = "ci" ]; then
/bin/sh: syntax error: unexpected end of file (expecting "fi")
make: *** [Makefile:10: all] Error 2
Your function is multiple line, which will try to execute as separate shell invocations. This will fail as any single line is not syntactically correct on its own. You can make it work by setting it up in a single line, i.e.:
$ cat Makefile
define generate_file
if [ "${RQM_SETUP}" = "ci" ]; then \
echo "$1" > $(2).txt; \
else \
echo "It is Not Setup"; \
fi
endef
all:
$(call generate_file,John Doe,101)
$(call generate_file,Peter Pan,102)
Output:
$ make
if [ "" = "ci" ]; then echo "John Doe" > 101.txt; else echo "It is Not Setup"; fi
It is Not Setup
if [ "" = "ci" ]; then echo "Peter Pan" > 102.txt; else echo "It is Not Setup"; fi
It is Not Setup

Manage exit code in Makefile define

I am creating a target that can be used as a hook to add some extra procedure when is needed.
This is the code in the main Makefile
export MSG:="Linux hook"
linux-build:
# echo "Dependency target"
linux-build-post: linux-build
# make -s -f linux.hook -q linux_build_post 2> /dev/null ; \
if [ $$? -le 1 ] ; then \
echo "Target exist" ; \
make -s -f linux.hook linux_build_post ; \
else \
echo "Target doesn't exist" ; \
fi
# touch $#
This is the code in the file linux.hook
linux_build_post:
# echo ${MSG}
This code works correctly, but now I am trying to create a template in the main Makefile. For example:
export MSG:="Linux hook"
linux-build:
# echo "Dependency target"
# Common hook target
# Parameters:
# 1: Target name
# 2: Dependecies
# 3: Hook name
define COMMON_HOOK_TARGET
$(1): $(2)
make -s -f $(3).hook -q $(1) 2> /dev/null ; \
if [ $$? -le 1 ] ; then \
echo "Target exist" ; \
make -s -f $(3).hook $(1) ; \
else \
echo "Target doesn't exist" ; \
fi
touch $(1)
endef
$(eval $(call COMMON_HOOK_TARGET,linux-build-post,linux-build))
But in this case the make command fails because $$? is replaced with the dependency linux-build then the if condition is evaluated like this if [ linux-build -le 1 ].
Error reported:
/bin/sh: 1: [: Illegal number: linux-build
How I can change the code in order to use $$? as the exit code of the previous command make -s -f $(3).hook -q $(1) 2> /dev/null ?
I think the answer is actually this:
if [ $$$$? -le 1 ] ; ...
The call to call turns "$$$$" into "$$", then when Make executes the rule it converts "$$?" to "$?" and passes it to the shell.

update a file with the content of a variable, if need be, in bash

In order to update some files only if their desired content has changed, I have been using this script:
updatefile()
{
# First parameter is file name.
# Second parameter is desired content.
local FILENAME
local DIRNAME
local FILEALREADYMATCHING
local CURRENTFILECONTENT
if [ $# -ne 2 ] ; then
return 99
fi
FILENAME=$(basename "$1")
DIRNAME=$(dirname "$FILENAME")
FILECONTENT="$2"
mkdir -p "$DIRNAME"
if [ -d "$DIRNAME" ] ; then
FILEALREADYMATCHING=true
if ! [ -f "$FILENAME" ] ; then
FILEALREADYMATCHING=false
else
CURRENTFILECONTENT="$(IFS= cat "$FILENAME")X"
CURRENTFILECONTENT=${CURRENTFILECONTENT%?}
if [ "$CURRENTFILECONTENT" != "$FILECONTENT" ] ; then
FILEALREADYMATCHING=false
fi
fi
if [ "$FILEALREADYMATCHING" != "true" ] ; then
printf '%s' "$2" > "$FILENAME"
fi
fi
}
But I found out that it carries on rewriting the file even when its current content is already matching the desired content. All the X appending and removing gymnastics did not help. Debug-printing the current and desired content shows no difference. What is wrong with the comparison I am using?
Alternatively, is there a standard way of changing a file's content without wearing off the drive? If it matters, I am using the most recent version of Bash on OpenWRT, but the needless overwriting also occurs on Debian testing, amd64.
Your code actually works perfectly fine when I tried it, however;
This may be where it's falling apart:
FILENAME=$(basename "$1") # <-- Path is REMOVED here
DIRNAME=$(dirname "$FILENAME") # <-- No point running, path is already gone
FILECONTENT="$2"
Say i have a file: /tmp/testfile
If i run basename /tmp/testfile I get: testfile
If I then run dirname testfile it doesn't magically find the original path again.
Reverse the order of argument assignment and try again:
DIRNAME=$(dirname "$1") # <-- No point running, path is already gone
FILENAME=$(basename "$1") # <-- Path is REMOVED here
FILECONTENT="$2"
Sidenote
Why don't you add some debug code to it to see where it's falling apart.
I find this sometimes help when bash -x is just a bit busy
#!/bin/bash
# First parameter is file name.
# Second parameter is desired content.
decho () {
if [ "$debug" == "1" ]; then
echo "$#"
return
fi
}
updatefile () {
local FILENAME
local DIRNAME
local FILEALREADYMATCHING
local CURRENTFILECONTENT
if [ $# -ne 2 ] ; then
decho returning #echo if debug=1
return 99
fi
FILENAME=$(basename "$1")
DIRNAME=$(dirname "$FILENAME")
FILECONTENT="$2"
mkdir -p "$DIRNAME"
if [ -d "$DIRNAME" ] ; then
FILEALREADYMATCHING=true
if ! [ -f "$FILENAME" ] ; then
FILEALREADYMATCHING=false
decho "file doesn't exist" #echo if debug=1
else
CURRENTFILECONTENT="$(IFS= cat "$FILENAME")X"
CURRENTFILECONTENT=${CURRENTFILECONTENT%?}
decho "file exists" #echo if debug=1
if [ "$CURRENTFILECONTENT" != "$FILECONTENT" ] ; then
decho "content is different" #echo if debug=1
FILEALREADYMATCHING=false
fi
fi
if [ "$FILEALREADYMATCHING" != "true" ] ; then
decho "file doesnt match, making it match now" #echo if debug=1
decho "file is $FILENAME by the way" #echo if debug=1
printf '%s' "$2" > "$FILENAME"
fi
fi
}
updatefile "$1" "$2"
So then, instead of invoking the script like this:
./script filename "Content"
invoke it like this:
debug=1 ./script filename "Content"

Test in if condition makefile on cygwin

I am trying to make the simplest comparison within an if condition in a makefile.
I am using cygwin. I keep getting the error XX was unexpected at this time. Where XX is the LHS of the comparison operator. This exact makefile runs on mks, but now I have to use cygwin because I am running it on a 64 bit windows at the moment.
Here is my code:
DEBUG=0
all:
if test $(DEBUG) != 0 then\
echo "not equal to 0"\
fi
I also tried this:
DEBUG=0
all:
if [ $(DEBUG) != 0 ] then\
echo "not equal to 0"\
fi
and this:
DEBUG=0
all:
if [ "$(DEBUG)" != "0" ] then\
echo "not equal to 0"\
fi
This is the error:
0 was unexpected at this time. *** [all] Error 255
====================================================
Updates:
I have tried adding the missing ; and also making it a one liner as #Jonas. suggested but still getting the same problem.
One Liner:
if [ $(DEBUG) != 0 ] ; then echo "not equal to 0" ; fi
With semicolons:
if [ $(DEBUG) != 0 ] ; then \
echo "not equal to 0" ; \
fi
And yes I am using tabs not spaces. And I have changed the line endings to LF only.
You need two ;. Without the last semicolon then fi is a parameter to echo. It works with: GNU Make 4.0 on Linux debian 3.16.0-4-amd64.
Note: There may be issues when using older versions of GNU Make.
DEBUG=1
all:
if [ $(DEBUG) != 0 ]; then \
echo "not equal to 0" ; \
fi

Resources