diff options
-rw-r--r-- | ChangeLog | 30 | ||||
-rw-r--r-- | NEWS | 30 | ||||
-rw-r--r-- | doc/make.texi | 67 | ||||
-rw-r--r-- | read.c | 374 | ||||
-rw-r--r-- | tests/ChangeLog | 8 | ||||
-rw-r--r-- | tests/scripts/features/export | 158 | ||||
-rw-r--r-- | tests/scripts/features/targetvars | 171 | ||||
-rw-r--r-- | tests/scripts/variables/private | 78 | ||||
-rw-r--r-- | variable.c | 169 | ||||
-rw-r--r-- | variable.h | 9 |
10 files changed, 585 insertions, 509 deletions
@@ -1,3 +1,18 @@ +2009-05-25 Paul Smith <psmith@gnu.org> + + Reworked the parser for variable assignments to allow multiple + modifiers, and in any order. Also allows variable and + prerequisites to be modifier names ('export', 'private', etc.) + + * NEWS: Add notes about user-visible changes. + + * read.c (struct vmodifiers): Remember what modifiers were seen. + (parse_var_assignment): New function to parse variable assignments. + (eval): Call the new function. Handle variable assignments earlier. + + * variable.c (parse_variable_definition): Only parse; don't create var. + (assign_variable_definition): Call parse, then create the var. + 2009-05-24 Paul Smith <psmith@gnu.org> * doc/make.texi: Fix the ISBN for the GNU make manual. Incorrect @@ -23,6 +38,21 @@ * function.c (func_shell): Don't close pipedes[1] if it is -1. Fixes Savannah bug #20495. +2009-02-23 Ramon Garcia <ramon.garcia.f@gmail.com> + + Introduce a new keyword "private" which applies to target-specific + variables and prevents their values from being inherited. + + * variable.h (struct variable): Add private_var flag to each variable. + Add a flag to specify which list entry switches to the parent target. + * variable.c (define_variable_in_set): Initialize private_var flag. + (lookup_variable): Skip private variables in parent contexts. + (initialize_file_variables): Set next_is_parent appropriately. + (print_variable): Show the private_var flag. + * read.c (eval): Recognize the private keyword. + (record_target_var): Set private_var. + * doc/make.texi (Suppressing Inheritance): Add documentation. + 2008-10-26 Paul Smith <psmith@gnu.org> * configure.in: Check for strndup(). @@ -1,6 +1,6 @@ GNU make NEWS -*-indented-text-*- History of user-visible changes. - 1 April 2006 + 25 May 2009 See the end of this file for copyrights and conditions. @@ -14,12 +14,30 @@ Version 3.81.90 * Compiling GNU make now requires a conforming ISO C 1989 compiler and standard runtime library. +* The parser for variable assignments has been enhanced to allow multiple + modifiers ('export', 'override', 'private' (see below)) on the same line as + variables, including define/endef variables, and in any order. Also, it is + possible to create variables and targets named as these modifiers. + +* WARNING: Backward-incompatibility! + As a result of the parser changes, two backward-compatibility issues exist: + first, a prerequisite containing an "=" cannot be escaped with a backslash + any longer. You must create a variable containing an "=" and use that + variable in the prerequisite. Second, variable names can no longer contain + whitespace, unless you put the whitespace in a variable and use the + variable. + * New special variable: .RECIPEPREFIX allows you to reset the recipe - introduction character from the default (TAB) to something else. The - first character of this variable value is the new recipe introduction - character. If the variable is set to the empty string, TAB is used - again. It can be set and reset at will; rules will be parsed - according to the current value. + introduction character from the default (TAB) to something else. The first + character of this variable value is the new recipe introduction character. + If the variable is set to the empty string, TAB is used again. It can be + set and reset at will; recipes will use the value active when they were + first parsed. + +* New variable modifier 'private': prefixing a variable assignment with the + modifier 'private' suppresses inheritance of that variable by + prerequisites. This is most useful for target- and pattern-specific + variables. Version 3.81 diff --git a/doc/make.texi b/doc/make.texi index 21f0270..4c0fe44 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -240,6 +240,7 @@ How to Use Variables basis. * Pattern-specific:: Target-specific variable values can be applied to a group of targets that match a pattern. +* Suppressing Inheritance:: Suppress inheritance of variables. * Special Variables:: Variables with special meaning or behavior. Advanced Features for Reference to Variables @@ -4691,6 +4692,7 @@ they have particular specialized uses. @xref{Automatic Variables}. basis. * Pattern-specific:: Target-specific variable values can be applied to a group of targets that match a pattern. +* Suppressing Inheritance:: Suppress inheritance of variables. * Special Variables:: Variables with special meaning or behavior. @end menu @@ -5625,19 +5627,9 @@ Set a target-specific variable value like this: @var{target} @dots{} : @var{variable-assignment} @end example -@noindent -or like this: - -@example -@var{target} @dots{} : override @var{variable-assignment} -@end example - -@noindent -or like this: - -@example -@var{target} @dots{} : export @var{variable-assignment} -@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. Multiple @var{target} values create a target-specific variable value for each member of the target list individually. @@ -5683,7 +5675,7 @@ will cause that prerequisite to be built and the prerequisite will inherit the target-specific value from the first target. It will ignore the target-specific values from any other targets. -@node Pattern-specific, Special Variables, Target-specific, Using Variables +@node Pattern-specific, Suppressing Inheritance, Target-specific, Using Variables @section Pattern-specific Variable Values @cindex pattern-specific variables @cindex variables, pattern-specific @@ -5704,15 +5696,6 @@ Set a pattern-specific variable value like this: @example @var{pattern} @dots{} : @var{variable-assignment} @end example - -@noindent -or like this: - -@example -@var{pattern} @dots{} : override @var{variable-assignment} -@end example - -@noindent where @var{pattern} is a %-pattern. As with target-specific variable values, multiple @var{pattern} values create a pattern-specific variable value for each pattern individually. The @var{variable-assignment} can @@ -5729,7 +5712,43 @@ For example: will assign @code{CFLAGS} the value of @samp{-O} for all targets matching the pattern @code{%.o}. -@node Special Variables, , Pattern-specific, Using Variables +@node Suppressing Inheritance, Special Variables, Pattern-specific, Using Variables +@section Suppressing Inheritance +@findex private +@cindex suppressing inheritance +@cindex inheritance, suppressing + +As described in previous sections, @code{make} variables are inherited +by prerequisites. This capability allows you to modify the behavior +of a prerequisite based on which targets caused it to be rebuilt. For +example, you might set a target-specific variable on a @code{debug} +target, then running @samp{make debug} will cause that variable to be +inherited by all prerequisites of @code{debug}, while just running +@samp{make all} (for example) would not have that assignment. + +Sometimes, however, you may not want a variable to be inherited. For +these situations, @code{make} provides the @code{private} modifier. +Although this modifier can be used with any variable assignment, it +makes the most sense with target- and pattern-specific variables. Any +variable marked @code{private} will be visible to its local target but +will not be inherited by prerequisites of that target. A global +variable marked @code{private} will be visible in the global scope but +will not be inherited by any target, and hence will not be visible +in any recipe. + +As an example, consider this makefile: +@example +EXTRA_CFLAGS = + +prog: private EXTRA_CFLAGS = -L/usr/local/lib +prog: a.o b.o +@end example + +Due to the @code{private} modifier, @code{a.o} and @code{b.o} will not +inherit the @code{EXTRA_CFLAGS} variable assignment from the +@code{progs} target. + +@node Special Variables, , Suppressing Inheritance, Using Variables @comment node-name, next, previous, up @section Other Special Variables @cindex makefiles, and special variables @@ -56,6 +56,17 @@ struct ebuffer struct floc floc; /* Info on the file in fp (if any). */ }; +/* Track the modifiers we can have on variable assignments */ + +struct vmodifiers + { + unsigned int assign_v:1; + unsigned int define_v:1; + unsigned int export_v:1; + unsigned int override_v:1; + unsigned int private_v:1; + }; + /* Types of "words" that can be read in a makefile. */ enum make_word_type { @@ -125,8 +136,9 @@ static int eval_makefile (const char *filename, int flags); static int eval (struct ebuffer *buffer, int flags); static long readline (struct ebuffer *ebuf); -static void do_define (char *name, unsigned int namelen, - enum variable_origin origin, struct ebuffer *ebuf); +static struct variable *do_define (char *name, unsigned int namelen, + enum variable_origin origin, + struct ebuffer *ebuf); static int conditional_line (char *line, int len, const struct floc *flocp); static void record_files (struct nameseq *filenames, const char *pattern, const char *pattern_percent, struct dep *deps, @@ -134,13 +146,21 @@ static void record_files (struct nameseq *filenames, const char *pattern, unsigned int commands_idx, int two_colon, const struct floc *flocp); static void record_target_var (struct nameseq *filenames, char *defn, - enum variable_origin origin, int enabled, + enum variable_origin origin, + struct vmodifiers *vmod, const struct floc *flocp); static enum make_word_type get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length); static void remove_comments (char *line); static char *find_char_unquote (char *string, int stop1, int stop2, int blank, int ignorevars); + + +/* Compare a word, both length and contents. + P must point to the word to be tested, and WLEN must be the length. +*/ +#define word1eq(s) (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1)) + /* Read in all the makefiles and return the chain of their names. */ @@ -434,8 +454,75 @@ eval_buffer (char *buffer) alloca (0); return r; } + +/* Check LINE to see if it's a variable assignment. + + It might use one of the modifiers "export", "override", "private", or it + might be one of the conditional tokens like "ifdef", "include", etc. + + If it's not a variable assignment, VMOD.V_ASSIGN is 0. Returns LINE. + + Returns a pointer to the first non-modifier character, and sets VMOD + based on the modifiers found if any, plus V_ASSIGN is 1. + */ +char * +parse_var_assignment (const char *line, struct vmodifiers *vmod) +{ + const char *p; + memset (vmod, '\0', sizeof (*vmod)); + + /* Find the start of the next token. If there isn't one we're done. */ + line = next_token (line); + if (*line == '\0') + return (char *)line; + + p = line; + while (1) + { + int wlen; + const char *p2; + enum variable_flavor flavor; + + p2 = parse_variable_definition (p, &flavor); + + /* If this is a variable assignment, we're done. */ + if (p2) + break; + + /* It's not a variable; see if it's a modifier. */ + p2 = end_of_token (p); + wlen = p2 - p; + + if (word1eq ("export")) + vmod->export_v = 1; + else if (word1eq ("override")) + vmod->override_v = 1; + else if (word1eq ("private")) + vmod->private_v = 1; + else if (word1eq ("define")) + { + /* We can't have modifiers after 'define' */ + vmod->define_v = 1; + p = next_token (p2); + break; + } + else + /* Not a variable or modifier: this is not a variable assignment. */ + return (char *)line; + + /* It was a modifier. Try the next word. */ + p = next_token (p2); + if (*p == '\0') + return (char *)line; + } + + /* Found a variable assignment. */ + vmod->assign_v = 1; + return (char *)p; +} + /* Read file FILENAME as a makefile and add its contents to the data base. SET_DEFAULT is true if we are allowed to set the default goal. */ @@ -502,7 +589,9 @@ eval (struct ebuffer *ebuf, int set_default) unsigned int wlen; char *p; char *p2; + struct vmodifiers vmod; + /* At the top of this loop, we are starting a brand new line. */ /* Grab the next line to be evaluated */ ebuf->floc.lineno += nlines; nlines = readline (ebuf); @@ -527,7 +616,7 @@ eval (struct ebuffer *ebuf, int set_default) continue; /* If there is no preceding rule line, don't treat this line - as a command, even though it begins with a tab character. + as a command, even though it begins with a recipe prefix. SunOS 4 make appears to behave this way. */ if (filenames != 0) @@ -566,7 +655,7 @@ eval (struct ebuffer *ebuf, int set_default) } } - /* This line is not a shell command line. Don't worry about tabs. + /* This line is not a shell command line. Don't worry about whitespace. Get more space if we need it; we don't need to preserve the current contents of the buffer. */ @@ -575,6 +664,7 @@ eval (struct ebuffer *ebuf, int set_default) collapsed_length = linelen+1; if (collapsed) free (collapsed); + /* Don't need xrealloc: we don't need to preserve the content. */ collapsed = xmalloc (collapsed_length); } strcpy (collapsed, line); @@ -582,183 +672,113 @@ eval (struct ebuffer *ebuf, int set_default) collapse_continuations (collapsed); remove_comments (collapsed); - /* Compare a word, both length and contents. */ -#define word1eq(s) (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1)) - p = collapsed; - while (isspace ((unsigned char)*p)) - ++p; + /* See if this is a variable assignment. We need to do this early, to + allow variables with names like 'ifdef', 'export', 'private', etc. */ + p = parse_var_assignment(collapsed, &vmod); + if (vmod.assign_v) + { + struct variable *v; + enum variable_origin origin = vmod.override_v ? o_override : o_file; - if (*p == '\0') - /* This line is completely empty--ignore it. */ - continue; + /* If we're ignoring then we're done now. */ + if (ignoring) + { + if (vmod.define_v) + in_ignored_define = 1; + continue; + } - /* Find the end of the first token. Note we don't need to worry about - * ":" here since we compare tokens by length (so "export" will never - * be equal to "export:"). - */ - for (p2 = p+1; *p2 != '\0' && !isspace ((unsigned char)*p2); ++p2) - ; - wlen = p2 - p; + /* If it's a multi-line define / endef, manage that. */ + if (vmod.define_v) + { + if (*p == '\0') + fatal (fstart, _("empty variable name")); - /* Find the start of the second token. If it looks like a target or - variable definition it can't be a preprocessor token so skip - them--this allows variables/targets named `ifdef', `export', etc. */ - while (isspace ((unsigned char)*p2)) - ++p2; + /* Let the variable name be the whole rest of the line, + with trailing blanks stripped (comments have already been + removed), so it could be a complex variable/function + reference that might contain blanks. */ + p2 = p + strlen (p); + while (isblank ((unsigned char)p2[-1])) + --p2; + v = do_define (p, p2 - p, origin, ebuf); + } + else + { + v = try_variable_definition (fstart, p, origin, 0); + assert (v != NULL); + } - if ((p2[0] == ':' || p2[0] == '+' || p2[0] == '=') && p2[1] == '\0') - { - /* It can't be a preprocessor token so skip it if we're ignoring */ - if (ignoring) - continue; + if (vmod.export_v) + v->export = v_export; + if (vmod.private_v) + v->private_var = 1; - goto skip_conditionals; + /* This line has been dealt with. */ + goto rule_complete; } - /* We must first check for conditional and `define' directives before - ignoring anything, since they control what we will do with - following lines. */ - - if (!in_ignored_define) - { - int i = conditional_line (p, wlen, fstart); - if (i != -2) - { - if (i == -1) - fatal (fstart, _("invalid syntax in conditional")); - - ignoring = i; - continue; - } - } + /* If this line is completely empty, ignore it. */ + if (*p == '\0') + continue; - if (word1eq ("endef")) - { - if (!in_ignored_define) - fatal (fstart, _("extraneous `endef'")); - in_ignored_define = 0; - continue; - } + p2 = end_of_token (p); + wlen = p2 - p; + p2 = next_token (p2); - if (word1eq ("define")) + /* If we're in an ignored define, skip this line (but maybe get out). */ + if (in_ignored_define) { - if (ignoring) - in_ignored_define = 1; - else - { - if (*p2 == '\0') - fatal (fstart, _("empty variable name")); + /* See if this is an endef line (plus optional comment). */ + if (word1eq ("endef") && (*p2 == '\0' || *p2 == '#')) + in_ignored_define = 0; - /* Let the variable name be the whole rest of the line, - with trailing blanks stripped (comments have already been - removed), so it could be a complex variable/function - reference that might contain blanks. */ - p = strchr (p2, '\0'); - while (isblank ((unsigned char)p[-1])) - --p; - do_define (p2, p - p2, o_file, ebuf); - } continue; } - if (word1eq ("override")) - { - if (*p2 == '\0') - error (fstart, _("empty `override' directive")); - - if (strneq (p2, "define", 6) - && (isblank ((unsigned char)p2[6]) || p2[6] == '\0')) - { - if (ignoring) - in_ignored_define = 1; - else - { - p2 = next_token (p2 + 6); - if (*p2 == '\0') - fatal (fstart, _("empty variable name")); - - /* Let the variable name be the whole rest of the line, - with trailing blanks stripped (comments have already been - removed), so it could be a complex variable/function - reference that might contain blanks. */ - p = strchr (p2, '\0'); - while (isblank ((unsigned char)p[-1])) - --p; - do_define (p2, p - p2, o_override, ebuf); - } - } - else if (!ignoring - && !try_variable_definition (fstart, p2, o_override, 0)) - error (fstart, _("invalid `override' directive")); + /* Check for conditional state changes. */ + { + int i = conditional_line (p, wlen, fstart); + if (i != -2) + { + if (i == -1) + fatal (fstart, _("invalid syntax in conditional")); - continue; - } + ignoring = i; + continue; + } + } + /* Nothing to see here... move along. */ if (ignoring) - /* Ignore the line. We continue here so conditionals - can appear in the middle of a rule. */ continue; - if (word1eq ("export")) + /* Manage the "export" keyword used outside of variable assignment + as well as "unexport". */ + if (word1eq ("export") || word1eq ("unexport")) { - /* 'export' by itself causes everything to be exported. */ - if (*p2 == '\0') - export_all_variables = 1; - else - { - struct variable *v; - - v = try_variable_definition (fstart, p2, o_file, 0); - if (v != 0) - v->export = v_export; - else - { - unsigned int l; - const char *cp; - char *ap; - - /* Expand the line so we can use indirect and constructed - variable names in an export command. */ - cp = ap = allocated_variable_expand (p2); - - for (p = find_next_token (&cp, &l); p != 0; - p = find_next_token (&cp, &l)) - { - v = lookup_variable (p, l); - if (v == 0) - v = define_variable_loc (p, l, "", o_file, 0, fstart); - v->export = v_export; - } - - free (ap); - } - } - goto rule_complete; - } + int exporting = *p == 'u' ? 0 : 1; - if (word1eq ("unexport")) - { + /* (un)export by itself causes everything to be (un)exported. */ if (*p2 == '\0') - export_all_variables = 0; + export_all_variables = exporting; else { unsigned int l; - struct variable *v; const char *cp; char *ap; /* Expand the line so we can use indirect and constructed - variable names in an unexport command. */ + variable names in an (un)export command. */ cp = ap = allocated_variable_expand (p2); for (p = find_next_token (&cp, &l); p != 0; p = find_next_token (&cp, &l)) { - v = lookup_variable (p, l); + struct variable *v = lookup_variable (p, l); if (v == 0) v = define_variable_loc (p, l, "", o_file, 0, fstart); - - v->export = v_noexport; + v->export = exporting ? v_export : v_noexport; } free (ap); @@ -766,7 +786,7 @@ eval (struct ebuffer *ebuf, int set_default) goto rule_complete; } - skip_conditionals: + /* Handle the special syntax for vpath. */ if (word1eq ("vpath")) { const char *cp; @@ -791,6 +811,7 @@ eval (struct ebuffer *ebuf, int set_default) goto rule_complete; } + /* Handle include and variants. */ if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude")) { /* We have found an `include' line specifying a nested @@ -849,10 +870,6 @@ eval (struct ebuffer *ebuf, int set_default) goto rule_complete; } - if (try_variable_definition (fstart, p, o_file, 0)) - /* This line has been dealt with. */ - goto rule_complete; - /* This line starts with a tab but was not caught above because there was no preceding target, and the line might have been usable as a variable definition. But now we know it is definitely lossage. */ @@ -871,8 +888,6 @@ eval (struct ebuffer *ebuf, int set_default) { enum make_word_type wtype; - enum variable_origin v_origin; - int exported; char *cmdleft, *semip, *lb_next; unsigned int plen = 0; char *colonp; @@ -1038,31 +1053,8 @@ eval (struct ebuffer *ebuf, int set_default) p2 = variable_buffer + l; } - /* See if it's an "override" or "export" keyword; if so see if what - comes after it looks like a variable definition. */ - - wtype = get_next_mword (p2, NULL, &p, &wlen); - - v_origin = o_file; - exported = 0; - if (wtype == w_static) - { - if (word1eq ("override")) - { - v_origin = o_override; - wtype = get_next_mword (p+wlen, NULL, &p, &wlen); - } - else if (word1eq ("export")) - { - exported = 1; - wtype = get_next_mword (p+wlen, NULL, &p, &wlen); - } - } - - if (wtype != w_eol) - wtype = get_next_mword (p+wlen, NULL, NULL, NULL); - - if (wtype == w_varassign) + p2 = parse_var_assignment (p2, &vmod); + if (vmod.assign_v) { /* If there was a semicolon found, add it back, plus anything after it. */ @@ -1074,7 +1066,9 @@ eval (struct ebuffer *ebuf, int set_default) semip, strlen (semip)+1); p = variable_buffer + l; } - record_target_var (filenames, p, v_origin, exported, fstart); + record_target_var (filenames, p2, + vmod.override_v ? o_override : o_file, + &vmod, fstart); filenames = 0; continue; } @@ -1319,7 +1313,7 @@ remove_comments (char *line) The first line has already been read, and NAME is the name of the variable to be defined. The following lines remain to be read. */ -static void +static struct variable * do_define (char *name, unsigned int namelen, enum variable_origin origin, struct ebuffer *ebuf) { @@ -1382,6 +1376,8 @@ do_define (char *name, unsigned int namelen, if (--nlevels == 0) { + struct variable *v; + /* Define the variable. */ if (idx == 0) definition[0] = '\0'; @@ -1389,10 +1385,10 @@ do_define (char *name, unsigned int namelen, definition[idx - 1] = '\0'; /* Always define these variables in the global set. */ - define_variable_global (var, strlen (var), definition, - origin, 1, &defstart); + v = define_variable_global (var, strlen (var), definition, + origin, 1, &defstart); free (definition); - return; + return (v); } } } @@ -1413,9 +1409,6 @@ do_define (char *name, unsigned int namelen, /* No `endef'!! */ fatal (&defstart, _("missing `endef', unterminated `define'")); - - /* NOTREACHED */ - return; } /* Interpret conditional commands "ifdef", "ifndef", "ifeq", @@ -1763,7 +1756,7 @@ uniquize_deps (struct dep *chain) static void record_target_var (struct nameseq *filenames, char *defn, - enum variable_origin origin, int exported, + enum variable_origin origin, struct vmodifiers *vmod, const struct floc *flocp) { struct nameseq *nextf; @@ -1795,7 +1788,7 @@ record_target_var (struct nameseq *filenames, char *defn, p->variable.fileinfo = *flocp; /* I don't think this can fail since we already determined it was a variable definition. */ - v = parse_variable_definition (&p->variable, defn); + v = assign_variable_definition (&p->variable, defn); assert (v != 0); if (v->flavor == f_simple) @@ -1825,14 +1818,15 @@ record_target_var (struct nameseq *filenames, char *defn, current_variable_set_list = f->variables; v = try_variable_definition (flocp, defn, origin, 1); if (!v) - error (flocp, _("Malformed target-specific variable definition")); + fatal (flocp, _("Malformed target-specific variable definition")); current_variable_set_list = global; } /* Set up the variable to be *-specific. */ v->origin = origin; v->per_target = 1; - v->export = exported ? v_export : v_default; + v->private_var = vmod->private_v; + v->export = vmod->export_v ? v_export : v_default; /* If it's not an override, check to see if there was a command-line setting. If so, reset the value. */ diff --git a/tests/ChangeLog b/tests/ChangeLog index d9a0488..2f4ea71 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2009-05-25 Paul Smith <psmith@gnu.org> + + * scripts/features/export: Test new variable parsing abilities. + +2009-02-23 Ramon Garcia <ramon.garcia.f@gmail.com> + + * scripts/variables/private: Create a new suite of tests for 'private'. + 2007-11-04 Paul Smith <psmith@gnu.org> * scripts/functions/eval: Update error message for command -> recipe. diff --git a/tests/scripts/features/export b/tests/scripts/features/export index 38efe11..81bff0c 100644 --- a/tests/scripts/features/export +++ b/tests/scripts/features/export @@ -6,12 +6,7 @@ $details = ""; # The test driver cleans out our environment for us so we don't have to worry # about that here. -open(MAKEFILE,"> $makefile"); - -# The Contents of the MAKEFILE ... - -print MAKEFILE <<'EOMAKE'; - +&run_make_test(' FOO = foo BAR = bar BOZ = boz @@ -40,76 +35,44 @@ endif all: @echo "FOO=$(FOO) BAR=$(BAR) BAZ=$(BAZ) BOZ=$(BOZ) BITZ=$(BITZ) BOTZ=$(BOTZ)" @echo "FOO=$$FOO BAR=$$BAR BAZ=$$BAZ BOZ=$$BOZ BITZ=$$BITZ BOTZ=$$BOTZ" - -EOMAKE - -close(MAKEFILE); - -# TEST 0: basics - -&run_make_with_options($makefile,"",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); +', + '', "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 1: make sure vars inherited from the parent are exported $extraENV{FOO} = 1; -&run_make_with_options($makefile,"",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO=foo BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); +&run_make_test(undef, '', "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO=foo BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 2: global export. Explicit unexport takes precedence. -&run_make_with_options($makefile,"EXPORT_ALL=1",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "EXPORT_ALL=1" , + "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 3: global unexport. Explicit export takes precedence. -&run_make_with_options($makefile,"UNEXPORT_ALL=1",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); +&run_make_test(undef, "UNEXPORT_ALL=1", + "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 4: both: in the above makefile the unexport comes last so that rules. -&run_make_with_options($makefile,"EXPORT_ALL=1 UNEXPORT_ALL=1",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); +&run_make_test(undef, "EXPORT_ALL=1 UNEXPORT_ALL=1", + "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 5: test the pseudo target. -&run_make_with_options($makefile,"EXPORT_ALL_PSEUDO=1",&get_logfile,0); - -$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz -FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"; - -&compare_output($answer,&get_logfile(1)); - +&run_make_test(undef, "EXPORT_ALL_PSEUDO=1", + "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz +FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n"); # TEST 6: Test the expansion of variables inside export -$makefile2 = &get_tmpfile; - -open(MAKEFILE, "> $makefile2"); - -print MAKEFILE <<'EOF'; - +&run_make_test(' foo = f-ok bar = b-ok @@ -125,24 +88,12 @@ export $(B)ar all: @echo foo=$(foo) bar=$(bar) @echo foo=$$foo bar=$$bar - -EOF - -close(MAKEFILE); - -&run_make_with_options($makefile2,"",&get_logfile,0); -$answer = "foo=f-ok bar=b-ok\nfoo=f-ok bar=b-ok\n"; -&compare_output($answer,&get_logfile(1)); - +', + "", "foo=f-ok bar=b-ok\nfoo=f-ok bar=b-ok\n"); # TEST 7: Test the expansion of variables inside unexport -$makefile3 = &get_tmpfile; - -open(MAKEFILE, "> $makefile3"); - -print MAKEFILE <<'EOF'; - +&run_make_test(' foo = f-ok bar = b-ok @@ -160,24 +111,12 @@ unexport $(B)ar all: @echo foo=$(foo) bar=$(bar) @echo foo=$$foo bar=$$bar - -EOF - -close(MAKEFILE); - -&run_make_with_options($makefile3,"",&get_logfile,0); -$answer = "foo=f-ok bar=b-ok\nfoo= bar=\n"; -&compare_output($answer,&get_logfile(1)); - +', + '', "foo=f-ok bar=b-ok\nfoo= bar=\n"); # TEST 7: Test exporting multiple variables on the same line -$makefile4 = &get_tmpfile; - -open(MAKEFILE, "> $makefile4"); - -print MAKEFILE <<'EOF'; - +&run_make_test(' A = a B = b C = c @@ -196,23 +135,14 @@ export F G H I J export D E $(SOME) all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J -EOF - -close(MAKEFILE); - -&run_make_with_options($makefile4,"",&get_logfile,0); -$answer = "A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j\n"; -&compare_output($answer,&get_logfile(1)); - +', + '', "A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j\n"); # TEST 8: Test unexporting multiple variables on the same line -$makefile5 = &get_tmpfile; - -open(MAKEFILE, "> $makefile5"); - -print MAKEFILE <<'EOF'; +@extraENV{qw(A B C D E F G H I J)} = qw(1 2 3 4 5 6 7 8 9 10); +&run_make_test(' A = a B = b C = c @@ -231,16 +161,26 @@ unexport F G H I J unexport D E $(SOME) all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J -EOF - -close(MAKEFILE); - -@extraENV{qw(A B C D E F G H I J)} = qw(1 2 3 4 5 6 7 8 9 10); - -&run_make_with_options($makefile5,"",&get_logfile,0); -$answer = "A= B= C= D= E= F= G= H= I= J=\n"; -&compare_output($answer,&get_logfile(1)); - +', + '', "A= B= C= D= E= F= G= H= I= J=\n"); + +# TEST 9: Check setting a variable named "export" + +&run_make_test(' +export = 123 +export export +export export = 456 +a: ; @echo "\$$(export)=$(export) / \$$export=$$export" +', + '', "\$(export)=456 / \$export=456\n"); + +# TEST 9: Check "export" as a target + +&run_make_test(' +a: export +export: ; @echo "$@" +', + '', "export\n"); # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/targetvars b/tests/scripts/features/targetvars index e2e9c90..ad0766c 100644 --- a/tests/scripts/features/targetvars +++ b/tests/scripts/features/targetvars @@ -6,9 +6,7 @@ Create a makefile containing various flavors of target-specific variable values, override and non-override, and using various variable expansion rules, semicolon interference, etc."; -open(MAKEFILE,"> $makefile"); - -print MAKEFILE <<'EOF'; +run_make_test(' SHELL = /bin/sh export FOO = foo export BAR = bar @@ -17,17 +15,17 @@ one two: ; @echo $(FOO) $(BAR) two: BAR = two three: ; BAR=1000 @echo $(FOO) $(BAR) -# Some things that shouldn't be target vars +# Some things that shouldn not be target vars funk : override funk : override adelic adelic override : ; echo $@ # Test per-target recursive variables four:FOO=x four:VAR$(FOO)=ok -four: ; @echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)' +four: ; @echo "$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)" five:FOO=x five six : VAR$(FOO)=good -five six: ;@echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx) $(VARfoo)' +five six: ;@echo "$(FOO) $(VAR$(FOO)) $(VAR) $(VARx) $(VARfoo)" # Test per-target variable inheritance seven: eight seven eight: ; @echo $@: $(FOO) $(BAR) @@ -41,8 +39,8 @@ nine-a: export BAZ = baz nine-a: ; @echo $$BAZ # Test = escaping EQ = = -ten: one\=two -ten: one \= two +ten: one$(EQ)two +ten: one $(EQ) two ten one$(EQ)two $(EQ):;@echo $@ .PHONY: one two three four five six seven eight nine ten $(EQ) one$(EQ)two # Test target-specific vars with pattern/suffix rules @@ -54,92 +52,58 @@ foo.q : RVAR += rvar %.r %.s %.t: ; @echo $(QVAR) $(RVAR) $(SVAR) $(TVAR) foo.r : RVAR += rvar foo.t : TVAR := $(QVAR) -EOF - -close(MAKEFILE); - -# TEST #1 - -&run_make_with_options($makefile, "one two three", &get_logfile); -$answer = "one bar\nfoo two\nBAR=1000\nfoo bar\n"; -&compare_output($answer,&get_logfile(1)); +', + "one two three", "one bar\nfoo two\nBAR=1000\nfoo bar\n"); # TEST #2 -&run_make_with_options($makefile, "one two FOO=1 BAR=2", &get_logfile); -$answer = "one 2\n1 2\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "one two FOO=1 BAR=2", "one 2\n1 2\n"); # TEST #3 -&run_make_with_options($makefile, "four", &get_logfile); -$answer = "x ok ok\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "four", "x ok ok\n"); # TEST #4 -&run_make_with_options($makefile, "seven", &get_logfile); -$answer = "eight: seven eight\nseven: seven seven\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "seven", "eight: seven eight\nseven: seven seven\n"); # TEST #5 -&run_make_with_options($makefile, "nine", &get_logfile); -$answer = "wallace bar wallace bar\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "nine", "wallace bar wallace bar\n"); # TEST #5-a -&run_make_with_options($makefile, "nine-a", &get_logfile); -$answer = "baz\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "nine-a", "baz\n"); # TEST #6 -&run_make_with_options($makefile, "ten", &get_logfile); -$answer = "one=two\none bar\n=\nfoo two\nten\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "ten", "one=two\none bar\n=\nfoo two\nten\n"); # TEST #6 -&run_make_with_options($makefile, "foo.q bar.q", &get_logfile); -$answer = "qvar = rvar\nqvar =\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "foo.q bar.q", "qvar = rvar\nqvar =\n"); # TEST #7 -&run_make_with_options($makefile, "foo.t bar.s", &get_logfile); -$answer = "qvar = qvar\nqvar =\n"; -&compare_output($answer,&get_logfile(1)); +run_make_test(undef, "foo.t bar.s", "qvar = qvar\nqvar =\n"); # TEST #8 # For PR/1378: Target-specific vars don't inherit correctly -$makefile2 = &get_tmpfile; - -open(MAKEFILE,"> $makefile2"); -print MAKEFILE <<'EOF'; +run_make_test(' foo: FOO = foo bar: BAR = bar foo: bar bar: baz baz: ; @echo $(FOO) $(BAR) -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile2", "", &get_logfile); -$answer = "foo bar\n"; -&compare_output($answer, &get_logfile(1)); +', "", "foo bar\n"); # TEST #9 # For PR/1380: Using += assignment in target-specific variables sometimes fails # Also PR/1831 -$makefile3 = &get_tmpfile; - -open(MAKEFILE,"> $makefile3"); -print MAKEFILE <<'EOF'; +run_make_test(' .PHONY: all one all: FOO += baz all: one; @echo $(FOO) @@ -149,43 +113,27 @@ FOO = bar one: FOO += biz one: FOO += boz one: ; @echo $(FOO) -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile3", "", &get_logfile); -$answer = "bar baz biz boz\nbar baz\n"; -&compare_output($answer, &get_logfile(1)); +', + '', "bar baz biz boz\nbar baz\n"); # Test #10 -&run_make_with_options("$makefile3", "one", &get_logfile); -$answer = "bar biz boz\n"; -&compare_output($answer, &get_logfile(1)); +run_make_test(undef, 'one', "bar biz boz\n"); # Test #11 # PR/1709: Test semicolons in target-specific variable values -$makefile4 = &get_tmpfile; - -open(MAKEFILE, "> $makefile4"); -print MAKEFILE <<'EOF'; +run_make_test(' foo : FOO = ; ok -foo : ; @echo '$(FOO)' -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile4", "", &get_logfile); -$answer = "; ok\n"; -&compare_output($answer, &get_logfile(1)); +foo : ; @echo "$(FOO)" +', + '', "; ok\n"); # Test #12 # PR/2020: More hassles with += target-specific vars. I _really_ think # I nailed it this time :-/. -$makefile5 = &get_tmpfile; - -open(MAKEFILE, "> $makefile5"); -print MAKEFILE <<'EOF'; +run_make_test(' .PHONY: a BLAH := foo @@ -195,20 +143,13 @@ a: ; @$(COMMAND) a: BLAH := bar a: COMMAND += snafu $(BLAH) -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile5", "", &get_logfile); -$answer = "bar snafu bar\n"; -&compare_output($answer, &get_logfile(1)); +', + '', "bar snafu bar\n"); # Test #13 # Test double-colon rules with target-specific variable values -$makefile6 = &get_tmpfile; - -open(MAKEFILE, "> $makefile6"); -print MAKEFILE <<'EOF'; +run_make_test(' W = bad X = bad foo: W = ok @@ -224,48 +165,30 @@ Z = nopat ifdef PATTERN fo% : Z = pat endif - -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile6", "foo", &get_logfile); -$answer = "ok ok foo nopat\nok ok foo nopat\n"; -&compare_output($answer, &get_logfile(1)); +', + 'foo', "ok ok foo nopat\nok ok foo nopat\n"); # Test #14 # Test double-colon rules with target-specific variable values and # inheritance -&run_make_with_options("$makefile6", "bar", &get_logfile); -$answer = "ok ok bar nopat\nok ok bar nopat\n"; -&compare_output($answer, &get_logfile(1)); +run_make_test(undef, 'bar', "ok ok bar nopat\nok ok bar nopat\n"); # Test #15 # Test double-colon rules with pattern-specific variable values -&run_make_with_options("$makefile6", "foo PATTERN=yes", &get_logfile); -$answer = "ok ok foo pat\nok ok foo pat\n"; -&compare_output($answer, &get_logfile(1)); - +run_make_test(undef, 'foo PATTERN=yes', "ok ok foo pat\nok ok foo pat\n"); # Test #16 # Test target-specific variables with very long command line # (> make default buffer length) -$makefile7 = &get_tmpfile; - -open(MAKEFILE, "> $makefile7"); -print MAKEFILE <<'EOF'; +run_make_test(' base_metals_fmd_reports.sun5 base_metals_fmd_reports CreateRealPositions CreateMarginFunds deals_changed_since : BUILD_OBJ=$(shell if [ -f "build_information.generate" ]; then echo "$(OBJ_DIR)/build_information.o"; else echo "no build information"; fi ) deals_changed_since: ; @echo $(BUILD_OBJ) - -EOF -close(MAKEFILE); - -&run_make_with_options("$makefile7", '', &get_logfile); -$answer = "no build information\n"; -&compare_output($answer, &get_logfile(1)); +', + '', "no build information\n"); # TEST #17 @@ -286,8 +209,7 @@ foo.x: FOOVAR = bar rules.mk : MYVAR = foo .INTERMEDIATE: foo.x rules.mk ', - '-I t1', - 'MYVAR= FOOVAR=bar ALLVAR=xxx'); + '-I t1', 'MYVAR= FOOVAR=bar ALLVAR=xxx'); rmfiles('t1/rules.mk'); rmdir('t1'); @@ -301,7 +223,20 @@ run_make_test(" VAR := \$\$FOO foo: VAR += BAR foo: ; \@echo '\$(VAR)'", - '', - '$FOO BAR'); + '', '$FOO BAR'); + +# TEST #19: Test define/endef variables as target-specific vars + +# run_make_test(' +# define b +# @echo global +# endef +# a: define b +# @echo local +# endef + +# a: ; $(b) +# ', +# '', "local\n"); 1; diff --git a/tests/scripts/variables/private b/tests/scripts/variables/private new file mode 100644 index 0000000..b4baf5f --- /dev/null +++ b/tests/scripts/variables/private @@ -0,0 +1,78 @@ +# -*-perl-*- + +$description = "Test 'private' variables."; + +$details = ""; + +# 1: Simple verification that private variables are not inherited +&run_make_test(' +a: +F = g +a: F = a +b: private F = b + +a b c: ; @echo $@: F=$(F) +a: b +b: c +', + '', "c: F=a\nb: F=b\na: F=a\n"); + +# 2: Again, but this time we start with "b" so "a"'s variable is not in scope +&run_make_test(undef, 'b', "c: F=g\nb: F=b\n"); + +# 3: Verification with pattern-specific variables +&run_make_test(' +t.a: + +F1 = g +F2 = g +%.a: private F1 = a +%.a: F2 = a + +t.a t.b: ; @echo $@: F1=$(F1) / F2=$(F2) +t.a: t.b +', + '', "t.b: F1=g / F2=a\nt.a: F1=a / F2=a\n"); + +# 4: Test private global variables +&run_make_test(' +a: +private F = g +G := $(F) +a: +b: F = b + +a b: ; @echo $@: F=$(F) / G=$(G) +a: b +', + '', "b: F=b / G=g\na: F= / G=g\n"); + +# 5: Multiple conditions on the same variable. Test export. +delete $ENV{'_X'}; +&run_make_test(' +_X = x +a: export override private _X = a +a: ; @echo _X=$(_X) / _X=$$_X +', + '', "_X=a / _X=a"); + +# 6: Test override. +&run_make_test(undef, '_X=c', "_X=a / _X=a\n"); + +# 7: Ensure keywords still work as targets +&run_make_test(' +a: export override private foo bar +foo bar export override private: ; @echo $@ +', + '', "export\noverride\nprivate\nfoo\nbar\n"); + +# 8: Ensure keywords still work as variables +&run_make_test(' +private = g +a: private = a +a: b +a b: ; @echo $@=$(private) +', + '', "b=a\na=a\n"); + +1; @@ -138,7 +138,7 @@ variable_hash_cmp (const void *xv, const void *yv) static struct variable_set global_variable_set; static struct variable_set_list global_setlist - = { 0, &global_variable_set }; + = { 0, &global_variable_set, 0 }; struct variable_set_list *current_variable_set_list = &global_setlist; /* Implement variables. */ @@ -221,6 +221,7 @@ define_variable_in_set (const char *name, unsigned int length, v->exp_count = 0; v->per_target = 0; v->append = 0; + v->private_var = 0; v->export = v_default; v->exportable = 1; @@ -340,6 +341,7 @@ lookup_variable (const char *name, unsigned int length) { const struct variable_set_list *setlist; struct variable var_key; + int is_parent = 0; var_key.name = (char *) name; var_key.length = length; @@ -351,8 +353,10 @@ lookup_variable (const char *name, unsigned int length) struct variable *v; v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key); - if (v) + if (v && (!is_parent || !v->private_var)) return v->special ? lookup_special_var (v) : v; + + is_parent |= setlist->next_is_parent; } #ifdef VMS @@ -463,6 +467,7 @@ initialize_file_variables (struct file *file, int reading) { initialize_file_variables (file->double_colon, reading); l->next = file->double_colon->variables; + l->next_is_parent = 0; return; } @@ -473,6 +478,7 @@ initialize_file_variables (struct file *file, int reading) initialize_file_variables (file->parent, reading); l->next = file->parent->variables; } + l->next_is_parent = 1; /* If we're not reading makefiles and we haven't looked yet, see if we can find pattern variables for this target. */ @@ -518,6 +524,7 @@ initialize_file_variables (struct file *file, int reading) /* Also mark it as a per-target and copy export status. */ v->per_target = p->variable.per_target; v->export = p->variable.export; + v->private_var = p->variable.private_var; } while ((p = lookup_pattern_var (p, file->name)) != 0); @@ -531,7 +538,9 @@ initialize_file_variables (struct file *file, int reading) if (file->pat_variables != 0) { file->pat_variables->next = l->next; + file->pat_variables->next_is_parent = l->next_is_parent; l->next = file->pat_variables; + l->next_is_parent = 0; } } @@ -552,6 +561,7 @@ create_new_variable_set (void) xmalloc (sizeof (struct variable_set_list)); setlist->set = set; setlist->next = current_variable_set_list; + setlist->next_is_parent = 0; return setlist; } @@ -623,6 +633,7 @@ pop_variable_scope (void) set = global_setlist.set; global_setlist.set = setlist->set; global_setlist.next = setlist->next; + global_setlist.next_is_parent = setlist->next_is_parent; } /* Free the one we no longer need. */ @@ -1250,66 +1261,32 @@ do_variable_definition (const struct floc *flocp, const char *varname, return v->special ? set_special_var (v) : v; } -/* Try to interpret LINE (a null-terminated string) as a variable definition. +/* Parse P (a null-terminated string) as a variable definition. - ORIGIN may be o_file, o_override, o_env, o_env_override, - or o_command specifying that the variable definition comes - from a makefile, an override directive, the environment with - or without the -e switch, or the command line. + If it is not a variable definition, return NULL. - See the comments for parse_variable_definition(). + If it is a variable definition, return a pointer to the char after the + assignment token and set *FLAVOR to the type of variable assignment. */ - If LINE was recognized as a variable definition, a pointer to its `struct - variable' is returned. If LINE is not a variable definition, NULL is - returned. */ - -struct variable * -parse_variable_definition (struct variable *v, char *line) +char * +parse_variable_definition (const char *p, enum variable_flavor *flavor) { - register int c; - register char *p = line; - register char *beg; - register char *end; - enum variable_flavor flavor = f_bogus; - char *name; + int wspace = 0; + + p = next_token (p); while (1) { - c = *p++; + int c = *p++; + + /* If we find a comment or EOS, it's not a variable definition. */ if (c == '\0' || c == '#') - return 0; - if (c == '=') - { - end = p - 1; - flavor = f_recursive; - break; - } - else if (c == ':') - if (*p == '=') - { - end = p++ - 1; - flavor = f_simple; - break; - } - else - /* A colon other than := is a rule line, not a variable defn. */ - return 0; - else if (c == '+' && *p == '=') - { - end = p++ - 1; - flavor = f_append; - break; - } - else if (c == '?' && *p == '=') - { - end = p++ - 1; - flavor = f_conditional; - break; - } - else if (c == '$') + return NULL; + + if (c == '$') { - /* This might begin a variable expansion reference. Make sure we - don't misrecognize chars inside the reference as =, := or +=. */ + /* This begins a variable expansion reference. Make sure we don't + treat chars inside the reference as assignment tokens. */ char closeparen; int count; c = *p++; @@ -1318,7 +1295,8 @@ parse_variable_definition (struct variable *v, char *line) else if (c == '{') closeparen = '}'; else - continue; /* Nope. */ + /* '$$' or '$X'. Either way, nothing special to do here. */ + continue; /* P now points past the opening paren or brace. Count parens or braces until it is matched. */ @@ -1333,15 +1311,84 @@ parse_variable_definition (struct variable *v, char *line) break; } } + continue; + } + + /* If we find whitespace skip it, and remember we found it. */ + if (isblank ((unsigned char)c)) + { + wspace = 1; + p = next_token (p); + c = *p++; + } + + + if (c == '=') + { + *flavor = f_recursive; + return (char *)p; } + /* Match assignment variants (:=, +=, ?=) */ + else if (*p == '=') + { + switch (c) + { + case ':': + *flavor = f_simple; + break; + case '+': + *flavor = f_append; + break; + case '?': + *flavor = f_conditional; + break; + default: + /* If we skipped whitespace, non-assignments means no var. */ + if (wspace) + return NULL; + + /* Might be assignment, or might be $= or #=. Check. */ + continue; + } + return (char *)++p; + } + else if (c == ':') + /* A colon other than := is a rule line, not a variable defn. */ + return NULL; + + /* If we skipped whitespace, non-assignments means no var. */ + if (wspace) + return NULL; } - v->flavor = flavor; + + return (char *)p; +} + +/* Try to interpret LINE (a null-terminated string) as a variable definition. + + If LINE was recognized as a variable definition, a pointer to its `struct + variable' is returned. If LINE is not a variable definition, NULL is + returned. */ + +struct variable * +assign_variable_definition (struct variable *v, char *line) +{ + char *beg; + char *end; + enum variable_flavor flavor; + char *name; beg = next_token (line); + line = parse_variable_definition (beg, &flavor); + if (!line) + return NULL; + + end = line - (flavor == f_recursive ? 1 : 2); while (end > beg && isblank ((unsigned char)end[-1])) --end; - p = next_token (p); - v->value = p; + line = next_token (line); + v->value = line; + v->flavor = flavor; /* Expand the name, so "$(foo)bar = baz" works. */ name = alloca (end - beg + 1); @@ -1362,7 +1409,7 @@ parse_variable_definition (struct variable *v, char *line) from a makefile, an override directive, the environment with or without the -e switch, or the command line. - See the comments for parse_variable_definition(). + See the comments for assign_variable_definition(). If LINE was recognized as a variable definition, a pointer to its `struct variable' is returned. If LINE is not a variable definition, NULL is @@ -1380,7 +1427,7 @@ try_variable_definition (const struct floc *flocp, char *line, else v.fileinfo.filenm = 0; - if (!parse_variable_definition (&v, line)) + if (!assign_variable_definition (&v, line)) return 0; vp = do_variable_definition (flocp, v.name, v.value, @@ -1429,6 +1476,8 @@ print_variable (const void *item, void *arg) } fputs ("# ", stdout); fputs (origin, stdout); + if (v->private_var) + fputs (" private", stdout); if (v->fileinfo.filenm) printf (_(" (from `%s', line %lu)"), v->fileinfo.filenm, v->fileinfo.lineno); @@ -1440,7 +1489,7 @@ print_variable (const void *item, void *arg) printf ("define %s\n%s\nendef\n", v->name, v->value); else { - register char *p; + char *p; printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":"); @@ -59,10 +59,12 @@ struct variable variable. */ unsigned int conditional:1; /* Nonzero if set with a ?=. */ unsigned int per_target:1; /* Nonzero if a target-specific variable. */ - unsigned int special:1; /* Nonzero if this is a special variable. */ + unsigned int special:1; /* Nonzero if this is a special variable. */ unsigned int exportable:1; /* Nonzero if the variable _could_ be exported. */ unsigned int expanding:1; /* Nonzero if currently being expanded. */ + unsigned int private_var:1; /* Nonzero avoids inheritance of this + target-specific variable. */ unsigned int exp_count:EXP_COUNT_BITS; /* If >1, allow this many self-referential expansions. */ @@ -92,6 +94,7 @@ struct variable_set_list { struct variable_set_list *next; /* Link in the chain. */ struct variable_set *set; /* Variable set. */ + int next_is_parent; /* True if next is a parent target. */ }; /* Structure used for pattern-specific variables. */ @@ -151,7 +154,9 @@ struct variable *do_variable_definition (const struct floc *flocp, enum variable_origin origin, enum variable_flavor flavor, int target_var); -struct variable *parse_variable_definition (struct variable *v, char *line); +char *parse_variable_definition (const char *line, + enum variable_flavor *flavor); +struct variable *assign_variable_definition (struct variable *v, char *line); struct variable *try_variable_definition (const struct floc *flocp, char *line, enum variable_origin origin, int target_var); |