Implementation of the second expansion in explicit

rules, static pattern rules and implicit rules.
This commit is contained in:
Boris Kolpackov 2005-02-27 21:40:23 +00:00
parent 9d153cc1b1
commit 659fc6b55e
18 changed files with 1113 additions and 340 deletions

View file

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

View file

@ -38,7 +38,7 @@ extern int getpid ();
/* Set FILE's automatic variables up. */
static void
void
set_file_variables (struct file *file)
{
struct dep *d;

View 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));

1
dep.h
View file

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

View file

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

109
file.c
View file

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

View 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))

View 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));
/* 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)
}
/* 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, *vp;
char *p, *vname;
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;
unsigned int len;
char *p2;
unsigned int order_only = 0; /* Set if '|' was seen. */
/* 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);
/* 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 is now the actual dependency name as substituted. */
p = get_next_word (dep->name, &len);
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
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));
: _("Trying rule prerequisite `%s'.\n"), name));
/* 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 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. */
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;
}
for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next)
if (strcmp (dep_name (expl_d), name) == 0) break;
/* 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 (expl_d != 0)
continue;
if (intermed_ok)
{
if (intermediate_file == 0)
intermediate_file
= (struct file *) alloca (sizeof (struct file));
DBS (DB_IMPLICIT,
/* 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));
name));
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;
}
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;
/* 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);
}
intermediate_file->name = xstrdup (name);
intermediate_file = 0;
/* A dependency of this rule does not exist.
Therefore, this rule fails. */
break;
}
continue;
}
/* This rule is no longer `in use' for recursive searches. */
/* 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;

4
make.h
View file

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

21
misc.c
View file

@ -525,6 +525,27 @@ copy_dep_chain (struct dep *d)
return firstnew;
}
/* 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);
}
}
#ifdef iAPX286
/* The losing compiler on this machine can't handle this macro. */

258
read.c
View file

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

View 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:

View file

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

View file

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

View file

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

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

View 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);

View 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)