Unexpected EOF in conditional construct in makefile - bash

I have the following target in my makefile
omp: main_omp.c omp_impl.o
if [[ ! -e ../bin/ ]]; then mkdir ../bin/ fi
gcc $(CFLAGS) ... # compilation et cetera
On executing make omp in the same directory causes make to terminate with the following error
if [[ ! -e ../bin ]]; then mkdir ../bin fi
/bin/sh: 1: Syntax error: end of file unexpected (expecting "fi")
make: *** [makefile:10: omp] Error 2
Executing the if ... fi statement in the terminal works as intended. I tried different combinations of double quotes, splitting into different lines etc and nothing works.
How do I fix this problem? Why is make running into an EOF over here?

You state:
Executing the if ... fi statement in the terminal works as intended.
I doubt that. If I cut-and-paste your example, I get a continuation prompt from the shell:
if [[ ! -e ../bin/ ]]; then mkdir ../bin/ fi
>
And that is logical. Your shell (either via the prompt or via make) sees that you want to execute mkdir with two arguments ../bin and fi. The solution is of course to make sure that the shell sees the fi as the next "command". To do that, you need to add a ; before the fi.

Related

How to use shell command in GNU Make to echo string

I have the following lines in my makefile:
.PHONY : clean
clean:
#echo "Running Clean"
$(shell if [ -e exe ]; then rm exe; else echo "no files"; fi)
When I run:
make clean
I get the following output on the shell
Running Clean
no files
make: no: Command not found
Makefile:22: recipe for target 'clean' failed
make: *** [clean] Error 127
Any suggestions?
The problem is the use of $(shell ...). What you want is:
.PHONY : clean
clean:
#echo "Running Clean"
#if [ -e exe ]; then rm exe; else echo "no files"; fi
As far as an explanation of what's going wrong -- when you first run the clean target, make will expand all make variables and functions in the recipes before it starts running them -- because $(shell ...) only has one $, this is considered a make function. Make runs the command, which outputs no files to stdout, and replaces the call with that string, and then starts executing the recipes... So now make sees the following:
clean:
#echo "Running Clean"
no files
When it tries to run no files, due to the lack of a #, it echos the line to the screen, and then passes the command to the shell. Because the shell doesn't recognize the keyword no it outputs the error you're seeing. Make itself then fails because the shell returned an error.
Hey all I'm the same guy who asked this question but I found an answer right after I posted this, I think I'll leave this up (unless this is against stackoverflow etiquette) in case someone else has the same problems. My solution was echoing the string to stdout.
$(shell if [ -e exe ]; then rm exe; else echo "no files" >&2; fi)

Bash unintentionally splitting command

