Implementation of the .DEFAULT_TARGET special variable.
authorBoris Kolpackov <boris@kolpackov.net>
Sun, 27 Feb 2005 22:24:30 +0000 (22:24 +0000)
committerBoris Kolpackov <boris@kolpackov.net>
Sun, 27 Feb 2005 22:24:30 +0000 (22:24 +0000)
ChangeLog
dep.h
file.c
filedef.h
main.c
misc.c
read.c
tests/ChangeLog
tests/scripts/variables/DEFAULT_TARGET [new file with mode: 0644]

index f9d027137ca3bec9a244e00059c73bf1129f16b8..becece8878b03d1267897e6d383f93c76ee78064 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+Mon Feb 28 00:18:20 2005  Boris Kolpackov  <boris@kolpackov.net>
+
+       Implementation of the .DEFAULT_TARGET special variable.
+
+       * read.c (eval): If necessary, update default_target_name
+       when reading rules.
+
+       * read.c (record_files): Update default_target_file if
+       default_target_name has changed.
+
+       * main.c (default_target_name): Define.
+
+       * main.c (main): Enter .DEFAULT_TARGET as make variable. If
+       default_target_name is set use default_target_file as a root
+       target to make.
+
+       * filedef.h (default_target_name): Declare.
+
+       * dep.h (free_dep_chain):
+       * misc.c (free_dep_chain): Change to operate on struct nameseq
+       and change name to free_ns_chain.
+
+       * file.c (snap_deps): Update to use free_ns_chain.
+
+
 Sun Feb 27 22:03:36 2005  Boris Kolpackov  <boris@kolpackov.net>
 
        Implementation of the second expansion in explicit rules,
diff --git a/dep.h b/dep.h
index 6e6adcef8f6ca32fc1a3c96793764c196a4701a4..01c31a30c7d36834471cf129697bce2a5718275e 100644 (file)
--- a/dep.h
+++ b/dep.h
@@ -72,7 +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 void free_ns_chain PARAMS ((struct nameseq *n));
 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/file.c b/file.c
index 70e53588821469de17871d466fc7079bd5272934..62a6a687060498f10b5dea7606dc4aae57400178 100644 (file)
--- a/file.c
+++ b/file.c
@@ -522,7 +522,7 @@ snap_deps (void)
               }
           }
 
-        free_dep_chain (old);
+        free_ns_chain ((struct nameseq*)old);
       }
   free (file_slot_0);
 
index b1d9b9a693f12d6d53d7870572f241c765c47c62..2822e8a782ae1ace93e43ce3cae640f755f7da71 100644 (file)
--- a/filedef.h
+++ b/filedef.h
@@ -100,6 +100,7 @@ struct file
 
 
 extern struct file *default_goal_file, *suffix_file, *default_file;
+extern char **default_target_name;
 
 
 extern struct file *lookup_file PARAMS ((char *name));
diff --git a/main.c b/main.c
index 42e1a00c625d9a0df7e15e6714afd95769d146fa..1035b8c2a4e553a1e75094f8605e6145b9683f3c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -461,6 +461,10 @@ unsigned int makelevel;
 
 struct file *default_goal_file;
 
+/* Pointer to the value of the .DEFAULT_TARGET special
+   variable.  */
+char ** default_target_name;
+
 /* Pointer to structure for the file .DEFAULT
    whose commands are used for any file that has none of its own.
    This is zero if the makefiles do not define .DEFAULT.  */
@@ -1537,10 +1541,17 @@ main (int argc, char **argv, char **envp)
   /* Define the default variables.  */
   define_default_variables ();
 
-  /* Read all the makefiles.  */
-
   default_file = enter_file (".DEFAULT");
 
+  {
+    struct variable *v = define_variable (
+      ".DEFAULT_TARGET", 15, "", o_default, 0);
+
+    default_target_name = &v->value;
+  }
+
+  /* Read all the makefiles.  */
+
   read_makefiles
     = read_all_makefiles (makefiles == 0 ? (char **) 0 : makefiles->list);
 
