Suppress messages in make clean (Makefile silent remove) - makefile

I'm wondering how I can avoid some echo in a Makefile :
clean:
rm -fr *.o
this rule will print:
$>make clean
rm -fr *.o
$>
How can I avoid that?

To start with: the actual command must be on the next line (or at least that is the case with GNU Make, it might be different with other Make's - I'm not sure of that)
clean:
rm -rf *.o
(note, you need a TAB before rm -rf *.o as in every rule)
Making it silent can be done by prefixing a #:
so your makefile becomes
clean:
#rm -rf *.o
If there are no *.o files to delete, you might still end up with an error message. To suppress these, add the following
clean:
-#rm -rf *.o 2>/dev/null || true
2>/dev/null pipes any error message to /dev/null - so you won't see any errors
the - in front of the command makes sure that make ignores a non-zero return code

In fact I was looking for something else, adding this line to the Makefile :
.SILENT:clean
while execute every step of the "clean" target silently.
Until someone point some drawback to this, I use this as my favourite solution!

I'm responding to this ancient topic because it comes up high in search and the answers are confusing. To do just what the user wants,all that is needed is:
clean:
#rm -f *.o
The # means that make will not echo that command.
The -f argument to rm tells rm to ignore any errors, like there being no *.o files, and to return success always.
I removed the -r from the OPs example, because it means recursive and here we are just rming .o files, nothing to recurse.
There's no need for the 2>&1 >/dev/null because with the -f there will be no errors printed.
.SILENT: clean
works in place of the #, but it isn't at the same place in the Makefile as the command that it affects, so someone maintaining the project later might be confused. That's why # is preferred. It is better locality of reference.

If you put an # in front of the command, it doesn't echo onto the shell. Try changing rm to #rm. (Reference)

From the manual: .SILENT is essentially obsolete since # is more flexible.
Much worse is that make prints far too much information. Warning/error/private messages are buried in the output. On the other hand -s (.SILENT) suppresses just anything. Especially the "nothing to be done" and "up to date" messages can be a pain. There is no option to suppress them. You have to filter them out actively or use something like colormake. Here is a solution for grep:
make | egrep -hiv 'nothing to be done|up to date'
But the output will have line numbers. The Perl solution is therefore better, because it suppresses line numbers and flushes stdout immediately:
make | perl -ne '$|=1; print unless /nothing to be done|up to date/i'
Make's a flawed tool. "What’s Wrong With GNU make?" explains this better than I can.

There's a great article on using .SILENT that explains how to conditionally activate it.
I have used that information to put this in my Makefile:
# Use `make V=1` to print commands.
$(V).SILENT:
# Example rule, only the #echo needs to be added to existing rules
*.o: %.c
#echo " [CC] $<"
gcc ...
What this does is if you run make normally, normal output is silenced and instead the echo commands work:
$ make
[CC] test.c
[CC] test2.c
But it allows you to debug problems by passing the V=1 parameter, which still shows the [CC] messages as it helps break up the output, but the traditional Makefile output is also visible:
$ make V=1
[CC] test.c
gcc ...
[CC] test2.c
gcc ...

Related

