How to use shell command in GNU Make to echo string - shell

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)

Related

Unexpected EOF in conditional construct in makefile

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.

Makefile Target Pattern with %

I am new to makefile.
All I want is, when a specific C file will be changed, I want to run one command. And finally from one folder, any of the C file will be changed then I want to run the same command with that filename.
.e.g.
ceedling test:filename
I have simple file called unittest.mk. I am not sure the following approach is correct or not.
I am ruinning the following command to run this file.
make -f unittest.mk StartUnitTest
Here is the unittest.mk file:
TEST_OBJS += \
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o: D:\ModApp\Apps\Paymark\ModApp_Paymark\test\test_txn_admin.c
echo $(*F)
echo $#
echo $<
StartUnitTest:
#echo Start Unit Test
$(TEST_OBJS)
#echo End Unit Test
When I run this file, it is giving the following error.
Start Unit Test
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o
process_begin: CreateProcess(D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o, D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\test_txn_admin.o, ...) failed.
make (e=193): Error 193
make: *** [StartUnitTest] Error 193
Finally once this will work, actually I want a target pattern with % as the following:
D:\ModApp\Apps\Paymark\ModApp_Paymark\build\test\out\%.o: D:\ModApp\Apps\Paymark\ModApp_Paymark\test\%.c
echo $(*F)
echo $#
echo $<
I have found the issue. I have changed the target "StartUnitTest" to the following and it is working now. Removed the echo messages.
StartUnitTest: $(TEST_OBJS)
Thank you MadScientist. the "make -d" does helped me to find the issue.

`2>/dev/null` does not work inside a Makefile

I tried to suppress an error from rm command by writing
Makefile:
...
clean: $(wildcard *.mod)
-rm $^ 2>/dev/null
...
I ran:
$ make clean
rm 2>/dev/null
make: [clean] Error 64 (ignored)
I still had gotten an error.
Anyway, when I tried
$ rm [some non-existent files] 2>/dev/null
on the bash shell, it just works fine.
How can I use 2>/dev/null inside a makefile?
2>dev/null will redirect the error output so you don't see it, it will not prevent the shell to raise the error level. And the - sign in front of your shell command will tell GNU make to continue even if the error level is raised but it will not either prevent the shell to raise it.
What you want is the shell not to raise the error level and this can be done like this :
Unix (credits to this answer)
-rm $^ 2>/dev/null ; true
Windows
-rm $^ 2>NUL || true
or if you don't have rm on Windows
-del /F /Q $^ 2>NUL || true
The message make: [clean] Error 64 (ignored) is being printed by make after it sees that your shell command has failed.
It will therefore not be affected by any redirection that you use in the recipe.
Two fixes:
Use the -f rm flag. rm -f never returns an error.
(Well, hardly ever anyway, and if it does you probably want to know about it!)
Stop the shell command returning an error: simply append || : to the command.
Say what? Well if the rm succeeds your job is done and make is happy. OTOH if rm fails, the shell runs the second command in the or.
: is a shell built-in that always succeeds, and is much preferable to true IMHO.
The first of these is best in this case,
though the second is a general, if somewhat less efficient, pattern.
.PHONY: clean
clean: ; rm -rf *.mod

How to delete the target file in case of error in Makefile?

In the following example I would like foo to be deleted in case of error. Unfortunately it doesn't work.
foo:
perl -e 'die()' > $# || [rm $# -a true]
What is it wrong?
GNU make can do that for you.
Special Built-in Target Names:
.DELETE_ON_ERROR
If .DELETE_ON_ERROR is mentioned as a target anywhere in the makefile, then make will delete the target of a rule if it has changed and its recipe exits with a nonzero exit status, just as it does when it receives a signal.
It is a general problem that creating a file is a non-atomic operation. And not always you can delete an incomplete or corrupted file on termination, for example, when the program is killed with SIGKILL or by the OOM-killer. In other words, all solutions involving removing the file are prone to failures.
The robust generic solution is:
Create the file with a temporary filename.
Once the file is complete and have correct permissions, rename it to the final name. Renaming a file is an atomic operations in UNIX, as long as the file stays in the same filesystem.
E.g.:
foo:
perl -e 'die()' > $#~
mv --force $#~ $#
This works for me…
foo:
#perl -e 'die()' > $# || { echo "removing $# because exit code was $${?}"; rm $#; }
Output
Died at -e line 1.
removing foo because exit code was 255

Makefile for Daemon

I am very new to Makefiles, so I am probably not doing this the best way (your input is much appreciated, since I would like to learn how/why mine is bad). Anyway, here is my problem:
I have a Daemon that I wrote for a program of mine and I am trying to install it with the Makefile (target is "install"). What the "install" target is supposed to do is move the daemon binary to a location, then move the "service script" to either /etc/init.d/ or /etc/rc.d/ (since different distros have different folders...). Here is my makefile so far:
all:
#echo "Making Components"
#cd Daemon; make
#echo "Components Built"
install:
#echo "Installing Components"
#mkdir -p /usr/lib/
#cp Daemon/myprog_d /usr/lib/myprog_d
-#test -d /etc/init.d && cp Scripts/myprog /etc/init.d/
-#test -d /etc/rc.d && cp Scripts/myprog /etc/rc.d/
-#test ! -d /etc/init.d -a ! -d /etc/rc.d && echo " Warning: Couldn't install script. Manually install Scripts/myprog"
#mkdir -p /var/log/
#echo "Installed."
uninstall:
#echo "Uninstalling Components"
#./Scripts/myprog stop > /dev/null
#rm -f /usr/lib/myprog_d
#echo "Uninstall complete"
clean:
#echo "Cleaning Components"
#cd Daemon; make clean
#echo "Clean complete"
As you can see, the "install" target tests to see if those two directories exist and, if they do, copies the script into them (I haven't yet done it to "uninstall", don't worry).
My first question: Is this the right way to do this? The "all" and "clean" targets work (there is another makefile in "Daemon/", as you can deduce), but I want to know if there is a cleaner way of doing this.
Secondly, because the "test" function returns non-zero, I had to do "-" before it so the error would be ignored. Unfortunately, that results in the "make install" output being:
Installing Components
make: [install] Error 1 (ignored)
make: [install] Error 1 (ignored)
Installed.
Which is very ugly and probably not good practice. What can I do in this case? (I have tried both -# and #-, but # will not suppress the "ignored" output)
Sincerely,
Chris
I'd do it this way:
#if [ -d /etc/init.d ]; then cp Scripts/myprog /etc/init.d/ ; fi
#if [ -d /etc/rc.d ]; then cp Scripts/myprog /etc/rc.d/ ; fi
And I'm a little confused by your next line (-#test ! -d /etc/init.d -a !...) but you can probably do it the same way.
That takes care of the error messages, but if you wanted to keep the makefile as it is, you could suppress them by running make -s.

Resources