From 93bd1bd93c1033352e5059ed721a0cc769449639 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 27 Feb 2005 22:24:30 +0000 Subject: [PATCH] Implementation of the .DEFAULT_TARGET special variable. --- ChangeLog | 25 +++++++ dep.h | 2 +- file.c | 2 +- filedef.h | 1 + main.c | 58 ++++++++++++--- misc.c | 18 ++--- read.c | 127 ++++++++++++++++++++++----------- tests/ChangeLog | 5 ++ tests/scripts/variables/DEFAULT_TARGET | 59 +++++++++++++++ 9 files changed, 236 insertions(+), 61 deletions(-) create mode 100644 tests/scripts/variables/DEFAULT_TARGET 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 + + 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 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 + + * scripts/variables/DEFAULT_TARGET: Test the .DEFAULT_TARGET + special variable. + Sun Feb 27 23:33:32 2005 Boris Kolpackov * 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; -- 2.7.4