`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

Why is make complaining "Nothing to be done for 'clean' "?

I want make to remove all files except the source files and the make rule file (i.e. the file named makefile), so I added a phony rule at the end of my makefile:
.PHONY:clean
clean:
$(shell ls | grep -v "[.][ch]" | grep -v makefile | xargs rm)
This does what I intend. But make always complains
make: Nothing to be done for 'clean'.
After I run make clean. Why does this message appear? And how can I make it disappear?
The use of $(shell ...) is unnecessary. It runs the command, then the output is used as if it was part of the Makefile. There is no output, so the resulting rule is:
clean:
i.e. the actual list of commands to update the clean target is empty.

Makefile cutting out variables in for loop

I'm writing my first complex Makefile for a highly-modularized project.
I have various sub-directories, each one has its own Makefile which supports at least the all and the clean targets.
These sub-Makefiles work just fine, however I have a problem with the main Makefile, that should call all the sub-Makefiles automatically from the list contained in the variable COMPONENTS.
I tried with the following Makefile:
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY = all clean FORCE $(OUTFILE) $(COMPONENTS)
all: $(OUTFILE)
$(OUTFILE): $(COMPONENTS)
./make_image
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $component; \
done
FORCE:
clean:
for component in $(COMPONENTS); do \
make -C $component clean; \
done
This results in the following error message:
for component in bootloader; do \
make -C omponent; \
done
make: *** omponent: No such file or directory. Stop.
make: *** [bootloader] Error 2
as if the $component expression was only parsed as $c. I don't understand why that happens and how to fix it.
Just double the dollar sign:
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $$component; \
done
The trouble is that with your makefile, Make expands $component before executing the rule. And since $c has no value (there is no such variable), it expands to nothing, leaving "omponent", which it passes to she shell, which complains that there's no such directory. (If you had written $(component), Make would have expanded it to nothing, since Make knows of no such variable, and then the shell would have complained that you were not specifying a directory at all.)
With the double dollar sign, Make expands $$component to $component, which it then passes to the shell, which interprets it as the loop variable, and everything proceeds as planned.
You really should have played around with a simple loop in a command, before attempting to do actual work with one.
Several issues.
.PHONY should be written as a dependency, not a macro definition
Don't write shell loops, use make syntax instead
When you call make recursively, you must do it via the ${MAKE} macro invocation
Leading to
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY: all
all: ${OUTFILE}
.PHONY: ${OUTFILE}
${OUTFILE}: ${COMPONENTS}
./make_image
.PHONY: ${COMPONENTS}
${COMPONENTS}:
${MAKE} -C $#
The advantage of this formulation is that it is parallel make friendly.
Always a test of a good Makefile.
Here make -j5 all will cause make to keep 5 commands running at once,
across all invocations of make.
Nice if you have 4 CPUs.
What about clean?
(Personally I hate clean targets—it's a sign of dodgy dependencies,
and of unhygienic mixing of source and target folders.)
Just add -clean (say) to each of the component names,
and repeat the pattern above.
CLEANS := $(addsuxffix -clean,${COMPONENTS})
.PHONY: clean
clean: ${CLEANS} ; #echo Clean succesful
.PHONY: ${CLEANS}
${CLEANS}: %-clean:
${MAKE} -C $* clean
These two sections can tidied up and combined into one if you feel so inclined.
Tip
Always run make with --warn (or --warn-undefined-variables to give it its full name) to catch inadvertent expansion of $c in things like $component.

How do I get the exit status from the first command in a pipe within make?

I'm trying to pipe to the output of the compiler to the tee command in windows, but I've ran into an issue where if the compiler fails within make it'll continue compiling the next file when I want to it to stop. Is there a way to have the exit status of the first command be the exit status of the second command?
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS) 2>&1 | tee build_log.txt
First off, I would leave any logging to the caller of make. Second off, this sort of piping is untidy in make. Third off, not a fan of losing the stderr stream inside make.
That said, this is a shell question. If you are using bash then see pipefail in the manual. Unfortunately I think it's quite tricky to turn on. (Yeah, I know you said windows, but I assume you aren't using the execrable cmd.)
SHELL := /bin/bash
passes:
(exit 1) |& cat
fails:
bash -c 'set -o pipefail; (exit 1) |& cat'
After Struggling a lot, I came to this solution ...
.ONESHELL:
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS) 2> temp_err_file
set EXIT_STATUS=%ERRORLEVEL%
type temp_err_file >> build_log.txt
type temp_err_file 1>&2
del /q temp_err_file
exit /b %EXIT_STATUS%
Here .ONESHELL allows make to run entire recipe in the single shell command instead of running each line in separate cmd and collect return status of each separately. Overall exit status depends on the main compilation command so in end it is necessary to exit with the status of compilation.
I know its not a clean solution involving the temp_err_file and if something goes wrong after compilation command, make would not be able to catch it but this I think is the best I can find to work with windows without losing stderr stream and logging.
A method that is shell independent and may be feasible in some cases is the following:
assume you have a recipe:
target:
try_making_target |& tee target.log
What I did was convert it to:
target:
(try_making_target || rm -f $#) |& tee target.log
test -e $#
The the piped command fails, the "fallback" (command after ||) will delete the goal file, and the final test will fail. Note that this example assumes the OS is Linux ('rm' for deletions) and that your shell supports the || operator.
This assumes that you are not interested in partial results when try_making_target fails. If you want to keep partial result in 'target', you can use other "marker" files to designate the success or failure of try_making_target. Something like this may work:
target:
touch $#.succeeded # Assume success
(try_making_target || rm -f $#.succeeded) |& tee target.log # Delete to mark failure
test -e $#.succeeded # Fail if marker
rm $#.succeeded # Remove unneeded marker
This last code would also work for phony goals, although you really should minimize the use of these (I try only using them as mnemonics for real files, which may have longish names).

make deleting dependency files

I'm not sure if it's gmake or gcc that I don't understand here.
I'm using the -MM and -MD options to generate dependency rules for the Unit Testing framework I'm using. Specifically:
$(TEST_OBJ_DIR)/%.d: $(TEST_SRC_DIR)/%.cpp
#$(CPPC) -MM -MD $< -o $#
#sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(TEST_OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1.o:|' $#
-include $(TEST_DEP_FILES)
When I run make, after all binaries are linked (properly), I see the following extra (unexplained) line before make exits
rm test/obj/dice.d test/obj/regex.o test/obj/inventoryContainer.d test/obj/color-string.d test/obj/dice.o test/obj/inventoryContainer.o test/obj/color-string.o test/obj/regex.d
From whence is that rm command coming? The only place - anywhere - that I have an rm command in my makefile is in the clean directive
test-clean:
rm -f $(TEST_BIN_FILES)
rm -f $(TEST_OBJ_DIR)/*.{a,d,o}
Any ideas?
make will automatically create intermediate files if necessary to chain two rules together, but it will delete them at the end of the build. You can use the .PRECIOUS special target to prevent it from removing them
One helpful option for debugging these kind of problems is the -n switch:
make -n {TARGET}
It will show you the commands it would run but won't actually run them. This lets you see what rules are firing but doesn't give you all the extra output that makes it difficult to diagnose the problem.
The -d debug flag can also be useful but be sure to run it in a context where you can scroll around easily, you'll be getting a lot of output. I usually use emacs shell mode as it has good searching functionality and saves the buffer.

Resources