windows-nt/Source/XPSP1/NT/sdktools/nmake/build.cpp
2020-09-26 16:20:57 +08:00

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