@@ -2058,18 +2069,47 @@ main (int argc, char **argv, char **envp)
     /* If there were no command-line goals, use the default.  */
     if (goals == 0)
       {
-       if (default_goal_file != 0)
-         {
-           goals = (struct dep *) xmalloc (sizeof (struct dep));
-           goals->next = 0;
-           goals->name = 0;
+        if (**default_target_name != '\0')
+          {
+            if (default_goal_file == 0 ||
+                strcmp (*default_target_name, default_goal_file->name) != 0)
+              {
+                default_goal_file = lookup_file (*default_target_name);
+
+                /* In case user set .DEFAULT_TARGET to a non-existent target
+                   name let's just enter this name into the table and let
+                   the standard logic sort it out. */
+                if (default_goal_file == 0)
+                  {
+                    struct nameseq *ns;
+                    char *p = *default_target_name;
+
+                    ns = multi_glob (
+                      parse_file_seq (&p, '\0', sizeof (struct nameseq), 1),
+                      sizeof (struct nameseq));
+
+                    /* .DEFAULT_TARGET should contain one target. */
+                    if (ns->next != 0)
+                      fatal (NILF, _(".DEFAULT_TARGET contains more than one target"));
+
+                    default_goal_file = enter_file (ns->name);
+
+                    ns->name = 0; /* It was reused by enter_file(). */
+                    free_ns_chain (ns);
+                  }
+              }
+
+            goals = (struct dep *) xmalloc (sizeof (struct dep));
+            goals->next = 0;
+            goals->name = 0;
             goals->ignore_mtime = 0;
-           goals->file = default_goal_file;
-         }
+            goals->file = default_goal_file;
+          }
       }
     else
       lastgoal->next = 0;
 
+
     if (!goals)
       {
         if (read_makefiles == 0)
diff --git a/misc.c b/misc.c
index 4f1f8647eaa345885968a94b88fcd9f86089723e..53c1923ee85402b597725dd0c6e2d7f85ea8260d 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -525,22 +525,22 @@ copy_dep_chain (struct dep *d)
   return firstnew;
 }
 \f
-/* Free a chain of `struct dep'. Each dep->name is freed
-   as well.  */
+/* Free a chain of `struct nameseq'. Each nameseq->name is freed
+   as well.  Can be used on `struct dep' chains.*/
 
 void
-free_dep_chain (struct dep *d)
+free_ns_chain (struct nameseq *n)
 {
-  register struct dep *tmp;
+  register struct nameseq *tmp;
 
-  while (d != 0)
+  while (n != 0)
   {
-    if (d->name != 0)
-      free (d->name);
+    if (n->name != 0)
+      free (n->name);
 
-    tmp = d;
+    tmp = n;
 
-    d = d->next;
+    n = n->next;
 
     free (tmp);
   }
diff --git a/read.c b/read.c
index 05d1a3d886574064fdfc94d01ad8d65ec7e95903..0862b482df1aea0f79450410e4c8dae6c5b94d7a 100644 (file)
--- a/read.c
+++ b/read.c
@@ -134,7 +134,7 @@ 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,
-                        const struct floc *flocp, int set_default));
+                        const struct floc *flocp));
 static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
                                        enum variable_origin origin,
                                        int enabled,
@@ -472,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,      \
-                        &fi, set_default);                                    \
+                        &fi);                                                 \
         }                                                                     \
       filenames = 0;                                                         \
       commands_idx = 0;                                                              \
@@ -1192,6 +1192,83 @@ eval (struct ebuffer *ebuf, int set_default)
             commands[commands_idx++] = '\n';
           }
 
+        /* Determine if this target should be made default. We used
+           to do this in record_files() but because of the delayed
+           target recording and because preprocessor directives are
+           legal in target's commands it is too late. Consider this
+           fragment for example:
+
+           foo:
+
+           ifeq ($(.DEFAULT_TARGET),foo)
+              ...
+           endif
+
+           Because the target is not recorded until after ifeq
+           directive is evaluated the .DEFAULT_TARGET does not
+           contain foo yet as one would expect. Because of this
+           we have to move some of the logic here. */
+
+        if (**default_target_name == '\0' && set_default)
+          {
+            char* name;
+            struct dep *d;
+            struct nameseq *t = filenames;
+
+            for (; t != 0; t = t->next)
+              {
+                int reject = 0;
+                name = t->name;
+
+                /* We have nothing to do if this is an implicit rule. */
+                if (strchr (name, '%') != 0)
+                  break;
+
+                /* See if this target's name does not start with a `.',
+                   unless it contains a slash.  */
+                if (*name == '.' && strchr (name, '/') == 0
+#ifdef HAVE_DOS_PATHS
+                    && strchr (name, '\\') == 0
+#endif
+                )
+                  continue;
+
+
+                /* If this file is a suffix, don't let it be
+                   the default goal file.  */
+                for (d = suffix_file->deps; d != 0; d = d->next)
+                  {
+                    register struct dep *d2;
+                    if (*dep_name (d) != '.' && streq (name, dep_name (d)))
+                      {
+                        reject = 1;
+                        break;
+                      }
+                    for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
+                      {
+                        register unsigned int len = strlen (dep_name (d2));
+                        if (!strneq (name, dep_name (d2), len))
+                          continue;
+                        if (streq (name + len, dep_name (d)))
+                          {
+                            reject = 1;
+                            break;
+                          }
+                      }
+
+                    if (reject)
+                      break;
+                  }
+
+                if (!reject)
+                  {
+                    (void) define_variable (
+                      ".DEFAULT_TARGET", 15, t->name, o_file, 0);
+                    break;
+                  }
+              }
+          }
+
         continue;
       }
 
