Implementation of the .DEFAULT_TARGET special variable.

This commit is contained in:
Boris Kolpackov 2005-02-27 22:24:30 +00:00
parent 659fc6b55e
commit 93bd1bd93c
9 changed files with 236 additions and 61 deletions

View File

@ -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> Sun Feb 27 22:03:36 2005 Boris Kolpackov <boris@kolpackov.net>
Implementation of the second expansion in explicit rules, Implementation of the second expansion in explicit rules,

2
dep.h
View File

@ -72,7 +72,7 @@ extern char *dep_name ();
#endif #endif
extern struct dep *copy_dep_chain PARAMS ((struct dep *d)); 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 struct dep *read_all_makefiles PARAMS ((char **makefiles));
extern int eval_buffer PARAMS ((char *buffer)); extern int eval_buffer PARAMS ((char *buffer));
extern int update_goal_chain PARAMS ((struct dep *goals)); extern int update_goal_chain PARAMS ((struct dep *goals));

2
file.c
View File

@ -522,7 +522,7 @@ snap_deps (void)
} }
} }
free_dep_chain (old); free_ns_chain ((struct nameseq*)old);
} }
free (file_slot_0); free (file_slot_0);

View File

@ -100,6 +100,7 @@ struct file
extern struct file *default_goal_file, *suffix_file, *default_file; extern struct file *default_goal_file, *suffix_file, *default_file;
extern char **default_target_name;
extern struct file *lookup_file PARAMS ((char *name)); extern struct file *lookup_file PARAMS ((char *name));

58
main.c
View File

@ -461,6 +461,10 @@ unsigned int makelevel;
struct file *default_goal_file; 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 /* Pointer to structure for the file .DEFAULT
whose commands are used for any file that has none of its own. whose commands are used for any file that has none of its own.
This is zero if the makefiles do not define .DEFAULT. */ 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 the default variables. */
define_default_variables (); define_default_variables ();
/* Read all the makefiles. */
default_file = enter_file (".DEFAULT"); 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_makefiles
= read_all_makefiles (makefiles == 0 ? (char **) 0 : makefiles->list); = 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 there were no command-line goals, use the default. */
if (goals == 0) if (goals == 0)
{ {
if (default_goal_file != 0) if (**default_target_name != '\0')
{ {
goals = (struct dep *) xmalloc (sizeof (struct dep)); if (default_goal_file == 0 ||
goals->next = 0; strcmp (*default_target_name, default_goal_file->name) != 0)
goals->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->ignore_mtime = 0;
goals->file = default_goal_file; goals->file = default_goal_file;
} }
} }
else else
lastgoal->next = 0; lastgoal->next = 0;
if (!goals) if (!goals)
{ {
if (read_makefiles == 0) if (read_makefiles == 0)

18
misc.c
View File

@ -525,22 +525,22 @@ copy_dep_chain (struct dep *d)
return firstnew; return firstnew;
} }
/* Free a chain of `struct dep'. Each dep->name is freed /* Free a chain of `struct nameseq'. Each nameseq->name is freed
as well. */ as well. Can be used on `struct dep' chains.*/
void 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) if (n->name != 0)
free (d->name); free (n->name);
tmp = d; tmp = n;
d = d->next; n = n->next;
free (tmp); free (tmp);
} }

127
read.c
View File

@ -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, static void record_files PARAMS ((struct nameseq *filenames, char *pattern, char *pattern_percent,
struct dep *deps, unsigned int cmds_started, char *commands, struct dep *deps, unsigned int cmds_started, char *commands,
unsigned int commands_idx, int two_colon, 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, static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
enum variable_origin origin, enum variable_origin origin,
int enabled, int enabled,
@ -472,7 +472,7 @@ eval (struct ebuffer *ebuf, int set_default)
fi.lineno = tgts_started; \ fi.lineno = tgts_started; \
record_files (filenames, pattern, pattern_percent, deps, \ record_files (filenames, pattern, pattern_percent, deps, \
cmds_started, commands, commands_idx, two_colon, \ cmds_started, commands, commands_idx, two_colon, \
&fi, set_default); \ &fi); \
} \ } \
filenames = 0; \ filenames = 0; \
commands_idx = 0; \ commands_idx = 0; \
@ -1192,6 +1192,83 @@ eval (struct ebuffer *ebuf, int set_default)
commands[commands_idx++] = '\n'; 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; continue;
} }
@ -1737,7 +1814,7 @@ static void
record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
struct dep *deps, unsigned int cmds_started, char *commands, struct dep *deps, unsigned int cmds_started, char *commands,
unsigned int commands_idx, int two_colon, unsigned int commands_idx, int two_colon,
const struct floc *flocp, int set_default) const struct floc *flocp)
{ {
struct nameseq *nextf; struct nameseq *nextf;
int implicit = 0; int implicit = 0;
@ -2013,45 +2090,13 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
name = f->name; name = f->name;
} }
/* See if this is first target seen whose name does /* See if this target is a default target and update
not start with a `.', unless it contains a slash. */ DEFAULT_GOAL_FILE if necessary. */
if (default_goal_file == 0 && set_default if (strcmp (*default_target_name, name) == 0 &&
&& (*name != '.' || strchr (name, '/') != 0 (default_goal_file == 0 ||
#ifdef HAVE_DOS_PATHS strcmp (default_goal_file->name, name) != 0))
|| strchr (name, '\\') != 0
#endif
))
{ {
int reject = 0; default_goal_file = f;
/* 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;
} }
} }

View 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> Sun Feb 27 23:33:32 2005 Boris Kolpackov <boris@kolpackov.net>
* scripts/features/se_explicit: Test the second expansion in * scripts/features/se_explicit: Test the second expansion in

View File

@ -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;