Implementation of the second expansion in explicit
authorBoris Kolpackov <boris@kolpackov.net>
Sun, 27 Feb 2005 21:40:23 +0000 (21:40 +0000)
committerBoris Kolpackov <boris@kolpackov.net>
Sun, 27 Feb 2005 21:40:23 +0000 (21:40 +0000)
rules, static pattern rules and implicit rules.

18 files changed:
ChangeLog
commands.c
commands.h
dep.h
expand.c
file.c
function.c
implicit.c
make.h
misc.c
read.c
tests/ChangeLog
tests/scripts/features/se_explicit [new file with mode: 0644]
tests/scripts/features/se_implicit [new file with mode: 0644]
tests/scripts/features/se_statpat [new file with mode: 0644]
tests/scripts/misc/general4
tests/scripts/variables/automatic
variable.h

index 701501e..f9d0271 100644 (file)
--- 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
index 6fdb5bb..97ec981 100644 (file)
@@ -38,7 +38,7 @@ extern int getpid ();
 \f
 /* Set FILE's automatic variables up.  */
 
-static void
+void
 set_file_variables (struct file *file)
 {
   struct dep *d;
index edec910..3d62ee8 100644 (file)
@@ -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 (file)
--- 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));
index 4440192..372a0f5 100644 (file)
--- 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 (file)
--- 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)
index bbb86d2..b344e66 100644 (file)
@@ -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))
index 10b41ff..7c1f4b7 100644 (file)
@@ -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));
 \f
 /* 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)
 }
 \f
 
+/* 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 (file)
--- 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 (file)
--- a/misc.c
+++ b/misc.c
@@ -525,6 +525,27 @@ copy_dep_chain (struct dep *d)
   return firstnew;
 }
 \f
+/* 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);
+  }
+
+}\f
 #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 (file)
--- 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;
        }
     }
 
index ffaf1b1..8bc29b1 100644 (file)
@@ -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 (file)
index 0000000..0e696be
--- /dev/null
@@ -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 (file)
index 0000000..b6b726c
--- /dev/null
@@ -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 (file)
index 0000000..9f1b4a3
--- /dev/null
@@ -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;
index dd77f53..3b4595f 100644 (file)
@@ -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;
index 2919960..a51ca20 100644 (file)
@@ -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);
index 4a23e4a..cb300c5 100644 (file)
@@ -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)