version 1.51, 2007/11/18 09:20:25 |
version 1.52, 2007/11/24 15:41:01 |
|
|
#include <limits.h> |
#include <limits.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <signal.h> |
#include <signal.h> |
|
#include <stddef.h> |
|
#include <string.h> |
|
#include <ohash.h> |
#include "config.h" |
#include "config.h" |
#include "defines.h" |
#include "defines.h" |
#include "dir.h" |
#include "dir.h" |
|
|
* MakeOODate. It is added to by |
* MakeOODate. It is added to by |
* Make_Update and subtracted from by |
* Make_Update and subtracted from by |
* MakeStartJobs */ |
* MakeStartJobs */ |
static int numNodes; /* Number of nodes to be processed. If this |
static struct ohash targets; /* stuff we must build */ |
* is non-zero when Job_Empty() returns |
|
* true, there's a cycle in the graph */ |
|
|
|
static void MakeAddChild(void *, void *); |
static void MakeAddChild(void *, void *); |
static void MakeHandleUse(void *, void *); |
static void MakeHandleUse(void *, void *); |
|
|
static bool try_to_make_node(GNode *); |
static bool try_to_make_node(GNode *); |
static void add_targets_to_make(Lst); |
static void add_targets_to_make(Lst); |
|
|
/*- |
static bool has_unmade_predecessor(GNode *); |
*----------------------------------------------------------------------- |
static void requeue_successors(GNode *); |
* MakeAddChild -- |
|
* Function used by Make_Run to add a child to the list l. |
static bool |
* It will only add the child if its make field is false. |
has_unmade_predecessor(GNode *gn) |
* |
|
* Side Effects: |
|
* The given list is extended |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static void |
|
MakeAddChild(void *to_addp, void *lp) |
|
{ |
{ |
GNode *to_add = (GNode *)to_addp; |
LstNode ln; |
Lst l = (Lst)lp; |
|
|
|
if (!to_add->must_make && !(to_add->type & OP_USE)) |
if (Lst_IsEmpty(&gn->preds)) |
Lst_EnQueue(l, to_add); |
return false; |
|
|
|
|
|
for (ln = Lst_First(&gn->preds); ln != NULL; ln = Lst_Adv(ln)) { |
|
GNode *pgn = (GNode *)Lst_Datum(ln); |
|
|
|
if (pgn->must_make && pgn->built_status == UNKNOWN) { |
|
if (DEBUG(MAKE)) |
|
printf("predecessor %s not made yet.\n", |
|
pgn->name); |
|
return true; |
|
} |
|
} |
|
return false; |
} |
} |
|
|
static void |
static void |
MakeHandleUse(void *pgn, void *cgn) |
requeue_successors(GNode *gn) |
{ |
{ |
Make_HandleUse((GNode *)pgn, (GNode *)cgn); |
LstNode ln; |
|
/* Deal with successor nodes. If any is marked for making and has an |
|
* unmade count of 0, has not been made and isn't in the examination |
|
* queue, it means we need to place it in the queue as it restrained |
|
* itself before. */ |
|
for (ln = Lst_First(&gn->successors); ln != NULL; ln = Lst_Adv(ln)) { |
|
GNode *succ = (GNode *)Lst_Datum(ln); |
|
|
|
if (succ->must_make && succ->unmade == 0 |
|
&& succ->built_status == UNKNOWN) |
|
(void)Lst_QueueNew(&toBeMade, succ); |
|
} |
} |
} |
|
|
/*- |
/*- |
|
|
pgn = (GNode *)Lst_Datum(ln); |
pgn = (GNode *)Lst_Datum(ln); |
if (pgn->must_make) { |
if (pgn->must_make) { |
pgn->unmade--; |
pgn->unmade--; |
|
if (DEBUG(MAKE)) |
|
printf("%s--=%d ", |
|
pgn->name, pgn->unmade); |
|
|
if ( ! (cgn->type & (OP_EXEC|OP_USE))) { |
if ( ! (cgn->type & (OP_EXEC|OP_USE))) { |
if (cgn->built_status == MADE) { |
if (cgn->built_status == MADE) { |
|
|
* predecessors will be dealt with in |
* predecessors will be dealt with in |
* MakeStartJobs. |
* MakeStartJobs. |
*/ |
*/ |
|
if (DEBUG(MAKE)) |
|
printf("QUEUING "); |
Lst_EnQueue(&toBeMade, pgn); |
Lst_EnQueue(&toBeMade, pgn); |
} else if (pgn->unmade < 0) { |
} else if (pgn->unmade < 0) { |
Error("Graph cycles through %s", pgn->name); |
Error("Child %s discovered graph cycles through %s", cgn->name, pgn->name); |
} |
} |
} |
} |
} |
} |
/* Deal with successor nodes. If any is marked for making and has an |
if (DEBUG(MAKE)) |
* unmade count of 0, has not been made and isn't in the examination |
printf("\n"); |
* queue, it means we need to place it in the queue as it restrained |
requeue_successors(cgn); |
* itself before. */ |
|
for (ln = Lst_First(&cgn->successors); ln != NULL; ln = Lst_Adv(ln)) { |
|
GNode *succ = (GNode *)Lst_Datum(ln); |
|
|
|
if (succ->must_make && succ->unmade == 0 |
|
&& succ->built_status == UNKNOWN) |
|
(void)Lst_QueueNew(&toBeMade, succ); |
|
} |
|
} |
} |
|
|
static bool |
static bool |
|
|
{ |
{ |
if (DEBUG(MAKE)) |
if (DEBUG(MAKE)) |
printf("Examining %s...", gn->name); |
printf("Examining %s...", gn->name); |
/* |
|
* Make sure any and all predecessors that are going to be made, |
if (gn->unmade != 0) { |
* have been. |
if (DEBUG(MAKE)) |
*/ |
printf(" Requeuing (%d)\n", gn->unmade); |
if (!Lst_IsEmpty(&gn->preds)) { |
add_targets_to_make(&gn->children); |
LstNode ln; |
Lst_EnQueue(&toBeMade, gn); |
|
return false; |
for (ln = Lst_First(&gn->preds); ln != NULL; ln = Lst_Adv(ln)){ |
|
GNode *pgn = (GNode *)Lst_Datum(ln); |
|
|
|
if (pgn->must_make && pgn->built_status == UNKNOWN) { |
|
if (DEBUG(MAKE)) |
|
printf( |
|
"predecessor %s not made yet.\n", |
|
pgn->name); |
|
/* |
|
* there's a predecessor as yet unmade, so we |
|
* just drop this node on the floor. When the |
|
* node in question has been made, it will |
|
* notice this node as being ready to make but |
|
* as yet unmade and will place the node on the |
|
* queue. |
|
*/ |
|
return false; |
|
} |
|
} |
|
} |
} |
|
if (has_been_built(gn)) { |
|
if (DEBUG(MAKE)) |
|
printf(" already made\n"); |
|
return false; |
|
} |
|
if (has_unmade_predecessor(gn)) { |
|
if (DEBUG(MAKE)) |
|
printf(" Dropping for now\n"); |
|
return false; |
|
} |
|
|
numNodes--; |
Suff_FindDeps(gn); |
|
if (gn->unmade != 0) { |
|
if (DEBUG(MAKE)) |
|
printf(" Requeuing (after deps: %d)\n", gn->unmade); |
|
add_targets_to_make(&gn->children); |
|
return false; |
|
} |
if (Make_OODate(gn)) { |
if (Make_OODate(gn)) { |
if (DEBUG(MAKE)) |
if (DEBUG(MAKE)) |
printf("out-of-date\n"); |
printf("out-of-date\n"); |
|
|
} |
} |
|
|
|
|
/* |
static void |
* Make an initial downward pass over the graph, marking nodes to be |
MakeAddChild(void *to_addp, void *lp) |
* made as we go down. We call Suff_FindDeps to find where a node is and |
{ |
* to get some children for it if it has none and also has no commands. |
GNode *gn = (GNode *)to_addp; |
* If the node is a leaf, we stick it on the toBeMade queue to |
|
* be looked at in a minute, otherwise we add its children to our queue |
if (!gn->must_make && !(gn->type & OP_USE)) |
* and go on about our business. |
Lst_EnQueue((Lst)lp, gn); |
|
} |
|
|
|
static void |
|
MakeHandleUse(void *pgn, void *cgn) |
|
{ |
|
Make_HandleUse((GNode *)pgn, (GNode *)cgn); |
|
} |
|
|
|
/* Add stuff to the toBeMade queue. we try to sort things so that stuff |
|
* that can be done directly is done right away. This won't be perfect, |
|
* since some dependencies are only discovered later (e.g., SuffFindDeps). |
*/ |
*/ |
static void |
static void |
add_targets_to_make(Lst targs) |
add_targets_to_make(Lst todo) |
{ |
{ |
LIST examine; /* List of targets to examine */ |
|
GNode *gn; |
GNode *gn; |
|
LIST examine; |
|
unsigned int slot; |
|
|
Lst_Clone(&examine, targs, NOCOPY); |
Lst_Clone(&examine, todo, NOCOPY); |
|
|
while ((gn = (GNode *)Lst_DeQueue(&examine)) != NULL) { |
while ((gn = (GNode *)Lst_DeQueue(&examine)) != NULL) { |
if (!gn->must_make) { |
if (gn->must_make) /* already known */ |
gn->must_make = true; |
continue; |
numNodes++; |
gn->must_make = true; |
|
|
look_harder_for_target(gn); |
slot = ohash_qlookup(&targets, gn->name); |
/* |
if (!ohash_find(&targets, slot)) |
* Apply any .USE rules before looking for implicit |
ohash_insert(&targets, slot, gn); |
* dependencies to make sure everything that should have |
|
* commands has commands ... |
|
*/ |
|
Lst_ForEach(&gn->children, MakeHandleUse, gn); |
|
Suff_FindDeps(gn); |
|
|
|
if (gn->unmade != 0) |
|
Lst_ForEach(&gn->children, MakeAddChild, |
look_harder_for_target(gn); |
&examine); |
/* |
else |
* Apply any .USE rules before looking for implicit |
Lst_EnQueue(&toBeMade, gn); |
* dependencies to make sure everything that should have |
|
* commands has commands ... |
|
*/ |
|
Lst_ForEach(&gn->children, MakeHandleUse, gn); |
|
expand_all_children(gn); |
|
|
|
if (gn->unmade != 0) { |
|
if (DEBUG(MAKE)) |
|
printf("%s: not queuing (%d unmade children)\n", |
|
gn->name, gn->unmade); |
|
Lst_ForEach(&gn->children, MakeAddChild, |
|
&examine); |
|
} else { |
|
if (DEBUG(MAKE)) |
|
printf("%s: queuing\n", gn->name); |
|
Lst_EnQueue(&toBeMade, gn); |
} |
} |
} |
} |
} |
} |
|
|
bool |
bool |
Make_Run(Lst targs) /* the initial list of targets */ |
Make_Run(Lst targs) /* the initial list of targets */ |
{ |
{ |
int errors; /* Number of errors the Job module reports */ |
int errors; /* Number of errors the Job module reports */ |
|
GNode *gn; |
|
unsigned int i; |
|
bool cycle; |
|
|
Static_Lst_Init(&toBeMade); |
Static_Lst_Init(&toBeMade); |
|
ohash_init(&targets, 10, &gnode_info); |
|
|
numNodes = 0; |
|
|
|
add_targets_to_make(targs); |
add_targets_to_make(targs); |
if (queryFlag) { |
if (queryFlag) { |
/* |
/* |
|
|
} |
} |
|
|
errors = Job_Finish(); |
errors = Job_Finish(); |
|
cycle = false; |
|
|
|
for (gn = ohash_first(&targets, &i); gn != NULL; |
|
gn = ohash_next(&targets, &i)) { |
|
if (has_been_built(gn)) |
|
continue; |
|
cycle = true; |
|
errors++; |
|
printf("Error: target %s unaccounted for (%s)\n", |
|
gn->name, status_to_string(gn)); |
|
} |
/* |
/* |
* Print the final status of each target. E.g. if it wasn't made |
* Print the final status of each target. E.g. if it wasn't made |
* because some inferior reported an error. |
* because some inferior reported an error. |
*/ |
*/ |
errors = errors == 0 && numNodes != 0; |
Lst_ForEach(targs, MakePrintStatus, &cycle); |
Lst_ForEach(targs, MakePrintStatus, &errors); |
if (errors) |
|
Fatal("Errors while building"); |
|
|
return true; |
return true; |
} |
} |