Process substitution in GNU Makefiles
On a bash prompt, one can execute diff using pseudo files:
diff <(echo test) <(echo test)
Adding this as is into a Makefile fails:
all:
diff <(echo test) <(echo test)
The error (hint: /bin/sh points to /bin/bash on this system):
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `diff <(echo test) <(echo test)'
What does it mean, and is there a way to still diff two outputs without using temporary files?
/bin/sh
may be bash
on your system, but when invoked as sh
, bash
will be running in POSIX mode (as if POSIXLY_CORRECT
was defined, or it was started with --posix
).
In this mode, process substitutions do not exist.
Solutions:
-
Use explicit temporary files:
all: command1 >tmpfile command2 | diff tmpfile - rm -f tmpfile
-
Use a
bash -c
in-line script:all: bash -c 'diff <(command1) <(command2)'
-
Define the Makefile variable
SHELL
as/bin/bash
(or whatever the path tobash
is on your system):SHELL=/bin/bash
If you want portability, go with the first solution. If you are OK with a dependency on bash
, pick the second. If you additionally don’t need to care about non-GNU make
implementations, use the third.
Regarding setting SHELL
: The POSIX standard says that executables in Makefiles should be invoked with the system()
C library function by make
. This function is not guaranteed to use the SHELL
environment variable (in fact, doing so is discouraged by the standard). The standard also goes to some length to say that setting the Makefile variable SHELL
should not affect the environment variable SHELL
. In most implementations of make
that I know of, however, the Makefile variable SHELL
will be used to execute the commands.
The suggestion in the Rationale for the make
utility is to use bash -c
:
The historical
MAKESHELL
feature, and related features provided by othermake
implementations, were omitted. In some implementations it is used to let a user override the shell to be used to runmake
commands. This was confusing; for a portablemake
, the shell should be chosen by the makefile writer. Further, a makefile writer cannot require an alternate shell to be used and still consider the makefile portable. While it would be possible to standardize a mechanism for specifying an alternate shell, existing implementations do not agree on such a mechanism, and makefile writers can already invoke an alternate shell by specifying the shell name in the rule for a target; for example:
python -c "foo"