How to check if gcc has failed, returned a warning, or succeeded in Bash? - bash

How would I go about checking whether gcc has succeeded in compiling a program, failed, or succeeded but with a warning?
#!/bin/sh
string=$(gcc helloworld.c -o helloworld)
if [ string -n ]; then
echo "Failure"
else
echo "Success!"
fi
This only checks whether it has succeeded or (failed or compiled with warnings).
-n means "is not null".
Thanks!
EDIT If it's not clear, this isn't working.

Your condition should be:
if [ $? -ne 0 ]
GCC will return zero on success, or something else on failure. That line says "if the last command returned something other than zero."

if gcc helloworld.c -o helloworld; then
echo "Success!";
else
echo "Failure";
fi
You want bash to test the return code, not the output. Your code captures stdout, but ignores the value returned by GCC (ie the value returned by main()).

To tell the difference between compiling completely cleanly and compiling with errors, first compile normally and test $?. If non-zero, compiling failed. Next, compile with the -Werror (warnings are treated as errors) option. Test $? - if 0, it compiled without warnings. If non-zero, it compiled with warnings.
Ex:
gcc -Wall -o foo foo.c
if [ $? -ne 0 ]
then
echo "Compile failed!"
exit 1
fi
gcc -Wall -Werror -o foo foo.c
if [ $? -ne 0 ]
then
echo "Compile succeeded, but with warnings"
exit 2
else
echo "Compile succeeded without warnings"
fi

Related

Makefile target unexpectadly raising error

My Makefile includes follow target
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then $(error Sorry BIN_FILE is directory); else ./$(BIN_FILE) $(RUN_ARGS); fi
But it raises the error no matter whether test passes or not. What is wrong? Why it raises an error even BIN_FILE is not directory? Have directive $(error... any special meaning?
Many thanks.
You can't embed a Make expression like that inside your shell script. Make performs all expansions of $(...) expressions before the shell even starts, so it sees your $(error ...) command and exits. You would need to emit this error using shell logic instead, doing something like:
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then \
echo "Sorry BIN_FILE is directory"; \
exit 1; \
fi
$(BIN_FILE) $(RUN_ARGS)
Or with slightly more compact logic:
run: $(BIN_FILE)
[ -d "$(BIN_FILE)" ] && { echo "$(BIN_FILE) is a directory"; exit 1; } ||:
$(BIN_FILE) $(RUN_ARGS)

How to pass error argument to next line in Makefile

For example, a Makefile is as below. There is only one target "Test".
Because "open" is not a valid command, the first line will come into error. How could I transfer this error information to the following conditional directive?
Test:
- open test.doc; ${foo} := $$?
ifeq (foo, 127)
echo "there is a fault"
else
echo "why?"
endif
By default, make will stop processing once a command return a non-zero status. You can change this by writing wrapper script, or by adding explicit SHELL commands to test the return code
Test:
- open test.doc ; foo=$$? ; \
if [ "$foo" = 127 ] ; then echo "There is a fault" ; else echo "Why ?" ; fi
Alternative:
Test:
- open test.doc ; \
if [ "$?" = 127 ] ; then echo "There is a fault" ; else echo "Why ?" ; fi

Shell `if` in Makefile executing both branches

I have the following rule in my Makefile compiled with GNU make 4.1:
multiboot_check: kernel.bin
if [ $(grub2-file --is-x86-multiboot $^) -eq 0 ]; then \
$(info $^: valid Multiboot Version 1 header); \
else \
$(error $^: invalid Multiboot Version 1 header); \
fi
When I run this, both the true and false branch are executed, resulting in the following output:
kernel.bin: valid Multiboot Version 1 header
Makefile:24: *** kernel.bin: invalid Multiboot Version 1 header. Stop.
Why is this occurring? Is there a better way?
I have tried putting it into a single line, and even reducing the if statement to [ 0 -eq 0 ], and it still executes both branches.
Your recipe is equivalent to:
multiboot_check: kernel.bin
# `make` directives evaluated for this target are not part of the recipe
$(info $^: valid Multiboot Version 1 header)
$(error $^: invalid Multiboot Version 1 header)
# The recipe is...
if [ -eq 0 ]; then \
; \
else \
; \
fi
To understand first why make does not order the lines as you have written them,
read this answer
The residual recipe:
if [ -eq 0 ]; then \
; \
else \
; \
fi
is syntactically invalid for the shell, because the unescaped $(grub2-file ...)
will be expanded by make and not the shell and will expand to nothing. You want
it to be a shell expansion, so you need to escape it for make:
$$(grub2-file...)
But although the residual recipe will provoke a shell syntax error, you
never see it, because make evaluates:
$(info $^: valid Multiboot Version 1 header)
$(error $^: invalid Multiboot Version 1 header)
before the recipe is run, and the $(error ...) directive terminates make
before it attempts to execute the invalid recipe.
Corrected for shell-expansion, the test:
[ $$(grub2-file --is-x86-multiboot $^) -eq 0 ]
will still fail to achieve what you apparently want. This test will
determine whether the standard output of the command grub2-file --is-x86-multiboot kernel.bin
is a string equal to 0. Presumably it never will be and what you actually
want to determine is whether the exit code of the command is 0.
In the light of all this, a better way is:
multiboot_check: kernel.bin
grub2-file --is-x86-multiboot $^; \
if [ $$? -eq 0 ]; then \
echo "$^: valid Multiboot Version 1 header"; \
else \
echo "$^: invalid Multiboot Version 1 header"; exit 1; \
fi

Shell script condition checking in Makefile

I've got the following Makefile script which calls a python test suite that writes the results to a file called test_results.txt. Then I display the files and read the last line which has a status that indicates whether all the test cases have been executed or not. Based on that value I echo a statement.
target: test.py
$(PYTHON) test.py
#cat test/test_results.txt
#if [ $(shell sed -e '$$!d' test/test_results.txt) = 0 ]; then\
echo "\nAll tests passed successfully";\
else \
echo "\nNot all the tests were passed";\
fi
When I run it, I get the following error: 0/bin/sh: 1: [: =: unexpected operator
It's much simpler to make test.py have a non-zero exit status if any test fails. Then your recipe is simply
target: test.py
#if $(PYTHON) test.py; then\
echo "\nAll tests passed successfully";\
else \
echo "\nNot all the tests were passed";\
fi
#cat test/test_results.txt

Makefile: capture error code in rule, ignore error, take decision based on error code

So, I currently have this:
TEST = "?"
oktest:
#-/bin/false && ([ $$? -eq 0 ] && echo "success!") || true; $(eval TEST = "all ok")
#echo $(TEST)
badtest:
#-/bin/false && ([ $$? -eq 0 ] && echo "success!") || true; $(eval TEST = "got error")
#echo $(TEST)
#T=echo;if [ $(TEST) = "got error" ]; then \
echo "some error detected, cleanup env and fail test"; \
/bin/false; \
fi
all: oktest badtest
As you can see, its based on:
ignoring error codes ("-" and "|| true", both will do, but I prefer this way)
doing run-time comparison with shell code.
aborting with shell command returning false.
Is there a way to make it shorter, save returned error code and compare it to known values in runtime - without falling back to embedded shell code or external scripts?

Resources