From 5b4d419476e9fbda8ea26017f6ec15956d103ed9 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Tue, 26 May 2009 01:31:40 +0000 Subject: Add 'private' variable modifier, feature submitted by Ramon Garcia. Rework the parser for variables to allow multiple modifiers and also allow for variables and targets with modifier names, like "export" and "private". --- ChangeLog | 30 +++ NEWS | 30 ++- doc/make.texi | 67 ++++--- read.c | 374 +++++++++++++++++++------------------- tests/ChangeLog | 8 + tests/scripts/features/export | 158 +++++----------- tests/scripts/features/targetvars | 171 ++++++----------- tests/scripts/variables/private | 78 ++++++++ variable.c | 169 +++++++++++------ variable.h | 9 +- 10 files changed, 585 insertions(+), 509 deletions(-) create mode 100644 tests/scripts/variables/private diff --git a/ChangeLog b/ChangeLog index 7b6ec17..803aa03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2009-05-25 Paul Smith + + 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 * 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 + + 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 * configure.in: Check for strndup(). diff --git a/NEWS b/NEWS index 15d3d6e..8004ab9 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/read.c b/read.c index eb4b212..ec6d6af 100644 --- a/read.c +++ b/read.c @@ -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 + + * scripts/features/export: Test new variable parsing abilities. + +2009-02-23 Ramon Garcia + + * scripts/variables/private: Create a new suite of tests for 'private'. + 2007-11-04 Paul Smith * 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; diff --git a/variable.c b/variable.c index 4dafab1..92a96ec 100644 --- a/variable.c +++ b/variable.c @@ -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 ? "+" : "" : ":"); diff --git a/variable.h b/variable.h index 8a80b76..d5b194f 100644 --- a/variable.h +++ b/variable.h @@ -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); -- cgit v1.2.3