diff --git a/ChangeLog b/ChangeLog index 701501e7..f9d02713 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,49 @@ +Sun Feb 27 22:03:36 2005 Boris Kolpackov + + Implementation of the second expansion in explicit rules, + static pattern rules and implicit rules. + + * read.c (eval): Refrain from chopping up rule's dependencies. + Store them in a struct dep as a single dependency line. Remove + the code that implements SySV-style automatic variables. + + * read.c (record_files): Adjust the code that handles static + pattern rules to expand all percents instead of only the first + one. Reverse the order in which dependencies are stored so that + when the second expansion reverses them again they appear in + the makefile order (with some exceptions, see comments in + the code). Remove the code that implements SySV-style automatic + variables. + + * file.c (snap_deps): Implement the second expansion and chopping + of dependency lines for explicit rules. + + * implicit.c (struct idep): Define an auxiliary data type to hold + implicit rule's dependencies after stem substitution and + expansion. + + * implicit.c (free_idep_chain): Implement. + + * implicit.c (get_next_word): Implement helper function for + parsing implicit rule's dependency lines into words taking + into account variable expansion requests. Used in the stem + splitting code. + + * implicit.c (pattern_search): Implement the second expansion + for implicit rules. Also fixes bug #12091. + + * commands.h (set_file_variables): Declare. + * commands.c (set_file_variables): Remove static specifier. + + * dep.h (free_dep_chain): Declare. + * misc.c (free_dep_chain): Implement. + + * variable.h (variable_expand_for_file): Declare. + * expand.c (variable_expand_for_file): Remove static specifier. + + * make.h (strip_whitespace): Declare. + * function.c (strip_whitespace): Remove static specifier. + 2005-02-24 Jonathan Grant * configure.in: Add MinGW configuration options, and extra w32 code diff --git a/commands.c b/commands.c index 6fdb5bbb..97ec9816 100644 --- a/commands.c +++ b/commands.c @@ -38,7 +38,7 @@ extern int getpid (); /* Set FILE's automatic variables up. */ -static void +void set_file_variables (struct file *file) { struct dep *d; diff --git a/commands.h b/commands.h index edec9103..3d62ee89 100644 --- a/commands.h +++ b/commands.h @@ -40,3 +40,4 @@ extern void execute_file_commands PARAMS ((struct file *file)); extern void print_commands PARAMS ((struct commands *cmds)); extern void delete_child_targets PARAMS ((struct child *child)); extern void chop_commands PARAMS ((struct commands *cmds)); +extern void set_file_variables PARAMS ((struct file *file)); diff --git a/dep.h b/dep.h index 4c9a1523..6e6adcef 100644 --- a/dep.h +++ b/dep.h @@ -72,6 +72,7 @@ extern char *dep_name (); #endif extern struct dep *copy_dep_chain PARAMS ((struct dep *d)); +extern void free_dep_chain PARAMS ((struct dep *d)); extern struct dep *read_all_makefiles PARAMS ((char **makefiles)); extern int eval_buffer PARAMS ((char *buffer)); extern int update_goal_chain PARAMS ((struct dep *goals)); diff --git a/expand.c b/expand.c index 4440192e..372a0f59 100644 --- a/expand.c +++ b/expand.c @@ -440,7 +440,7 @@ expand_argument (const char *str, const char *end) /* Expand LINE for FILE. Error messages refer to the file and line where FILE's commands were found. Expansion uses FILE's variable set list. */ -static char * +char * variable_expand_for_file (char *line, struct file *file) { char *result; diff --git a/file.c b/file.c index ae997451..70e53588 100644 --- a/file.c +++ b/file.c @@ -416,12 +416,14 @@ snap_deps (void) { register struct file *f; register struct file *f2; - register struct dep *d; + register struct dep *d, *d1; register struct file **file_slot_0; register struct file **file_slot; register struct file **file_end; - /* Enter each dependency name as a file. */ + /* Perform second expansion and enter each dependency + name as a file. */ + /* We must use hash_dump (), because within this loop we might add new files to the table, possibly causing an in-situ table expansion. */ @@ -429,16 +431,99 @@ snap_deps (void) file_end = file_slot_0 + files.ht_fill; for (file_slot = file_slot_0; file_slot < file_end; file_slot++) for (f2 = *file_slot; f2 != 0; f2 = f2->prev) - for (d = f2->deps; d != 0; d = d->next) - if (d->name != 0) - { - d->file = lookup_file (d->name); - if (d->file == 0) - d->file = enter_file (d->name); - else - free (d->name); - d->name = 0; - } + { + struct dep *new = 0; + struct dep *old = f2->deps; + unsigned int last_dep_has_cmds = f2->updating; + + f2->updating = 0; + f2->deps = 0; + + /* We are going to do second expansion so initialize file + variables for the file. */ + initialize_file_variables (f2, 0); + + for (d = old; d != 0; d = d->next) + { + if (d->name != 0) + { + char *p; + struct dep **d_ptr; + + set_file_variables (f2); + + p = variable_expand_for_file (d->name, f2); + + /* Parse the dependencies. */ + new = (struct dep *) + multi_glob ( + parse_file_seq (&p, '|', sizeof (struct dep), 1), + sizeof (struct dep)); + + if (*p) + { + /* Files that follow '|' are special prerequisites that + need only exist in order to satisfy the dependency. + Their modification times are irrelevant. */ + + struct dep *d; + for (d_ptr = &new; *d_ptr; d_ptr = &(*d_ptr)->next) + ; + ++p; + + *d_ptr = (struct dep *) + multi_glob ( + parse_file_seq (&p, '\0', sizeof (struct dep), 1), + sizeof (struct dep)); + + for (d = *d_ptr; d != 0; d = d->next) + d->ignore_mtime = 1; + } + + /* Enter them as files. */ + for (d1 = new; d1 != 0; d1 = d1->next) + { + d1->file = lookup_file (d1->name); + if (d1->file == 0) + d1->file = enter_file (d1->name); + else + free (d1->name); + d1->name = 0; + } + + /* Add newly parsed deps to f2->deps. If this is the last + dependency line and this target has commands then put + it in front so the last dependency line (the one with + commands) ends up being the first. This is important + because people expect $< to hold first prerequisite + from the rule with commands. If it is not the last + dependency line or the rule does not have commands + then link it at the end so it appears in makefile + order. */ + + if (new != 0) + { + if (d->next == 0 && last_dep_has_cmds) + { + for (d_ptr = &new; *d_ptr; d_ptr = &(*d_ptr)->next) + ; + + *d_ptr = f2->deps; + f2->deps = new; + } + else + { + for (d_ptr = &(f2->deps); *d_ptr; d_ptr = &(*d_ptr)->next) + ; + + *d_ptr = new; + } + } + } + } + + free_dep_chain (old); + } free (file_slot_0); for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev) diff --git a/function.c b/function.c index bbb86d2e..b344e66a 100644 --- a/function.c +++ b/function.c @@ -706,7 +706,7 @@ func_words (char *o, char **argv, const char *funcname UNUSED) * If the string is empty or contains nothing but whitespace, endpp will be * begpp-1. */ -static char * +char * strip_whitespace (const char **begpp, const char **endpp) { while (*begpp <= *endpp && isspace ((unsigned char)**begpp)) diff --git a/implicit.c b/implicit.c index 10b41ffe..7c1f4b73 100644 --- a/implicit.c +++ b/implicit.c @@ -22,9 +22,13 @@ Boston, MA 02111-1307, USA. */ #include "rule.h" #include "dep.h" #include "debug.h" +#include "variable.h" +#include "job.h" /* struct child, used inside commands.h */ +#include "commands.h" /* set_file_variables */ -static int pattern_search PARAMS ((struct file *file, int archive, unsigned int depth, - unsigned int recursions)); +static int +pattern_search PARAMS ((struct file *file, int archive, + unsigned int depth, unsigned int recursions)); /* For a FILE which has no commands specified, try to figure out some from the implicit pattern rules. @@ -61,6 +65,126 @@ try_implicit_rule (struct file *file, unsigned int depth) } +/* Struct idep captures information about implicit prerequisites + that come from implicit rules. */ +struct idep +{ + struct idep *next; /* struct dep -compatible interface */ + char *name; /* name of the prerequisite */ + struct file *intermediate_file; /* intermediate file, 0 otherwise */ + char *intermediate_pattern; /* pattern for intermediate file */ + unsigned char had_stem; /* had % substituted with stem */ + unsigned char ignore_mtime; /* ignore_mtime flag */ +}; + +static void +free_idep_chain (struct idep* p) +{ + register struct idep* n; + register struct file *f; + + for (; p != 0; p = n) + { + n = p->next; + + if (p->name) + { + free (p->name); + + f = p->intermediate_file; + + if (f != 0 + && (f->stem < f->name + || f->stem > f->name + strlen (f->name))) + free (f->stem); + } + + free (p); + } +} + + +/* Scans the BUFFER for the next word with whitespace as a separator. + Returns the pointer to the beginning of the word. LENGTH hold the + length of the word. */ + +static char * +get_next_word (char *buffer, unsigned int *length) +{ + char *p = buffer, *beg; + char c; + + /* Skip any leading whitespace. */ + while (isblank ((unsigned char)*p)) + ++p; + + beg = p; + c = *(p++); + + if (c == '\0') + return 0; + + + /* We already found the first value of "c", above. */ + while (1) + { + char closeparen; + int count; + + switch (c) + { + case '\0': + case ' ': + case '\t': + goto done_word; + + case '$': + c = *(p++); + if (c == '$') + break; + + /* This is a variable reference, so read it to the matching + close paren. */ + + if (c == '(') + closeparen = ')'; + else if (c == '{') + closeparen = '}'; + else + /* This is a single-letter variable reference. */ + break; + + for (count = 0; *p != '\0'; ++p) + { + if (*p == c) + ++count; + else if (*p == closeparen && --count < 0) + { + ++p; + break; + } + } + break; + + case '|': + goto done; + + default: + break; + } + + c = *(p++); + } + done_word: + --p; + + done: + if (length) + *length = p - beg; + + return beg; +} + /* Search the pattern rules for a rule with an existing dependency to make FILE. If a rule is found, the appropriate commands and deps are put in FILE and 1 is returned. If not, 0 is returned. @@ -93,20 +217,13 @@ pattern_search (struct file *file, int archive, except during a recursive call. */ struct file *intermediate_file = 0; - /* List of dependencies found recursively. */ - struct file **intermediate_files - = (struct file **) xmalloc (max_pattern_deps * sizeof (struct file *)); + /* This linked list records all the prerequisites actually + found for a rule along with some other useful information + (see struct idep for details). */ + struct idep* deps = 0; - /* List of the patterns used to find intermediate files. */ - char **intermediate_patterns - = (char **) alloca (max_pattern_deps * sizeof (char *)); - - /* This buffer records all the dependencies actually found for a rule. */ - char **found_files = (char **) alloca (max_pattern_deps * sizeof (char *)); - /* Remember whether the associated dep has an "ignore_mtime" flag set. */ - unsigned char *found_files_im = (unsigned char *) alloca (max_pattern_deps * sizeof (unsigned char)); - /* Number of dep names now in FOUND_FILES. */ - unsigned int deps_found = 0; + /* 1 if we need to remove explicit prerequisites, 0 otherwise. */ + unsigned int remove_explicit_deps = 0; /* Names of possible dependencies are constructed in this buffer. */ register char *depname = (char *) alloca (namelen + max_pattern_dep_length); @@ -146,9 +263,13 @@ pattern_search (struct file *file, int archive, register unsigned int i = 0; /* uninit checks OK */ register struct rule *rule; - register struct dep *dep; + register struct dep *dep, *expl_d; - char *p, *vp; + char *p, *vname; + + struct idep *d; + struct idep **id_ptr; + struct dep **d_ptr; #ifndef NO_ARCHIVES if (archive || ar_name (filename)) @@ -295,19 +416,27 @@ pattern_search (struct file *file, int archive, tryrules[i] = 0; } + /* We are going to do second expansion so initialize file variables + for the rule. */ + initialize_file_variables (file, 0); + /* Try each rule once without intermediate files, then once with them. */ for (intermed_ok = 0; intermed_ok == !!intermed_ok; ++intermed_ok) { /* Try each pattern rule till we find one that applies. - If it does, copy the names of its dependencies (as substituted) - and store them in FOUND_FILES. DEPS_FOUND is the number of them. */ + If it does, expand its dependencies (as substituted) + and chain them in DEPS. */ for (i = 0; i < nrules; i++) { + struct file *f; + unsigned int failed = 0; int check_lastslash; rule = tryrules[i]; + remove_explicit_deps = 0; + /* RULE is nil when we discover that a rule, already placed in TRYRULES, should not be applied. */ if (rule == 0) @@ -337,149 +466,258 @@ pattern_search (struct file *file, int archive, DBS (DB_IMPLICIT, (_("Trying pattern rule with stem `%.*s'.\n"), (int) stemlen, stem)); + /* Temporary assign STEM to file->stem and set file variables. */ + file->stem = stem; + set_file_variables (file); + /* Try each dependency; see if it "exists". */ - deps_found = 0; + /* @@ There is always only one dep line for any given implicit + rule. So the loop is not necessary. Can rule->deps be 0? + + Watch out for conversion of suffix rules to implicit rules. + */ + for (dep = rule->deps; dep != 0; dep = dep->next) { - struct file *f; + unsigned int len; + char *p2; + unsigned int order_only = 0; /* Set if '|' was seen. */ - /* If the dependency name has a %, substitute the stem. */ - p = strchr (dep_name (dep), '%'); - if (p != 0) - { - register unsigned int i; - if (check_lastslash) - { - /* Copy directory name from the original FILENAME. */ - i = lastslash - filename + 1; - bcopy (filename, depname, i); - } - else - i = 0; - bcopy (dep_name (dep), depname + i, p - dep_name (dep)); - i += p - dep_name (dep); - bcopy (stem, depname + i, stemlen); - i += stemlen; - strcpy (depname + i, p + 1); - p = depname; - } - else - p = dep_name (dep); + /* In an ideal world we would take the dependency line, + substitute the stem, re-expand the whole line and + chop it into individual prerequisites. Unfortunately + this won't work because of the "check_lastslash" twist. + Instead, we will have to go word by word, taking $()'s + into account, for each word we will substitute the stem, + re-expand, chop it up, and, if check_lastslash != 0, + add the directory part to each resulting prerequisite. */ - /* P is now the actual dependency name as substituted. */ + p = get_next_word (dep->name, &len); - if (file_impossible_p (p)) - { - /* If this dependency has already been ruled - "impossible", then the rule fails and don't - bother trying it on the second pass either - since we know that will fail too. */ - DBS (DB_IMPLICIT, - (p == depname + while (1) + { + int add_dir = 0; + int had_stem = 0; + + if (p == 0) + break; /* No more words */ + + /* If the dependency name has %, substitute the stem. */ + + for (p2 = p; p2 < p + len && *p2 != '%'; ++p2); + + if (p2 < p + len) + { + register unsigned int i = p2 - p; + bcopy (p, depname, i); + bcopy (stem, depname + i, stemlen); + bcopy (p2 + 1, depname + i + stemlen, len - i - 1); + depname[len + stemlen - 1] = '\0'; + + if (check_lastslash) + add_dir = 1; + + had_stem = 1; + } + else + { + bcopy (p, depname, len); + depname[len] = '\0'; + } + + p2 = variable_expand_for_file (depname, file); + + /* Parse the dependencies. */ + + while (1) + { + id_ptr = &deps; + + for (; *id_ptr; id_ptr = &(*id_ptr)->next) + ; + + *id_ptr = (struct idep *) + multi_glob ( + parse_file_seq (&p2, + order_only ? '\0' : '|', + sizeof (struct idep), + 1), sizeof (struct idep)); + + /* @@ It would be nice to teach parse_file_seq or + multi_glob to add prefix. This would save us + some reallocations. */ + + if (order_only || add_dir || had_stem) + { + unsigned long l = lastslash - filename + 1; + + for (d = *id_ptr; d != 0; d = d->next) + { + if (order_only) + d->ignore_mtime = 1; + + if (add_dir) + { + char *p = d->name; + + d->name = xmalloc (strlen (p) + l + 1); + + bcopy (filename, d->name, l); + bcopy (p, d->name + l, strlen (p) + 1); + + free (p); + } + + if (had_stem) + d->had_stem = 1; + } + } + + if (!order_only && *p2) + { + ++p2; + order_only = 1; + continue; + } + + break; + } + + p += len; + p = get_next_word (p, &len); + } + } + + /* Reset the stem in FILE. */ + + file->stem = 0; + + /* @@ This loop can be combined with the previous one. I do + it separately for now for transparency.*/ + + for (d = deps; d != 0; d = d->next) + { + char *name = d->name; + + if (file_impossible_p (name)) + { + /* If this dependency has already been ruled + "impossible", then the rule fails and don't + bother trying it on the second pass either + since we know that will fail too. */ + DBS (DB_IMPLICIT, + (d->had_stem ? _("Rejecting impossible implicit prerequisite `%s'.\n") : _("Rejecting impossible rule prerequisite `%s'.\n"), - p)); - tryrules[i] = 0; - break; - } + name)); + tryrules[i] = 0; - intermediate_files[deps_found] = 0; + failed = 1; + break; + } - DBS (DB_IMPLICIT, - (p == depname + DBS (DB_IMPLICIT, + (d->had_stem ? _("Trying implicit prerequisite `%s'.\n") - : _("Trying rule prerequisite `%s'.\n"), p)); + : _("Trying rule prerequisite `%s'.\n"), name)); - /* The DEP->changed flag says that this dependency resides in a - nonexistent directory. So we normally can skip looking for - the file. However, if CHECK_LASTSLASH is set, then the - dependency file we are actually looking for is in a different - directory (the one gotten by prepending FILENAME's directory), - so it might actually exist. */ + /* If this prerequisite also happened to be explicitly + mentioned for FILE skip all the test below since it + it has to be built anyway, no matter which implicit + rule we choose. */ - if (((f = lookup_file (p)) != 0 && f->is_target) - || ((!dep->changed || check_lastslash) && file_exists_p (p))) - { - found_files_im[deps_found] = dep->ignore_mtime; - found_files[deps_found++] = xstrdup (p); - continue; - } - /* This code, given FILENAME = "lib/foo.o", dependency name - "lib/foo.c", and VPATH=src, searches for "src/lib/foo.c". */ - vp = p; - if (vpath_search (&vp, (FILE_TIMESTAMP *) 0)) - { - DBS (DB_IMPLICIT, - (_("Found prerequisite `%s' as VPATH `%s'\n"), p, vp)); - strcpy (vp, p); - found_files_im[deps_found] = dep->ignore_mtime; - found_files[deps_found++] = vp; - continue; - } + for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next) + if (strcmp (dep_name (expl_d), name) == 0) break; - /* We could not find the file in any place we should look. - Try to make this dependency as an intermediate file, - but only on the second pass. */ + if (expl_d != 0) + continue; - if (intermed_ok) - { - if (intermediate_file == 0) - intermediate_file - = (struct file *) alloca (sizeof (struct file)); - DBS (DB_IMPLICIT, + + /* The DEP->changed flag says that this dependency resides in a + nonexistent directory. So we normally can skip looking for + the file. However, if CHECK_LASTSLASH is set, then the + dependency file we are actually looking for is in a different + directory (the one gotten by prepending FILENAME's directory), + so it might actually exist. */ + + /* @@ dep->changed check is disabled. */ + if (((f = lookup_file (name)) != 0 && f->is_target) + /*|| ((!dep->changed || check_lastslash) && */ + || file_exists_p (name)) + { + continue; + } + + /* This code, given FILENAME = "lib/foo.o", dependency name + "lib/foo.c", and VPATH=src, searches for "src/lib/foo.c". */ + vname = name; + if (vpath_search (&vname, (FILE_TIMESTAMP *) 0)) + { + DBS (DB_IMPLICIT, + (_("Found prerequisite `%s' as VPATH `%s'\n"), + name, + vname)); + + free (vname); + continue; + } + + + /* We could not find the file in any place we should look. + Try to make this dependency as an intermediate file, + but only on the second pass. */ + + if (intermed_ok) + { + if (intermediate_file == 0) + intermediate_file + = (struct file *) alloca (sizeof (struct file)); + + DBS (DB_IMPLICIT, (_("Looking for a rule with intermediate file `%s'.\n"), - p)); + name)); - bzero ((char *) intermediate_file, sizeof (struct file)); - intermediate_file->name = p; - if (pattern_search (intermediate_file, 0, depth + 1, - recursions + 1)) - { - p = xstrdup (p); - intermediate_patterns[deps_found] - = intermediate_file->name; - intermediate_file->name = p; - intermediate_files[deps_found] = intermediate_file; - intermediate_file = 0; - found_files_im[deps_found] = dep->ignore_mtime; - /* Allocate an extra copy to go in FOUND_FILES, - because every elt of FOUND_FILES is consumed - or freed later. */ - found_files[deps_found++] = xstrdup (p); - continue; - } + bzero ((char *) intermediate_file, sizeof (struct file)); + intermediate_file->name = name; + if (pattern_search (intermediate_file, + 0, + depth + 1, + recursions + 1)) + { + d->intermediate_file = intermediate_file; + d->intermediate_pattern = intermediate_file->name; - /* If we have tried to find P as an intermediate - file and failed, mark that name as impossible - so we won't go through the search again later. */ - file_impossible (p); - } + intermediate_file->name = xstrdup (name); + intermediate_file = 0; - /* A dependency of this rule does not exist. - Therefore, this rule fails. */ - break; - } + continue; + } - /* This rule is no longer `in use' for recursive searches. */ + /* If we have tried to find P as an intermediate + file and failed, mark that name as impossible + so we won't go through the search again later. */ + file_impossible (name); + } + + /* A dependency of this rule does not exist. Therefore, + this rule fails. */ + failed = 1; + break; + } + + /* This rule is no longer `in use' for recursive searches. */ rule->in_use = 0; - if (dep != 0) - { - /* This pattern rule does not apply. - If some of its dependencies succeeded, - free the data structure describing them. */ - while (deps_found-- > 0) - { - register struct file *f = intermediate_files[deps_found]; - free (found_files[deps_found]); - if (f != 0 - && (f->stem < f->name - || f->stem > f->name + strlen (f->name))) - free (f->stem); - } - } + if (failed) + { + /* This pattern rule does not apply. If some of its + dependencies succeeded, free the data structure + describing them. */ + free_idep_chain (deps); + deps = 0; + } else /* This pattern rule does apply. Stop looking for one. */ break; @@ -511,11 +749,30 @@ pattern_search (struct file *file, int archive, This includes the intermediate files, if any. Convert them into entries on the deps-chain of FILE. */ - while (deps_found-- > 0) + if (remove_explicit_deps) + { + /* Remove all the dependencies that didn't come from + this implicit rule. */ + + dep = file->deps; + while (dep != 0) + { + struct dep *next = dep->next; + free (dep->name); + free ((char *)dep); + dep = next; + } + file->deps = 0; + } + + expl_d = file->deps; /* We will add them at the end. */ + d_ptr = &file->deps; + + for (d = deps; d != 0; d = d->next) { register char *s; - if (intermediate_files[deps_found] != 0) + if (d->intermediate_file != 0) { /* If we need to use an intermediate file, make sure it is entered as a target, with the info that was @@ -524,13 +781,13 @@ pattern_search (struct file *file, int archive, a target; therefore we can assume that the deps and cmds of F below are null before we change them. */ - struct file *imf = intermediate_files[deps_found]; + struct file *imf = d->intermediate_file; register struct file *f = enter_file (imf->name); f->deps = imf->deps; f->cmds = imf->cmds; f->stem = imf->stem; f->also_make = imf->also_make; - imf = lookup_file (intermediate_patterns[deps_found]); + imf = lookup_file (d->intermediate_pattern); if (imf != 0 && imf->precious) f->precious = 1; f->intermediate = 1; @@ -547,8 +804,9 @@ pattern_search (struct file *file, int archive, } dep = (struct dep *) xmalloc (sizeof (struct dep)); - dep->ignore_mtime = found_files_im[deps_found]; - s = found_files[deps_found]; + dep->ignore_mtime = d->ignore_mtime; + s = d->name; /* Hijacking the name. */ + d->name = 0; if (recursions == 0) { dep->name = 0; @@ -567,7 +825,7 @@ pattern_search (struct file *file, int archive, dep->file = 0; dep->changed = 0; } - if (intermediate_files[deps_found] == 0 && tryrules[foundrule]->terminal) + if (d->intermediate_file == 0 && tryrules[foundrule]->terminal) { /* If the file actually existed (was not an intermediate file), and the rule that found it was a terminal one, then we want @@ -579,10 +837,13 @@ pattern_search (struct file *file, int archive, else dep->file->tried_implicit = 1; } - dep->next = file->deps; - file->deps = dep; + + *d_ptr = dep; + d_ptr = &dep->next; } + *d_ptr = expl_d; + if (!checked_lastslash[foundrule]) { /* Always allocate new storage, since STEM might be @@ -629,7 +890,7 @@ pattern_search (struct file *file, int archive, } done: - free (intermediate_files); + free_idep_chain (deps); free (tryrules); return rule != 0; diff --git a/make.h b/make.h index ad46e1f8..e9ea18a9 100644 --- a/make.h +++ b/make.h @@ -460,6 +460,10 @@ extern void user_access PARAMS ((void)); extern void make_access PARAMS ((void)); extern void child_access PARAMS ((void)); +extern char * +strip_whitespace PARAMS ((const char **begpp, const char **endpp)); + + #ifdef HAVE_VFORK_H # include #endif diff --git a/misc.c b/misc.c index 5532369d..4f1f8647 100644 --- a/misc.c +++ b/misc.c @@ -525,6 +525,27 @@ copy_dep_chain (struct dep *d) return firstnew; } +/* Free a chain of `struct dep'. Each dep->name is freed + as well. */ + +void +free_dep_chain (struct dep *d) +{ + register struct dep *tmp; + + while (d != 0) + { + if (d->name != 0) + free (d->name); + + tmp = d; + + d = d->next; + + free (tmp); + } + +} #ifdef iAPX286 /* The losing compiler on this machine can't handle this macro. */ diff --git a/read.c b/read.c index 291c6919..05d1a3d8 100644 --- a/read.c +++ b/read.c @@ -134,7 +134,6 @@ static int conditional_line PARAMS ((char *line, const struct floc *flocp)); static void record_files PARAMS ((struct nameseq *filenames, char *pattern, char *pattern_percent, struct dep *deps, unsigned int cmds_started, char *commands, unsigned int commands_idx, int two_colon, - int have_sysv_atvar, const struct floc *flocp, int set_default)); static void record_target_var PARAMS ((struct nameseq *filenames, char *defn, enum variable_origin origin, @@ -457,7 +456,6 @@ eval (struct ebuffer *ebuf, int set_default) unsigned int cmds_started, tgts_started; int ignoring = 0, in_ignored_define = 0; int no_targets = 0; /* Set when reading a rule without targets. */ - int have_sysv_atvar = 0; struct nameseq *filenames = 0; struct dep *deps = 0; long nlines = 0; @@ -474,7 +472,7 @@ eval (struct ebuffer *ebuf, int set_default) fi.lineno = tgts_started; \ record_files (filenames, pattern, pattern_percent, deps, \ cmds_started, commands, commands_idx, two_colon, \ - have_sysv_atvar, &fi, set_default); \ + &fi, set_default); \ } \ filenames = 0; \ commands_idx = 0; \ @@ -869,6 +867,7 @@ eval (struct ebuffer *ebuf, int set_default) char *cmdleft, *semip, *lb_next; unsigned int len, plen = 0; char *colonp; + const char *end, *beg; /* Helpers for whitespace stripping. */ /* Record the previous rule. */ @@ -917,6 +916,7 @@ eval (struct ebuffer *ebuf, int set_default) } p2 = variable_expand_string(NULL, lb_next, len); + while (1) { lb_next += len; @@ -1094,16 +1094,6 @@ eval (struct ebuffer *ebuf, int set_default) } } - /* Do any of the prerequisites appear to have $@ etc.? */ - have_sysv_atvar = 0; - if (!posix_pedantic) - for (p = strchr (p2, '$'); p != 0; p = strchr (p+1, '$')) - if (p[1] == '@' || ((p[1] == '(' || p[1] == '{') && p[2] == '@')) - { - have_sysv_atvar = 1; - break; - } - /* Is this a static pattern rule: `target: %targ: %dep; ...'? */ p = strchr (p2, ':'); while (p != 0 && p[-1] == '\\') @@ -1168,26 +1158,20 @@ eval (struct ebuffer *ebuf, int set_default) else pattern = 0; - /* Parse the dependencies. */ - deps = (struct dep *) - multi_glob (parse_file_seq (&p2, '|', sizeof (struct dep), 1), - sizeof (struct dep)); - if (*p2) + /* Strip leading and trailing whitespaces. */ + beg = p2; + end = beg + strlen (beg) - 1; + strip_whitespace (&beg, &end); + + if (beg <= end && *beg != '\0') { - /* Files that follow '|' are special prerequisites that - need only exist in order to satisfy the dependency. - Their modification times are irrelevant. */ - struct dep **deps_ptr = &deps; - struct dep *d; - for (deps_ptr = &deps; *deps_ptr; deps_ptr = &(*deps_ptr)->next) - ; - ++p2; - *deps_ptr = (struct dep *) - multi_glob (parse_file_seq (&p2, '\0', sizeof (struct dep), 1), - sizeof (struct dep)); - for (d = *deps_ptr; d != 0; d = d->next) - d->ignore_mtime = 1; + deps = (struct dep*) xmalloc (sizeof (struct dep)); + deps->next = 0; + deps->name = savestring (beg, end - beg + 1); + deps->file = 0; } + else + deps = 0; commands_idx = 0; if (cmdleft != 0) @@ -1753,7 +1737,7 @@ static void record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, struct dep *deps, unsigned int cmds_started, char *commands, unsigned int commands_idx, int two_colon, - int have_sysv_atvar, const struct floc *flocp, int set_default) + const struct floc *flocp, int set_default) { struct nameseq *nextf; int implicit = 0; @@ -1846,126 +1830,35 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, /* We use patsubst_expand to do the work of translating the target pattern, the target's name and the dependencies' patterns into plain dependency names. */ - char *buffer = variable_expand (""); - for (d = this; d != 0; d = d->next) - { - char *o; - char *percent = find_percent (d->name); - if (percent == 0) - continue; - o = patsubst_expand (buffer, name, pattern, d->name, - pattern_percent+1, percent+1); + if (find_percent (this->name) != 0) + { + char stem[PATH_MAX]; + char *o; + char *buffer = variable_expand (""); + + o = patsubst_expand (buffer, name, pattern, "%", + pattern_percent + 1, 0); + + + strncpy (stem, buffer, o - buffer); + stem[o - buffer] = '\0'; + + o = subst_expand (buffer, this->name, "%", stem, + 1, strlen (stem), 0); + /* If the name expanded to the empty string, that's illegal. */ if (o == buffer) fatal (flocp, _("target `%s' leaves prerequisite pattern empty"), name); - free (d->name); - d->name = savestring (buffer, o - buffer); + free (this->name); + this->name = savestring (buffer, o - buffer); } } } - /* If at least one of the dependencies uses $$@ etc. deal with that. - It would be very nice and very simple to just expand everything, but - it would break a lot of backward compatibility. Maybe that's OK - since we're just emulating a SysV function, and if we do that then - why not emulate it completely (that's what SysV make does: it - re-expands the entire prerequisite list, all the time, with $@ - etc. in scope). But, it would be a pain indeed to document this - ("iff you use $$@, your prerequisite lists is expanded twice...") - Ouch. Maybe better to make the code more complex. */ - - if (have_sysv_atvar) - { - char *p; - int tlen = strlen (name); - char *fnp = strrchr (name, '/'); - int dlen; - int flen; - - if (fnp) - { - dlen = fnp - name; - ++fnp; - flen = strlen (fnp); - } - else - { - dlen = 0; - fnp = name; - flen = tlen; - } - - - for (d = this; d != 0; d = d->next) - for (p = strchr (d->name, '$'); p != 0; p = strchr (p+1, '$')) - { - char *s = p; - char *at; - int atlen; - - /* If it's '$@', '$(@', or '${@', it's escaped */ - if ((++p)[0] == '$' - && (p[1] == '@' - || ((p[1] == '(' || p[1] == '{') && p[2] == '@'))) - { - bcopy (p, s, strlen (p)+1); - continue; - } - - /* Maybe found one. We like anything of any form matching @, - [({]@[}):], or [({]@[DF][}):]. */ - - if (! (p[0] == '@' - || ((p[0] == '(' || p[0] == '{') && (++p)[0] == '@' - && (((++p)[0] == ')' || p[0] == '}' || p[0] == ':') - || ((p[1] == ')' || p[1] == '}' || p[1] == ':') - && (p[0] == 'D' || p[0] == 'F')))))) - continue; - - /* Found one. Compute the length and string ptr. Move p - past the variable reference. */ - switch (p[0]) - { - case 'D': - atlen = dlen; - at = name; - p += 2; - break; - - case 'F': - atlen = flen; - at = fnp; - p += 2; - break; - - default: - atlen = tlen; - at = name; - ++p; - break; - } - - /* Get more space. */ - { - int soff = s - d->name; - int poff = p - d->name; - d->name = (char *) xrealloc (d->name, - strlen (d->name) + atlen + 1); - s = d->name + soff; - p = d->name + poff; - } - - /* Copy the string over. */ - bcopy(p, s+atlen, strlen (p)+1); - bcopy(at, s, atlen); - p = s + atlen - 1; - } - } - if (!two_colon) { /* Single-colon. Combine these dependencies @@ -2003,6 +1896,7 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, f->cmds = 0; if (cmds != 0) f->cmds = cmds; + /* Defining .SUFFIXES with no dependencies clears out the list of suffixes. */ if (f == suffix_file && this == 0) @@ -2017,41 +1911,63 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, } f->deps = 0; } - else if (f->deps != 0) + else if (this != 0) { /* Add the file's old deps and the new ones in THIS together. */ - struct dep *firstdeps, *moredeps; - if (cmds != 0) - { - /* This is the rule with commands, so put its deps first. - The rationale behind this is that $< expands to the - first dep in the chain, and commands use $< expecting - to get the dep that rule specifies. */ - firstdeps = this; - moredeps = f->deps; - } - else - { - /* Append the new deps to the old ones. */ - firstdeps = f->deps; - moredeps = this; - } + if (f->deps != 0) + { + struct dep **d_ptr = &f->deps; - if (firstdeps == 0) - firstdeps = moredeps; - else - { - d = firstdeps; - while (d->next != 0) - d = d->next; - d->next = moredeps; - } + while ((*d_ptr)->next != 0) + d_ptr = &(*d_ptr)->next; - f->deps = firstdeps; + if (cmds != 0) + { + /* This is the rule with commands, so put its deps + last. The rationale behind this is that $< expands + to the first dep in the chain, and commands use $< + expecting to get the dep that rule specifies. + However the second expansion algorithm reverses + the order thus we need to make it last here. */ + + (*d_ptr)->next = this; + } + else + { + /* This is the rule without commands. Put its + dependencies at the end but before dependencies + from the rule with commands (if any). This way + everyhting appears in makefile order. */ + + if (f->cmds != 0) + { + this->next = *d_ptr; + *d_ptr = this; + } + else + (*d_ptr)->next = this; + } + } + else + f->deps = this; + + /* This is a hack. I need a way to communicate to snap_deps() + that the last dependency line in this file came with commands + (so that logic in snap_deps() can put it in front and all + this $< -logic works). I cannot's simply rely oon file->cmds + being not 0 because of the cases like the following: + + foo: bar + foo: + ... + + I am going to temporarily "borrow" UPDATING member in + `struct file' for this. */ + + if (cmds != 0) + f->updating = 1; } - else - f->deps = this; /* If this is a static pattern rule, set the file's stem to the part of its name that matched the `%' in the pattern, @@ -2135,7 +2051,7 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, } if (!reject) - default_goal_file = f; + default_goal_file = f; } } diff --git a/tests/ChangeLog b/tests/ChangeLog index ffaf1b15..8bc29b12 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,19 @@ +Sun Feb 27 23:33:32 2005 Boris Kolpackov + + * scripts/features/se_explicit: Test the second expansion in + explicit rules. + + * scripts/features/se_implicit: Test the second expansion in + implicit rules. + + * scripts/features/se_statpat: Test the second expansion in + static pattern rules. + + * tests/scripts/variables/automatic: Fix to work with the second + expansion. + + * scripts/misc/general4: Add a test for bug #12091. + 2005-02-09 Paul D. Smith * scripts/features/recursion: Test command line variable settings: diff --git a/tests/scripts/features/se_explicit b/tests/scripts/features/se_explicit new file mode 100644 index 00000000..0e696bee --- /dev/null +++ b/tests/scripts/features/se_explicit @@ -0,0 +1,105 @@ +# -*-perl-*- +$description = "Test second expansion in ordinary rules."; + +$details = ""; + +# Test #1: automatic variables. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +foo: bar baz + +foo: biz | buz + +foo: $$@.1 \ + $$<.2 \ + $$(addsuffix .3,$$^) \ + $$(addsuffix .4,$$+) \ + $$|.5 \ + $$*.6 + +', +'', +'bar +baz +biz +buz +foo.1 +bar.2 +bar.3 +baz.3 +biz.3 +bar.4 +baz.4 +biz.4 +buz.5 +.6 +'); + + +# Test #2: target/pattern -specific variables. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +foo.x: $$a $$b + +foo.x: a := bar + +%.x: b := baz + +', +'', +'bar +baz +'); + + +# Test #3: order of prerequisites. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +all: foo bar baz + +# Subtest #1 +# +foo: foo.1; @: + +foo: foo.2 + +foo: foo.3 + + +# Subtest #2 +# +bar: bar.2 + +bar: bar.1; @: + +bar: bar.3 + + +# Subtest #3 +# +baz: baz.1 + +baz: baz.2 + +baz: ; @: + +', +'', +'foo.1 +foo.2 +foo.3 +bar.1 +bar.2 +bar.3 +baz.1 +baz.2 +'); + +# This tells the test driver that the perl test script executed properly. +1; diff --git a/tests/scripts/features/se_implicit b/tests/scripts/features/se_implicit new file mode 100644 index 00000000..b6b726c8 --- /dev/null +++ b/tests/scripts/features/se_implicit @@ -0,0 +1,188 @@ +# -*-perl-*- +$description = "Test second expansion in ordinary rules."; + +$details = ""; + +use Cwd; + +$dir = cwd; +$dir =~ s,.*/([^/]+)$,../$1,; + + +# Test #1: automatic variables. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +foo.a: bar baz + +foo.a: biz | buz + +foo.%: 1.$$@ \ + 2.$$< \ + $$(addprefix 3.,$$^) \ + $$(addprefix 4.,$$+) \ + 5.$$| \ + 6.$$* + @: + +1.foo.a \ +2.bar \ +3.bar \ +3.baz \ +3.biz \ +4.bar \ +4.baz \ +4.biz \ +5.buz \ +6.a: + @echo $@ + +', +'', +'1.foo.a +2.bar +3.bar +3.baz +3.biz +4.bar +4.baz +4.biz +5.buz +6.a +bar +baz +biz +buz +'); + + +# Test #2: target/pattern -specific variables. +# +run_make_test(' +foo.x: + +foo.%: $$(%_a) $$(%_b) bar + @: + +foo.x: x_a := bar + +%.x: x_b := baz + +bar baz: ; @echo $@ + +', +'', +'bar +baz +'); + + +# Test #3: order of prerequisites. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +all: foo bar baz + + +# Subtest #1 +# +%oo: %oo.1; @: + +foo: foo.2 + +foo: foo.3 + +foo.1: ; @echo $@ + + +# Subtest #2 +# +bar: bar.2 + +%ar: %ar.1; @: + +bar: bar.3 + +bar.1: ; @echo $@ + + +# Subtest #3 +# +baz: baz.1 + +baz: baz.2 + +%az: ; @: + +', +'', +'foo.1 +foo.2 +foo.3 +bar.1 +bar.2 +bar.3 +baz.1 +baz.2 +'); + + +# Test #4: stem splitting logic. +# +run_make_test(' +$(dir)/tmp/bar.o: + +$(dir)/tmp/foo/bar.c: ; @echo $@ +$(dir)/tmp/bar/bar.c: ; @echo $@ +foo.h: ; @echo $@ + +%.o: $$(addsuffix /%.c,foo bar) foo.h + @echo $@: {$<} $^ + +', +"dir=$dir", +"$dir/tmp/foo/bar.c +$dir/tmp/bar/bar.c +foo.h +$dir/tmp/bar.o: {$dir/tmp/foo/bar.c} $dir/tmp/foo/bar.c $dir/tmp/bar/bar.c foo.h +"); + + +# Test #5: stem splitting logic and order-only prerequisites. +# +run_make_test(' +$(dir)/tmp/foo.o: $(dir)/tmp/foo.c +$(dir)/tmp/foo.c: ; @echo $@ +bar.h: ; @echo $@ + +%.o: %.c|bar.h + @echo $@: {$<} {$|} $^ + +', +"dir=$dir", +"$dir/tmp/foo.c +bar.h +$dir/tmp/foo.o: {$dir/tmp/foo.c} {bar.h} $dir/tmp/foo.c +"); + + +# Test #6: lack of implicit prerequisites. +# +run_make_test(' +foo.o: foo.c +foo.c: ; @echo $@ + +%.o: + @echo $@: {$<} $^ + +', +'', +'foo.c +foo.o: {foo.c} foo.c +'); + + +# This tells the test driver that the perl test script executed properly. +1; diff --git a/tests/scripts/features/se_statpat b/tests/scripts/features/se_statpat new file mode 100644 index 00000000..9f1b4a32 --- /dev/null +++ b/tests/scripts/features/se_statpat @@ -0,0 +1,106 @@ +# -*-perl-*- +$description = "Test second expansion in static pattern rules."; + +$details = ""; + +# Test #1: automatic variables. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +foo.a foo.b: foo.%: bar.% baz.% + +foo.a foo.b: foo.%: biz.% | buz.% + +foo.a foo.b: foo.%: $$@.1 \ + $$<.2 \ + $$(addsuffix .3,$$^) \ + $$(addsuffix .4,$$+) \ + $$|.5 \ + $$*.6 + +', +'', +'bar.a +baz.a +biz.a +buz.a +foo.a.1 +bar.a.2 +bar.a.3 +baz.a.3 +biz.a.3 +bar.a.4 +baz.a.4 +biz.a.4 +buz.a.5 +a.6 +'); + + +# Test #2: target/pattern -specific variables. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +foo.x foo.y: foo.%: $$(%_a) $$($$*_b) + +foo.x: x_a := bar + +%.x: x_b := baz + + +', +'', +'bar +baz +'); + + +# Test #3: order of prerequisites. +# +run_make_test(' +.DEFAULT: ; @echo $@ + +all: foo.a bar.a baz.a + +# Subtest #1 +# +foo.a foo.b: foo.%: foo.%.1; @: + +foo.a foo.b: foo.%: foo.%.2 + +foo.a foo.b: foo.%: foo.%.3 + + +# Subtest #2 +# +bar.a bar.b: bar.%: bar.%.2 + +bar.a bar.b: bar.%: bar.%.1; @: + +bar.a bar.b: bar.%: bar.%.3 + + +# Subtest #3 +# +baz.a baz.b: baz.%: baz.%.1 + +baz.a baz.b: baz.%: baz.%.2 + +baz.a baz.b: ; @: + +', +'', +'foo.a.1 +foo.a.2 +foo.a.3 +bar.a.1 +bar.a.2 +bar.a.3 +baz.a.1 +baz.a.2 +'); + +# This tells the test driver that the perl test script executed properly. +1; diff --git a/tests/scripts/misc/general4 b/tests/scripts/misc/general4 index dd77f539..3b4595f1 100644 --- a/tests/scripts/misc/general4 +++ b/tests/scripts/misc/general4 @@ -6,9 +6,6 @@ which have either broken at some point in the past or seem likely to break."; open(MAKEFILE,"> $makefile"); - -# The contents of the Makefile ... - print MAKEFILE <<'EOF'; # Make sure that subdirectories built as prerequisites are actually handled # properly. @@ -21,11 +18,36 @@ dir/subdir/file.b: dir/subdir ; @echo touch dir/subdir/file.b dir/subdir/%.a: dir/subdir/%.b ; @echo cp $< $@ EOF - close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); $answer = "mkdir -p dir/subdir\ntouch dir/subdir/file.b\ncp dir/subdir/file.b dir/subdir/file.a\n"; &compare_output($answer,&get_logfile(1)); + +# Test implicit rules + +&touch('foo.c'); +run_make_test(' +foo: foo.o +', + 'CC="@echo cc" OUTPUT_OPTION=', + 'cc -c foo.c +cc foo.o -o foo'); +unlink('foo.c'); + + +# Test other implicit rule searching + +&touch('bar'); +run_make_test(' +test.foo: +%.foo : baz ; @echo done $< +%.foo : bar ; @echo done $< +fox: baz +', + '', + 'done bar'); +unlink('bar'); + 1; diff --git a/tests/scripts/variables/automatic b/tests/scripts/variables/automatic index 2919960b..a51ca20c 100644 --- a/tests/scripts/variables/automatic +++ b/tests/scripts/variables/automatic @@ -67,11 +67,11 @@ EOF close(MAKEFILE); &run_make_with_options($makefile2, "$dir/foo $dir/bar", &get_logfile); -$answer = ".x\n$dir/foo.x\n\$.x\n\$@.x\n$dir.x\nfoo.x\n$dir/bar.x\nbar.x\n"; +$answer = ".x\n$dir/foo.x\nx\n\$@.x\n$dir.x\nfoo.x\n$dir/bar.x\nbar.x\n"; &compare_output($answer, &get_logfile(1)); &run_make_with_options($makefile2, "$dir/x.z $dir/y.z", &get_logfile); -$answer = ".x\n$dir/x.z.x\n\$.x\n\$@.x\n$dir.x\nx.z.x\n.y\n$dir/y.z.y\n\$.y\n\$@.y\n$dir.y\ny.z.y\n"; +$answer = ".x\n$dir/x.z.x\nx\n\$@.x\n$dir.x\nx.z.x\n.y\n$dir/y.z.y\n\y\n\$@.y\n$dir.y\ny.z.y\n"; &compare_output($answer, &get_logfile(1)); &run_make_with_options($makefile2, "$dir/biz", &get_logfile); diff --git a/variable.h b/variable.h index 4a23e4ac..cb300c5d 100644 --- a/variable.h +++ b/variable.h @@ -112,6 +112,7 @@ extern struct variable_set_list *current_variable_set_list; /* expand.c */ extern char *variable_buffer_output PARAMS ((char *ptr, char *string, unsigned int length)); extern char *variable_expand PARAMS ((char *line)); +extern char *variable_expand_for_file PARAMS ((char *line, struct file *file)); extern char *allocated_variable_expand_for_file PARAMS ((char *line, struct file *file)); #define allocated_variable_expand(line) \ allocated_variable_expand_for_file (line, (struct file *) 0)