@@ -1737,7 +1814,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,
-              const struct floc *flocp, int set_default)
+              const struct floc *flocp)
 {
   struct nameseq *nextf;
   int implicit = 0;
@@ -2013,45 +2090,13 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
          name = f->name;
        }
 
-      /* See if this is first target seen whose name does
-        not start with a `.', unless it contains a slash.  */
-      if (default_goal_file == 0 && set_default
-         && (*name != '.' || strchr (name, '/') != 0
-#ifdef HAVE_DOS_PATHS
-                          || strchr (name, '\\') != 0
-#endif
-             ))
+      /* See if this target is a default target and update
+         DEFAULT_GOAL_FILE if necessary.  */
+      if (strcmp (*default_target_name, name) == 0 &&
+          (default_goal_file == 0 ||
+           strcmp (default_goal_file->name, name) != 0))
        {
-         int reject = 0;
-
-         /* If this file is a suffix, don't
-            let it be the default goal file.  */
-
-         for (d = suffix_file->deps; d != 0; d = d->next)
-           {
-             register struct dep *d2;
-             if (*dep_name (d) != '.' && streq (name, dep_name (d)))
-               {
-                 reject = 1;
-                 break;
-               }
-             for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
-               {
-                 register unsigned int len = strlen (dep_name (d2));
-                 if (!strneq (name, dep_name (d2), len))
-                   continue;
-                 if (streq (name + len, dep_name (d)))
-                   {
-                     reject = 1;
-                     break;
-                   }
-               }
-             if (reject)
-               break;
-           }
-
-         if (!reject)
-            default_goal_file = f;
+          default_goal_file = f;
        }
     }
 
index 8bc29b12b6bed42265ace96295d34f7ac4de0781..462c2e6219aa1c5cdedd58b0f5c55559544017bf 100644 (file)
@@ -1,3 +1,8 @@
+Mon Feb 28 00:31:14 2005  Boris Kolpackov  <boris@kolpackov.net>
+
+       * scripts/variables/DEFAULT_TARGET: Test the .DEFAULT_TARGET
+       special variable.
+
 Sun Feb 27 23:33:32 2005  Boris Kolpackov  <boris@kolpackov.net>
 
        * scripts/features/se_explicit: Test the second expansion in
diff --git a/tests/scripts/variables/DEFAULT_TARGET b/tests/scripts/variables/DEFAULT_TARGET
new file mode 100644 (file)
index 0000000..76b2a23
--- /dev/null
@@ -0,0 +1,59 @@
+#                                                                    -*-perl-*-
+$description = "Test the .DEFAULT_TARGET special variable.";
+
+$details = "";
+
+# Test #1: basic logic.
+#
+run_make_test('
+# Basics.
+#
+foo: ; @:
+
+ifneq ($(.DEFAULT_TARGET),foo)
+$(error )
+endif
+
+# Reset to empty.
+#
+.DEFAULT_TARGET :=
+
+bar: ; @:
+
+ifneq ($(.DEFAULT_TARGET),bar)
+$(error )
+endif
+
+# Change to a different target.
+#
+
+.DEFAULT_TARGET := baz
+
+baz: ; @echo $@
+',
+'',
+'baz');
+
+
+# Test #2: unknown target.
+#
+run_make_test('
+.DEFAULT_TARGET := foo
+',
+'',
+'make: *** No rule to make target `foo\'.  Stop.',
+512);
+
+
+# Test #2: more than one target.
+#
+run_make_test('
+.DEFAULT_TARGET := foo bar
+',
+'',
+'make: *** .DEFAULT_TARGET contains more than one target.  Stop.',
+512);
+
+
+# This tells the test driver that the perl test script executed properly.
+1;