Compare commits

...

10 Commits

Author SHA1 Message Date
Tim Magill
646aeb50a1 * remake.c:
(update_file_1):
	(check_dep): fixed problem with double colon rules and circular
	precursor detection.

	* remake.c (update_file_1): simplified handling of files with no
	command into a single block.

	* remake.c (notice_finished_file): fixed obsolete comment.
2000-03-14 04:30:07 +00:00
Tim Magill
fed90f1fa8 * remake.c (update_goal_chain): re-arranged logic so that it makes
more sense.  After update_file(), the target status is used to
	handle the goal and possibly remove it from the goal chain.

	* remake.c (update_file): If -q we can bail as soon as we detect
	that something needs to be remade.
2000-03-12 19:50:20 +00:00
Tim Magill
6a1a4343f3 more cleanup 2000-03-11 23:27:28 +00:00
Tim Magill
e27c3fc91b more cleanup 2000-03-11 23:19:51 +00:00
Tim Magill
9f3539c4ae removed excess loop in update_goal_chain() 2000-03-01 04:46:33 +00:00
Tim Magill
1b40ffb900 changed return type of recursive update functions 2000-02-21 04:17:36 +00:00
Tim Magill
0d621b0627 removed essentially unused variable from update_goal_chain 2000-02-21 02:02:58 +00:00
Tim Magill
5ed230d3f8 cleaned up usage of filedef->update_status 2000-02-13 04:24:24 +00:00
Tim Magill
b0e3148933 minor cleanup 2000-02-12 22:32:30 +00:00
Tim Magill
3b4369f170 removed updated member from struct filedef 2000-02-09 05:49:41 +00:00
7 changed files with 359 additions and 250 deletions

116
ChangeLog
View File

@@ -1,3 +1,119 @@
2000-03-13 Tim Magill <magill@gate.net>
* remake.c:
(update_file_1):
(check_dep): fixed problem with double colon rules and circular
precursor detection.
2000-03-12 Tim Magill <magill@gate.net>
* remake.c (update_goal_chain): re-arranged logic so that it makes
more sense. After update_file(), the target status is used to
handle the goal and possibly remove it from the goal chain.
* remake.c (update_file_1): simplified handling of files with no
command into a single block.
* remake.c (notice_finished_file): fixed obsolete comment.
* remake.c (update_file): If -q we can bail as soon as we detect
that something needs to be remade.
2000-03-11 Tim Magill <magill@gate.net>
* remake.c
(update_file): If not -k, we can bail when a failure is detected.
If we don't, its possible that dependecies of double_colon rules
will get built after a failure has been detected.
(update_goal_chain): removed unused updating_makefiles
(update_file_1): cleaned up post remake_file() handling
2000-02-29 Tim Magill <magill@gate.net>
* remake.c (update_goal_chain): eliminated double colon loop.
update_file() handles the double colon logic, so
update_goal_chain() doesn't have to.
* remake.c (update_file):
(update_file_1): Moved consedered test to update_file_1() so
update_file() can more easily return a target_state_t which
represents the state of the entire double colon rule.
2000-02-20 Tim Magill <magill@gate.net>
* remake.c (check_dep):
(update_file_1):
(update_goal_chain):
(check_dep): Changed return value of target updating functions to
be enum target_state_t. This allows each caller to know the state
of the target being processed after each call. Thus,
update_goal_chain() can know from the return value of
update_file() whether or not a target has completed and can be
removed from the goal chain.
* remake.c (update_goal_chain): removed variable
ocommands_started, it was essentially unused.
2000-02-12 Tim Magill <magill@gate.net>
* remake.c (update_file_1):
(notice_finished_file):
(remake_file):
* main.c (main):
* job.c (reap_children):
(start_job_command):
* file.c (enter_file)
(remove_intermediates):
(print_file):
* filedef.h: converted update_status field from a short int, to an
unsigned int :1. previously, update status indicated whether the
file had actually executed any commands, won, lost, or lost under
-q conditions. command_state indicates more clearly whether or
not any commands have been executed for this file. lost or lost
under -q conditions can be determined by a combination of
question_flag and lost. Thus, update_status needs only indicate
won or lost.
* implicit.c (try_implicit_rule):
* remake.c (check_dep):
* remake.c (update_file_1):
move setting of tried_implicit into try_implicit_rule(). It is
only called in two places and the flag is set immediately after.
* remake.c (update_file_1): collapsed if statement based on
file->command_state into switch statment which immediately
follows. Removed code that could not be reached.
* remake.c (update_file): advance f pointer so as not to consider
same file twice after breaking loop.
* file.c (rehash_file): replaced hard coded check_renamed loop
with standard macro. seemed like the right thing to do.
2000-02-09 Tim Magill <magill@gate.net>
Started branch for cleaning up internal make structure regarding
recursion paths. Branch name is filedef-cleanup and was started
from a point on the mainline at filedef-cleanup-base. either tags
are suitable for cvs commands.
* file.c, filedef.h, main.c, remake.c (updated): Removed updated
member from 'struct filedef'. filedef->command_state fully
contains the same information and more.
2000-02-07 Paul D. Smith <psmith@gnu.org> 2000-02-07 Paul D. Smith <psmith@gnu.org>
For += target-specific variables we need to remember which For += target-specific variables we need to remember which

