summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@kolpackov.net>2005-02-27 21:40:23 +0000
committerBoris Kolpackov <boris@kolpackov.net>2005-02-27 21:40:23 +0000
commit659fc6b55e28740c74d66dbe3bda765d1004a12e (patch)
treea3b99015012abca13c0f7729274dff1bba322f1f
parent9d153cc1b1e467cd6245755c32f78efbd62142c2 (diff)
downloadgunmake-659fc6b55e28740c74d66dbe3bda765d1004a12e.tar.gz
Implementation of the second expansion in explicit
rules, static pattern rules and implicit rules.
-rw-r--r--ChangeLog46
-rw-r--r--commands.c2
-rw-r--r--commands.h1
-rw-r--r--dep.h1
-rw-r--r--expand.c2
-rw-r--r--file.c109
-rw-r--r--function.c2
-rw-r--r--implicit.c581
-rw-r--r--make.h4
-rw-r--r--misc.c21
-rw-r--r--read.c258
-rw-r--r--tests/ChangeLog16
-rw-r--r--tests/scripts/features/se_explicit105
-rw-r--r--tests/scripts/features/se_implicit188
-rw-r--r--tests/scripts/features/se_statpat106
-rw-r--r--tests/scripts/misc/general430
-rw-r--r--tests/scripts/variables/automatic4
-rw-r--r--variable.h1
18 files changed, 1125 insertions, 352 deletions
diff --git a/ChangeLog b/ChangeLog
index 701501e..f9d0271 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,49 @@
+Sun Feb 27 22:03:36 2005 Boris Kolpackov <boris@kolpackov.net>
+
+ 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 <jg@jguk.org>
* configure.in: Add MinGW configuration options, and extra w32 code
diff --git a/commands.c b/commands.c
index 6fdb5bb..97ec981 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 edec910..3d62ee8 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 4c9a152..6e6adce 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 4440192..372a0f5 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 ae99745..70e5358 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 bbb86d2..b344e66 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 10b41ff..7c1f4b7 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, *vname;
- char *p, *vp;
+ 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;
-
- /* 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);
-
- /* P is now the actual dependency name as substituted. */
-
- 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
+ unsigned int len;
+ char *p2;
+ unsigned int order_only = 0; /* Set if '|' was seen. */
+
+ /* 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 = get_next_word (dep->name, &len);
+
+ 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));
-
- /* 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 (((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;
- }
-
- /* 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,
+ : _("Trying rule prerequisite `%s'.\n"), name));
+
+ /* 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. */
+
+ for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next)
+ if (strcmp (dep_name (expl_d), name) == 0) break;
+
+ if (expl_d != 0)
+ continue;
+
+
+
+ /* 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));
-
- 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;
- }
-
- /* 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);
- }
-
- /* A dependency of this rule does not exist.
- Therefore, this rule fails. */
- break;
- }
-
- /* This rule is no longer `in use' for recursive searches. */
+ name));
+
+ 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;
+
+ intermediate_file->name = xstrdup (name);
+ intermediate_file = 0;
+
+ continue;
+ }
+
+ /* 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 ad46e1f..e9ea18a 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 <vfork.h>
#endif
diff --git a/misc.c b/misc.c
index 5532369..4f1f864 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 291c691..05d1a3d 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 ffaf1b1..8bc29b1 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,19 @@
+Sun Feb 27 23:33:32 2005 Boris Kolpackov <boris@kolpackov.net>
+
+ * 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 <psmith@gnu.org>
* 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 0000000..0e696be
--- /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 0000000..b6b726c
--- /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 0000000..9f1b4a3
--- /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 dd77f53..3b4595f 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 2919960..a51ca20 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 4a23e4a..cb300c5 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)