Currently I had a rsync command which is failing once every ~15 minutes due to poor network condition. I had written a script to rerun the rsync, however the script does not work as intended because bash is unintentionally breaking up the command I passed in:
$ cat exit-trap.sh
#!/bin/bash
count=1
while :
do
echo ==============
echo Run \#$count
$#
if [[ $? -eq 0 ]] ; then
exit
fi
echo Run \#$count failed
let count++
sleep 15
done
$ ./exit-trap.sh rsync --output-format="# %i %n%L" source::dir target
==============
Run #1
Unexpected remote arg: source::dir
rsync error: syntax or usage error (code 1) at main.c(1348) [sender=3.1.1]
After poking around for a while I guess what rsync recevied in argv is `["rsync", "--output-format=#", "%i", "%n%L", "source::dir", "target"]. The output format is appearantly unintentionally splitted into indiviual pieces, causing a syntax error. Is there a way to fix this issue?
PS: So far I've also tried sh -c $#, sh -c \"$#\", and
./exit-trap.sh rsync --output-format=\"# %i %n%L\" source::dir target
./exit-trap.sh rsync --output-format=\\\"# %i %n%L\\\" source::dir target
./exit-trap.sh "rsync --output-format=\"# %i %n%L\" source::dir target"
None of these works.
You need to use "$#" as described here https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html#Special-Parameters:
($#) Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$#" is equivalent to "$1" "$2" ….

What is this error in a Makefile?

Using 'make' from the ARM DS-5 5.22 release on Cygwin, I am processing a makefile containing the lines:
if [[ ! -d "$(OBJ_DIR)" ]]; \
then $(MD) "$(OBJ_DIR)"; fi
This evaluates to:
if [[ ! -d "../../output/obj" ]]; \
then mkdir -p "../../output/obj"; fi
When making, I get an error:
make -C ./src/modules
make[1]: Entering directory `C:/Users/me/Documents/proj/src/modules'
if [[ ! -d "../../output/obj" ]]; \
then mkdir -p "../../output/obj"; fi
! was unexpected at this time.
make[1]: *** [setenv] Error 255
make[1]: Leaving directory `C:/Users/me/Documents/proj/src/modules'
make: *** [drivers] Error 2
When explicitly running the mkdir command from the command line, it works as expected and the output/obj directory is created with no problems.
This script works fine on other computers.
What does the ! was unexpected at this time. error mean, and how to fix that?
UPDATE: The make command is invoked from the following shell script:
#!/bin/bash
set -e
export PATH='./:/usr/bin/:/cygdrive/c/Program Files/ARMCompiler6.6/bin/'
export ARM_PRODUCT_PATH='c:/DS-5_v5.22/sw/mappings'
export USEARMCOMPILER6=1
export DS5VER=DS-5_5.22
/cygdrive/c/DS-5_v5.22/bin/make "$#"

How do I check whether a file or file directory exist in bash?

I currently have this bash script (which is located in my home directory, i.e., /home/username/ and I am running it as root as it's necessary for the icon copying lines):
cd /home/username/Pictures/Icon*
declare -a A={Arch,Debian,Fedora,Mageia,Manjaro,OpenSUSE}
declare -a B={Adwaita,Faenza,gnome,Humanity}
for i in $A; do
for j in $B; do
if test -e /usr/share/icons/$j/scalable ; else
mkdir /usr/share/icons/$j/scalable/
fi
if test -e /usr/share/icons/$j/scalable/$i.svg ; else
cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg
fi
done
done
What I want this script to do is to copy icons from my Pictures/Icons and logos directory to the scalable theme (specified in $B) subdirectories in /usr/share/icons. Before it does this, however, I'd like it to create a scalable directory in these theme subdirectories if it does not already exist. The problem is that the else part of the conditionals is not being read properly, as I keep receiving this error:
./copyicon.sh: line 8: syntax error near unexpected token `else'
./copyicon.sh: line 8: ` if test -e /usr/share/icons/$j/scalable ; else'
If you're wondering why the test -e ... in the conditional it's based on a textbook on bash scripting I've been following.
Checking file and/or directory existence
To check whether a file exists in bash, you use the -f operator. For directories, use -d. Example usage:
$ mkdir dir
$ [ -d dir ] && echo exists!
exists!
$ rmdir dir
$ [ -d dir ] && echo exists!
$ touch file
$ [ -f file ] || echo "doesn't exist..."
$ rm file
$ [ -f file ] || echo "doesn't exist..."
doesn't exist...
For more information simply execute man test.
A note on -e, this test operator checks whether a file exists. While this may seem like a good choice, it's better to use -f which will return false if the file isn't a regular file. /dev/null for example is a file but nor a regular file. Having the check return true is undesired in this case.
A note on variables
Be sure to quote variables too, once you have a space or any other special character contained in a variable it can have undesired side effects. So when you test for existence of files and directories, wrap the file/dir in double quotes. Something like [ -f "/path/to/some/${dir}/" ] will work while the following would fail if there is a space in dir: [ -f /path/to/some/${dir}/ ].
Fixing the syntax error
You are experiencing a syntax error in the control statements. A bash if clause is structured as following:
if ...; then
...
fi
Or optional with an else clause:
if ...; then
...
else
...
fi
You cannot omit the then clause. If you wish to only use the else clause you should negate the condition. Resulting in following code:
if [ ! -f "/usr/share/icons/$j/scalable" ]; then
mkdir "/usr/share/icons/$j/scalable/"
fi
Here we add an exclamation point (!) to flip the expression's evaluation. If the expression evaluates to true, the same expression preceded by ! will return false and the other way around.
You can't skip the then part of the if statement, easiest solution would be to just negate the test
if [[ ! -e /usr/share/icons/${j}/scalable ]] ; then
mkdir /usr/share/icons/${j}/scalable/
fi
if [[ ! -e /usr/share/icons/${j}/scalable/${i}.svg ]] ; then
cp -a ${i}*.svg /usr/share/icons/${j}/scalable/${i}.svg
fi
I left it with -e (exists), but you might consider using -d for directories or -f for files and some error handling to catch stuff (e.g. /usr/share/icons/$j/scalable/ exists, but is a file and not a directory for whatever reason.)
I also noticed that in your original code you are potentially trying to copy multiple files into one:
cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg
I left it that way in my example in case you are sure that it is always only one file and are intentionally renaming it. If not I'd suggest only specifying a target directory.

Make fails checking if directory exists

I googled for this, but I can't figure out why Bash complains with the following code to check if a directory exists:
test.mk
#!/bin/bash
MYDIR="dl"
all:
if [ ! -d $MYDIR ]; then
#if [ ! -d "${MYDIR}" ]; then
#if [ ! -d ${MYDIR} ]; then
#Here
fi
make -f test.mk
if [ ! -d YDIR ]; then
/bin/sh: Syntax error: end of file unexpected
make: *** [all] Error 2
Does someone know why it fails? And why does it call /bin/sh instead of /bin/bash? Thank you.
Edit: unlike Bash, make doesn't support multi-line block. Here's working code:
MYDIR="dl"
all:
if [ ! -d ${MYDIR} ]; then\
echo "Here";\
else\
echo "There";\
fi
The #!/bin/bash shebang that you inserted at top is useless, and it is treated by make as a comment.
make sends by default commands to /bin/sh. To specify a different shell, use the macro SHELL = /bin/bash.
Moreover, you need to escape your variable:
if [ ! -d ${MYDIR} ]
I'm not sure if make can handle multi-line statements, so try to put all the if block in a line.
if [ ! -d ${MYDIR} ]; then DO_SOMETHING; DO_SOMETHING_ELSE; fi
You're feeding test.mk to make, not to bash. Then make sends individual lines to the shell, not whole blocks.
make uses its SHELL macro to determine which shell to use. You can override it to make it use bash.
The reason why you're getting YDIR is that make has silly rules about variable interpolation. Write $(MYDIR), not $MYDIR.
try bracing your variable:
${MYDIR}

Resources