27
file.c
View File

@@ -166,7 +166,7 @@ enter_file (name)
new = (struct file *) xmalloc (sizeof (struct file)); new = (struct file *) xmalloc (sizeof (struct file));
bzero ((char *) new, sizeof (struct file)); bzero ((char *) new, sizeof (struct file));
new->name = new->hname = name; new->name = new->hname = name;
new->update_status = -1; new->update_status = 0; /* assume the file won */
if (f == 0) if (f == 0)
{ {
@@ -199,8 +199,7 @@ rehash_file (file, name)
register unsigned int oldhash; register unsigned int oldhash;
register char *n; register char *n;
while (file->renamed != 0) check_renamed(file);
file = file->renamed;
/* Find the hash values of the old and new names. */ /* Find the hash values of the old and new names. */
@@ -361,7 +360,6 @@ in favor of those for `%s'."),
MERGE (precious); MERGE (precious);
MERGE (tried_implicit); MERGE (tried_implicit);
MERGE (updating); MERGE (updating);
MERGE (updated);
MERGE (is_target); MERGE (is_target);
MERGE (cmd_target); MERGE (cmd_target);
MERGE (phony); MERGE (phony);
@@ -397,7 +395,7 @@ remove_intermediates (sig)
&& !f->secondary) && !f->secondary)
{ {
int status; int status;
if (f->update_status == -1) if (f->command_state == cs_not_started)
/* If nothing would have created this file yet, /* If nothing would have created this file yet,
don't print an "rm" command for it. */ don't print an "rm" command for it. */
continue; continue;
@@ -659,7 +657,7 @@ print_file (f)
file_timestamp_sprintf (buf, f->last_mtime); file_timestamp_sprintf (buf, f->last_mtime);
printf (_("# Last modified %s\n"), buf); printf (_("# Last modified %s\n"), buf);
} }
puts (f->updated puts ((f->command_state == cs_finished)
? _("# File has been updated.") : _("# File has not been updated.")); ? _("# File has been updated.") : _("# File has not been updated."));
switch (f->command_state) switch (f->command_state)
{ {
@@ -670,26 +668,19 @@ print_file (f)
puts (_("# Dependencies commands running (THIS IS A BUG).")); puts (_("# Dependencies commands running (THIS IS A BUG)."));
break; break;
case cs_not_started: case cs_not_started:
break;
case cs_finished: case cs_finished:
switch (f->update_status) switch (f->update_status)
{ {
case -1:
break;
case 0: case 0:
/* file won */
puts (_("# Successfully updated.")); puts (_("# Successfully updated."));
break; break;
case 1: case 1:
assert (question_flag); /* file lost */
puts (_("# Needs to be updated (-q is set).")); puts (question_flag ? _("# Needs to be updated (-q is set).")
:_("# Failed to be updated."));
break; break;
case 2:
puts (_("# Failed to be updated."));
break;
default:
puts (_("# Invalid value in `update_status' member!"));
fflush (stdout);
fflush (stderr);
abort ();
} }
break; break;
default: default:

View File

@@ -60,9 +60,6 @@ struct file
the same file. Otherwise this is null. */ the same file. Otherwise this is null. */
struct file *double_colon; struct file *double_colon;
short int update_status; /* Status of the last attempt to update,
or -1 if none has been made. */
enum /* State of the commands. */ enum /* State of the commands. */
{ /* Note: It is important that cs_not_started be zero. */ { /* Note: It is important that cs_not_started be zero. */
cs_not_started, /* Not yet started. */ cs_not_started, /* Not yet started. */
@@ -71,12 +68,15 @@ struct file
cs_finished /* Commands finished. */ cs_finished /* Commands finished. */
} command_state ENUM_BITFIELD (2); } command_state ENUM_BITFIELD (2);
unsigned int update_status:1; /* update status signifies only
whether the file won or lost */
unsigned int precious:1; /* Non-0 means don't delete file on quit */ unsigned int precious:1; /* Non-0 means don't delete file on quit */
unsigned int tried_implicit:1; /* Nonzero if have searched unsigned int tried_implicit:1; /* Nonzero if have searched
for implicit rule for making for implicit rule for making
this file; don't search again. */ this file; don't search again. */
unsigned int updating:1; /* Nonzero while updating deps of this file */ unsigned int updating:1; /* Nonzero while updating deps of this file */
unsigned int updated:1; /* Nonzero if this file has been remade. */
unsigned int is_target:1; /* Nonzero if file is described as target. */ unsigned int is_target:1; /* Nonzero if file is described as target. */
unsigned int cmd_target:1; /* Nonzero if file was given on cmd line. */ unsigned int cmd_target:1; /* Nonzero if file was given on cmd line. */
unsigned int phony:1; /* Nonzero if this is a phony file unsigned int phony:1; /* Nonzero if this is a phony file

View File

@@ -37,6 +37,7 @@ try_implicit_rule (file, depth)
struct file *file; struct file *file;
unsigned int depth; unsigned int depth;
{ {
int ret = 0;
DBF (DB_IMPLICIT, _("Looking for an implicit rule for `%s'.\n")); DBF (DB_IMPLICIT, _("Looking for an implicit rule for `%s'.\n"));
/* The order of these searches was previously reversed. My logic now is /* The order of these searches was previously reversed. My logic now is
@@ -45,21 +46,24 @@ try_implicit_rule (file, depth)
should come first. */ should come first. */
if (pattern_search (file, 0, depth, 0)) if (pattern_search (file, 0, depth, 0))
return 1; ret = 1;
#ifndef NO_ARCHIVES #ifndef NO_ARCHIVES
/* If this is an archive member reference, use just the /* If this is an archive member reference, use just the
archive member name to search for implicit rules. */ archive member name to search for implicit rules. */
if (ar_name (file->name)) else if (ar_name (file->name))
{ {
DBF (DB_IMPLICIT, DBF (DB_IMPLICIT,
_("Looking for archive-member implicit rule for `%s'.\n")); _("Looking for archive-member implicit rule for `%s'.\n"));
if (pattern_search (file, 1, depth, 0)) if (pattern_search (file, 1, depth, 0))
return 1; {
ret = 1;
}
} }
#endif #endif
return 0; file->tried_implicit = 1;
return ret;
} }

