make/tests/scripts/functions/shell
Paul Smith 70ba0357a0 [SV 63040] shell: Fall back to the callers environment
If we detect a recursive variable reference when constructing the
environment for the shell function, return the original value from the
caller's environment.  Other options such as failing, returning the
empty string, or returning the unexpanded make variable value have
been shown to not behave well in real-world environments.  If the
variable doesn't exist in the caller's environment, return the empty
string.

Found by Sergei Trofimovich <slyich@gmail.com> when testing older
versions of autoconf.

* NEWS: Clarify this behavior.
* doc/make.texi (Shell Function): Ditto.  Also add info about !=.
* src/expand.c (recursively_expand_for_file): Search the caller's
environment if we detect a recursive variable expansion.
* tests/scripts/functions/shell: Add tests for this behavior.
2022-09-10 16:27:47 -04:00

205 lines
4.6 KiB
Perl

# -*-perl-*-
$description = 'Test the $(shell ...) function.';
$details = '';
# Test standard shell
run_make_test('.PHONY: all
OUT := $(shell echo hi)
all: ; @echo $(OUT)
','','hi');
# Test shells inside rules.
run_make_test('.PHONY: all
all: ; @echo $(shell echo hi)
','','hi');
# Verify .SHELLSTATUS
run_make_test('.PHONY: all
PRE := $(.SHELLSTATUS)
$(shell exit 0)
OK := $(.SHELLSTATUS)
$(shell exit 1)
BAD := $(.SHELLSTATUS)
all: ; @echo PRE=$(PRE) OK=$(OK) BAD=$(BAD)
','','PRE= OK=0 BAD=1');
# Test unescaped comment characters in shells. Savannah bug #20513
run_make_test(q!
FOO := $(shell echo '#')
foo: ; echo '$(FOO)'
!,
'', "echo '#'\n#\n");
# Test that exported variables are passed to $(shell ...)
$ENV{FOO} = 'baz';
run_make_test(q!
OUT = $(shell echo $$FOO)
foo: ; @echo '$(OUT)'
!,
'', 'baz');
$ENV{FOO} = 'baz';
run_make_test(q!
FOO = bar
OUT = $(shell echo $$FOO)
foo: ; @echo '$(OUT)'
!,
'', 'bar');
run_make_test(q!
export FOO = bar
OUT = $(shell echo $$FOO)
foo: ; @echo '$(OUT)'
!,
'', 'bar');
# Test shells inside exported environment variables, simply expanded.
run_make_test('
export HI := $(shell echo hi)
.PHONY: all
all: ; @echo $$HI
',
'','hi');
# Test shells inside exported environment variables. See SV 10593
run_make_test('
export HI = $(shell echo hi)
.PHONY: all
all: ; @echo $$HI
',
'',"hi");
$ENV{HI} = 'foo';
run_make_test('
HI = $(shell echo hi)
.PHONY: all
all: ; @echo $$HI
',
'',"hi");
$ENV{HI} = 'foo';
run_make_test('
HI = $(shell echo hi)
bad := $(HI)
.PHONY: all
all: ; @echo $$HI $(bad)
',
'',"hi hi");
# SV 63016: Exported variable that contains a variable containing $(shell...)
run_make_test('
HI = $(shell echo hi)
export bad = $(HI)
.PHONY: all
all:; : $(HI)
',
'',": hi");
$ENV{HI} = 'outer';
run_make_test('
export HI = $(shell echo $$HI)
.PHONY: all
all:; @echo $$HI
',
'',"outer");
$ENV{HI} = 'outer';
run_make_test('
export HI = $(shell echo $$HI)
.PHONY: all
all:; : $(HI)
',
'',": outer");
if ($port_type ne 'W32') {
# Test shell errors in recipes including offset
# This needs to be ported to Windows, or else Windows error messages
# need to converted to look like more normal make errors.
run_make_test('
.RECIPEPREFIX = >
all:
>@echo hi
>$(shell ./basdfdfsed there)
>@echo $(.SHELLSTATUS)
',
'', "#MAKE#: ./basdfdfsed: $ERR_no_such_file\nhi\n127\n");
run_make_test('
$(shell ./basdfdfsed where)
all: ; @echo $(.SHELLSTATUS)
',
'', "#MAKE#: ./basdfdfsed: $ERR_no_such_file\n127\n");
# Test SHELLSTATUS for kill.
# This test could be ported to Windows, using taskkill ... ?
# Figure out the exit code for SIGINT
my $pid = fork();
if (! $pid) {
exec('kill -2 $$') or die "exec: Cannot execute sleep\n";
}
waitpid($pid, 0);
# .SHELLSTATUS for a signal gives 128 + the signal number
my $ret = $?;
if ($ret > 255) {
# Solaris 10 perl 5.8.4 puts signal number + 128 into the high 8 bits.
$ret >>= 8;
}
$ret |= 128;
run_make_test('.PHONY: all
$(shell kill -2 $$$$)
STAT := $(.SHELLSTATUS)
all: ; @echo STAT=$(STAT)
','',"STAT=$ret\n");
# Test that not-found errors can be redirected
if ($ERR_command_not_found) {
$_ = $ERR_command_not_found;
s/#CMDNAME#/bad-command/g;
run_make_test(q!
out := $(shell bad-command 2>&1)
all: ; @echo '$(.SHELLSTATUS): $(out)'
!,
'', "127: $_\n");
}
# If we're using pipes for jobserver, then we will close them and not
# allow them to be available to sub-makes invoked via $(shell ...)
if (exists $FEATURES{'jobserver'}) {
run_make_test(q!
ifeq ($(ELT),)
default:; @$(MAKE) -f #MAKEFILE# ELT=1
else ifeq ($(ELT),1)
OUTPUT := $(shell $(MAKE) -f #MAKEFILE# ELT=2)
$(info $(OUTPUT))
default:;: $(ELT)
else
default:;: $(ELT)
endif
!,
'--no-print-directory -j2 --jobserver-style=pipe', "#MAKE#[2]: warning: jobserver unavailable: using -j1. Add '+' to parent make rule.\n: 2\n: 1");
}
}
# If we're not using pipes for jobserver, then they are available in sub-makes
# invoked by $(shell ...)
if ($port_type eq 'W32' || exists $FEATURES{'jobserver-fifo'}) {
run_make_test(q!
ifeq ($(ELT),)
default:; @$(MAKE) -f #MAKEFILE# ELT=1
else ifeq ($(ELT),1)
OUTPUT := $(shell $(MAKE) -f #MAKEFILE# ELT=2)
$(info $(OUTPUT))
default:;: $(ELT)
else
default:;: $(ELT)
endif
!,
'--no-print-directory -j2', ": 2\n: 1");
}
1;