1006 lines
30 KiB
C++
1006 lines
30 KiB
C++
// BUILD.C -- build routines
|
|
//
|
|
// Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Purpose:
|
|
// Module contains routines to build targets
|
|
//
|
|
// Revision History:
|
|
// 04-Feb-2000 BTF Ported to Win64
|
|
// 18-Jul-1996 GP Support "batch" inference rules
|
|
// 15-Nov-1993 JR Major speed improvements
|
|
// 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
|
|
// 04-Aug-1993 HV Fixed Ikura bug #178. This is a separate bug but ArunJ
|
|
// just reopen 178 anyway.
|
|
// 07-Jul-1993 HV Fixed Ikura bug #178: Option K does not give non zero
|
|
// return code when it should.
|
|
// 10-May-1993 HV Add include file mbstring.h
|
|
// Change the str* functions to STR*
|
|
// 08-Jun-1992 SS Port to DOSX32
|
|
// 16-May-1991 SB Truncated History ... rest is now on SLM
|
|
// 16-May-1991 SB Separated parts that should be in other modules
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
// In order to make comparing dates easier, we cast the FILEINFO buffer to
|
|
// be of type BOGUS, which has one long where the two unsigneds (for date
|
|
// and time) are in the original buffer. That way only need a single compare.
|
|
|
|
#ifdef CHECK_RECURSION_LEVEL
|
|
#define MAXRECLEVEL 10000 // Maximum recursion level
|
|
#endif
|
|
|
|
// function prototypes for the module
|
|
// I make as many things static as possible, just to be extra cautious
|
|
|
|
|
|
int build(MAKEOBJECT*, UCHAR, time_t *, BOOL, char *, BATCHLIST**);
|
|
|
|
MAKEOBJECT * makeTempObject(char*, UCHAR);
|
|
void insertSort(DEPLIST **pDepList, DEPLIST *pElement);
|
|
BOOL nextToken(char**, char**);
|
|
DEPLIST * createDepList(BUILDBLOCK *pBlock, char *objectName);
|
|
void addBatch(BATCHLIST **pBatchList, RULELIST *pRule,
|
|
MAKEOBJECT *pObject, char *dollarLt);
|
|
int doBatchCommand (BATCHLIST *pBatch);
|
|
int RecLevel = 0; // Static recursion level. Changed from function
|
|
// parameter because of treatment of recursive makes.
|
|
int execBatchList(BATCHLIST *);
|
|
void freeBatchList(BATCHLIST **);
|
|
int invokeBuildEx(char *, UCHAR, time_t *, char *, BATCHLIST **);
|
|
|
|
// we have to check for expansion on targets -- firstTarget had to be
|
|
// expanded earlier to tell whether or not we were dealing w/ a rule, etc.,
|
|
// but targets from commandline might have macros, wildcards in them
|
|
|
|
int
|
|
processTree()
|
|
{
|
|
STRINGLIST *p;
|
|
char *v;
|
|
NMHANDLE searchHandle;
|
|
int status;
|
|
time_t dateTime;
|
|
|
|
for (p = makeTargets; p; p = makeTargets) {
|
|
if (_tcspbrk(makeTargets->text, "*?")) { // expand wildcards
|
|
struct _finddata_t finddata;
|
|
char *szFilename;
|
|
|
|
if (szFilename = findFirst(makeTargets->text, &finddata, &searchHandle)) {
|
|
do {
|
|
v = prependPath(makeTargets->text, szFilename);
|
|
dateTime = getDateTime(&finddata);
|
|
status = invokeBuild(v, flags, &dateTime, NULL);
|
|
FREE(v);
|
|
if ((status < 0) && (ON(gFlags, F1_QUESTION_STATUS))) {
|
|
freeStringList(p); // Was not being freed
|
|
return(-1);
|
|
}
|
|
} while (szFilename = findNext(&finddata, searchHandle));
|
|
} else {
|
|
makeError(0, NO_WILDCARD_MATCH, makeTargets->text);
|
|
}
|
|
} else {
|
|
dateTime = 0L;
|
|
status = invokeBuild(makeTargets->text, flags, &dateTime, NULL);
|
|
if ((status < 0) && (ON(gFlags, F1_QUESTION_STATUS))) {
|
|
freeStringList(p); // Was not being freed
|
|
return(255); // Haituanv: change -1 to 255 to follow the manual
|
|
}
|
|
}
|
|
makeTargets = p->next;
|
|
FREE_STRINGLIST(p);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
invokeBuild(
|
|
char *target,
|
|
UCHAR pFlags,
|
|
time_t *timeVal,
|
|
char *pFirstDep)
|
|
{
|
|
int status = 0;
|
|
BATCHLIST *pLocalBatchList = NULL;
|
|
status += invokeBuildEx(target,
|
|
pFlags,
|
|
timeVal,
|
|
pFirstDep,
|
|
&pLocalBatchList);
|
|
|
|
if (pLocalBatchList) {
|
|
status += execBatchList (pLocalBatchList);
|
|
freeBatchList (&pLocalBatchList);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
int
|
|
invokeBuildEx(
|
|
char *target,
|
|
UCHAR pFlags,
|
|
time_t *timeVal,
|
|
char *pFirstDep,
|
|
BATCHLIST **ppBatchList)
|
|
{
|
|
MAKEOBJECT *object;
|
|
BOOL fInmakefile = TRUE;
|
|
int rc;
|
|
|
|
++RecLevel;
|
|
#ifdef CHECK_RECURSION_LEVEL
|
|
if (RecLevel > MAXRECLEVEL)
|
|
makeError(0, TOO_MANY_BUILDS_INTERNAL);
|
|
#endif
|
|
if (!(object = findTarget(target))) {
|
|
object = makeTempObject(target, pFlags);
|
|
fInmakefile = FALSE;
|
|
}
|
|
rc = build(object, pFlags, timeVal, fInmakefile, pFirstDep, ppBatchList);
|
|
--RecLevel;
|
|
return(rc);
|
|
}
|
|
|
|
|
|
int
|
|
build(
|
|
MAKEOBJECT *object,
|
|
UCHAR parentFlags,
|
|
time_t *targetTime,
|
|
BOOL fInmakefile,
|
|
char *pFirstDep,
|
|
BATCHLIST **ppBatchList)
|
|
{
|
|
STRINGLIST *questionList,
|
|
*starList,
|
|
*temp,
|
|
*implComList;
|
|
struct _finddata_t finddata; // buffer for getting file times
|
|
NMHANDLE tHandle;
|
|
BUILDLIST *b;
|
|
RULELIST *rule; // pointer to rule found to build target
|
|
BUILDBLOCK *block,
|
|
*explComBlock;
|
|
DEPLIST *deps, *deplist;
|
|
char name[MAXNAME];
|
|
int rc, status = 0;
|
|
time_t targTime, // target's time in file system
|
|
newTargTime, // target's time after being rebuilt
|
|
tempTime,
|
|
depTime, // time of dependency just built
|
|
maxDepTime; // time of most recent dependency built
|
|
BOOL built; // flag: target built with doublecolon commands
|
|
time_t *blockTime; // points to dateTime of cmd. block
|
|
extern char *makeStr;
|
|
extern UCHAR okToDelete;
|
|
UCHAR okDel;
|
|
BATCHLIST *pLocalBatchList;
|
|
|
|
|
|
#ifdef DEBUG_ALL
|
|
printf("Build '%s'\n", object->name);
|
|
#endif
|
|
|
|
// The first dependent or inbuilt rule dependent is reqd for extmake syntax
|
|
// handling. If it has a value then it is the dependent corr to the inf rule
|
|
// otherwise it should be the first dependent specified
|
|
|
|
if (!object) {
|
|
*targetTime = 0L;
|
|
return(0);
|
|
}
|
|
|
|
if (ON(object->flags3, F3_BUILDING_THIS_ONE)) // detect cycles
|
|
makeError(0, CYCLE_IN_TREE, object->name);
|
|
|
|
if (object->ppBatch) {
|
|
// we need to build an object that is already placed in a batch list
|
|
// Go ahead and build the whole batch list
|
|
BATCHLIST **ppBatch = object->ppBatch;
|
|
status += execBatchList (*ppBatch);
|
|
freeBatchList(ppBatch);
|
|
*targetTime = object->dateTime;
|
|
return status;
|
|
}
|
|
|
|
if (ON(object->flags3, F3_ALREADY_BUILT)) {
|
|
if (ON(parentFlags, F2_DISPLAY_FILE_DATES))
|
|
printDate(RecLevel*2, object->name, object->dateTime);
|
|
*targetTime = object->dateTime;
|
|
if ( OFF(gFlags, F1_QUESTION_STATUS) &&
|
|
RecLevel == 1 &&
|
|
OFF(object->flags3, F3_OUT_OF_DATE) &&
|
|
findFirst(object->name, &finddata, &tHandle)) {
|
|
// Display 'up-to-date' msg for built level-1 targets
|
|
// that exist as files. [VS98 1930]
|
|
makeMessage(TARGET_UP_TO_DATE, object->name);
|
|
}
|
|
return(ON(object->flags3, F3_OUT_OF_DATE)? 1 : 0);
|
|
}
|
|
|
|
questionList = NULL;
|
|
starList = NULL;
|
|
implComList = NULL;
|
|
explComBlock = NULL;
|
|
block = NULL;
|
|
targTime = 0L;
|
|
newTargTime = 0L;
|
|
tempTime = 0L;
|
|
depTime = 0L;
|
|
maxDepTime = 0L;
|
|
blockTime = NULL;
|
|
pLocalBatchList = NULL;
|
|
|
|
|
|
SET(object->flags3, F3_BUILDING_THIS_ONE);
|
|
dollarStar = dollarAt = object->name;
|
|
|
|
// For Double Colon case we need the date of target before it's target's are
|
|
// built. For all other cases the date matters only if dependents are up
|
|
// to date. NOT TRUE: WE ALSO NEED THE TARGET'S TIME for @?
|
|
|
|
b = object->buildList;
|
|
if (b && ON(b->buildBlock->flags, F2_DOUBLECOLON)
|
|
&& findFirst(object->name, &finddata, &tHandle)) {
|
|
targTime = getDateTime(&finddata);
|
|
|
|
}
|
|
|
|
for (; b; b = b->next) {
|
|
depTime = 0L;
|
|
block = b->buildBlock;
|
|
if (block->dateTime != 0) { // cmd. block already executed
|
|
targTime = __max(targTime, block->dateTime);
|
|
built = TRUE;
|
|
continue; // so set targTime and skip this block
|
|
}
|
|
blockTime = &block->dateTime;
|
|
|
|
deplist = deps = createDepList(block, object->name);
|
|
for (;deps; deps = deps->next) {
|
|
tempTime = deps->depTime;
|
|
rc = invokeBuildEx(deps->name, // build the dependent
|
|
block->flags,
|
|
&tempTime, NULL, &pLocalBatchList);
|
|
status += rc;
|
|
if (fOptionK && rc) {
|
|
MAKEOBJECT *obj = findTarget(deps->name);
|
|
assert(obj != NULL);
|
|
if (OFF(obj->flags3, F3_ERROR_IN_CHILD)) {
|
|
fSlashKStatus = FALSE;
|
|
makeError(0, BUILD_FAILED_SLASH_K, deps->name);
|
|
}
|
|
SET(object->flags3, F3_ERROR_IN_CHILD);
|
|
}
|
|
depTime = __max(depTime, tempTime);/*if rebuilt, change time*/
|
|
|
|
// If target exists then we need it's timestamp to correctly construct $?
|
|
|
|
if (!targTime && OFF(block->flags, F2_DOUBLECOLON) &&
|
|
findFirst(object->name, &finddata, &tHandle)) {
|
|
object->dateTime = targTime = getDateTime(&finddata);
|
|
}
|
|
|
|
// If dependent was rebuilt, add to $?. [RB]
|
|
|
|
if (ON(object->flags2, F2_FORCE_BUILD) ||
|
|
targTime < tempTime ||
|
|
(fRebuildOnTie && targTime == tempTime)
|
|
) {
|
|
temp = makeNewStrListElement();
|
|
temp->text = makeString(deps->name);
|
|
appendItem(&questionList, temp);
|
|
}
|
|
|
|
// Always add dependent to $**. Must allocate new item because two
|
|
// separate lists. [RB]
|
|
|
|
temp = makeNewStrListElement();
|
|
temp->text = makeString(deps->name);
|
|
appendItem(&starList, temp);
|
|
}
|
|
|
|
if (pLocalBatchList) {
|
|
// Perform deferred batch builds and free batch list
|
|
status += execBatchList (pLocalBatchList);
|
|
freeBatchList(&pLocalBatchList);
|
|
}
|
|
|
|
// Free dependent list
|
|
|
|
for (deps = deplist; deps ; deps = deplist) {
|
|
FREE(deps->name);
|
|
deplist = deps->next;
|
|
FREE(deps);
|
|
}
|
|
|
|
// Now, all dependents are built.
|
|
|
|
if (ON(block->flags, F2_DOUBLECOLON)) {
|
|
|
|
// do doublecolon commands
|
|
|
|
if (block->buildCommands) {
|
|
dollarQuestion = questionList;
|
|
dollarStar = dollarAt = object->name;
|
|
dollarLessThan = dollarDollarAt = NULL;
|
|
dollarStarStar = starList;
|
|
if (((fOptionK && OFF(object->flags3, F3_ERROR_IN_CHILD)) ||
|
|
status == 0) &&
|
|
(targTime < depTime) ||
|
|
(fRebuildOnTie && (targTime == depTime)) ||
|
|
(targTime == 0 && depTime == 0) ||
|
|
(!block->dependents)
|
|
) {
|
|
|
|
// do commands if necessary
|
|
|
|
okDel = okToDelete;
|
|
okToDelete = TRUE;
|
|
|
|
// if the first dependent is not set use the first one
|
|
// from the list of dependents
|
|
|
|
pFirstDep = pFirstDep ? pFirstDep : (dollarStarStar ?
|
|
dollarStarStar->text : NULL);
|
|
status += doCommands(object->name,
|
|
block->buildCommands,
|
|
block->buildMacros,
|
|
block->flags,
|
|
pFirstDep);
|
|
|
|
if (OFF(object->flags2, F2_NO_EXECUTE) &&
|
|
findFirst(object->name, &finddata, &tHandle))
|
|
newTargTime = getDateTime(&finddata);
|
|
else if (maxDepTime)
|
|
newTargTime = maxDepTime;
|
|
else
|
|
curTime(&newTargTime); // currentTime
|
|
|
|
// set time for this block
|
|
block->dateTime = newTargTime;
|
|
built = TRUE;
|
|
|
|
// 5/3/92 BryanT If these both point to the same list,
|
|
// don't free twice.
|
|
|
|
if (starList != questionList) {
|
|
freeStringList(starList);
|
|
freeStringList(questionList);
|
|
} else {
|
|
freeStringList(starList);
|
|
}
|
|
|
|
starList = questionList = NULL;
|
|
okToDelete = okDel;
|
|
}
|
|
|
|
if (fOptionK && ON(object->flags3, F3_ERROR_IN_CHILD))
|
|
makeError(0, TARGET_ERROR_IN_CHILD, object->name);
|
|
}
|
|
} else {
|
|
|
|
// singlecolon; set explComBlock
|
|
|
|
if (block->buildCommands)
|
|
if (explComBlock)
|
|
makeError(0, TOO_MANY_RULES, object->name);
|
|
else
|
|
explComBlock = block;
|
|
maxDepTime = __max(maxDepTime, depTime);
|
|
}
|
|
|
|
if (ON(block->flags, F2_DOUBLECOLON) && !b->next) {
|
|
CLEAR(object->flags3, F3_BUILDING_THIS_ONE);
|
|
SET(object->flags3, F3_ALREADY_BUILT);
|
|
if (status > 0)
|
|
SET(object->flags3, F3_OUT_OF_DATE);
|
|
else
|
|
CLEAR(object->flags3, F3_OUT_OF_DATE);
|
|
targTime = __max(newTargTime, targTime);
|
|
object->dateTime = targTime;
|
|
*targetTime = targTime;
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
dollarLessThan = dollarDollarAt = NULL;
|
|
|
|
if (!(targTime = *targetTime)) { //???????
|
|
if (object->dateTime) {
|
|
targTime = object->dateTime;
|
|
} else if (findFirst(object->name, &finddata, &tHandle)) {
|
|
targTime = getDateTime(&finddata);
|
|
}
|
|
}
|
|
|
|
if (ON(object->flags2, F2_DISPLAY_FILE_DATES)) {
|
|
printDate(RecLevel*2, object->name, targTime);
|
|
}
|
|
|
|
built = FALSE;
|
|
|
|
// look for implicit dependents and use rules to build the target
|
|
|
|
// The order of the if's decides whether the dependent is inferred
|
|
// from the inference rule or not, even when the explicit command block is
|
|
// present, currently it is infered (XENIX MAKE compatibility)
|
|
|
|
if (rule = useRule(object,
|
|
name,
|
|
targTime,
|
|
&questionList,
|
|
&starList,
|
|
&status,
|
|
&maxDepTime,
|
|
&pFirstDep)
|
|
) {
|
|
if (!explComBlock) {
|
|
dollarLessThan = name;
|
|
implComList = rule->buildCommands;
|
|
}
|
|
}
|
|
|
|
dollarStar = dollarAt = object->name;
|
|
dollarQuestion = questionList;
|
|
dollarStarStar = starList;
|
|
|
|
if (((fOptionK && OFF(object->flags3, F3_ERROR_IN_CHILD)) || status == 0) &&
|
|
(targTime < maxDepTime ||
|
|
(fRebuildOnTie && (targTime == maxDepTime)) ||
|
|
(targTime == 0 && maxDepTime == 0) ||
|
|
ON(object->flags2, F2_FORCE_BUILD)
|
|
)
|
|
) {
|
|
okDel = okToDelete; // Yes, can delete while executing commands
|
|
okToDelete = TRUE;
|
|
|
|
if (explComBlock) {
|
|
// if the first dependent is not set use the first one from the
|
|
// list of dependents
|
|
pFirstDep = pFirstDep ? pFirstDep :
|
|
(dollarStarStar ? dollarStarStar->text : NULL);
|
|
status += doCommands(object->name, // do singlecolon commands
|
|
explComBlock->buildCommands,
|
|
explComBlock->buildMacros,
|
|
explComBlock->flags,
|
|
pFirstDep);
|
|
}
|
|
else if (implComList) {
|
|
if (rule->fBatch && OFF(gFlags, F1_NO_BATCH)) {
|
|
addBatch(ppBatchList,
|
|
rule,
|
|
object,
|
|
dollarLessThan);
|
|
}
|
|
else {
|
|
status += doCommands(object->name, // do rule's commands
|
|
implComList,
|
|
rule->buildMacros,
|
|
object->flags2,
|
|
pFirstDep);
|
|
}
|
|
}
|
|
else if (ON(gFlags, F1_TOUCH_TARGETS)) { // for /t with no commands...
|
|
if (block)
|
|
status += doCommands(object->name,
|
|
block->buildCommands,
|
|
block->buildMacros,
|
|
block->flags,
|
|
pFirstDep);
|
|
}
|
|
// if Option K specified don't exit ... pass on return code
|
|
else if (!fInmakefile && targTime == 0) { // lose
|
|
// Haituanv: If option K, then set the return code 'status'
|
|
// to 1 to indicate a failure. This fixes Ikura bug #178.
|
|
if (fOptionK) {
|
|
status = 1;
|
|
#ifdef DEBUG_OPTION_K
|
|
printf("DEBUG: %s(%d): status = %d\n", __FILE__, __LINE__, status);
|
|
#endif
|
|
} else
|
|
makeError(0, CANT_MAKE_TARGET, object->name);
|
|
}
|
|
okToDelete = okDel;
|
|
// if cmd exec'ed or has 0 deps then currentTime else max of dep times
|
|
if (explComBlock || implComList || !dollarStarStar) {
|
|
curTime(&newTargTime);
|
|
|
|
// Add 2 to ensure the time for this node is >= the time the file
|
|
// system might have used (mainly useful when running a very fast
|
|
// machine where the file system doesn't have the resolution of the
|
|
// system timer... We don't have to to this in the curTime
|
|
// above since it's only hit when nothing is built anywhere...
|
|
|
|
newTargTime +=2;
|
|
} else
|
|
newTargTime = maxDepTime;
|
|
|
|
if (blockTime && explComBlock)
|
|
// set block's time, if a real cmd. block was executed
|
|
*blockTime = newTargTime;
|
|
}
|
|
else if (OFF(gFlags, F1_QUESTION_STATUS) &&
|
|
RecLevel == 1 &&
|
|
!built &&
|
|
OFF(object->flags3, F3_ERROR_IN_CHILD))
|
|
makeMessage(TARGET_UP_TO_DATE, object->name);
|
|
|
|
if (fOptionK && status) {
|
|
// 4-Aug-1993 Haituanv: Ikura bug #178 again: We should set fSlashKStatus=FALSE
|
|
// so that main() knows the build failed under /K option.
|
|
fSlashKStatus = FALSE;
|
|
|
|
if (ON(object->flags3, F3_ERROR_IN_CHILD))
|
|
makeError(0, TARGET_ERROR_IN_CHILD, object->name);
|
|
else if (RecLevel == 1)
|
|
makeError(0, BUILD_FAILED_SLASH_K, object->name);
|
|
}
|
|
|
|
if (ON(gFlags, F1_QUESTION_STATUS) && RecLevel == 1 ) {
|
|
// 5/3/92 BryanT If these both point to the same list, don't
|
|
// free twice.
|
|
|
|
if (starList!= questionList) {
|
|
freeStringList(starList);
|
|
freeStringList(questionList);
|
|
} else {
|
|
freeStringList(starList);
|
|
}
|
|
|
|
return(numCommands ? -1 : 0);
|
|
}
|
|
|
|
CLEAR(object->flags3, F3_BUILDING_THIS_ONE);
|
|
if (!object->ppBatch) {
|
|
SET(object->flags3, F3_ALREADY_BUILT);
|
|
if (status > 0)
|
|
SET(object->flags3, F3_OUT_OF_DATE);
|
|
else
|
|
CLEAR(object->flags3, F3_OUT_OF_DATE);
|
|
}
|
|
|
|
targTime = __max(newTargTime, targTime);
|
|
object->dateTime = targTime;
|
|
|
|
*targetTime = targTime;
|
|
|
|
// 5/3/92 BryanT If these both point to the same list, don't
|
|
// free twice.
|
|
|
|
if (starList!= questionList) {
|
|
freeStringList(starList);
|
|
freeStringList(questionList);
|
|
} else {
|
|
freeStringList(starList);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
DEPLIST *
|
|
createDepList(
|
|
BUILDBLOCK *bBlock,
|
|
char *objectName
|
|
)
|
|
{
|
|
BOOL again; // flag: wildcards found in dependent name
|
|
char *s, *t;
|
|
char *source, *save, *token;
|
|
char *depName, *depPath;
|
|
char *tempStr;
|
|
STRINGLIST *sList, *pMacros;
|
|
DEPLIST *depList = NULL, *pNew;
|
|
struct _finddata_t finddata;
|
|
NMHANDLE searchHandle;
|
|
|
|
pMacros = bBlock->dependentMacros;
|
|
|
|
// expand Macros in Dependent list
|
|
for (sList = bBlock->dependents; sList; sList = sList->next) {
|
|
for (s = sList->text; *s && *s != '$'; s = _tcsinc(s)) {
|
|
if (*s == ESCH)
|
|
s++;
|
|
}
|
|
if (*s) {
|
|
// set $$@ properly, The dependency macros will then expand right
|
|
dollarDollarAt = objectName;
|
|
source = expandMacros(sList->text, &pMacros);
|
|
} else
|
|
source = sList->text;
|
|
|
|
save = makeString(source);
|
|
// build list for all dependents
|
|
for (t = save; nextToken(&t, &token);) {
|
|
if (*token == '{') {
|
|
// path list found
|
|
for (depName = token; *depName && *depName != '}'; depName = _tcsinc(depName)) {
|
|
if (*depName == ESCH) {
|
|
depName++;
|
|
}
|
|
}
|
|
|
|
if (*depName) {
|
|
*depName++ = '\0';
|
|
++token;
|
|
}
|
|
} else {
|
|
depName = token; // If no path list, set
|
|
token = NULL; // token to null.
|
|
}
|
|
|
|
// depName is now name of dependency file ...
|
|
|
|
again = FALSE;
|
|
putDateTime(&finddata, 0L);
|
|
depPath = makeString(depName);
|
|
if (_tcspbrk(depName, "*?") || token) { // do wildcards in filename
|
|
if (tempStr = searchPath(token, depName, &finddata, &searchHandle)){
|
|
again = TRUE;
|
|
FREE(depPath);
|
|
depName = tempStr; // depName gets actual name
|
|
depPath = prependPath(depName, getFileName(&finddata));
|
|
} // depPath gets full path
|
|
}
|
|
|
|
// Add to the dependent list
|
|
|
|
do {
|
|
pNew = MakeNewDepListElement();
|
|
// if name contains spaces and has no quotes,
|
|
// add enclosing quotes around it [DS 14575]
|
|
if (_tcschr(depPath, ' ') && !_tcschr(depPath, '\"')) {
|
|
pNew->name = (char *)rallocate (_tcslen(depPath)+3);
|
|
*(pNew->name) = '\"';
|
|
*(pNew->name+1) = '\0';
|
|
_tcscat (pNew->name, depPath);
|
|
_tcscat (pNew->name, "\"");
|
|
}
|
|
else {
|
|
pNew->name = makeString(depPath);
|
|
}
|
|
|
|
if (!fDescRebuildOrder || findFirst(depPath, &finddata, &searchHandle)) {
|
|
pNew->depTime = getDateTime(&finddata);
|
|
} else {
|
|
pNew->depTime = 0L;
|
|
}
|
|
|
|
if (fDescRebuildOrder) {
|
|
insertSort(&depList, pNew);
|
|
} else {
|
|
appendItem((STRINGLIST**)&depList, (STRINGLIST*)pNew);
|
|
}
|
|
FREE(depPath);
|
|
} while (again &&
|
|
_tcspbrk(depName, "*?") && // do all wildcards
|
|
findNext(&finddata, searchHandle) &&
|
|
(depPath = prependPath(depName, getFileName(&finddata)))
|
|
);
|
|
}
|
|
// One dependent (w/wildcards?) was expanded
|
|
|
|
if (source != sList->text) {
|
|
FREE(source);
|
|
}
|
|
|
|
FREE(save);
|
|
}
|
|
|
|
// Now, all dependents are done ...
|
|
|
|
return(depList);
|
|
}
|
|
|
|
void
|
|
insertSort(
|
|
DEPLIST **pDepList,
|
|
DEPLIST *pElement
|
|
)
|
|
{
|
|
time_t item;
|
|
DEPLIST *pList, *current;
|
|
|
|
item = pElement->depTime;
|
|
pList = current = *pDepList;
|
|
|
|
for (;pList && item <= pList->depTime; pList = pList->next) {
|
|
current = pList;
|
|
}
|
|
|
|
if (current == pList) {
|
|
*pDepList = pElement;
|
|
} else {
|
|
current->next = pElement;
|
|
pElement->next = pList;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
nextToken(
|
|
char **pNext,
|
|
char **pToken
|
|
)
|
|
{
|
|
char *s = *pNext;
|
|
|
|
while (*s && WHITESPACE(*s)) {
|
|
++s;
|
|
}
|
|
|
|
if (!*(*pToken = s)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Token begins here
|
|
*pToken = s;
|
|
|
|
if (*s == '"') {
|
|
while (*s && *++s != '"')
|
|
;
|
|
|
|
if (!*s) {
|
|
// lexer possible internal error: missed a quote
|
|
makeError(0, LEXER_INTERNAL);
|
|
}
|
|
|
|
if (*++s) {
|
|
*s++ = '\0';
|
|
}
|
|
|
|
*pNext = s;
|
|
return(TRUE);
|
|
} else if (*s == '{') {
|
|
// skip to '}' outside quotes
|
|
for (;*s;) {
|
|
s++;
|
|
if (*s == '"') {
|
|
s++; // Skip the first quote
|
|
while (*s && *s++ != '"'); // Skip all including the last quote
|
|
}
|
|
if (*s == '}') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!*s) {
|
|
// lexer possible internal error: missed a brace
|
|
makeError(0, MISSING_CLOSING_BRACE);
|
|
}
|
|
|
|
if (*++s == '"') {
|
|
while (*s && *++s != '"')
|
|
;
|
|
|
|
if (!*s) {
|
|
// lexer possible internal error: missed a quote
|
|
makeError(0, LEXER_INTERNAL);
|
|
}
|
|
|
|
if (*++s) {
|
|
*s++ = '\0';
|
|
}
|
|
|
|
*pNext = s;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
while (*s && !WHITESPACE(*s)) {
|
|
++s;
|
|
}
|
|
|
|
if (*s) {
|
|
*s++ = '\0';
|
|
}
|
|
|
|
*pNext = s;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void
|
|
freeStringList(
|
|
STRINGLIST *list
|
|
)
|
|
{
|
|
STRINGLIST *temp;
|
|
|
|
while (temp = list) {
|
|
list = list->next;
|
|
FREE(temp->text);
|
|
FREE_STRINGLIST(temp);
|
|
}
|
|
}
|
|
|
|
|
|
// makeTempObject -- make an object to represent implied dependents
|
|
//
|
|
// We add implied dependents to the target table, but use a special struct
|
|
// that has no pointer to a build list -- they never get removed.
|
|
// time-space trade-off -- can remove them, but will take more proc time.
|
|
|
|
MAKEOBJECT *
|
|
makeTempObject(
|
|
char *target,
|
|
UCHAR flags
|
|
)
|
|
{
|
|
MAKEOBJECT *object;
|
|
unsigned i;
|
|
|
|
object = makeNewObject();
|
|
object->name = makeString(target);
|
|
object->flags2 = flags;
|
|
object->flags3 = 0;
|
|
object->dateTime = 0L;
|
|
object->buildList = NULL;
|
|
i = hash(target, MAXTARGET, (BOOL) TRUE);
|
|
prependItem((STRINGLIST**)targetTable+i, (STRINGLIST*)object);
|
|
return(object);
|
|
}
|
|
|
|
|
|
void
|
|
addBatch(
|
|
BATCHLIST **ppBatchList,
|
|
RULELIST *pRule,
|
|
MAKEOBJECT *pObject,
|
|
char *dollarLt
|
|
)
|
|
{
|
|
STRINGLIST *temp;
|
|
BATCHLIST *pBatch;
|
|
BATCHLIST *pBatchPrev = 0;
|
|
|
|
for(pBatch = *ppBatchList; pBatch; pBatch = pBatch->next) {
|
|
if (pBatch->pRule == pRule &&
|
|
pBatch->flags == pObject->flags2)
|
|
break;
|
|
pBatchPrev = pBatch;
|
|
}
|
|
if (!pBatch) {
|
|
pBatch = makeNewBatchListElement();
|
|
pBatch->pRule = pRule;
|
|
pBatch->flags = pObject->flags2;
|
|
if (pBatchPrev) {
|
|
pBatchPrev->next = pBatch;
|
|
}
|
|
else if(*ppBatchList) {
|
|
(*ppBatchList)->next = pBatch;
|
|
}
|
|
else
|
|
*ppBatchList = pBatch;
|
|
}
|
|
|
|
temp = makeNewStrListElement();
|
|
temp->text = makeString(pObject->name);
|
|
appendItem(&pBatch->nameList, temp);
|
|
|
|
temp = makeNewStrListElement();
|
|
temp->text = makeString(dollarLessThan);
|
|
appendItem(&pBatch->dollarLt, temp);
|
|
|
|
assert(!pObject->ppBatch);
|
|
pObject->ppBatch = ppBatchList;
|
|
}
|
|
|
|
|
|
int doBatchCommand (
|
|
BATCHLIST *pBatch
|
|
)
|
|
{
|
|
size_t cbStr = 0;
|
|
int rc;
|
|
char *pchBuf;
|
|
STRINGLIST *pStrList;
|
|
RULELIST *pRule = pBatch->pRule;
|
|
assert (pBatch->dollarLt);
|
|
assert (pBatch->nameList);
|
|
|
|
// form $<
|
|
for (pStrList = pBatch->dollarLt; pStrList; pStrList = pStrList->next) {
|
|
cbStr += _tcslen(pStrList->text) + 1;
|
|
// allow space for quotes if text contains spaces
|
|
if (_tcschr(pStrList->text, ' '))
|
|
cbStr += 2;
|
|
}
|
|
pchBuf = (char *)allocate(cbStr + 1);
|
|
*pchBuf = 0;
|
|
for (pStrList = pBatch->dollarLt; pStrList; pStrList = pStrList->next) {
|
|
BOOL fQuote;
|
|
// Quote only if not quoted and contains spaces [vs98:8677]
|
|
fQuote = pStrList->text[0] != '"' && _tcschr(pStrList->text, ' ');
|
|
if (fQuote)
|
|
_tcscat(pchBuf, "\"");
|
|
_tcscat(pchBuf, pStrList->text);
|
|
_tcscat(pchBuf, fQuote ? "\" " : " ");
|
|
}
|
|
dollarLessThan = pchBuf;
|
|
|
|
rc = doCommandsEx(pBatch->nameList,
|
|
pRule->buildCommands,
|
|
pRule->buildMacros,
|
|
pBatch->flags,
|
|
NULL);
|
|
|
|
if (rc == 0) {
|
|
STRINGLIST *pName;
|
|
MAKEOBJECT *pObject;
|
|
for (pName = pBatch->nameList; pName; pName = pName->next) {
|
|
pObject = findTarget(pName->text);
|
|
assert (pObject);
|
|
|
|
SET(pObject->flags3, F3_ALREADY_BUILT);
|
|
CLEAR(pObject->flags3, F3_OUT_OF_DATE);
|
|
|
|
pObject->ppBatch = 0;
|
|
}
|
|
}
|
|
|
|
FREE (pchBuf);
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
execBatchList(
|
|
BATCHLIST *pBList
|
|
)
|
|
{
|
|
int status = 0;
|
|
if (pBList) {
|
|
BATCHLIST *pBatch;
|
|
for (pBatch = pBList; pBatch; pBatch=pBatch->next) {
|
|
status += doBatchCommand (pBatch);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
void
|
|
freeBatchList(
|
|
BATCHLIST **ppBList
|
|
)
|
|
{
|
|
BATCHLIST *pBatch = *ppBList;
|
|
|
|
while (pBatch) {
|
|
BATCHLIST *pTmp;
|
|
free_stringlist(pBatch->nameList);
|
|
free_stringlist(pBatch->dollarLt);
|
|
pTmp = pBatch;
|
|
pBatch = pBatch->next;
|
|
FREE(pTmp);
|
|
}
|
|
*ppBList = NULL;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_ALL
|
|
void
|
|
DumpList(
|
|
STRINGLIST *pList
|
|
)
|
|
{
|
|
// STRINGLIST *p;
|
|
printf("* ");
|
|
while (pList) {
|
|
printf(pList->text);
|
|
printf(",");
|
|
pList = pList->next;
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|