mirror of
https://git.savannah.gnu.org/git/make.git
synced 2025-12-23 00:01:32 -05:00
Implementation of the second expansion in explicit
rules, static pattern rules and implicit rules.
This commit is contained in:
parent
9d153cc1b1
commit
659fc6b55e
46
ChangeLog
46
ChangeLog
@ -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>
|
2005-02-24 Jonathan Grant <jg@jguk.org>
|
||||||
|
|
||||||
* configure.in: Add MinGW configuration options, and extra w32 code
|
* configure.in: Add MinGW configuration options, and extra w32 code
|
||||||
|
|||||||
@ -38,7 +38,7 @@ extern int getpid ();
|
|||||||
|
|
||||||
/* Set FILE's automatic variables up. */
|
/* Set FILE's automatic variables up. */
|
||||||
|
|
||||||
static void
|
void
|
||||||
set_file_variables (struct file *file)
|
set_file_variables (struct file *file)
|
||||||
{
|
{
|
||||||
struct dep *d;
|
struct dep *d;
|
||||||
|
|||||||
@ -40,3 +40,4 @@ extern void execute_file_commands PARAMS ((struct file *file));
|
|||||||
extern void print_commands PARAMS ((struct commands *cmds));
|
extern void print_commands PARAMS ((struct commands *cmds));
|
||||||
extern void delete_child_targets PARAMS ((struct child *child));
|
extern void delete_child_targets PARAMS ((struct child *child));
|
||||||
extern void chop_commands PARAMS ((struct commands *cmds));
|
extern void chop_commands PARAMS ((struct commands *cmds));
|
||||||
|
extern void set_file_variables PARAMS ((struct file *file));
|
||||||
|
|||||||
1
dep.h
1
dep.h
@ -72,6 +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 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
expand.c
2
expand.c
@ -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
|
/* 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. */
|
FILE's commands were found. Expansion uses FILE's variable set list. */
|
||||||
|
|
||||||
static char *
|
char *
|
||||||
variable_expand_for_file (char *line, struct file *file)
|
variable_expand_for_file (char *line, struct file *file)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
|
|||||||
109
file.c
109
file.c
@ -416,12 +416,14 @@ snap_deps (void)
|
|||||||
{
|
{
|
||||||
register struct file *f;
|
register struct file *f;
|
||||||
register struct file *f2;
|
register struct file *f2;
|
||||||
register struct dep *d;
|
register struct dep *d, *d1;
|
||||||
register struct file **file_slot_0;
|
register struct file **file_slot_0;
|
||||||
register struct file **file_slot;
|
register struct file **file_slot;
|
||||||
register struct file **file_end;
|
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 must use hash_dump (), because within this loop
|
||||||
we might add new files to the table, possibly causing
|
we might add new files to the table, possibly causing
|
||||||
an in-situ table expansion. */
|
an in-situ table expansion. */
|
||||||
@ -429,16 +431,99 @@ snap_deps (void)
|
|||||||
file_end = file_slot_0 + files.ht_fill;
|
file_end = file_slot_0 + files.ht_fill;
|
||||||
for (file_slot = file_slot_0; file_slot < file_end; file_slot++)
|
for (file_slot = file_slot_0; file_slot < file_end; file_slot++)
|
||||||
for (f2 = *file_slot; f2 != 0; f2 = f2->prev)
|
for (f2 = *file_slot; f2 != 0; f2 = f2->prev)
|
||||||
for (d = f2->deps; d != 0; d = d->next)
|
{
|
||||||
if (d->name != 0)
|
struct dep *new = 0;
|
||||||
{
|
struct dep *old = f2->deps;
|
||||||
d->file = lookup_file (d->name);
|
unsigned int last_dep_has_cmds = f2->updating;
|
||||||
if (d->file == 0)
|
|
||||||
d->file = enter_file (d->name);
|
f2->updating = 0;
|
||||||
else
|
f2->deps = 0;
|
||||||
free (d->name);
|
|
||||||
d->name = 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);
|
free (file_slot_0);
|
||||||
|
|
||||||
for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
|
for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
|
||||||
|
|||||||
@ -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
|
* If the string is empty or contains nothing but whitespace, endpp will be
|
||||||
* begpp-1.
|
* begpp-1.
|
||||||
*/
|
*/
|
||||||
static char *
|
char *
|
||||||
strip_whitespace (const char **begpp, const char **endpp)
|
strip_whitespace (const char **begpp, const char **endpp)
|
||||||
{
|
{
|
||||||
while (*begpp <= *endpp && isspace ((unsigned char)**begpp))
|
while (*begpp <= *endpp && isspace ((unsigned char)**begpp))
|
||||||
|
|||||||
557
implicit.c
557
implicit.c
@ -22,9 +22,13 @@ Boston, MA 02111-1307, USA. */
|
|||||||
#include "rule.h"
|
#include "rule.h"
|
||||||
#include "dep.h"
|
#include "dep.h"
|
||||||
#include "debug.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,
|
static int
|
||||||
unsigned int recursions));
|
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
|
/* For a FILE which has no commands specified, try to figure out some
|
||||||
from the implicit pattern rules.
|
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
|
/* 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
|
FILE. If a rule is found, the appropriate commands and deps are put in FILE
|
||||||
and 1 is returned. If not, 0 is returned.
|
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. */
|
except during a recursive call. */
|
||||||
struct file *intermediate_file = 0;
|
struct file *intermediate_file = 0;
|
||||||
|
|
||||||
/* List of dependencies found recursively. */
|
/* This linked list records all the prerequisites actually
|
||||||
struct file **intermediate_files
|
found for a rule along with some other useful information
|
||||||
= (struct file **) xmalloc (max_pattern_deps * sizeof (struct file *));
|
(see struct idep for details). */
|
||||||
|
struct idep* deps = 0;
|
||||||
|
|
||||||
/* List of the patterns used to find intermediate files. */
|
/* 1 if we need to remove explicit prerequisites, 0 otherwise. */
|
||||||
char **intermediate_patterns
|
unsigned int remove_explicit_deps = 0;
|
||||||
= (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;
|
|
||||||
|
|
||||||
/* Names of possible dependencies are constructed in this buffer. */
|
/* Names of possible dependencies are constructed in this buffer. */
|
||||||
register char *depname = (char *) alloca (namelen + max_pattern_dep_length);
|
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 unsigned int i = 0; /* uninit checks OK */
|
||||||
register struct rule *rule;
|
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
|
#ifndef NO_ARCHIVES
|
||||||
if (archive || ar_name (filename))
|
if (archive || ar_name (filename))
|
||||||
@ -295,19 +416,27 @@ pattern_search (struct file *file, int archive,
|
|||||||
tryrules[i] = 0;
|
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. */
|
/* Try each rule once without intermediate files, then once with them. */
|
||||||
for (intermed_ok = 0; intermed_ok == !!intermed_ok; ++intermed_ok)
|
for (intermed_ok = 0; intermed_ok == !!intermed_ok; ++intermed_ok)
|
||||||
{
|
{
|
||||||
/* Try each pattern rule till we find one that applies.
|
/* Try each pattern rule till we find one that applies.
|
||||||
If it does, copy the names of its dependencies (as substituted)
|
If it does, expand its dependencies (as substituted)
|
||||||
and store them in FOUND_FILES. DEPS_FOUND is the number of them. */
|
and chain them in DEPS. */
|
||||||
|
|
||||||
for (i = 0; i < nrules; i++)
|
for (i = 0; i < nrules; i++)
|
||||||
{
|
{
|
||||||
|
struct file *f;
|
||||||
|
unsigned int failed = 0;
|
||||||
int check_lastslash;
|
int check_lastslash;
|
||||||
|
|
||||||
rule = tryrules[i];
|
rule = tryrules[i];
|
||||||
|
|
||||||
|
remove_explicit_deps = 0;
|
||||||
|
|
||||||
/* RULE is nil when we discover that a rule,
|
/* RULE is nil when we discover that a rule,
|
||||||
already placed in TRYRULES, should not be applied. */
|
already placed in TRYRULES, should not be applied. */
|
||||||
if (rule == 0)
|
if (rule == 0)
|
||||||
@ -337,149 +466,258 @@ pattern_search (struct file *file, int archive,
|
|||||||
DBS (DB_IMPLICIT, (_("Trying pattern rule with stem `%.*s'.\n"),
|
DBS (DB_IMPLICIT, (_("Trying pattern rule with stem `%.*s'.\n"),
|
||||||
(int) stemlen, stem));
|
(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". */
|
/* 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)
|
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. */
|
/* In an ideal world we would take the dependency line,
|
||||||
p = strchr (dep_name (dep), '%');
|
substitute the stem, re-expand the whole line and
|
||||||
if (p != 0)
|
chop it into individual prerequisites. Unfortunately
|
||||||
{
|
this won't work because of the "check_lastslash" twist.
|
||||||
register unsigned int i;
|
Instead, we will have to go word by word, taking $()'s
|
||||||
if (check_lastslash)
|
into account, for each word we will substitute the stem,
|
||||||
{
|
re-expand, chop it up, and, if check_lastslash != 0,
|
||||||
/* Copy directory name from the original FILENAME. */
|
add the directory part to each resulting prerequisite. */
|
||||||
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);
|
|
||||||
|
|
||||||
/* P is now the actual dependency name as substituted. */
|
p = get_next_word (dep->name, &len);
|
||||||
|
|
||||||
if (file_impossible_p (p))
|
while (1)
|
||||||
{
|
{
|
||||||
/* If this dependency has already been ruled
|
int add_dir = 0;
|
||||||
"impossible", then the rule fails and don't
|
int had_stem = 0;
|
||||||
bother trying it on the second pass either
|
|
||||||
since we know that will fail too. */
|
if (p == 0)
|
||||||
DBS (DB_IMPLICIT,
|
break; /* No more words */
|
||||||
(p == depname
|
|
||||||
|
/* 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 implicit prerequisite `%s'.\n")
|
||||||
: _("Rejecting impossible rule prerequisite `%s'.\n"),
|
: _("Rejecting impossible rule prerequisite `%s'.\n"),
|
||||||
p));
|
name));
|
||||||
tryrules[i] = 0;
|
tryrules[i] = 0;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate_files[deps_found] = 0;
|
failed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
DBS (DB_IMPLICIT,
|
DBS (DB_IMPLICIT,
|
||||||
(p == depname
|
(d->had_stem
|
||||||
? _("Trying implicit prerequisite `%s'.\n")
|
? _("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
|
/* If this prerequisite also happened to be explicitly
|
||||||
nonexistent directory. So we normally can skip looking for
|
mentioned for FILE skip all the test below since it
|
||||||
the file. However, if CHECK_LASTSLASH is set, then the
|
it has to be built anyway, no matter which implicit
|
||||||
dependency file we are actually looking for is in a different
|
rule we choose. */
|
||||||
directory (the one gotten by prepending FILENAME's directory),
|
|
||||||
so it might actually exist. */
|
|
||||||
|
|
||||||
if (((f = lookup_file (p)) != 0 && f->is_target)
|
for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next)
|
||||||
|| ((!dep->changed || check_lastslash) && file_exists_p (p)))
|
if (strcmp (dep_name (expl_d), name) == 0) break;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We could not find the file in any place we should look.
|
if (expl_d != 0)
|
||||||
Try to make this dependency as an intermediate file,
|
continue;
|
||||||
but only on the second pass. */
|
|
||||||
|
|
||||||
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"),
|
(_("Looking for a rule with intermediate file `%s'.\n"),
|
||||||
p));
|
name));
|
||||||
|
|
||||||
bzero ((char *) intermediate_file, sizeof (struct file));
|
bzero ((char *) intermediate_file, sizeof (struct file));
|
||||||
intermediate_file->name = p;
|
intermediate_file->name = name;
|
||||||
if (pattern_search (intermediate_file, 0, depth + 1,
|
if (pattern_search (intermediate_file,
|
||||||
recursions + 1))
|
0,
|
||||||
{
|
depth + 1,
|
||||||
p = xstrdup (p);
|
recursions + 1))
|
||||||
intermediate_patterns[deps_found]
|
{
|
||||||
= intermediate_file->name;
|
d->intermediate_file = intermediate_file;
|
||||||
intermediate_file->name = p;
|
d->intermediate_pattern = intermediate_file->name;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have tried to find P as an intermediate
|
intermediate_file->name = xstrdup (name);
|
||||||
file and failed, mark that name as impossible
|
intermediate_file = 0;
|
||||||
so we won't go through the search again later. */
|
|
||||||
file_impossible (p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A dependency of this rule does not exist.
|
continue;
|
||||||
Therefore, this rule fails. */
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
rule->in_use = 0;
|
||||||
|
|
||||||
if (dep != 0)
|
if (failed)
|
||||||
{
|
{
|
||||||
/* This pattern rule does not apply.
|
/* This pattern rule does not apply. If some of its
|
||||||
If some of its dependencies succeeded,
|
dependencies succeeded, free the data structure
|
||||||
free the data structure describing them. */
|
describing them. */
|
||||||
while (deps_found-- > 0)
|
free_idep_chain (deps);
|
||||||
{
|
deps = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
/* This pattern rule does apply. Stop looking for one. */
|
/* This pattern rule does apply. Stop looking for one. */
|
||||||
break;
|
break;
|
||||||
@ -511,11 +749,30 @@ pattern_search (struct file *file, int archive,
|
|||||||
This includes the intermediate files, if any.
|
This includes the intermediate files, if any.
|
||||||
Convert them into entries on the deps-chain of FILE. */
|
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;
|
register char *s;
|
||||||
|
|
||||||
if (intermediate_files[deps_found] != 0)
|
if (d->intermediate_file != 0)
|
||||||
{
|
{
|
||||||
/* If we need to use an intermediate file,
|
/* If we need to use an intermediate file,
|
||||||
make sure it is entered as a target, with the info that was
|
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
|
a target; therefore we can assume that the deps and cmds
|
||||||
of F below are null before we change them. */
|
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);
|
register struct file *f = enter_file (imf->name);
|
||||||
f->deps = imf->deps;
|
f->deps = imf->deps;
|
||||||
f->cmds = imf->cmds;
|
f->cmds = imf->cmds;
|
||||||
f->stem = imf->stem;
|
f->stem = imf->stem;
|
||||||
f->also_make = imf->also_make;
|
f->also_make = imf->also_make;
|
||||||
imf = lookup_file (intermediate_patterns[deps_found]);
|
imf = lookup_file (d->intermediate_pattern);
|
||||||
if (imf != 0 && imf->precious)
|
if (imf != 0 && imf->precious)
|
||||||
f->precious = 1;
|
f->precious = 1;
|
||||||
f->intermediate = 1;
|
f->intermediate = 1;
|
||||||
@ -547,8 +804,9 @@ pattern_search (struct file *file, int archive,
|
|||||||
}
|
}
|
||||||
|
|
||||||
dep = (struct dep *) xmalloc (sizeof (struct dep));
|
dep = (struct dep *) xmalloc (sizeof (struct dep));
|
||||||
dep->ignore_mtime = found_files_im[deps_found];
|
dep->ignore_mtime = d->ignore_mtime;
|
||||||
s = found_files[deps_found];
|
s = d->name; /* Hijacking the name. */
|
||||||
|
d->name = 0;
|
||||||
if (recursions == 0)
|
if (recursions == 0)
|
||||||
{
|
{
|
||||||
dep->name = 0;
|
dep->name = 0;
|
||||||
@ -567,7 +825,7 @@ pattern_search (struct file *file, int archive,
|
|||||||
dep->file = 0;
|
dep->file = 0;
|
||||||
dep->changed = 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),
|
/* If the file actually existed (was not an intermediate file),
|
||||||
and the rule that found it was a terminal one, then we want
|
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
|
else
|
||||||
dep->file->tried_implicit = 1;
|
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])
|
if (!checked_lastslash[foundrule])
|
||||||
{
|
{
|
||||||
/* Always allocate new storage, since STEM might be
|
/* Always allocate new storage, since STEM might be
|
||||||
@ -629,7 +890,7 @@ pattern_search (struct file *file, int archive,
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
free (intermediate_files);
|
free_idep_chain (deps);
|
||||||
free (tryrules);
|
free (tryrules);
|
||||||
|
|
||||||
return rule != 0;
|
return rule != 0;
|
||||||
|
|||||||
4
make.h
4
make.h
@ -460,6 +460,10 @@ extern void user_access PARAMS ((void));
|
|||||||
extern void make_access PARAMS ((void));
|
extern void make_access PARAMS ((void));
|
||||||
extern void child_access PARAMS ((void));
|
extern void child_access PARAMS ((void));
|
||||||
|
|
||||||
|
extern char *
|
||||||
|
strip_whitespace PARAMS ((const char **begpp, const char **endpp));
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_VFORK_H
|
#ifdef HAVE_VFORK_H
|
||||||
# include <vfork.h>
|
# include <vfork.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
21
misc.c
21
misc.c
@ -525,6 +525,27 @@ copy_dep_chain (struct dep *d)
|
|||||||
return firstnew;
|
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
|
#ifdef iAPX286
|
||||||
/* The losing compiler on this machine can't handle this macro. */
|
/* The losing compiler on this machine can't handle this macro. */
|
||||||
|
|
||||||
|
|||||||
258
read.c
258
read.c
@ -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,
|
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,
|
||||||
int have_sysv_atvar,
|
|
||||||
const struct floc *flocp, int set_default));
|
const struct floc *flocp, int set_default));
|
||||||
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,
|
||||||
@ -457,7 +456,6 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
unsigned int cmds_started, tgts_started;
|
unsigned int cmds_started, tgts_started;
|
||||||
int ignoring = 0, in_ignored_define = 0;
|
int ignoring = 0, in_ignored_define = 0;
|
||||||
int no_targets = 0; /* Set when reading a rule without targets. */
|
int no_targets = 0; /* Set when reading a rule without targets. */
|
||||||
int have_sysv_atvar = 0;
|
|
||||||
struct nameseq *filenames = 0;
|
struct nameseq *filenames = 0;
|
||||||
struct dep *deps = 0;
|
struct dep *deps = 0;
|
||||||
long nlines = 0;
|
long nlines = 0;
|
||||||
@ -474,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, \
|
||||||
have_sysv_atvar, &fi, set_default); \
|
&fi, set_default); \
|
||||||
} \
|
} \
|
||||||
filenames = 0; \
|
filenames = 0; \
|
||||||
commands_idx = 0; \
|
commands_idx = 0; \
|
||||||
@ -869,6 +867,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
char *cmdleft, *semip, *lb_next;
|
char *cmdleft, *semip, *lb_next;
|
||||||
unsigned int len, plen = 0;
|
unsigned int len, plen = 0;
|
||||||
char *colonp;
|
char *colonp;
|
||||||
|
const char *end, *beg; /* Helpers for whitespace stripping. */
|
||||||
|
|
||||||
/* Record the previous rule. */
|
/* Record the previous rule. */
|
||||||
|
|
||||||
@ -917,6 +916,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
}
|
}
|
||||||
|
|
||||||
p2 = variable_expand_string(NULL, lb_next, len);
|
p2 = variable_expand_string(NULL, lb_next, len);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
lb_next += len;
|
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; ...'? */
|
/* Is this a static pattern rule: `target: %targ: %dep; ...'? */
|
||||||
p = strchr (p2, ':');
|
p = strchr (p2, ':');
|
||||||
while (p != 0 && p[-1] == '\\')
|
while (p != 0 && p[-1] == '\\')
|
||||||
@ -1168,26 +1158,20 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
else
|
else
|
||||||
pattern = 0;
|
pattern = 0;
|
||||||
|
|
||||||
/* Parse the dependencies. */
|
/* Strip leading and trailing whitespaces. */
|
||||||
deps = (struct dep *)
|
beg = p2;
|
||||||
multi_glob (parse_file_seq (&p2, '|', sizeof (struct dep), 1),
|
end = beg + strlen (beg) - 1;
|
||||||
sizeof (struct dep));
|
strip_whitespace (&beg, &end);
|
||||||
if (*p2)
|
|
||||||
|
if (beg <= end && *beg != '\0')
|
||||||
{
|
{
|
||||||
/* Files that follow '|' are special prerequisites that
|
deps = (struct dep*) xmalloc (sizeof (struct dep));
|
||||||
need only exist in order to satisfy the dependency.
|
deps->next = 0;
|
||||||
Their modification times are irrelevant. */
|
deps->name = savestring (beg, end - beg + 1);
|
||||||
struct dep **deps_ptr = &deps;
|
deps->file = 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
deps = 0;
|
||||||
|
|
||||||
commands_idx = 0;
|
commands_idx = 0;
|
||||||
if (cmdleft != 0)
|
if (cmdleft != 0)
|
||||||
@ -1753,7 +1737,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,
|
||||||
int have_sysv_atvar, const struct floc *flocp, int set_default)
|
const struct floc *flocp, int set_default)
|
||||||
{
|
{
|
||||||
struct nameseq *nextf;
|
struct nameseq *nextf;
|
||||||
int implicit = 0;
|
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
|
/* We use patsubst_expand to do the work of translating
|
||||||
the target pattern, the target's name and the dependencies'
|
the target pattern, the target's name and the dependencies'
|
||||||
patterns into plain dependency names. */
|
patterns into plain dependency names. */
|
||||||
char *buffer = variable_expand ("");
|
|
||||||
|
|
||||||
for (d = this; d != 0; d = d->next)
|
if (find_percent (this->name) != 0)
|
||||||
{
|
{
|
||||||
char *o;
|
char stem[PATH_MAX];
|
||||||
char *percent = find_percent (d->name);
|
char *o;
|
||||||
if (percent == 0)
|
char *buffer = variable_expand ("");
|
||||||
continue;
|
|
||||||
o = patsubst_expand (buffer, name, pattern, d->name,
|
o = patsubst_expand (buffer, name, pattern, "%",
|
||||||
pattern_percent+1, percent+1);
|
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
|
/* If the name expanded to the empty string, that's
|
||||||
illegal. */
|
illegal. */
|
||||||
if (o == buffer)
|
if (o == buffer)
|
||||||
fatal (flocp,
|
fatal (flocp,
|
||||||
_("target `%s' leaves prerequisite pattern empty"),
|
_("target `%s' leaves prerequisite pattern empty"),
|
||||||
name);
|
name);
|
||||||
free (d->name);
|
free (this->name);
|
||||||
d->name = savestring (buffer, o - buffer);
|
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)
|
if (!two_colon)
|
||||||
{
|
{
|
||||||
/* Single-colon. Combine these dependencies
|
/* Single-colon. Combine these dependencies
|
||||||
@ -2003,6 +1896,7 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
|
|||||||
f->cmds = 0;
|
f->cmds = 0;
|
||||||
if (cmds != 0)
|
if (cmds != 0)
|
||||||
f->cmds = cmds;
|
f->cmds = cmds;
|
||||||
|
|
||||||
/* Defining .SUFFIXES with no dependencies
|
/* Defining .SUFFIXES with no dependencies
|
||||||
clears out the list of suffixes. */
|
clears out the list of suffixes. */
|
||||||
if (f == suffix_file && this == 0)
|
if (f == suffix_file && this == 0)
|
||||||
@ -2017,41 +1911,63 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
|
|||||||
}
|
}
|
||||||
f->deps = 0;
|
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. */
|
/* Add the file's old deps and the new ones in THIS together. */
|
||||||
|
|
||||||
struct dep *firstdeps, *moredeps;
|
if (f->deps != 0)
|
||||||
if (cmds != 0)
|
{
|
||||||
{
|
struct dep **d_ptr = &f->deps;
|
||||||
/* 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 (firstdeps == 0)
|
while ((*d_ptr)->next != 0)
|
||||||
firstdeps = moredeps;
|
d_ptr = &(*d_ptr)->next;
|
||||||
else
|
|
||||||
{
|
|
||||||
d = firstdeps;
|
|
||||||
while (d->next != 0)
|
|
||||||
d = d->next;
|
|
||||||
d->next = moredeps;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
/* If this is a static pattern rule, set the file's stem to
|
||||||
the part of its name that matched the `%' in the pattern,
|
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)
|
if (!reject)
|
||||||
default_goal_file = f;
|
default_goal_file = f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
2005-02-09 Paul D. Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* scripts/features/recursion: Test command line variable settings:
|
* scripts/features/recursion: Test command line variable settings:
|
||||||
|
|||||||
105
tests/scripts/features/se_explicit
Normal file
105
tests/scripts/features/se_explicit
Normal 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;
|
||||||
188
tests/scripts/features/se_implicit
Normal file
188
tests/scripts/features/se_implicit
Normal 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;
|
||||||
106
tests/scripts/features/se_statpat
Normal file
106
tests/scripts/features/se_statpat
Normal 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;
|
||||||
@ -6,9 +6,6 @@ which have either broken at some point in the past or seem likely to
|
|||||||
break.";
|
break.";
|
||||||
|
|
||||||
open(MAKEFILE,"> $makefile");
|
open(MAKEFILE,"> $makefile");
|
||||||
|
|
||||||
# The contents of the Makefile ...
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
print MAKEFILE <<'EOF';
|
||||||
# Make sure that subdirectories built as prerequisites are actually handled
|
# Make sure that subdirectories built as prerequisites are actually handled
|
||||||
# properly.
|
# 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 $< $@
|
dir/subdir/%.a: dir/subdir/%.b ; @echo cp $< $@
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
close(MAKEFILE);
|
close(MAKEFILE);
|
||||||
|
|
||||||
&run_make_with_options($makefile,"",&get_logfile);
|
&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";
|
$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));
|
&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;
|
1;
|
||||||
|
|||||||
@ -67,11 +67,11 @@ EOF
|
|||||||
close(MAKEFILE);
|
close(MAKEFILE);
|
||||||
|
|
||||||
&run_make_with_options($makefile2, "$dir/foo $dir/bar", &get_logfile);
|
&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));
|
&compare_output($answer, &get_logfile(1));
|
||||||
|
|
||||||
&run_make_with_options($makefile2, "$dir/x.z $dir/y.z", &get_logfile);
|
&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));
|
&compare_output($answer, &get_logfile(1));
|
||||||
|
|
||||||
&run_make_with_options($makefile2, "$dir/biz", &get_logfile);
|
&run_make_with_options($makefile2, "$dir/biz", &get_logfile);
|
||||||
|
|||||||
@ -112,6 +112,7 @@ extern struct variable_set_list *current_variable_set_list;
|
|||||||
/* expand.c */
|
/* expand.c */
|
||||||
extern char *variable_buffer_output PARAMS ((char *ptr, char *string, unsigned int length));
|
extern char *variable_buffer_output PARAMS ((char *ptr, char *string, unsigned int length));
|
||||||
extern char *variable_expand PARAMS ((char *line));
|
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));
|
extern char *allocated_variable_expand_for_file PARAMS ((char *line, struct file *file));
|
||||||
#define allocated_variable_expand(line) \
|
#define allocated_variable_expand(line) \
|
||||||
allocated_variable_expand_for_file (line, (struct file *) 0)
|
allocated_variable_expand_for_file (line, (struct file *) 0)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user