Reputation: 8011
Some of the targets in my Makefile
run programs whose output (which they send to stdout
) I am interested in. For a reason not known to me, the authors of make
decided to echo the executed commands to stdout
, which pollutes the latter.
A hard way around this problem that involves swapping file descriptors was suggested here. I am wondering if there is a simpler way to force make
echo to stderr
.
I looked through the man
page of make
, but did not find anything to this end besides the -s
option. I prefer to preserve the echo of commands, but have it in stderr
.
I also tried making an auxiliary target (which I made a prerequisite of all other targets), in which I put:
exec 3>&2
exec 2>&1
exec 1>&3
but bash complained that 3 wasn't a valid file descriptor. I tried only exec 1>&2
, but that did not have any effect...
Upvotes: 3
Views: 2124
Reputation: 221
Another posix-incompatible solution is to put
#!/bin/bash
exec 3>&2; exec 2>&1; exec 1>&3;
into helper/stderr
relative to my project, and
helperdir = helper
SHELL = BASH_ENV="$(helperdir)/stderr" /bin/bash
into my Makefile.
Now all executed rule code output is redirected to stderr file descriptor.
BASH_ENV
environment variable does, if set to a script path, execute that script at every bash invocation.
Upvotes: 0
Reputation: 8011
Suppose we have the following Makefile
:
target-1:
target-1-body
target-2:
target-2-body
target-3:
target-3-body
We change it as follows:
target-1-raw:
target-1-body
target-2-raw:
target-2-body
target-3-raw:
target-3-body
%-raw:
echo "Error: Target $@ does not exist!"
%:
@make $@-raw 3>&2 2>&1 1>&3
The invocation is same as before, e.g. make target-1
.
With two additional targets we made make
output to stderr
.
FYI: I am trying to develop this solution further so the user would not be able to invoke the raw targets directly.
Upvotes: 0
Reputation: 58598
What you can do entirely in the Makefile
is this:
define REDIR
@printf 1>&2 "%s\n" '$(1)'; $(1)
endef
.PHONY: all
all:
$(call REDIR,echo updating .stamp)
$(call REDIR,touch .stamp)
That is to say, take control of the command echoing yourself via a macro. Unfortunately, it involves writing your recipe lines as `$(call ...) syntax.
REDIR
now implements the semantics of echoing the command, and executing it, via macro expansion.
The 1>&2
is Bash-specific syntax for duplicating the standard error file descriptor to standard out, so the command then effectively prints to standard output.
Test run:
$ make
echo updating .stamp
updating .stamp
touch .stamp
$ make 2> /dev/null
updating .stamp
As you can see, updating .stamp
, which is the output of our explicitly coded echo
line, nicely goes to standard output. The commands are implicitly sent to standard error.
Upvotes: 2
Reputation: 57163
If you don't want to pollute the output of echo of what make produces, can't you simply run
make -n >&2 && make -s
This is the sample Makefile:
all:
ls
echo done
Here is the output of make
:
ls Makefile echo done done
Here is output of make -n >&2 && make -s
:
ls echo done Makefile done
Naturally, output of either step can be redirected to file.
Upvotes: 1
Reputation: 100876
The reason make shows the command line on stdout is because that's what the POSIX standard for make requires, and 30+ years of history expect. See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html and search for the section on "STDOUT".
You cannot modify the file descriptors in the make program from within a recipe, because the recipe is run in a subshell: any changes you make to the file descriptors only take effect in the subshell. It's not possible in UNIX for a child process to modify the file descriptors of its parent.
Similarly, each line in a recipe in make is run in a different subshell. If you want to do fancy things like redirect output for a recipe you'll have to write it all on one line:
exec 3>&2; exec 2>&1; exec 1>&3; <my command here>
Of course if you intend to do this a lot I would put that in a make variable and use that variable instead.
There is no way to get make to write its output to stderr instead of stdout, unless you want to modify the source code for GNU make and use the version you build yourself instead. It would actually be straightforward to do this as long as you're using a newer version of GNU make (4.0 and above) since all output is generated from one plase (in output.c
).
Upvotes: 5