summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@kolpackov.net>2005-02-27 22:24:30 +0000
committerBoris Kolpackov <boris@kolpackov.net>2005-02-27 22:24:30 +0000
commit93bd1bd93c1033352e5059ed721a0cc769449639 (patch)
treeb3d69f597a1e32659e227388346363cfdbdec465
parent659fc6b55e28740c74d66dbe3bda765d1004a12e (diff)
downloadgunmake-93bd1bd93c1033352e5059ed721a0cc769449639.tar.gz
Implementation of the .DEFAULT_TARGET special variable.
-rw-r--r--ChangeLog25
-rw-r--r--dep.h2
-rw-r--r--file.c2
-rw-r--r--filedef.h1
-rw-r--r--main.c58
-rw-r--r--misc.c18
-rw-r--r--read.c127
-rw-r--r--tests/ChangeLog5
-rw-r--r--tests/scripts/variables/DEFAULT_TARGET59
9 files changed, 236 insertions, 61 deletions
diff --git a/ChangeLog b/ChangeLog
index f9d0271..becece8 100644
--- 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 6e6adce..01c31a3 100644
--- 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 70e5358..62a6a68 100644
--- 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);
diff --git a/filedef.h b/filedef.h
index b1d9b9a..2822e8a 100644
--- 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 42e1a00..1035b8c 100644
--- 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 4f1f864..53c1923 100644
--- a/misc.c
+++ b/misc.c
@@ -525,22 +525,22 @@ copy_dep_chain (struct dep *d)
return firstnew;
}
-/* 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 05d1a3d..0862b48 100644
--- 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;
}
}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 8bc29b1..462c2e6 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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
index 0000000..76b2a23
--- /dev/null
+++ b/tests/scripts/variables/DEFAULT_TARGET
@@ -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;