How to get rid of "Error 4" when running Makefile - makefile

I have this Makefile that work well:
$ cat Makefile
PMD := ~/pmd-bin-6.31.0/bin/run.sh pmd
gen_quality_analysis:
$(PMD) -d MyCode/ -R rulesets/java/quickstart.xml -f text > quality_analysis/report.txt
$
My problem is that I get an Error 4 from Makefile
$ LANG=en make gen_quality_analysis
~/pmd-bin-6.31.0/bin/run.sh pmd -d AtlasPOC2/ -R rulesets/java/quickstart.xml -f text > quality_analysis/report.txt
Feb 14, 2021 9:53:35 AM net.sourceforge.pmd.PMD encourageToUseIncrementalAnalysis
WARNING: This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.31.0/pmd_userdocs_incremental_analysis.html
make: *** [Makefile:15: gen_quality_analysis] Error 4
$
How can I get ride of this
make: *** [Makefile:15: gen_quality_analysis] Error 4
line ?
Note: despite of the fact I get this error 4 the job work well an I get the expected report.txt

In order of preference:
Fix the program ~/pmd-bin-6.31.0/bin/run.sh pmd to return 0 on success.
Write a wrapper that changes the exit status from 4 to 0:
$(PMD) -d ...;\
status=$$?;\
[ $$status -eq 4 ] && status=0;\
exit $$status
Ignore errors for that target with .IGNORE: gen_quality_analysis.
Ignore all errors by running make with make -i.

From the help documentation of pmd - the return code is 4 when violations are found, and this is expected.
https://pmd.github.io/latest/pmd_userdocs_cli_reference.html
Add '-failOnViolation false' if you'd like the return code to be 0 even if violations are found.

Related

How do I exclude test files from makefile shell command?

I'm tyring to exclude test files in my makefile shell command.
When i run the following makefile target,
api-run:
${API_COMPOSE} sh -c "go run -mod=vendor cmd/serverd/*.go"
I get this error
go: cannot run *_test.go files (cmd/serverd/router_test.go)
make: *** [api-run] Error 1
I've tried adjusting the command into
api-run:
${API_COMPOSE} sh -c "for file in \$(ls cmd/serverd/*.go | grep -v _test.go); do go run -mod=vendor \$file; done"
but end up getting this error
sh: 1: Syntax error: "done" unexpected (expecting "do")
make: *** [api-run] Error 2

buildkite syntax error when running code coverage on golang

I'm running the following make target in a buildkite step
cover:
go test ./... -coverprofile=coverage.out
ccover=$$(go tool cover -func cover.out | grep total | grep -Eo '[0-9]+\.[0-9]+');
if [ $$(bc <<< "$$ccover < 20.0") -eq 1 ]; then\
exit 1;\
fi
This is what my buildkite yaml looks like
- label: ":coverage: check code coverage"
key: "cvg"
command:
- make cover
Now when this executes on the server I get the following error
/bin/sh: syntax error: unexpected redirection
make: *** [Makefile:20: analysecover] Error 2
Error: The command exited with status 1
Any idea on what could be going wrong here ?

How to derive the meaning of error produced by make?

Having this simple makefile rule:
exe:
for i in *; do [ -x "$$i" ] && echo "$$i"; done
Will output:
for i in *; do [ -x "$i" ] && echo "$i"; done
executablefile
make: *** [makefile:6: exe] Error 1
So it does, what I want, but even then, error with no other message. But not only for this particular example (which I still do not get), I would like to know, how to get some more info from bugs in makefile (is there a makefile debugger?). From makefile manual the *** is for fatal error, which ends compilation, but yet it outputs the executablefile (so it did compiled to that point). Apart from fatal error, - warnings give more info, so why do not do fatal errors as well?
explanation of this example
some advices how to debug makefile scripts
This is not an error from make, that's why there's no other information.
Make runs a shell and gives the shell your recipe to invoke. If the shell exits with success (exit code 0) then make assumes that the command it ran worked. If the shell exits with failure (any exit code other than 0), then make assumes the command it ran failed. Make doesn't know why it failed, make assumes that whatever command failed will have printed some information about why. All make knows is the exit code, so that's all make can tell you:
make: *** [makefile:6: exe] Error 1
This means that make ran the recipe for target exe at makefile line number 6, and that command exited with an error code 1 (which is not 0, hence a failure).
Why did this happen? Let's look at your shell script:
for i in *; do [ -x "$$i" ] && echo "$$i"; done
Let's suppose the last file matching * (so the last time we go through the loop) the file is not executable. That means the test of the last file [ -x "$$i" ] will fail. Since that's the last command that the shell runs before it exits, that will be the exit code of the shell, and you have a failure.
You need to be sure that the shell exits with success. One way to do that is ensure the last command the shell runs is always success; maybe something like this:
for i in *; do [ -x "$$i" ] && echo "$$i"; done; true

