make/tests/scripts/variables/MAKEFLAGS
Dmitry Goncharov dc2d963989 [SV 63347] Always add command line variable assignments to MAKEFLAGS
This commit introduces two visible changes:
1. Keep command line variable assignments in MAKEFLAGS at all times,
   even while parsing makefiles.
2. Define makeflags immediately when a makefile modifies MAKEFLAGS.

The new MAKEFLAGS and MAKEOVERRIDES initialization procedure:
1. decode_switches (argc, argv, o_command) is called to parse command
   line variable assignments.
2. Command line variable assignments go through quote_for_env.
   Initialize -*-command-variables-*- to the quoted values.
3. MAKEOVERRIDES is initialized to refer to -*-command-variables-*-
   with origin o_env to keep the definitions in the database intact.
4. define_makeflags() is called which adds MAKEOVERRIDES to MAKEFLAGS.
5. Makefiles are parsed.  If a makefile modifies MAKEFLAGS, the new
   value of MAKEFLAGS is defined right away.
6. Env switches are decoded again as o_env.  The definitions set by
   decode_switches at step 1 stay intact, as o_command beats o_env.

We must preserve the original intact definitions in order to detect
failure cases; for example:
  $ cat makefile
  all:; $(hello)
  $ make hello='$(world'
  makefile:1: *** unterminated variable reference.  Stop.

* src/makeint.h: Declare enum variable_origin, struct variable and
define_makeflags().  Add parameter origin to decode_env_switches().
* src/main.c (define_makeflags): Remove "all". If a variable is
assigned on the command line then append MAKEOVERRIDES to MAKEFLAGS.
(decode_env_switches): Replace parameter env with origin.
(decode_switches): Replace parameter env with origin.
Treat origin == o_command as env == 0.
(handle_non_switch_argument): Replace parameter env with origin.
Treat origin == o_command as env == 0.
(main): Call decode_switches() with origin==o_command before parsing
makefiles.  Call decode_switches() with origin==o_env after parsing
makefiles.
* src/variable.c (set_special_var): Define makeflags at parse time,
each time a makefile modifies MAKEFLAGS.
(do_variable_definition): Strip command line variable assignments from
MAKEFLAGS before appending extra flags.  set_special_var() adds them
back.
* tests/scripts/variables/MAKEFLAGS: Add tests.
2022-11-28 10:50:55 -05:00

372 lines
9.9 KiB
Perl

