I'm using the following Makefile, which should check whether the files are in some directory *.rpm, if the files hasn't been found i'm going to execute some command (run a script or make the rpm)
Here's a snippet from my Makefile
include /home/user/workspace/test/exec_recipe.mk
export BUILD_LOGS_DIR = $(CURDIR)/build_logs
.PHONY: my_rpm
libpdutil_rpm:
#echo "Making target 'libpdutil_rpm'. Time: $$(date +%T)"
cd /home/user/workspace/test/build/test && $(call exec_recipe,$(ls /home/user/workspace/test/build/test/rpmbuild/RPMS/x86_64d/*.rpm) || $(./test.sh),test.log,)
#echo "Finished making target 'my_rpm'. Time: $$(date +%T)"
And here's the exec_recipe.mk
SHELL:=/bin/bash
exec_recipe = \
echo "The logs dir is: $$BUILD_LOGS_DIR"; \
log_name="$(2)"; \
echo "The log name is $$log_name"; \
cmd="$(1)"; \
eval "$$cmd" 2>&1 | tee -a "$$BUILD_LOGS_DIR/$$log_name"; rc="$${PIPESTATUS[0]}"; \
if [ $$rc = 0 ]; then \
res="PASS"; \
else \
res="FAIL"; \
fi; \
flock $(SUMMARY) echo "Making target '$#': $$res" >> $(SUMMARY); \
exit $$rc
So the problem is when it's reaching the execution of the exec_recipe.mk it giving me some errors:
/bin/bash: eval: line 0: syntax error near unexpected token||'
/bin/bash: eval: line 0: || '
I'm sure that i'm doing something wrong..
I need the helper make file, because i'm using it for other purposes as well
You have to escape all the dollar signs that you don't want make to interpret. You've forgotten to escape the dollar signs in the arguments to $(call exec_recipe,...):
cd /home/user/workspace/test/build/test && $(call exec_recipe,$$(ls /home/user/workspace/test/build/test/rpmbuild/RPMS/x86_64d/*.rpm) || $$(./test.sh),test.log,)
I'm not sure what the final comma is for but whatever.
Related
so I have a large Makefile that runs all of my tests for my particular project. Each target is a different group of tests. The script will run the target, store its output into a temporary file.
Currently the target looks like this:
count:
# USE: make count test=<name of test to run>
# Save output to target
$(MAKE) $(test) > last_output.txt
cat last_output.txt
# Print Passed
#cat last_output.txt | { grep -E -w "SUCCESS|RELAX-PASS" || true; }
# Print Failed
#cat last_output.txt | { grep -E -w "FAILED" || true; }
# Failed Count
#echo "\e[1;31mFAILED:\e[1;37m"
#cat last_output.txt | { grep -c "FAILED" || true; }
# Passed Count
#echo "\e[1;32mPASSED:\e[1;37m"
#cat last_output.txt | grep -E -c "SUCCESS|RELAX-PASS"
# Count all
#echo "TOTAL: "
#cat last_output.txt | { grep -E -c "FAILED|SUCCESS|RELAX-PASS" || true; }
And the instruction to execute it looks like:
make count test=add
What I was wondering was if I could not specify test= when I'm running the command so that it would look like this:
make count add
and then the add target will execute which looks like:
add:
clear && run.pl add_0.asm
clear && run.pl add_1.asm
clear && run.pl add_2.asm
clear && run.pl add_3.asm
ect.
Every command line argument to make is either an option, a variable assignment, or a target. There's no possible way to treat an argument as anything else. So when you run make check add, the add will be a target that make will attempt to build and there's no way to have it be considered any other way.
As #Beta suggests if you are willing to embed to the test name into the target, like make count_add, then you can do this:
SHOW = \
cat last_output.txt; \
grep -E -w "SUCCESS|RELAX-PASS" < last_output.txt; \
grep -E -w "FAILED" < last_output.txt; \
echo "\e[1;31mFAILED:\e[1;37m"; \
grep -c "FAILED" < last_output.txt; \
echo "\e[1;32mPASSED:\e[1;37m"; \
grep -E -c "SUCCESS|RELAX-PASS" < last_output.txt; \
echo "TOTAL: "; \
grep -E -c "FAILED|SUCCESS|RELAX-PASS" < last_output.txt;
true
count:
$(MAKE) $(test) > last_output.txt
#$(SHOW)
count_%:
$(MAKE) $* > last_output.txt
#$(SHOW)
If you don't want to do that, the only possible solution is to use ifeq combined with $(MAKECMDGOALS) to break your makefile up into two sections: one that has a .DEFAULT target that does nothing (to ignore the extra targets like add etc.) and a second that runs those targets.
I wanted to write the commands as a conditional statement for if i.e., when the command executes without any error it should print success and failure if it fails.
I'm even trying to prevent from printing the long console messages of the commands.
Below is the code that I'm using, which is working perfectly without any conditional statements like printing success or failures messages.
$(BASENAME).pdf: $(BASENAME).ps
ps2pdf $(BASENAME).ps $(BASENAME).pdf
$(BASENAME).ps: $(BASENAME).dvi
dvips -Ppdf $(BASENAME).dvi -o $(BASENAME).ps
I tried changing above as
$(BASENAME).pdf: $(BASENAME).ps
#out=ps2pdf $(BASENAME).ps $(BASENAME).pdf > /dev/null 2>&1 && echo " 5. ps2pdf successful" || echo " 5. ps2pdf failed"
$(BASENAME).ps: $(BASENAME).dvi
dvips -Ppdf $(BASENAME).dvi -o $(BASENAME).ps > /dev/null 2>&1\
if [ $? -eq 0 ];\
then \
echo " 4. dvips successful";\
else \
echo " 4. dvips failed";\
exit 2;\
fi
which is failing my execution and providing me error while running the code like below error
dvips -Ppdf basename_04.dvi -o basename_04.ps \
if [ basename_04.dvi -eq 0 ];\
then \
echo " 4. dvips successful";\
else \
echo " 4. dvips failed";\
exit 2;\
fi
/bin/sh: -c: line 2: syntax error near unexpected token `then'
/bin/sh: -c: line 2: ` then \'
make: *** [basename_04.ps] Error 1
Even after updating with semi-colon, I'm getting below error.
dvips -Ppdf nkukunur_04.dvi -o nkukunur_04.ps > /dev/null 2>&1 ;\
if [ nkukunur_04.dvi -eq 0 ]; \
then \
echo " 4. dvips successful"; \
else \
echo " 4. dvips failed"; \
exit 2; \
fi
/bin/sh: line 1: [: nkukunur_04.dvi: integer expression expected
4. dvips failed
Please help me in fixing the issue.
You are missing a semicolon — and a double-dollar:
$(BASENAME).ps: $(BASENAME).dvi
dvips -Ppdf $(BASENAME).dvi -o $(BASENAME).ps; \
if [ $$? -eq 0 ]; \
then \
echo " 4. dvips successful"; \
else \
echo " 4. dvips failed"; \
exit 2; \
fi
The semicolon missed was the one at the end of the first command line (after $(BASENAME).ps and before the backslash). Assume that make replaces backslash-newline with a blank (not a newline). That's why you need the semicolon after the test; it's why you need one before the if too.
The double-dollar is needed because $? is a Make macro, but you want the shell to see and interpret $?. So, you hide it from Make with $$, which is a macro invocation that expands to $.
So, I don't know what the best practices are for using conditional directives vs using plain Bash conditionals like you are doing. But to fix your code, remove the backslash after the very first line:
dvips -Ppdf $(BASENAME).dvi -o $(BASENAME).ps <<<< removed backslash
if [ $? -eq 0 ];\
then \
echo " 4. dvips successful";\
else \
echo " 4. dvips failed";\
exit 2;\
fi
Otherwise all lines are executed as a single bash statement: the if up until the semicolon are passed as arguments to dvips and then begins a new statement, thus the unexpected token error.
I am setting up a makefile to run a test that involves diffing a list of generated files against their counterparts in a master directory. I can get the loop to work and to exit if the diff fails but I haven't figured out how to include a failure message and still exit the for loop (and the make process) immediately.
Here is the core of what I have:
OUTFILE = $(patsubst $(MASTER_DIR1)/%,$(OUTDIR1)/%,$$file)
default: test
$(OUTDIR1):
$(MKDIR) $(OUTDIR1)
test: $(OUTDIR)
#for file in $(MASTER_LIST1); do \
echo $$file; \
diff $(IGNORE_OPT) \
$$file \
$(OUTFILE) \
&& exit 1; \
done
This exits on fail but doesn't inform the user as to why. I tried changing the && exit 1; line to:
&& (echo "Diff failed"; exit 1; ); \
That displayed the error message but no longer exited.
How do I add an error message and still exit right away?
TIA!
Based on the help provided by Etan Reisner in his comment to the original question, this is the solution that worked:
test: $(OUTDIR)
#for file in $(MASTER_LIST1); do \
echo $$file; \
diff $(IGNORE_OPT) \
$$file \
$(OUTFILE) \
&& { echo "Diff failed"; exit 1; }; \
done
I have a makefile rule in while I am executing a linux tool. I need to check the exit status of the tool command, and if that command fails the make has to be aborted.
I tried checking with $?, $$? \$? etc in the makefile. But they gives me syntax error when makefile runs.
What is the right way to do this ?
Here is the relevant rule in Makefile
mycommand \
if [ $$? -ne 0 ]; \
then \
echo "mycommand failed"; \
false; \
fi
In the makefile-:
mycommand || (echo "mycommand failed $$?"; exit 1)
Each line in the makefile action invokes a new shell - the error must be checked in the action line where the command failed.
If mycommand fails the logic branches to the echo statement then exits.
Here are a couple of other approaches:
shell & .SHELLSTATUS
some_recipe:
#echo $(shell echo 'doing stuff'; exit 123)
#echo 'command exited with $(.SHELLSTATUS)'
#exit $(.SHELLSTATUS)
Output:
$ make some_recipe
doing stuff
command exited with 123
make: *** [Makefile:4: some_recipe] Error 123
It does have the caveat that the shell command output isn't streamed, so you just end up with a dump to stdout when it finishes.
$?
some_recipe:
#echo 'doing stuff'; sh -c 'exit 123';\
EXIT_CODE=$$?;\
echo "command exited with $$EXIT_CODE";\
exit $$EXIT_CODE
Or, a bit easier to read:
.ONESHELL:
some_recipe:
#echo 'doing stuff'; sh -c 'exit 123'
#EXIT_CODE=$$?
#echo "command exited with $$EXIT_CODE"
#exit $$EXIT_CODE
Output:
$ make some_recipe
doing stuff
command exited with 123
make: *** [Makefile:2: some_recipe] Error 123
It's essentially one string of commands, executed in the same shell.
If all you want is for the make to be aborted iff the tool exits with a nonzero status, make will already do that by default.
Example Makefile:
a: b
#echo making $#
b:
#echo making $#
#false
#echo already failed
.
This is what happens with my make:
$ make
making b
make: *** [Makefile:6: b] Error 1
Make sure partially or wholly created targets are removed in case you fail.
For instance, this
a: b
#gena $+ > $#
b:
#genb > $#
is incorrect: if on the first try, genb fails, it will probably leave an incorrect b, which, on the second try, make will assume is correct. So you need to do something like
a: b
#gena $+ > $# || { rm $#; exit 1; }
b:
#genb > $#
To those who can't still fix it, the original snippet in the question missed a semicolon after mycommand. So, the working example is:
mycommand; \ # <<== here's the missing semicolon
if [ $$? -ne 0 ]; \
then \
echo "mycommand failed"; \
false; \
fi
This is following question.
I have Makefile.real (Makefile from prev question):
all: a b
a:
echo a
exit 1
b:
echo b start
sleep 1
echo b end
Now I want to create Makefile that is simple wrap of Makefile.real:
It calls make with Makefile.real with the same args as it was called
It should print error message id Makefile.real fails
This is my goal - print error message in the end of parallel make
(see question)
Therefore following commands should terminate with error message:
make -j1 a b (1)
make -j2 a b (2)
I suspect Makefile should be something close to:
%:
$(MAKE) -f Makefile.real $(MAKECMDGOALS); \
res=$$?; if [ $$res != 0 ]; then echo "Failed!!!"; fi; exit $$res
The problem is that target '%' will be called twice for a and b for (2).
Any ideas?
This is the solution I ended with
ifneq ($(REAL_MAKE),1)
# run_make will be called once (it's .PHONY target),
# even if make is called with several targets
%: run_make
#:
.PHONY: run_make
run_make:
$(MAKE) $(MAKECMDGOALS) REAL_MAKE=1; \
if [ $$? -ne 0 ]; then \
echo "*** Error ***" >&2; \
exit 1; \
fi
else # REAL_MAKE defined (actual makefile)
### HERE comes original make we want to wrap ###
endif # # REAL_MAKE defined (actual makefile)