https://www.gnu.org/software/gnulib/manual/html_node/C99-features-assumed.html
The configure script should verify the compiler has these features.
+* Target-specific variables can now be marked "unexport".
+
+* Exporting / unexporting target-specific variables is handled correctly, so
+ that the attribute of the most specific variable setting is used.
+
* Special targets like .POSIX are detected upon definition, ensuring that any
change in behavior takes effect immediately, before the next line is parsed.
@end example
Target-specific variable assignments can be prefixed with any or all of the
-special keywords @code{export}, @code{override}, or @code{private};
-these apply their normal behavior to this instance of the variable only.
+special keywords @code{export}, @code{unexport}, @code{override}, or
+@code{private}; these apply their normal behavior to this instance of the
+variable only.
Multiple @var{target} values create a target-specific variable value for
each member of the target list individually.
unsigned int assign_v:1;
unsigned int define_v:1;
unsigned int undefine_v:1;
- unsigned int export_v:1;
unsigned int override_v:1;
unsigned int private_v:1;
+ enum variable_export export_v ENUM_BITFIELD (2);
};
/* Types of "words" that can be read in a makefile. */
wlen = p2 - p;
if (word1eq ("export"))
- vmod->export_v = 1;
+ vmod->export_v = v_export;
+ else if (word1eq ("unexport"))
+ vmod->export_v = v_noexport;
else if (word1eq ("override"))
vmod->override_v = 1;
else if (word1eq ("private"))
assert (v != NULL);
- if (vmod.export_v)
- v->export = v_export;
+ if (vmod.export_v != v_default)
+ v->export = vmod.export_v;
if (vmod.private_v)
v->private_var = 1;
/* Set up the variable to be *-specific. */
v->per_target = 1;
v->private_var = vmod->private_v;
- if (vmod->export_v)
- v->export = v_export;
+ if (vmod->export_v != v_default)
+ v->export = vmod->export_v;
/* If it's not an override, check to see if there was a command-line
setting. If so, reset the value. */
\f
int export_all_variables;
+static int
+should_export (const struct variable *v)
+{
+ switch (v->export)
+ {
+ case v_export:
+ break;
+
+ case v_noexport:
+ return 0;
+
+ case v_ifset:
+ if (v->origin == o_default)
+ return 0;
+ break;
+
+ case v_default:
+ if (v->origin == o_default || v->origin == o_automatic)
+ /* Only export default variables by explicit request. */
+ return 0;
+
+ /* The variable doesn't have a name that can be exported. */
+ if (! v->exportable)
+ return 0;
+
+ if (! export_all_variables
+ && v->origin != o_command
+ && v->origin != o_env && v->origin != o_env_override)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
/* Create a new environment for FILE's commands.
If FILE is nil, this is for the 'shell' function.
The child's MAKELEVEL variable is incremented. */
struct variable makelevel_key;
char **result_0;
char **result;
+ /* If we got no value from the environment then never add the default. */
+ int added_SHELL = shell_var.value == 0;
if (file == 0)
set_list = current_variable_set_list;
hash_init (&table, VARIABLE_BUCKETS,
variable_hash_1, variable_hash_2, variable_hash_cmp);
- /* Run through all the variable sets in the list,
- accumulating variables in TABLE. */
+ /* Run through all the variable sets in the list, accumulating variables
+ in TABLE. We go from most specific to least, so the first variable we
+ encounter is the keeper. */
for (s = set_list; s != 0; s = s->next)
{
struct variable_set *set = s->set;
+ int isglobal = set == &global_variable_set;
+
v_slot = (struct variable **) set->table.ht_vec;
v_end = v_slot + set->table.ht_size;
for ( ; v_slot < v_end; v_slot++)
if (! HASH_VACANT (*v_slot))
{
- struct variable **new_slot;
+ struct variable **evslot;
struct variable *v = *v_slot;
- /* If this is a per-target variable and it hasn't been touched
- already then look up the global version and take its export
- value. */
- if (v->per_target && v->export == v_default)
- {
- struct variable *gv;
+ evslot = (struct variable **) hash_find_slot (&table, v);
- gv = lookup_variable_in_set (v->name, strlen (v->name),
- &global_variable_set);
- if (gv)
- v->export = gv->export;
+ if (HASH_VACANT (*evslot))
+ {
+ /* If we're not global, or we are and should export, add it. */
+ if (!isglobal || should_export (v))
+ hash_insert_at (&table, v, evslot);
}
-
- switch (v->export)
+ else if ((*evslot)->export == v_default)
{
- case v_default:
- if (v->origin == o_default || v->origin == o_automatic)
- /* Only export default variables by explicit request. */
- continue;
-
- /* The variable doesn't have a name that can be exported. */
- if (! v->exportable)
- continue;
-
- if (! export_all_variables
- && v->origin != o_command
- && v->origin != o_env && v->origin != o_env_override)
- continue;
- break;
-
- case v_export:
- break;
-
- case v_noexport:
- {
- /* If this is the SHELL variable and it's not exported,
- then add the value from our original environment, if
- the original environment defined a value for SHELL. */
- if (streq (v->name, "SHELL") && shell_var.value)
- {
- v = &shell_var;
- break;
- }
- continue;
- }
-
- case v_ifset:
- if (v->origin == o_default)
- continue;
- break;
+ /* We already have a variable but we don't know its status. */
+ (*evslot)->export = v->export;
}
-
- new_slot = (struct variable **) hash_find_slot (&table, v);
- if (HASH_VACANT (*new_slot))
- hash_insert_at (&table, v, new_slot);
}
}
makelevel_key.length = MAKELEVEL_LENGTH;
hash_delete (&table, &makelevel_key);
- result = result_0 = xmalloc ((table.ht_fill + 2) * sizeof (char *));
+ result = result_0 = xmalloc ((table.ht_fill + 3) * sizeof (char *));
v_slot = (struct variable **) table.ht_vec;
v_end = v_slot + table.ht_size;
{
struct variable *v = *v_slot;
+ /* This might be here because it was a target-specific variable that
+ we didn't know the status of when we added it. */
+ if (! should_export (v))
+ continue;
+
+ /* If this is the SHELL variable remember we already added it. */
+ if (!added_SHELL && streq (v->name, "SHELL"))
+ added_SHELL = 1;
+
/* If V is recursively expanded and didn't come from the environment,
expand its value. If it came from the environment, it should
go back into the environment unchanged. */
}
}
+ if (!added_SHELL)
+ *result++ = xstrdup (concat (3, shell_var.name, "=", shell_var.value));
+
*result = xmalloc (100);
sprintf (*result, "%s=%u", MAKELEVEL_NAME, makelevel + 1);
*++result = 0;
f_append_value /* Append unexpanded value */
};
+enum variable_export
+{
+ v_default = 0, /* Decide in target_environment. */
+ v_export, /* Export this variable. */
+ v_noexport, /* Don't export this variable. */
+ v_ifset /* Export it if it has a non-default value. */
+};
+
/* Structure that represents one variable definition.
Each bucket of the hash table is a chain of these,
chained through 'next'. */
enum variable_origin
origin ENUM_BITFIELD (3); /* Variable origin. */
enum variable_export
- {
- v_export, /* Export this variable. */
- v_noexport, /* Don't export this variable. */
- v_ifset, /* Export it if it has a non-default value. */
- v_default /* Decide in target_environment. */
- } export ENUM_BITFIELD (2);
+ export ENUM_BITFIELD (2); /* Export control. */
};
/* Structure that represents a variable set. */
',
'', "\$(export)=456 / \$export=456\n");
-# TEST 9: Check "export" as a target
+# TEST 10: Check "export" as a target
&run_make_test('
a: export
',
'', "export\n");
+# Check export and assignment of a variable on the same line
+
+$ENV{hello} = 'moon';
+
+run_make_test(q!
+all: ; @echo hello=$(hello) hello=$$hello
+export hello=sun
+!,
+ '', "hello=sun hello=sun\n");
+
+# Check unexport and assignment of a variable on the same line
+
+$ENV{hello} = 'moon';
+
+run_make_test(q!
+all: ; @echo hello=$(hello) hello=$$hello
+unexport hello=sun
+!,
+ '', "hello=sun hello=\n");
+
# This tells the test driver that the perl test script executed properly.
1;
one: override FOO = one
one two: ; @echo $(FOO) $(BAR)
two: BAR = two
+.RECIPEPREFIX = >
three: ; BAR=1000
- @echo $(FOO) $(BAR)
+> @echo $(FOO) $(BAR)
# Some things that shouldn not be target vars
funk : override
funk : override adelic
!,
'', 'hello=sun');
+# Support target-specific unexport
+
+$ENV{hello} = "moon";
+run_make_test(q!
+unexport hello=sun
+all: base exp
+base exp: ; @echo hello=$$hello
+exp: export hello=world
+!,
+ '', "hello=\nhello=world\n");
+
+$ENV{hello} = "moon";
+run_make_test(q!
+hello=sun
+all: base exp
+base exp: ; @echo hello=$$hello
+exp: unexport hello=world
+!,
+ '', "hello=sun\nhello=\n");
+
+run_make_test(q!
+all:; @echo hello=$$hello
+unexport hello=sun
+dummy: hello?=world
+!,
+ '', 'hello=');
+
+$ENV{hello} = "moon";
+run_make_test(q!
+all:; @echo hello=$$hello
+hello=sun
+dummy: unexport hello=world
+!,
+ '', 'hello=sun');
+
+run_make_test(q!
+all: mid
+mid: base
+
+ifeq ($(midexport),export)
+mid: export hello=mid
+else ifeq ($(midexport),unexport)
+mid: unexport hello=mid
+else
+mid: hello=mid
+endif
+
+ifeq ($(baseexport),export)
+base: export hello=base
+else ifeq ($(baseexport),unexport)
+base: unexport hello=base
+else
+base: hello=base
+endif
+
+all mid base:; @echo $@ make=$(hello) shell=$$hello
+!,
+ '', "base make=base shell=\nmid make=mid shell=\nall make= shell=\n");
+
+# Test base settings with env var
+$ENV{hello} = "environ";
+run_make_test(undef,
+ '', "base make=base shell=base\nmid make=mid shell=mid\nall make=environ shell=environ\n");
+
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'baseexport=export', "base make=base shell=base\nmid make=mid shell=mid\nall make=environ shell=environ\n");
+
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'baseexport=unexport', "base make=base shell=\nmid make=mid shell=mid\nall make=environ shell=environ\n");
+
+# Test mid settings with env var
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'midexport=export', "base make=base shell=base\nmid make=mid shell=mid\nall make=environ shell=environ\n");
+
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'midexport=export baseexport=unexport', "base make=base shell=\nmid make=mid shell=mid\nall make=environ shell=environ\n");
+
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'midexport=unexport', "base make=base shell=\nmid make=mid shell=\nall make=environ shell=environ\n");
+
+$ENV{hello} = "environ";
+run_make_test(undef,
+ 'midexport=unexport baseexport=export', "base make=base shell=base\nmid make=mid shell=\nall make=environ shell=environ\n");
+
+# Test base settings without env var
+run_make_test(undef,
+ 'baseexport=export', "base make=base shell=base\nmid make=mid shell=\nall make= shell=\n");
+
+run_make_test(undef,
+ 'baseexport=unexport', "base make=base shell=\nmid make=mid shell=\nall make= shell=\n");
+
+# Test mid settings with env var
+run_make_test(undef,
+ 'midexport=export', "base make=base shell=base\nmid make=mid shell=mid\nall make= shell=\n");
+
+run_make_test(undef,
+ 'midexport=export baseexport=unexport', "base make=base shell=\nmid make=mid shell=mid\nall make= shell=\n");
+
+run_make_test(undef,
+ 'midexport=unexport', "base make=base shell=\nmid make=mid shell=\nall make= shell=\n");
+
+run_make_test(undef,
+ 'midexport=unexport baseexport=export', "base make=base shell=base\nmid make=mid shell=\nall make= shell=\n");
+
+
+
# TEST #19: Test define/endef variables as target-specific vars
# run_make_test('
# '', "local\n");
1;
-
-### Local Variables:
-### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action))
-### End:
$ENV{SHELL} = '/dev/null';
run_make_test('all:;@echo "$(SHELL)"', '', $mshell);
-# According to POSIX, any value of SHELL set in the makefile should _NOT_ be
-# exported to the subshell! I wanted to set SHELL to be $^X (perl) in the
-# makefile, but make runs $(SHELL) -c 'commandline' and that doesn't work at
-# all when $(SHELL) is perl :-/. So, we just add an extra initial /./ which
-# works well on UNIX and seems to work OK on at least some non-UNIX systems.
+# According to POSIX, any value of SHELL set in the makefile should not be
+# exported to the subshell. A more portable option might be to set SHELL to
+# be $^X (perl) in the makefile, and set .SHELLFLAGS to -e.
$ENV{SHELL} = $mshell;
my $altshell = "/./$mshell";
my $altshell2 = "/././$mshell";
+
if ($mshell =~ m,^([a-zA-Z]:)([\\/])(.*),) {
$altshell = "$1$2.$2$3";
$altshell2 = "$1$2.$2.$2$3";