Makefile result message during parallel build

Given a Makefile that's often times run with the -j flag for parallel builds. I want it to terminate with a result message. I would like this message to say if the build failed, and if it failed, what the error was. It doesn't have to say anything if the build succeeded (although it could) but it must warn the user when a target failed to build and why.
This behavior is already there during sequential builds, but not during parallel builds. Parallel builds interweaves the output and an error message is often overlooked because output from other targets might push the failed target's error off screen. A careless developer might see no errors on his/her screen and assume the build succeeded.
It's quite an intuitive feature and I've searched for an answer, but it doesn't seem like there's any straight forward solutions. Any ideas?
You basically run
make -j 8 2> >(tee /tmp/error.log)
test $? -ne 0 && echo "build errors:"
cat /tmp/error.log
and you get all of stderr after the build finishes.
-- EDIT --
Updating to use tee, to output on stdout and into file:
Make returns non-zero if one of its recipe's fails so you could do something like this from the command line (assuming bash shell):
make 2>&1 | tee build.log
[ ${PIPESTATUS}[0] -eq 0 ] || ( echo "MAKE FAILED!"; grep --color build.log "Error:" )
The ${PIPESTATUS}[0] gives you the exit code of the first command (make 2>&1) as opposed to the exit status of the entire command (which would the exit status of tee if the make failed). It is bash specific, so it won't work in zsh for example.
Alternatively you could add the same logic as the top level target of a recursive make.
ifndef IN_RECURSION
export IN_RECURSION:=1
$(info At top level -- defining default target)
_default:
#echo "doing recursive call of make"
#$(MAKE) $(MAKECMDGOALS) IN_RECURSION=1 2>&1 | tee build.log; \
[ ${PIPESTATUS}[0] -eq 0 ] || ( echo "MAKE FAILED!"; grep --color "Error:" build.log )
.PHONY: _default
endif
all:
....
Note that in this case the \ used to catinate the two recipe lines is crucial, as the second command must run in the same shell instance as the first.

is the common Makefile idiom "> $#" (redirecting output to the target) wrong?

It is hard to believe, but it seems to me that the common Makefile idiom "> $#" is wrong. In particular, a target whose rule has a command that fails but uses this redirection will fail the first time around but not subsequent times. This is because even though the command fails, the redirection "succeeds" in the sense of creating an up-to-date (albeit zero-length) target.
It seems to me that the correct thing to do is to redirect to a temporary and on success rename this temporary to the target.
Here's and example Makefile:
bad-target:
command-that-will-fail > $#
good-target:
command-that-will-fail > $#.tmp || ( rm $#.tmp; false )
mv $#.tmp $#
clean:
rm -f bad-target good-target
And here's a sequence of commands illustrating the problem and its solution:
$ make clean
rm -f bad-target good-target
$ make bad-target
command-that-will-fail > bad-target
/bin/sh: command-that-will-fail: not found
make: *** [bad-target] Error 127
$ make bad-target
make: `bad-target' is up to date.
$ make good-target
command-that-will-fail > good-target.tmp || ( rm good-target.tmp; false )
/bin/sh: command-that-will-fail: not found
make: *** [good-target] Error 1
$ make good-target
command-that-will-fail > good-target.tmp || ( rm good-target.tmp; false )
/bin/sh: command-that-will-fail: not found
make: *** [good-target] Error 1
If you're using GNU make, you may also add the .DELETE_ON_ERROR special target to your makefile. This will cause make to delete the output file if there is an error during execution of the commands for that file:
all: foo.o
foo.o:
echo bogus! > $#
exit 1
.DELETE_ON_ERROR:
Here's an example of this makefile in action:
$ gmake
echo bogus! > foo.o
exit 1
gmake: *** [foo.o] Error 1
gmake: *** Deleting file `foo.o'
This is easier to use than your version, since you need not modify every rule in the makefile.
Perhaps this is an obvious solution, but many common commands provide a -o flag or similar. So instead of output redirection, you should use the rule:
target: target.dep
some_command target.dep -o $#
If the command fails, the output file will not be created. I haven't had many occasions to use output redirection in makefiles.
Yes, this is definitely something to bear in mind.
But sometimes you're OK with the command failing and you don't want to retry it, because the error would only repeat itself.
In that case leaving an empty result is OK. Just don't make subsequent processing count on there being meaningful content in that file.

Resources