# -*-perl-*-
$description = "Test proper behavior of MAKEFLAGS";
$details = "DETAILS";
# Normal flags aren't prefixed with "-"
run_make_test(q!
all: ; @echo /$(MAKEFLAGS)/
!,
'-e -r -R', '/erR/');
# Long arguments mean everything is prefixed with "-"
run_make_test(q!
all: ; @echo /$(MAKEFLAGS)/
!,
'--no-print-directory -e -r -R --trace', "#MAKEFILE#:2: update target 'all' due to: target does not exist
echo /erR --trace --no-print-directory/
/erR --trace --no-print-directory/");
# Recursive invocations of make should accumulate MAKEFLAGS values.
# Savannah bug #2216
run_make_test(q!
MSG = Fails
.RECIPEPREFIX = >
all:
> @echo '$@: MAKEFLAGS=$(MAKEFLAGS)'
> @MSG=Works $(MAKE) -e -f #MAKEFILE# jump
jump:
> @echo '$@ $(MSG): MAKEFLAGS=$(MAKEFLAGS)'
> @$(MAKE) -f #MAKEFILE# print
print:
> @echo '$@ $(MSG): MAKEFLAGS=$(MAKEFLAGS)'
.PHONY: all jump print
!,
'--no-print-directory',
'all: MAKEFLAGS= --no-print-directory
jump Works: MAKEFLAGS=e --no-print-directory
print Works: MAKEFLAGS=e --no-print-directory');
# Ensure MAKEFLAGS updates are handled immediately rather than later
mkdir('foo', 0777);
mkdir('bar', 0777);
run_make_test(q!
$(info MAKEFLAGS=$(MAKEFLAGS))
$(info INCLUDE_DIRS=$(.INCLUDE_DIRS))
MAKEFLAGS += -Ibar
$(info MAKEFLAGS=$(MAKEFLAGS))
$(info INCLUDE_DIRS=$(.INCLUDE_DIRS))
.PHONY: all
all: ; @echo 'MAKEFLAGS=$(MAKEFLAGS)' "\$$MAKEFLAGS=$$MAKEFLAGS"
!,
'-I- -Ifoo', 'MAKEFLAGS= -I- -Ifoo
INCLUDE_DIRS=foo
MAKEFLAGS= -I- -Ifoo -Ibar
INCLUDE_DIRS=foo bar
MAKEFLAGS= -I- -Ifoo -Ibar $MAKEFLAGS= -I- -Ifoo -Ibar');
rmdir('foo');
rmdir('bar');
# Test that command line switches are all present in MAKEFLAGS.
# sv 62514.
my @opts;
my @flavors;
# Simple flags.
@opts = ('i', 'k', 'n', 'q', 'r', 's', 'w', 'd');
exists $FEATURES{'check-symlink'} and push @opts, 'L';
@flavors = ('=', ':=', ':::=', '+=-');
for my $fl (@flavors) {
for my $opt (@opts) {
run_make_test("
MAKEFLAGS${fl}B
all:; \$(info makeflags='\$(MAKEFLAGS)')
", "-$opt", "/makeflags='B$opt'/");
}
}
# Switches which carry arguments.
@opts = (' -I/tmp', ' -Onone', ' --debug=b', ' -l2.5');
for my $fl (@flavors) {
for my $opt (@opts) {
run_make_test("
MAKEFLAGS${fl}B
all:; \$(info makeflags='\$(MAKEFLAGS)')
", "$opt", "/makeflags='B$opt'/");
}
}
# Long options which take no arguments.
# sv 62514.
@opts = (' --no-print-directory', ' --warn-undefined-variables', ' --trace');
for my $fl (@flavors) {
for my $opt (@opts) {
run_make_test("
MAKEFLAGS${fl}B
all:; \$(info makeflags='\$(MAKEFLAGS)')
", "$opt", "/makeflags='B$opt'/");
}
}
# Test that make filters out duplicates.
# Each option is specified in the makefile, env and on the command line.
@opts = (' -I/tmp', ' -Onone', ' --debug=b', ' -l2.5');
for my $fl (@flavors) {
for my $opt (@opts) {
$ENV{'MAKEFLAGS'} = $opt;
run_make_test("
MAKEFLAGS${fl}B $opt
all:; \$(info makeflags='\$(MAKEFLAGS)')
", "$opt", "/makeflags='B$opt'/");
}
}
# Test that make filters out duplicates.
# Each option is specified in the makefile, env and on the command line.
# decode_switches reallocates when the number of parameters in sl->list exceeds 5.
# This test exercises the realloc branch.
for my $fl (@flavors) {
$ENV{'MAKEFLAGS'} = '-I1 -Onone --debug=b -l2.5 -I2 -I3 -I4 -I5 -I6 -I2 -I2';
run_make_test("
MAKEFLAGS${fl}B -I1 -Onone --debug=b -l2.5 -I2 -I3 -I4 -I5 -I6 -I2 -I2
all:; \$(info makeflags='\$(MAKEFLAGS)')
",
'-I1 -Onone --debug=b -l2.5 -I2 -I3 -I4 -I5 -I6',
"/makeflags='B -I1 -I2 -I3 -I4 -I5 -I6 -l2.5 -Onone --debug=b'/");
}
# A mix of multiple flags from env, the makefile and command line.
# Skip -L since it's not available everywhere
for my $fl (@flavors) {
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables --trace';
run_make_test("
MAKEFLAGS${fl}iknqrswd -I/tmp -I/tmp -Onone -Onone -l2.5 -l2.5
all:; \$(info makeflags='\$(MAKEFLAGS)')
",
'-Onone -l2.5 -l2.5 -Onone -I/tmp -iknqrswd -i -n -s -k -I/tmp',
"/makeflags='Bdiknqrsw -I/tmp -l2.5 -Onone --trace --warn-undefined-variables'/");
}
# Verify MAKEFLAGS are all available to shell function at parse time.
for my $fl (@flavors) {
my $answer = 'Biknqrs -I/tmp -l2.5 -Onone --no-print-directory --warn-undefined-variables';
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables';
run_make_test("
MAKEFLAGS${fl}iknqrsw -I/tmp -I/tmp -Onone -Onone -l2.5 -l2.5 --no-print-directory
\$(info at parse time '\$(MAKEFLAGS)')
XX := \$(shell echo \"\$\$MAKEFLAGS\")
all:; \$(info at build time makeflags='\$(XX)')
",
'-Onone -l2.5 -l2.5 -Onone -I/tmp -iknqrs -i -n -s -k -I/tmp',
"at parse time '$answer'
at build time makeflags='$answer'");
}
# Verify MAKEFLAGS and command line definitions are all available to shell function at parse time.
for my $fl (@flavors) {
$ENV{'MAKEFLAGS'} = 'ikB --no-print-directory --warn-undefined-variables';
my $answer = 'Biknqrs -I/tmp -l2.5 -Onone --no-print-directory --warn-undefined-variables -- hello=world';
run_make_test("
MAKEFLAGS${fl}iknqrsw -I/tmp -I/tmp -Onone -Onone -l2.5 -l2.5 --no-print-directory
\$(info at parse time '\$(MAKEFLAGS)')
XX := \$(shell echo \"\$\$MAKEFLAGS\")
all:; \$(info at build time makeflags='\$(XX)')
",
'-Onone -l2.5 -l2.5 -Onone -I/tmp -iknqrs -i -n -s -k -I/tmp hello=world',
"at parse time '$answer'
at build time makeflags='$answer'");
}
# Verify that command line arguments are included in MAKEFLAGS
run_make_test(q!
all: ; @echo $(MAKEFLAGS)
!,
'-e FOO=bar -r -R', 'erR -- FOO=bar');
# Long arguments mean everything is prefixed with "-"
run_make_test(q!
all: ; @echo /$(MAKEFLAGS)/
!,
'--no-print-directory -e -r -R --trace FOO=bar',
"#MAKEFILE#:2: update target 'all' due to: target does not exist
echo /erR --trace --no-print-directory -- FOO=bar/
/erR --trace --no-print-directory -- FOO=bar/");
# sv 63347.
# Verify that command line arguments are included in MAKEFLAGS
# when makefiles are parsed.
my $answer = 'erR -- hello:=world FOO=bar';
run_make_test(q!
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, '-e FOO=bar -r -R hello:=world',
"$answer
$answer
#MAKE#: 'all' is up to date.\n");
# sv 63347.
# Same as above, with makefile setting the value of the same variables as
# defined on the cli.
my $answer = 'erR -- hello:=world FOO=bar';
run_make_test(q!
$(info $(MAKEFLAGS))
FOO=moon
hello:=moon
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, '-e FOO=bar -r -R hello:=world',
"$answer
$answer
$answer
#MAKE#: 'all' is up to date.\n");
# sv 63347.
# Same as above, with makefile overriding the value of cli definition.
my $answer = 'erR -- hello:=world FOO=bar';
run_make_test(q!
$(info $(MAKEFLAGS))
override FOO=moon
override hello:=moon
export hello
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, '-e FOO=bar -r -R hello:=world',
"$answer
$answer
$answer
#MAKE#: 'all' is up to date.\n");
# Same as above, and makefile overrides the value of cli definition.
# resets MAKEOVERRIDES.
my $answer = 'rR -- hello:=world FOO=bar';
run_make_test(q!
$(info $(MAKEFLAGS))
override FOO=moon
override hello:=moon
export hello
$(info $(MAKEFLAGS))
MAKEOVERRIDES=
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, 'FOO=bar -r -R hello:=world',
"$answer
$answer
rR -- \nrR
#MAKE#: 'all' is up to date.\n");
# sv 63347.
# MAKEFLAGS set is env and makefile sets MAKEFLAGS and there is a command
# line definition.
my $answer = ' -- bye=moon hello=world';
$ENV{'MAKEFLAGS'} = 'hello=world';
run_make_test(q!
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, 'bye=moon',
" -- bye=moon hello=world
-- bye=moon hello=world
#MAKE#: 'all' is up to date.\n");
# sv 63347.
# Conditional assignment and MAKEFLAGS.
my $answer = 'B -- bye=moon hello=world';
$ENV{'MAKEFLAGS'} = 'hello=world';
run_make_test(q!
$(info $(MAKEFLAGS))
MAKEFLAGS?=-k
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, '-B bye=moon',
"$answer
$answer
$answer
#MAKE#: 'all' is up to date.\n");
# sv 63347.
# MAKEFLAGS set is env and makefile sets MAKEFLAGS and there is a command
# line definition.
for my $fl (@flavors) {
my $answer = ' -- bye=moon hello=world';
$ENV{'MAKEFLAGS'} = 'hello=world';
run_make_test("
\$(info \$(MAKEFLAGS))
MAKEFLAGS${fl}R
\$(info \$(MAKEFLAGS))
all:; \$(info \$(MAKEFLAGS))
", 'bye=moon',
"$answer
R$answer
rR$answer
#MAKE#: 'all' is up to date.\n");
}
# sv 63347.
# Test changes introduced by makefiles to MAKEFLAGS.
for my $fl (@flavors) {
my $answer = 'rR --no-print-directory -- hello:=world FOO=bar';
run_make_test(q!
MAKEFLAGS+=--no-print-directory
$(info $(MAKEFLAGS))
MAKEFLAGS+=-k
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, 'FOO=bar -r -R hello:=world',
"$answer
k$answer
k$answer
#MAKE#: 'all' is up to date.\n");
}
# sv 63347.
# Test changes introduced by makefiles to MAKEFLAGS.
# Same as above, but with -e.
for my $fl (@flavors) {
my $answer = 'erR -- hello:=world FOO=bar';
run_make_test(q!
MAKEFLAGS+=--no-print-directory
$(info $(MAKEFLAGS))
MAKEFLAGS+=-k
$(info $(MAKEFLAGS))
all:; $(info $(MAKEFLAGS))
!, '-e FOO=bar -r -R hello:=world',
"$answer
$answer
$answer
#MAKE#: 'all' is up to date.\n");
}
mkdir('bye', 0777);
create_file('bye/makefile',
'hello=moon
all:; $(info $(hello))');
# sv 63347.
# Test that a cli definition takes precendence over a definition set in
# submake.
run_make_test(q!
v:=$(shell $(MAKE) -C bye --no-print-directory)
all: ; $(info $(v))
!, 'hello=world', "world #MAKE#[1]: 'all' is up to date.\n#MAKE#: 'all' is up to date.");
# Same as above with the shell assignment operator.
run_make_test(q!
v \!= $(MAKE) -C bye --no-print-directory
all: ; $(info $(v))
!, 'hello=world', "world #MAKE#[1]: 'all' is up to date.\n#MAKE#: 'all' is up to date.");
unlink('bye/makefile');
rmdir('bye');
# sv 63347
# Invalid command line variable definition.
run_make_test(q!
all:; $(info $(hello))
!, 'hello=\'$(world\'', "#MAKEFILE#:2: *** unterminated variable reference. Stop.\n", 512);
# sv 63347
# An unused invalid command line variable definition is ignored.
run_make_test(q!
all:; $(info good)
!, 'hello=\'$(world\'', "good\n#MAKE#: 'all' is up to date.\n");
1;