6
job.c
View File

@@ -665,7 +665,7 @@ reap_children (block, err)
delete non-precious targets, and abort. */ delete non-precious targets, and abort. */
static int delete_on_error = -1; static int delete_on_error = -1;
child_error (c->file->name, exit_code, exit_sig, coredump, 0); child_error (c->file->name, exit_code, exit_sig, coredump, 0);
c->file->update_status = 2; c->file->update_status = 1;
if (delete_on_error == -1) if (delete_on_error == -1)
{ {
struct file *f = lookup_file (".DELETE_ON_ERROR"); struct file *f = lookup_file (".DELETE_ON_ERROR");
@@ -693,7 +693,7 @@ reap_children (block, err)
Since there are more commands that wanted to be run, Since there are more commands that wanted to be run,
the target was not completely remade. So we treat the target was not completely remade. So we treat
this as if a command had failed. */ this as if a command had failed. */
c->file->update_status = 2; c->file->update_status = 1;
} }
else else
{ {
@@ -1250,7 +1250,7 @@ start_job_command (child)
return; return;
error: error:
child->file->update_status = 2; child->file->update_status = 1;
notice_finished_file (child->file); notice_finished_file (child->file);
return; return;
} }

25
main.c
View File

@@ -1254,7 +1254,6 @@ int main (int argc, char ** argv)
/* Make sure the temporary file will not be remade. */ /* Make sure the temporary file will not be remade. */
stdin_nm = savestring (name, sizeof (name) -1); stdin_nm = savestring (name, sizeof (name) -1);
f = enter_file (stdin_nm); f = enter_file (stdin_nm);
f->updated = 1;
f->update_status = 0; f->update_status = 0;
f->command_state = cs_finished; f->command_state = cs_finished;
/* Can't be intermediate, or it'll be removed too early for /* Can't be intermediate, or it'll be removed too early for
@@ -1518,7 +1517,6 @@ int main (int argc, char ** argv)
{ {
f = enter_command_line_file (*p); f = enter_command_line_file (*p);
f->last_mtime = f->mtime_before_update = (FILE_TIMESTAMP) 1; f->last_mtime = f->mtime_before_update = (FILE_TIMESTAMP) 1;
f->updated = 1;
f->update_status = 0; f->update_status = 0;
f->command_state = cs_finished; f->command_state = cs_finished;
} }
@@ -1606,17 +1604,11 @@ int main (int argc, char ** argv)
switch (update_goal_chain (read_makefiles, 1)) switch (update_goal_chain (read_makefiles, 1))
{ {
case 1:
default:
#define BOGUS_UPDATE_STATUS 0
assert (BOGUS_UPDATE_STATUS);
break;
case -1: case -1:
/* Did nothing. */ /* Did nothing. */
break; break;
case 2: case 1:
/* Failed to update. Figure out if we care. */ /* Failed to update. Figure out if we care. */
{ {
/* Nonzero if any makefile was successfully remade. */ /* Nonzero if any makefile was successfully remade. */
@@ -1633,7 +1625,7 @@ int main (int argc, char ** argv)
again to print an error. */ again to print an error. */
d->file->considered = 0; d->file->considered = 0;
if (d->file->updated) if (d->file->command_state == cs_finished)
{ {
/* This makefile was updated. */ /* This makefile was updated. */
if (d->file->update_status == 0) if (d->file->update_status == 0)
@@ -1836,18 +1828,19 @@ int main (int argc, char ** argv)
{ {
case -1: case -1:
/* Nothing happened. */ /* Nothing happened. */
/* FALL THRU */
case 0: case 0:
/* Updated successfully. */ /* Updated successfully. */
status = EXIT_SUCCESS; status = EXIT_SUCCESS;
break; break;
case 2:
/* Updating failed. POSIX.2 specifies exit status >1 for this;
but in VMS, there is only success and failure. */
status = EXIT_FAILURE ? 2 : EXIT_FAILURE;
break;
case 1: case 1:
if (question_flag)
/* We are under -q and would run some commands. */ /* We are under -q and would run some commands. */
status = EXIT_FAILURE; status = EXIT_FAILURE;
else
/* Updating failed. POSIX.2 specifies exit status >1 for this;
but in VMS, there is only success and failure. */
status = EXIT_FAILURE ? 2 : EXIT_FAILURE;
break; break;
default: default:
abort (); abort ();

415
remake.c
View File

@@ -43,12 +43,39 @@ Boston, MA 02111-1307, USA. */
extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth)); extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth));
/* this enum encapsulates the return status of the recursive update
functions. Note: these values are important so that the state of
precursors can be or'ed together */
typedef enum {
ts_done = 0, /* all precursors and target are complete */
ts_incomplete = 1, /* some precursor or target is still running */
ts_failed = 3, /* some precursor or target failed */
ts_sentinal /* make ANSI happy with non trailing , */
} target_state_t;
/* the test for circular dependencies is based just on the 'updating'
bit in 'struct file'. However double colon targets have seperate
'struct file's. Therefore, we will lose if we don't notice that a
double colon targets are really the same file. Use the 'struct
file' at the base of the double colon chain. */
#define set_updating(file) (((file)->double_colon ? \
(file)->double_colon : (file))->updating = 1)
#define clr_updating(file) (((file)->double_colon ? \
(file)->double_colon : (file))->updating = 0)
#define tst_updating(file) (((file)->double_colon ? \
(file)->double_colon : (file))->updating)
/* Incremented when a command is started (under -n, when one would be). */ /* Incremented when a command is started (under -n, when one would be). */
unsigned int commands_started = 0; unsigned int commands_started = 0;
static int update_file PARAMS ((struct file *file, unsigned int depth)); static target_state_t update_file PARAMS ((struct file *file, unsigned int depth));
static int update_file_1 PARAMS ((struct file *file, unsigned int depth)); static target_state_t update_file_1 PARAMS ((struct file *file, unsigned int depth));
static int check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr)); static target_state_t check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr));
static int touch_file PARAMS ((struct file *file)); static int touch_file PARAMS ((struct file *file));
static void remake_file PARAMS ((struct file *file)); static void remake_file PARAMS ((struct file *file));
static FILE_TIMESTAMP name_mtime PARAMS ((char *name)); static FILE_TIMESTAMP name_mtime PARAMS ((char *name));
@@ -62,8 +89,6 @@ static int library_search PARAMS ((char **lib, FILE_TIMESTAMP *mtime_ptr));
should only make one goal at a time and return as soon as one goal whose should only make one goal at a time and return as soon as one goal whose
`changed' member is nonzero is successfully made. */ `changed' member is nonzero is successfully made. */
/* We need to know this "lower down" for correct error handling. */
static int updating_makefiles = 0;
int int
update_goal_chain (goals, makefiles) update_goal_chain (goals, makefiles)
@@ -74,10 +99,6 @@ update_goal_chain (goals, makefiles)
unsigned int j = job_slots; unsigned int j = job_slots;
int status = -1; int status = -1;
updating_makefiles = makefiles;
#define MTIME(file) (makefiles ? file_mtime_no_search (file) \
: file_mtime (file))
/* Duplicate the chain so we can remove things from it. */ /* Duplicate the chain so we can remove things from it. */
@@ -117,103 +138,73 @@ update_goal_chain (goals, makefiles)
{ {
/* Iterate over all double-colon entries for this file. */ /* Iterate over all double-colon entries for this file. */
struct file *file; struct file *file;
int stop = 0, any_not_updated = 0; target_state_t target_state;
for (file = g->file->double_colon ? g->file->double_colon : g->file; target_state = ts_done;
file != NULL;
file = file->prev) file = g->file;
check_renamed (file);
if (makefiles)
{ {
unsigned int ocommands_started; if (file->cmd_target)
int x;
check_renamed (file);
if (makefiles)
{ {
if (file->cmd_target) touch_flag = t;
{ question_flag = q;
touch_flag = t; just_print_flag = n;
question_flag = q;
just_print_flag = n;
}
else
touch_flag = question_flag = just_print_flag = 0;
} }
else
/* Save the old value of `commands_started' so we can compare touch_flag = question_flag = just_print_flag = 0;
later. It will be incremented when any commands are
actually run. */
ocommands_started = commands_started;
x = update_file (file, makefiles ? 1 : 0);
check_renamed (file);
/* Set the goal's `changed' flag if any commands were started
by calling update_file above. We check this flag below to
decide when to give an "up to date" diagnostic. */
g->changed += commands_started - ocommands_started;
/* If we updated a file and STATUS was not already 1, set it to
1 if updating failed, or to 0 if updating succeeded. Leave
STATUS as it is if no updating was done. */
stop = 0;
if ((x != 0 || file->updated) && status < 1)
{
if (file->update_status != 0)
{
/* Updating failed, or -q triggered. The STATUS value
tells our caller which. */
status = file->update_status;
/* If -q just triggered, stop immediately. It doesn't
matter how much more we run, since we already know
the answer to return. */
stop = (!keep_going_flag && !question_flag
&& !makefiles);
}
else
{
FILE_TIMESTAMP mtime = MTIME (file);
check_renamed (file);
if (file->updated && g->changed &&
mtime != file->mtime_before_update)
{
/* Updating was done. If this is a makefile and
just_print_flag or question_flag is set (meaning
-n or -q was given and this file was specified
as a command-line target), don't change STATUS.
If STATUS is changed, we will get re-exec'd, and
enter an infinite loop. */
if (!makefiles
|| (!just_print_flag && !question_flag))
status = 0;
if (makefiles && file->dontcare)
/* This is a default makefile; stop remaking. */
stop = 1;
}
}
}
/* Keep track if any double-colon entry is not finished.
When they are all finished, the goal is finished. */
any_not_updated |= !file->updated;
if (stop)
break;
} }
/* Reset FILE since it is null at the end of the loop. */ commands_started = 0;
file = g->file;
if (stop || !any_not_updated) target_state = update_file (file, makefiles ? 1 : 0);
check_renamed (file);
/* Set the goal's `changed' flag if any commands were started
by calling update_file above. We check this flag below to
decide when to give an "up to date" diagnostic. */
g->changed |= commands_started ? 1 : 0;
/* process the file according to its return */
switch (target_state)
{ {
/* If we have found nothing whatever to do for the goal, case ts_failed:
print a message saying nothing needs doing. */ /* Bail early? */
if (!keep_going_flag && !question_flag && !makefiles)
return 1;
status = 1; /* mark status failed, keep going */
/* -k, Fall Though !!! */
case ts_done:
{
FILE_TIMESTAMP mtime =
(makefiles ? file_mtime_no_search (file) : file_mtime (file));
check_renamed (file);
if (g->changed && mtime != file->mtime_before_update)
{
/* Updating was done. If this is a makefile and
just_print_flag or question_flag is set (meaning
-n or -q was given and this file was specified
as a command-line target), don't change STATUS.
If STATUS is changed, we will get re-exec'd, and
enter an infinite loop. */
if (status < 0)
{
if (!makefiles || (!just_print_flag && !question_flag))
status = 0;
}
}
}
if (!makefiles if (!makefiles
/* If the update_status is zero, we updated successfully /* If the update_status is zero, we updated successfully
or not at all. G->changed will have been set above if or not at all. G->changed will have been set above if
any commands were actually started for this goal. */ any commands were actually started for this goal. */
&& file->update_status == 0 && !g->changed && !file->update_status && !g->changed
/* Never give a message under -s or -q. */ /* Never give a message under -s or -q. */
&& !silent_flag && !question_flag) && !silent_flag && !question_flag)
message (1, ((file->phony || file->cmds == 0) message (1, ((file->phony || file->cmds == 0)
@@ -221,24 +212,27 @@ update_goal_chain (goals, makefiles)
: _("`%s' is up to date.")), : _("`%s' is up to date.")),
file->name); file->name);
/* This goal is finished. Remove it from the chain. */ case ts_incomplete:
break; /* do nothing */
}
/* advance to next target in goal chain */
if (target_state == ts_incomplete)
{
lastgoal = g;
g = g->next;
}
else
{
if (lastgoal == 0) if (lastgoal == 0)
goals = g->next; goals = g->next;
else else
lastgoal->next = g->next; lastgoal->next = g->next;
/* Free the storage. */ free (g);
free ((char *) g);
g = lastgoal == 0 ? goals : lastgoal->next; g = lastgoal == 0 ? goals : lastgoal->next;
if (stop)
break;
}
else
{
lastgoal = g;
g = g->next;
} }
} }
@@ -258,9 +252,10 @@ update_goal_chain (goals, makefiles)
return status; return status;
} }
/* If FILE is not up to date, execute the commands for it. /* If FILE is not up to date, execute the commands for it. Return
Return 0 if successful, 1 if unsuccessful; ts_done if FILE has completed successfully, ts_incomplete if FILE
but with some flag settings, just call `exit' if unsuccessful. or any of its precursors are incomplete, or ts_failed if FILE or
any of its precursors failed.
DEPTH is the depth in recursions of this function. DEPTH is the depth in recursions of this function.
We increment it during the consideration of our dependencies, We increment it during the consideration of our dependencies,
@@ -270,36 +265,32 @@ update_goal_chain (goals, makefiles)
If there are multiple double-colon entries for FILE, If there are multiple double-colon entries for FILE,
each is considered in turn. */ each is considered in turn. */
static int static target_state_t
update_file (file, depth) update_file (file, depth)
struct file *file; struct file *file;
unsigned int depth; unsigned int depth;
{ {
register int status = 0; register target_state_t status = ts_done;
register struct file *f; register struct file *f;
f = file->double_colon ? file->double_colon : file; f = file->double_colon ? file->double_colon : file;
/* Prune the dependency graph: if we've already been here on _this_
pass through the dependency graph, we don't have to go any further.
We won't reap_children until we start the next pass, so no state
change is possible below here until then. */
if (f->considered == considered)
{
DBF (DB_VERBOSE, _("Pruning file `%s'.\n"));
return 0;
}
/* This loop runs until we start commands for a double colon rule, or until /* This loop runs until we start commands for a double colon rule, or until
the chain is exhausted. */ the chain is exhausted. */
for (; f != 0; f = f->prev) for (; f != 0; f = f->prev)
{ {
f->considered = considered;
status |= update_file_1 (f, depth); status |= update_file_1 (f, depth);
check_renamed (f); check_renamed (f);
if (status != 0 && !keep_going_flag) if (status == ts_failed)
{
if (!keep_going_flag || question_flag)
return ts_failed;
else
break;
}
if (status == ts_incomplete)
break; break;
if (f->command_state == cs_running if (f->command_state == cs_running
@@ -307,65 +298,88 @@ update_file (file, depth)
{ {
/* Don't run the other :: rules for this /* Don't run the other :: rules for this
file until this rule is finished. */ file until this rule is finished. */
status = 0; status = ts_incomplete;
break; break;
} }
} }
/* Process the remaining rules in the double colon chain so they're marked /* Process the remaining rules in the double colon chain so they're marked
considered. Start their prerequisites, too. */ considered. Start their prerequisites, too. */
for (; f != 0 ; f = f->prev) for (f = (f ? f->prev : 0); f != 0 ; f = f->prev)
{ {
struct dep *d; struct dep *d;
f->considered = considered; f->considered = considered;
for (d = f->deps; d != 0; d = d->next) for (d = f->deps; d != 0; d = d->next)
status |= update_file (d->file, depth + 1); if ((status |= update_file (d->file, depth + 1)) == ts_failed)
{
if (!keep_going_flag || question_flag)
return ts_failed;
}
} }
return status; return status;
} }
/* Consider a single `struct file' and update it as appropriate. */ /* Consider a single `struct file' and update it as appropriate.
static int Return ts_done if FILE has completed successfully, ts_incomplete if
FILE or any of its precursors are incomplete, or ts_failed if FILE
or any of its precursors failed.
*/
static target_state_t
update_file_1 (file, depth) update_file_1 (file, depth)
struct file *file; struct file *file;
unsigned int depth; unsigned int depth;
{ {
register FILE_TIMESTAMP this_mtime; register FILE_TIMESTAMP this_mtime;
int noexist, must_make, deps_changed; int noexist, must_make, deps_changed;
int dep_status = 0; target_state_t dep_status = ts_done;
register struct dep *d, *lastd; register struct dep *d, *lastd;
int running = 0; int running = 0;
/* Prune the dependency graph: if we've already been here on _this_
pass through the dependency graph, we don't have to go any further.
We won't reap_children until we start the next pass, so no state
change is possible below here until then. */
if (file->considered == considered)
{
DBF (DB_VERBOSE, _("Pruning file `%s'.\n"));
if (file->command_state == cs_finished)
{
return file->update_status ? ts_failed : ts_done;
}
return ts_incomplete;
}
file->considered = considered;
DBF (DB_VERBOSE, _("Considering target file `%s'.\n")); DBF (DB_VERBOSE, _("Considering target file `%s'.\n"));
if (file->updated)
{
if (file->update_status > 0)
{
DBF (DB_VERBOSE,
_("Recently tried and failed to update file `%s'.\n"));
return file->update_status;
}
DBF (DB_VERBOSE, _("File `%s' was considered already.\n"));
return 0;
}
switch (file->command_state) switch (file->command_state)
{ {
case cs_not_started: case cs_not_started:
case cs_deps_running: case cs_deps_running:
break; break;
case cs_running: case cs_running:
DBF (DB_VERBOSE, _("Still updating file `%s'.\n")); DBF (DB_VERBOSE, _("Still updating file `%s'.\n"));
return 0; return ts_incomplete;
case cs_finished: case cs_finished:
DBF (DB_VERBOSE, _("Finished updating file `%s'.\n")); if (file->update_status != 0)
return file->update_status; {
DBF (DB_VERBOSE,
_("Recently tried and failed to update file `%s'.\n"));
return ts_failed;
}
DBF (DB_VERBOSE, _("File `%s' was considered already.\n"));
return ts_done;
default: default:
abort (); abort ();
} }
@@ -373,7 +387,7 @@ update_file_1 (file, depth)
++depth; ++depth;
/* Notice recursive update of the same file. */ /* Notice recursive update of the same file. */
file->updating = 1; set_updating(file);
/* Looking at the file's modtime beforehand allows the possibility /* Looking at the file's modtime beforehand allows the possibility
that its name may be changed by a VPATH search, and thus it may that its name may be changed by a VPATH search, and thus it may
@@ -383,28 +397,30 @@ update_file_1 (file, depth)
this_mtime = file_mtime (file); this_mtime = file_mtime (file);
check_renamed (file); check_renamed (file);
noexist = this_mtime == (FILE_TIMESTAMP) -1; must_make = noexist = this_mtime == (FILE_TIMESTAMP) -1;
if (noexist) if (noexist)
DBF (DB_BASIC, _("File `%s' does not exist.\n")); DBF (DB_BASIC, _("File `%s' does not exist.\n"));
must_make = noexist;
/* If file was specified as a target with no commands, /* If file was specified as a target with no commands,
come up with some default commands. */ come up with some default commands. */
if (!file->phony && file->cmds == 0 && !file->tried_implicit) if (file->cmds == 0)
{ {
if (try_implicit_rule (file, depth)) if (!file->phony && !file->tried_implicit)
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n")); {
else if (try_implicit_rule (file, depth))
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n")); DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
file->tried_implicit = 1; else
} DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
if (file->cmds == 0 && !file->is_target }
&& default_file != 0 && default_file->cmds != 0) if (!file->is_target
{ && default_file != 0 && default_file->cmds != 0)
DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n")); {
file->cmds = default_file->cmds; DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
file->cmds = default_file->cmds;
}
} }
/* Update all non-intermediate files we depend on, if necessary, /* Update all non-intermediate files we depend on, if necessary,
@@ -421,7 +437,7 @@ update_file_1 (file, depth)
mtime = file_mtime (d->file); mtime = file_mtime (d->file);
check_renamed (d->file); check_renamed (d->file);
if (d->file->updating) if (tst_updating(d->file))
{ {
error (NILF, _("Circular %s <- %s dependency dropped."), error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name); file->name, d->file->name);
@@ -453,7 +469,7 @@ update_file_1 (file, depth)
while (f != 0); while (f != 0);
} }
if (dep_status != 0 && !keep_going_flag) if (dep_status == ts_failed && !keep_going_flag)
break; break;
if (!running) if (!running)
@@ -490,7 +506,7 @@ update_file_1 (file, depth)
while (f != 0); while (f != 0);
} }
if (dep_status != 0 && !keep_going_flag) if (dep_status == ts_failed && !keep_going_flag)
break; break;
if (!running) if (!running)
@@ -499,7 +515,7 @@ update_file_1 (file, depth)
} }
} }
file->updating = 0; clr_updating(file);
DBF (DB_VERBOSE, _("Finished prerequisites of target file `%s'.\n")); DBF (DB_VERBOSE, _("Finished prerequisites of target file `%s'.\n"));
@@ -508,14 +524,14 @@ update_file_1 (file, depth)
set_command_state (file, cs_deps_running); set_command_state (file, cs_deps_running);
--depth; --depth;
DBF (DB_VERBOSE, _("The prerequisites of `%s' are being made.\n")); DBF (DB_VERBOSE, _("The prerequisites of `%s' are being made.\n"));
return 0; return ts_incomplete;
} }
/* If any dependency failed, give up now. */ /* If any dependency failed, give up now. */
if (dep_status != 0) if (dep_status == ts_failed)
{ {
file->update_status = dep_status; file->update_status = 1; /* if any precursors failed, so did we */
notice_finished_file (file); notice_finished_file (file);
depth--; depth--;
@@ -527,7 +543,7 @@ update_file_1 (file, depth)
error (NILF, error (NILF,
_("Target `%s' not remade because of errors."), file->name); _("Target `%s' not remade because of errors."), file->name);
return dep_status; return ts_failed;
} }
if (file->command_state == cs_deps_running) if (file->command_state == cs_deps_running)
@@ -632,7 +648,7 @@ update_file_1 (file, depth)
file = file->prev; file = file->prev;
} }
return 0; return ts_done;
} }
DBF (DB_BASIC, _("Must remake target `%s'.\n")); DBF (DB_BASIC, _("Must remake target `%s'.\n"));
@@ -651,32 +667,24 @@ update_file_1 (file, depth)
if (file->command_state != cs_finished) if (file->command_state != cs_finished)
{ {
DBF (DB_VERBOSE, _("Commands of `%s' are being run.\n")); DBF (DB_VERBOSE, _("Commands of `%s' are being run.\n"));
return 0; return ts_incomplete;
} }
switch (file->update_status) if (file->update_status)
{ {
case 2: DBF (DB_BASIC, (question_flag ? _("Target file `%s' needs remade under -q.\n")
DBF (DB_BASIC, _("Failed to remake target file `%s'.\n")); : _("Failed to remake target file `%s'.\n")));
break; return ts_failed;
case 0: }
DBF (DB_BASIC, _("Successfully remade target file `%s'.\n")); else
break; {
case 1: DBF (DB_BASIC, _("Successfully remade target file `%s'.\n"));
DBF (DB_BASIC, _("Target file `%s' needs remade under -q.\n")); return ts_done;
break;
default:
assert (file->update_status >= 0 && file->update_status <= 2);
break;
} }
file->updated = 1;
return file->update_status;
} }
/* Set FILE's `updated' flag and re-check its mtime and the mtime's of all /* re-check FILE's mtime and the mtime's of all files listed in its
files listed in its `also_make' member. Under -t, this function also `also_make' member. Under -t, this function also touches FILE.
touches FILE.
On return, FILE->update_status will no longer be -1 if it was. */ On return, FILE->update_status will no longer be -1 if it was. */
@@ -688,7 +696,6 @@ notice_finished_file (file)
int ran = file->command_state == cs_running; int ran = file->command_state == cs_running;
file->command_state = cs_finished; file->command_state = cs_finished;
file->updated = 1;
if (touch_flag if (touch_flag
/* The update status will be: /* The update status will be:
@@ -715,7 +722,7 @@ notice_finished_file (file)
file->update_status = 0; file->update_status = 0;
else else
/* Should set file's modification date and do nothing else. */ /* Should set file's modification date and do nothing else. */
file->update_status = touch_file (file); file->update_status = touch_file (file) ? 1 : 0;
} }
} }
@@ -751,7 +758,7 @@ notice_finished_file (file)
f->last_mtime = file->last_mtime; f->last_mtime = file->last_mtime;
} }
if (ran && file->update_status != -1) if (ran)
/* We actually tried to update FILE, which has /* We actually tried to update FILE, which has
updated its also_make's as well (if it worked). updated its also_make's as well (if it worked).
If it didn't work, it wouldn't work again for them. If it didn't work, it wouldn't work again for them.
@@ -759,7 +766,6 @@ notice_finished_file (file)
for (d = file->also_make; d != 0; d = d->next) for (d = file->also_make; d != 0; d = d->next)
{ {
d->file->command_state = cs_finished; d->file->command_state = cs_finished;
d->file->updated = 1;
d->file->update_status = file->update_status; d->file->update_status = file->update_status;
if (ran && !d->file->phony) if (ran && !d->file->phony)
@@ -769,10 +775,6 @@ notice_finished_file (file)
never be done because the target is already updated. */ never be done because the target is already updated. */
(void) f_mtime (d->file, 0); (void) f_mtime (d->file, 0);
} }
else if (file->update_status == -1)
/* Nothing was done for FILE, but it needed nothing done.
So mark it now as "succeeded". */
file->update_status = 0;
} }
/* Check whether another file (whose mtime is THIS_MTIME) /* Check whether another file (whose mtime is THIS_MTIME)
@@ -780,9 +782,13 @@ notice_finished_file (file)
If it does, store 1 in *MUST_MAKE_PTR. If it does, store 1 in *MUST_MAKE_PTR.
In the process, update any non-intermediate files In the process, update any non-intermediate files
that FILE depends on (including FILE itself). that FILE depends on (including FILE itself).
Return nonzero if any updating failed. */
static int Return ts_done if FILE has completed successfully, ts_incomplete if
FILE or any of its precursors are incomplete, or ts_failed if FILE
or any of its precursors failed.
*/
static target_state_t
check_dep (file, depth, this_mtime, must_make_ptr) check_dep (file, depth, this_mtime, must_make_ptr)
struct file *file; struct file *file;
unsigned int depth; unsigned int depth;
@@ -790,10 +796,10 @@ check_dep (file, depth, this_mtime, must_make_ptr)
int *must_make_ptr; int *must_make_ptr;
{ {
register struct dep *d; register struct dep *d;
int dep_status = 0; target_state_t dep_status = ts_done;
++depth; ++depth;
file->updating = 1; set_updating(file);
if (!file->intermediate) if (!file->intermediate)
/* If this is a non-intermediate file, update it and record /* If this is a non-intermediate file, update it and record
@@ -818,7 +824,6 @@ check_dep (file, depth, this_mtime, must_make_ptr)
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n")); DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
else else
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n")); DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
file->tried_implicit = 1;
} }
if (file->cmds == 0 && !file->is_target if (file->cmds == 0 && !file->is_target
&& default_file != 0 && default_file->cmds != 0) && default_file != 0 && default_file->cmds != 0)
@@ -845,7 +850,7 @@ check_dep (file, depth, this_mtime, must_make_ptr)
d = file->deps; d = file->deps;
while (d != 0) while (d != 0)
{ {
if (d->file->updating) if (tst_updating(d->file))
{ {
error (NILF, _("Circular %s <- %s dependency dropped."), error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name); file->name, d->file->name);
@@ -868,7 +873,7 @@ check_dep (file, depth, this_mtime, must_make_ptr)
dep_status |= check_dep (d->file, depth, this_mtime, dep_status |= check_dep (d->file, depth, this_mtime,
must_make_ptr); must_make_ptr);
check_renamed (d->file); check_renamed (d->file);
if (dep_status != 0 && !keep_going_flag) if (dep_status == ts_failed && !keep_going_flag)
break; break;
if (d->file->command_state == cs_running if (d->file->command_state == cs_running
@@ -884,7 +889,7 @@ check_dep (file, depth, this_mtime, must_make_ptr)
} }
} }
file->updating = 0; clr_updating(file);
return dep_status; return dep_status;
} }
@@ -985,7 +990,7 @@ remake_file (file)
error (NILF, msg_parent, "*** ", error (NILF, msg_parent, "*** ",
file->name, file->parent->name, "."); file->name, file->parent->name, ".");
} }
file->update_status = 2; file->update_status = 1;
} }
} }
else else
@@ -1138,7 +1143,7 @@ f_mtime (file, search)
static FILE_TIMESTAMP now = 0; static FILE_TIMESTAMP now = 0;
if (!clock_skew_detected if (!clock_skew_detected
&& mtime != (FILE_TIMESTAMP)-1 && mtime > now && mtime != (FILE_TIMESTAMP)-1 && mtime > now
&& !file->updated) && !(file->command_state == cs_finished))
{ {
/* This file's time appears to be in the future. /* This file's time appears to be in the future.
Update our concept of the present, and compare again. */ Update our concept of the present, and compare again. */