Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

834 lines
26 KiB

/*** BUILD.C -- build routines *************************************************
*
* Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
*
* Purpose:
* Module contains routines to build targets
*
* Revision History:
* 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 "nmake.h"
#include "nmmsg.h"
#include "proto.h"
#include "globals.h"
#include "grammar.h"
/*
* 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.
*/
#define MAX(A,B) ((A) > (B) ? (A) : (B))
#define MAXRECLEVEL 10000 /* Maximum recursion level */
/*
* External data declarations used only by this module.
*/
#ifdef DEBUG_MEMORY
extern unsigned long blocksize;
extern unsigned long alloccount;
extern BOOL fDebugMem;
extern FILE *memory;
extern unsigned long freecount;
#endif
/*
* External function declarations.
*/
#ifdef HEAP
extern void NEAR heapdump(char *file, int line);
#endif
/*
* function prototypes for the module
* I make as many things static as possible, just to be extra cautious
*/
LOCAL int NEAR build(MAKEOBJECT*, UCHAR, unsigned long*, BOOL, char *);
LOCAL MAKEOBJECT * NEAR makeTempObject(char*, UCHAR);
LOCAL void NEAR insertSort(DEPLIST **pDepList, DEPLIST *pElement);
LOCAL BOOL NEAR nextToken(char**, char**);
LOCAL DEPLIST * NEAR createDepList(BUILDBLOCK *pBlock, char *objectName);
LOCAL RecLevel = 0; /* Static recursion level. Changed from function */
/* parameter because of treatment of recursive makes.*/
/* 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 NEAR
processTree()
{
STRINGLIST *p;
char *v;
NMHANDLE searchHandle;
int status;
unsigned long dateTime;
for (p = makeTargets; p; p = makeTargets) {
if (_ftcspbrk(makeTargets->text, "*?")) { /* expand wildcards */
void *findBuf = _alloca(resultbuf_size);
if (findFirst(makeTargets->text, &findBuf, &searchHandle)) {
do {
v = prependPath(makeTargets->text, getFileName(&findBuf));
dateTime = getDateTime(&findBuf);
status = invokeBuild(v, flags, &dateTime, NULL);
FREE(v);
if ((status < 0) && (ON(gFlags, F1_QUESTION_STATUS))) {
/* Was not being freed */
freeStringList(p);
return(-1);
}
} while (findNext(&findBuf, 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))) {
/* Was not being freed */
freeStringList(p);
return(255); // Haituanv: change -1 to 255 to follow the manual
}
}
makeTargets = p->next;
FREE_STRINGLIST(p);
}
return(0);
}
int NEAR
invokeBuild(
char *target,
UCHAR pFlags,
unsigned long *timeVal,
char *pFirstDep)
{
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);
--RecLevel;
return(rc);
}
#pragma check_stack(on)
LOCAL int NEAR
build(
MAKEOBJECT *object,
UCHAR parentFlags,
unsigned long *targetTime,
BOOL fInmakefile,
char *pFirstDep)
{
STRINGLIST *questionList,
*starList,
*temp,
*implComList;
void *tempBuf; /* 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;
unsigned long 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 */
unsigned long *blockTime; /* points to dateTime of cmd. block */
extern char *makeStr;
extern UCHAR okToDelete;
UCHAR okDel;
#ifdef DEBUG_MEMORY
if (fDebug) {
fprintf(memory, "Build '%s'\n", object->name);
mem_status();
}
#endif
#ifdef DEBUG_ALL
printf("Build '%s'\n", object->name);
#endif
#ifdef DEBUG_ALL
if (fDebug)
heapdump(__FILE__, __LINE__);
#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 (ON(object->flags3, F3_ALREADY_BUILT)) {
if (ON(parentFlags, F2_DISPLAY_FILE_DATES))
printDate(RecLevel*2, object->name, object->dateTime);
*targetTime = object->dateTime;
return(ON(object->flags3, F3_OUT_OF_DATE)? 1 : 0);
}
tempBuf = _alloca(resultbuf_size);
questionList = NULL;
starList = NULL;
implComList = NULL;
explComBlock = NULL;
targTime = 0L;
newTargTime = 0L;
tempTime = 0L;
depTime = 0L;
maxDepTime = 0L;
blockTime = NULL;
SET(object->flags3, F3_BUILDING_THIS_ONE);
dollarStar = dollarAt = object->name;
#ifdef DEBUG_ALL
printf ("DEBUG: build 1\n");
#endif
//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, &tempBuf, &tHandle)) {
targTime = getDateTime(&tempBuf);
}
#ifdef DEBUG_ALL
printf ("DEBUG: build 2\n");
#endif
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 = invokeBuild(deps->name, /* build the dependent */
block->flags,
&tempTime, NULL);
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, &tempBuf, &tHandle)) {
object->dateTime = targTime = getDateTime(&tempBuf);
}
/*
* 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);
}
/* 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, &tempBuf, &tHandle))
newTargTime = getDateTime(&tempBuf);
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;
} /* targTime <= depTime .... */
if (fOptionK && ON(object->flags3, F3_ERROR_IN_CHILD))
makeError(0, TARGET_ERROR_IN_CHILD, object->name);
} /* block->buildCommands .....*/
}
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);
}
} /* for b = object->buildList */
#ifdef DEBUG_ALL
printf ("DEBUG: build 3\n");
#endif
dollarLessThan = dollarDollarAt = NULL;
if (!(targTime = *targetTime)) { //???????
if (object->dateTime) {
targTime = object->dateTime;
}
else if (findFirst(object->name, &tempBuf, &tHandle)) {
targTime = getDateTime(&tempBuf);
}
}
#ifdef DEBUG_ALL
printf ("DEBUG: build 4\n");
#endif
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;
}
#ifdef DEBUG_ALL
printf ("DEBUG: build 5\n");
#endif
#ifdef DEBUG_HEAP
heapdump(__FILE__, __LINE__);
#endif
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;
#ifdef DEBUG_ALL
printf ("DEBUG: build 6\n");
#endif
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);
//freeList(block->buildCommands);
//freeList(block->buildMacros);
}
else if (implComList)
status += doCommands(object->name, /* do rule's commands */
implComList,
rule->buildMacros,
object->flags2,
pFirstDep);
//for /t with no commands ...
else if (ON(gFlags, F1_TOUCH_TARGETS))
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 build anywhere...
newTargTime +=2;
}
else
newTargTime = maxDepTime;
if (blockTime && explComBlock) *blockTime = newTargTime;
/* set block's time, if a real cmd. block was executed */
}
else if (OFF(gFlags, F1_QUESTION_STATUS) && RecLevel == 1 && !built
&& OFF(object->flags3, F3_ERROR_IN_CHILD))
makeMessage(TARGET_UP_TO_DATE, object->name);
#ifdef DEBUG_ALL
printf ("DEBUG: build 7\n");
#endif
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);
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);
}
#pragma check_stack()
LOCAL DEPLIST * NEAR
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;
void *dBuf = allocate(resultbuf_size);
int searchHandle;
pMacros = bBlock->dependentMacros;
// expand Macros in Dependent list
for (sList = bBlock->dependents; sList; sList = sList->next) {
for (s = sList->text; *s && *s != '$'; 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++)
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(&dBuf, 0L);
depPath = makeString(depName);
if (_ftcspbrk(depName, "*?") || token) /* do wildcards in*/
if (tempStr = searchPath(token,depName,dBuf,&searchHandle)){ /*filename*/
again = TRUE;
FREE(depPath);
depName = tempStr; /* depName gets actual name*/
depPath = prependPath(depName, getFileName(&dBuf));
}; /*depPath gets full path */
// Add to the dependent list
do {
pNew = MakeNewDepListElement();
pNew->name = makeString(depPath);
if (!fDescRebuildOrder
|| findFirst(depPath, &dBuf, &searchHandle)) {
pNew->depTime = getDateTime(&dBuf);
}
else {
pNew->depTime = 0L;
}
if (fDescRebuildOrder)
insertSort(&depList, pNew);
else {
appendItem((STRINGLIST**)&depList, (STRINGLIST*)pNew);
}
FREE(depPath);
} while (again
&& _ftcspbrk(depName, "*?") /* do all wildcards */
&& findNext(&dBuf, searchHandle)
&& (depPath = prependPath(depName, getFileName(&dBuf))));
}
// One dependent (w/wildcards?) was expanded
if (source != sList->text)
FREE(source);
FREE(save);
}
// Now, all dependents are done ...
FREE(dBuf);
return(depList);
}
LOCAL void NEAR
insertSort(
DEPLIST **pDepList,
DEPLIST *pElement
) {
unsigned long 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;
}
return;
}
LOCAL BOOL NEAR
nextToken(pNext, pToken)
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;)
if (*s == '"')
while (*s && *s != '"')
++s;
else 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 NEAR
freeList(list)
STRINGLIST *list; //Not really a STRINGLIST *
{
STRINGLIST *temp;
while (temp = list) {
list = list->next;
FREE(temp->text);
FREE(temp);
}
}
void NEAR 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.
*/
LOCAL MAKEOBJECT * NEAR
makeTempObject(target, flags)
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);
}
BOOL NEAR
inSuffixList(suffix)
char *suffix;
{
STRINGLIST *p;
for (p = dotSuffixList; p; p = p->next)
if (!_ftcsicmp(suffix, p->text))
return((BOOL)TRUE);
return((BOOL)FALSE);
}
#ifdef DEBUG_LIST
void NEAR DumpList(pList)
STRINGLIST *pList;
{
STRINGLIST *p;
printf("* ");
while (pList) {
printf(pList->text);
printf(",");
pList = pList->next;
}
printf("\n");
}
#endif