|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1989 - 1994.
//
// File: buildmak.c
//
// Contents: This is the Make module for the NT Build Tool (BUILD.EXE)
//
// The Make module scans directories for file names and edits the
// data base appropriately.
//
// Functions:
//
// History: 16-May-89 SteveWo Created
// ... See SLM log
// 26-Jul-94 LyleC Cleanup/Add Pass0 support
//
//----------------------------------------------------------------------------
#include "build.h"
#define SCANFLAGS_CHICAGO 0x00000002
#define SCANFLAGS_OS2 0x00000004
#define SCANFLAGS_POSIX 0x00000008
#define SCANFLAGS_CRT 0x00000010
ULONG ScanFlagsLast; ULONG ScanFlagsCurrent;
USHORT GlobalSequence; USHORT LocalSequence; ULONG idFileToCompile = 1; BOOL fLineCleared = TRUE;
char szRecurse[] = " . . . . . . . . ."; char szAsterisks[] = " ********************";
char *pszSdkLibDest; char *pszDdkLibDest; char *pszIncOak; char *pszIncDdk; char *pszIncWdm; char *pszIncSdk; char *pszIncCrt; char *pszIncMfc; char *pszIncOs2; char *pszIncPosix; char *pszIncChicago;
char szCheckedAltDir[] = " CHECKED_ALT_DIR=1";
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array, type) (sizeof(array)/sizeof(type))
#endif
//
// The following definitions are for the ObjectDirFlag entry in the TARGETDATA
// struct.
//
//
// TD_OBJECTDIR maps to ObjectDirectory[iObjectDir]\foobar.tar
// TD_PASS0HDRDIR maps to $(PASS0_HEADERDIR)\foobar.tar
// TD_PASS0DIR1 maps to $(PASS0_SOURCEDIR)\foobar.tar or $(PASS0_CLIENTDIR)\foobar.tar
// TD_PASS0DIR2 maps to $(MIDL_UUIDDIR)\foobar.tar or $(PASS0_SERVERDIR)\foobar.tar
//
// where .tar is the given target extension, ObjectDirectory[iObjectDir] is the
// appropriate object directory for that platform, and the macros are expanded
// to the values given in the sources file.
//
#define TD_OBJECTDIR 1
#define TD_PASS0HDRDIR 2
#define TD_PASS0DIR1 3
#define TD_PASS0DIR2 4
#define TD_MCSOURCEDIR 5
typedef struct _tagTARGETDATA { UCHAR ObjectDirFlag; // Indicates what object dir should be used.
LPSTR pszTargetExt; // Extension of target. (Including '.')
} TARGETDATA, *LPTARGETDATA;
typedef struct _tagOBJECTTARGETINFO { LPSTR pszSourceExt; // Extension of source file (including '.').
UCHAR NumData; // Number of entries in [Data].
LPTARGETDATA Data; // Pointer to array of TARGETDATAs.
} OBJECTTARGETINFO, *LPOBJECTTARGETINFO;
typedef struct _tagOBJECTTARGETARRAY { int cTargetInfo; OBJECTTARGETINFO **aTargetInfo; } OBJECTTARGETARRAY;
//
// TARGETDATA information is used by both BuildCompileTarget() and
// WriteObjectsDefinition() via the GetTargetData() function. Do not put
// extensions in this table whose TARGETDATA consists entirely of
// { TD_OBJECTDIR, ".obj" } because that is the default. Instead you must
// modify the switch statement in WriteObjectsDefinition.
//
// The first target in each TARGETDATA array is considered the 'rule target'
// because that is the target for which the inference rule in makefile.def is
// written. The 'rule target' will always be deleted in addition to the
// out-of-date target if *any* of the targets are out of date.
//
//
// The following data defines the *PASS0* mappings of source extensions
// to target files:
//
// .idl -> $(PASS0_HEADERDIR)\.h,
// $(PASS0_SOURCEDIR)\_p.c,
// $(MIDL_UUIDDIR)\_i.c
// .asn -> $(PASS0_HEADERDIR)\.h,
// $(PASS0_HEADERDIR)\.c
// .mc -> $(PASS0_HEADERDIR)\.h, $(PASS0_SOURCEDIR)\.rc
// .odl -> obj\*\.tlb
// .tdl -> obj\*\.tlb
//
// .mc -> $(PASS0_HEADERDIR)\.h, $(PASS0_HEADERDIR)\.rc
TARGETDATA MCData0[] = { { TD_PASS0HDRDIR, ".h" }, { TD_MCSOURCEDIR, ".rc" } }; OBJECTTARGETINFO MCInfo0 = { ".mc", ARRAY_SIZE(MCData0, TARGETDATA), MCData0 };
// .asn -> $(PASS0_HEADERDIR)\.h, $(PASS0_SOURCEDIR)\.c
TARGETDATA AsnData0[] = { { TD_PASS0HDRDIR, ".h" }, { TD_PASS0DIR1, ".c" }, }; OBJECTTARGETINFO AsnInfo0 = { ".asn", ARRAY_SIZE(AsnData0, TARGETDATA), AsnData0 };
// .odl/.tdl -> obj\*\.tlb
TARGETDATA TLBData0 = { TD_OBJECTDIR, ".tlb" };
OBJECTTARGETINFO TLBInfo0 = { ".tdl", ARRAY_SIZE(TLBData0, TARGETDATA), &TLBData0 };
OBJECTTARGETINFO TLB2Info0 = { ".odl", ARRAY_SIZE(TLBData0, TARGETDATA), &TLBData0 };
// .thk -> obj\*\.asm
TARGETDATA THKData0 = { TD_OBJECTDIR, ".asm" };
OBJECTTARGETINFO THKInfo0 = { ".thk", ARRAY_SIZE(THKData0, TARGETDATA), &THKData0 };
// .mof -> obj\*\.mof, obj\*\.bmf
TARGETDATA MOFData0[] = { {TD_OBJECTDIR, ".mof" }, {TD_OBJECTDIR, ".bmf" } }; OBJECTTARGETINFO MOFInfo0 = { ".mof", ARRAY_SIZE(MOFData0, TARGETDATA), MOFData0 };
// ------
LPOBJECTTARGETINFO aTargetInfo0[] = { &MCInfo0, &AsnInfo0, &TLBInfo0, &TLB2Info0, &THKInfo0, &MOFInfo0, }; #define CTARGETINFO0 ARRAY_SIZE(aTargetInfo0, LPOBJECTTARGETINFO)
//
// The following data defines the *PASS1* mappings of source extensions
// to target files:
//
// .rc -> obj\*\.res
// .asn -> obj\*\.obj
// .thk -> obj\*\.asm,
// .java -> obj\*\.class,
// obj\*\.obj,
//
// .rc -> obj\*\.res
TARGETDATA RCData1 = { TD_OBJECTDIR, ".res" }; OBJECTTARGETINFO RCInfo1 = { ".rc", ARRAY_SIZE(RCData1, TARGETDATA), &RCData1 };
// .thk -> .asm -> .obj
TARGETDATA THKData1[] = { {TD_OBJECTDIR, ".obj" } }; OBJECTTARGETINFO THKInfo1 = { ".thk", ARRAY_SIZE(THKData1, TARGETDATA), THKData1 };
// .java -> .class
TARGETDATA JAVAData1[] = { {TD_OBJECTDIR, ".class" } }; OBJECTTARGETINFO JAVAInfo1 = { ".java", ARRAY_SIZE(JAVAData1, TARGETDATA), JAVAData1 };
// ------
LPOBJECTTARGETINFO aTargetInfo1[] = { &RCInfo1, &THKInfo1, &JAVAInfo1, }; #define CTARGETINFO1 ARRAY_SIZE(aTargetInfo1, LPOBJECTTARGETINFO)
OBJECTTARGETARRAY aTargetArray[] = { { CTARGETINFO0, aTargetInfo0 }, { CTARGETINFO1, aTargetInfo1 }, };
// ------
// MIDL stuff -- IDL files have two potential sets of targets, depending
// on if the IDL_TYPE flag was set to 'ole' in the sources file or not.
//
// IDL_TYPE = ole
// .idl -> $(PASS0_HEADERDIR)\.h,
// $(PASS0_SOURCEDIR)\_p.c,
// $(MIDL_UUIDDIR)\_i.c
TARGETDATA IDLDataOle0[] = { { TD_PASS0HDRDIR, ".h" }, // Header File
// { TD_PASS0DIR1, "_p.c" }, // Proxy Stub File
// { TD_PASS0DIR2, "_i.c" }, // UUID file
}; OBJECTTARGETINFO IDLInfoOle0 = { ".idl", ARRAY_SIZE(IDLDataOle0, TARGETDATA), IDLDataOle0 };
// IDL_TYPE = rpc
// .idl -> $(PASS0_HEADERDIR)\.h,
// $(PASS0_CLIENTDIR)\_c.c,
// $(PASS0_SERVERDIR)\_s.c,
TARGETDATA IDLDataRpc0[] = { { TD_PASS0HDRDIR, ".h" }, // Header File
// { TD_PASS0DIR1, "_c.c" }, // Client Stub File
// { TD_PASS0DIR2, "_s.c" }, // Server Stub File
}; OBJECTTARGETINFO IDLInfoRpc0 = { ".idl", ARRAY_SIZE(IDLDataRpc0, TARGETDATA), IDLDataRpc0 };
// ------
LPOBJECTTARGETINFO aMidlTargetInfo0[] = { &IDLInfoOle0, &IDLInfoRpc0, }; UCHAR cMidlTargetInfo0 = ARRAY_SIZE(aMidlTargetInfo0, LPOBJECTTARGETINFO);
// ------
//
// Any extension not given in the above table is assumed to have a target in
// the ObjectDirectory[iObjectDir] (obj\*) & have a target extension of .obj.
//
TARGETDATA DefaultData = { TD_OBJECTDIR, ".obj" };
//*******
TARGET * BuildCompileTarget( FILEREC *pfr, LPSTR pszfile, USHORT TargetIndex, LPSTR pszConditionalIncludes, DIRREC *pdrBuild, DIRSUP *pdsBuild, LONG iPass, LPSTR *ppszObjectDir, LPSTR pszSourceDir);
//+---------------------------------------------------------------------------
//
// Function: ExpandObjAsterisk
//
// Synopsis: Expand an asterisk in a filename to a platform name
//
// Arguments: [pbuf] -- Output buffer for new filename
// [pszpath] -- Input filename w/ asterisk
// [ppszObjectDirectory] -- String[2] to replace asterisk with
//
//----------------------------------------------------------------------------
VOID ExpandObjAsterisk( LPSTR pbuf, LPSTR pszpath, LPSTR *ppszObjectDirectory) { SplitToken(pbuf, '*', &pszpath); if (*pszpath == '*') { assert(strncmp( pszObjDirSlash, ppszObjectDirectory[iObjectDir], strlen(pszObjDirSlash)) == 0); strcat(pbuf, ppszObjectDirectory[iObjectDir] + strlen(pszObjDirSlash)); strcat(pbuf, pszpath + 1); } }
//+---------------------------------------------------------------------------
//
// Function: CountSourceLines
//
// Synopsis: Counts the source lines in a given file, including headers if
// the '-S' option was given.
//
// Arguments: [idScan] -- Used to catch multiple inclusions
// [pfr] -- File to scan
//
// Returns: Number of lines
//
//----------------------------------------------------------------------------
LONG CountSourceLines(USHORT idScan, FILEREC *pfr) { INCLUDEREC *pir;
AssertFile(pfr);
// if we have already seen this file before, then assume
// that #if guards prevent it's inclusion
if (pfr->idScan == idScan) { return(0L); }
pfr->idScan = idScan;
// Start off with the file itself
pfr->TotalSourceLines = pfr->SourceLines;
if (fStatusTree) {
//
// If the user asked for include file line counts, then walk include
// tree, accruing nested include file line counts .
//
for (pir = pfr->IncludeFilesTree; pir != NULL; pir = pir->NextTree) { AssertInclude(pir); if (pir->pfrInclude != NULL) { AssertFile(pir->pfrInclude); pfr->TotalSourceLines += CountSourceLines(idScan, pir->pfrInclude); } } } return(pfr->TotalSourceLines); }
//+---------------------------------------------------------------------------
//
// Function: CleanNTTargetFile0
//
// Synopsis: Parses pzFiles and deletes all files listed.
// pzFile must have been allocated by MakeMacroString.
// No asterisk expansion performed.
//
// This is used when fClean is TRUE and SOURCES_OPTIONS
// includes -c0. See ReadSourcesFile. Note that
// SOURCES_OPTIONS must be defined before NTTARGETFILE0.
// This is a mechanism to delete target files not
// included in _objects.mac.
//
// Arguments: [pzFiles] -- List of files
//
//----------------------------------------------------------------------------
VOID CleanNTTargetFile0 (char * pzFiles) { BOOL fRestoreSep; char * pzDelete;
while (*pzFiles != '\0') { pzDelete = pzFiles;
// Find end of the next file name and NULL terminate it (if needed)
fRestoreSep = FALSE; while (*pzFiles != '\0') { if (*pzFiles == ' ') { fRestoreSep = TRUE; *pzFiles = '\0'; break; } else { pzFiles++; } }
DeleteSingleFile (NULL, pzDelete, FALSE);
if (fRestoreSep) { *pzFiles++ = ' '; } } }
//+---------------------------------------------------------------------------
//
// Function: ProcessSourceDependencies
//
// Synopsis: Scan all source files in a given directory tree to determine
// which files are out of date and need to be compiled and/or
// linked.
//
// Arguments: [DirDB] -- Directory to process
// [pds] -- Supplementary directory information
// [DateTimeSources] -- Timestamp of 'sources' file
//
//----------------------------------------------------------------------------
VOID ProcessSourceDependencies(DIRREC *DirDB, DIRSUP *pds, ULONG DateTimeSources) { TARGET *Target; ULONG DateTimePch = 0; // Actual timestamp of pch preserved here.
UINT i; SOURCEREC *apsr[3]; SOURCEREC **ppsr; char path[DB_MAX_PATH_LENGTH]; static USHORT idScan = 0;
AssertDir(DirDB);
apsr[0] = pds->psrSourcesList[0]; apsr[2] = NULL;
//
// For a clean build, just delete all targets
//
if (fFirstScan && fClean && !fKeep) { DeleteMultipleFiles("obj", "*.*"); // _objects.mac
for (i = 0; i < CountTargetMachines; i++) { assert(strncmp( pszObjDirSlash, TargetMachines[i]->ObjectDirectory[iObjectDir], strlen(pszObjDirSlash)) == 0); DeleteMultipleFiles(TargetMachines[i]->ObjectDirectory[iObjectDir], "*.*");
apsr[1] = pds->psrSourcesList[TargetToPossibleTarget[i] + 1];
//
// Delete the pch file if we have one.
//
if (pds->PchTarget != NULL) { char TargetDir[DB_MAX_PATH_LENGTH]; ExpandObjAsterisk(TargetDir, pds->PchTargetDir, TargetMachines[i]->ObjectDirectory);
//
// Kind of a cludgy way to do this, but we must ensure that
// we don't delete a pch file that was built earlier on during
// this same build. We do this by comparing the timestamp of
// the pch file against the time we started the build.
//
if ((*pDateTimeFile)(TargetDir, pds->PchTarget) <= BuildStartTime) { DeleteSingleFile(TargetDir, pds->PchTarget, FALSE); if (DirDB->PchObj != NULL) { ExpandObjAsterisk(path, DirDB->PchObj, TargetMachines[i]->ObjectDirectory); DeleteSingleFile(NULL, path, FALSE); } else { char *p; strcpy(path, pds->PchTarget); p = strrchr(path, '.'); if (p != NULL && strcmp(p, ".pch") == 0) { strcpy(p, ".obj"); DeleteSingleFile(TargetDir, path, FALSE); } } } }
if (DirDB->DirFlags & DIRDB_PASS0) { for (ppsr = apsr; *ppsr != NULL; ppsr++) { SOURCEREC *psr;
for (psr = *ppsr; psr != NULL; psr = psr->psrNext) { FILEREC *pfr;
AssertSource(psr);
pfr = psr->pfrSource;
//
// Pass Zero files have different target directories.
//
if (pfr->FileFlags & FILEDB_PASS0) { USHORT j; //
// If the file has multiple targets, (e.g. .mc,
// .idl or .asn), then loop through all targets.
//
j = 0; while (Target = BuildCompileTarget( pfr, pfr->Name, j, pds->ConditionalIncludes, DirDB, pds, 0, // pass 0
TargetMachines[i]->ObjectDirectory, TargetMachines[i]->SourceDirectory)) {
DeleteSingleFile(NULL, Target->Name, FALSE);
FreeMem(&Target, MT_TARGET);
j++; } } } } }
if ((DirDB->DirFlags & DIRDB_TARGETFILE0) && (DirDB->NTTargetFile0 != NULL)) { CleanNTTargetFile0 (DirDB->NTTargetFile0); } } }
if (fFirstScan && (DirDB->DirFlags & DIRDB_TARGETFILE0)) { DirDB->DirFlags |= DIRDB_PASS0NEEDED; }
if (!fQuickZero || !fFirstScan || !RecurseLevel) { GenerateObjectsDotMac(DirDB, pds, DateTimeSources); } else if (fFirstScan) { SOURCEREC *psr; USHORT i; USHORT j; BOOL fNeedCompile = FALSE;
if ( !(DirDB->DirFlags & DIRDB_PASS0NEEDED)) {
for (i = 0; i < CountTargetMachines; i++) {
for (psr = pds->psrSourcesList[0]; psr != NULL; psr = psr->psrNext) { FILEREC *pfr;
AssertSource(psr);
pfr = psr->pfrSource;
AssertFile(pfr);
if (pfr->FileFlags & FILEDB_PASS0) {
for (j = 0; Target = BuildCompileTarget( pfr, pfr->Name, j, pds->ConditionalIncludes, DirDB, pds, 0, TargetMachines[i]->ObjectDirectory, TargetMachines[i]->SourceDirectory); j++) {
if ((psr->SrcFlags & SOURCEDB_FILE_MISSING) || (Target->DateTime == 0) || ((pfr->FileFlags & FILEDB_C) && Target->DateTime < DateTimePch)) { fNeedCompile = TRUE; }
FreeMem(&Target, MT_TARGET); }
if (fNeedCompile) { DirDB->DirFlags |= DIRDB_PASS0NEEDED; } } } } }
if (DirDB->DirFlags & DIRDB_PASS0NEEDED) { GenerateObjectsDotMac(DirDB, pds, DateTimeSources); } }
if ((DirDB->TargetExt != NULL) && (DirDB->TargetName != NULL) && (DirDB->TargetPath != NULL) && (fClean && !fKeep)) { // If we haven't already deleted the final target, do so now.
if (_memicmp(DirDB->TargetPath, pszObjDirSlash, strlen(pszObjDirSlash) -1)) { for (i = 0; i < CountTargetMachines; i++) { FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, DirDB->TargetPath, DirDB->TargetName, DirDB->TargetExt); DeleteSingleFile(NULL, path, FALSE); FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, DirDB->TargetPath, DirDB->TargetName, ".pdb"); DeleteSingleFile(NULL, path, FALSE); } } }
if (pds->fNoTarget) { if (apsr[0] || !(DirDB->DirFlags & DIRDB_PASS0NEEDED) || fSemiQuicky) { // If there's sources to compile, mark as such then get out.
DirDB->DirFlags |= DIRDB_COMPILENEEDED; } return; }
if (fQuicky) { if (fSemiQuicky) DirDB->DirFlags |= DIRDB_COMPILENEEDED; else DirDB->DirFlags |= DIRDB_PASS0NEEDED; return; }
//
// For a DLL or LIB target, ensure that it will be rebuilt
//
if (DirDB->TargetPath != NULL && DirDB->TargetName != NULL && ((DirDB->DirFlags & DIRDB_DLLTARGET) || (DirDB->TargetExt != NULL && strcmp(DirDB->TargetExt, ".lib") == 0))) {
for (i = 0; i < CountTargetMachines; i++) { FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, DirDB->TargetPath, DirDB->TargetName, ".lib");
if (ProbeFile(NULL, path) == -1) { DirDB->DirFlags |= DIRDB_COMPILENEEDED; } else if (fFirstScan && (fCleanLibs || (fClean && !fKeep))) { DeleteSingleFile(NULL, path, FALSE); DirDB->DirFlags |= DIRDB_COMPILENEEDED; } } }
//
// If the scan flags have changed (or haven't been set), then indicate
// that we should look for the actual location of global included files
// instead of assuming it's in the same location as we last knew. This is
// because different directories my include the same file from different
// places.
//
if (GlobalSequence == 0 || ScanFlagsLast == 0 || ScanFlagsLast != ScanFlagsCurrent) {
GlobalSequence++; // don't reuse snapped global includes
if (GlobalSequence == 0) { GlobalSequence++; } ScanFlagsLast = ScanFlagsCurrent; }
//
// Do the same as above for locally included files.
//
LocalSequence++; // don't reuse snapped local includes
if (LocalSequence == 0) { LocalSequence++; }
for (i = 0; i < CountTargetMachines; i++) {
//
// Ensure that precompiled headers are rebuilt as necessary.
//
if (!fPassZero && (pds->PchInclude != NULL || pds->PchTarget != NULL)) { LPSTR p;
ExpandObjAsterisk( path, pds->PchTargetDir != NULL? pds->PchTargetDir : pszObjDirSlashStar, TargetMachines[i]->ObjectDirectory);
if (!CanonicalizePathName(path, CANONICALIZE_DIR, path)) { DateTimePch = ULONG_MAX; // always out of date
goto ProcessSourceList; } strcat(path, "\\");
//
// If they gave a target directory for the pch file, then use it,
// otherwise assume it's in the same directory as the .h file.
//
if (pds->PchTarget != NULL) { strcat(path, pds->PchTarget); } else { assert(pds->PchInclude != NULL); p = path + strlen(path); if ( DirDB->Pch ) { strcpy(p, DirDB->Pch); } else { strcpy(p, pds->PchInclude); if ((p = strrchr(p, '.')) != NULL) { *p = '\0'; } strcat(path, ".pch"); } }
//
// 'path' now contains the (possibly relative) path name of
// the PCH target: "..\path\foobar.pch"
//
Target = BuildCompileTarget( NULL, path, 0, pds->ConditionalIncludes, DirDB, NULL, 1, // pass 1
TargetMachines[i]->ObjectDirectory, TargetMachines[i]->SourceDirectory);
DateTimePch = Target->DateTime;
if (DateTimePch == 0) { // Target doesn't exist
DateTimePch = ULONG_MAX; // Always out of date
}
if (fClean && !fKeep && fFirstScan) { // Target will be deleted later if it exists.
} else if (pds->PchInclude == NULL) {
//
// The SOURCES file didn't indicate where the source file
// for the .pch is, so assume the .pch binary is up to date
// with respect to the source includes and with respect to
// the pch source file itself.
//
// char szFullPath[DB_MAX_PATH_LENGTH];
// CanonicalizePathName(DirDB->Name, CANONICALIZE_DIR, szFullPath);
//BuildMsg("SOURCES file in %s gives PRECOMPILED_TARGET but not "
// "PRECOMPILED_INCLUDE.\n", szFullPath);
Target->DateTime = 0; // Don't delete pch target
} else { FILEREC *pfrPch = NULL;
path[0] = '\0';
if (pds->PchIncludeDir != NULL) { strcpy(path, pds->PchIncludeDir); strcat(path, "\\"); } strcat(path, pds->PchInclude);
if ((pds->PchIncludeDir != NULL) && (IsFullPath(pds->PchIncludeDir))) { DIRREC *DirDBPch;
DirDBPch = FindSourceDirDB(pds->PchIncludeDir, pds->PchInclude, TRUE);
if (DirDBPch) { pfrPch = FindSourceFileDB(DirDBPch, pds->PchInclude, NULL); } } else { pfrPch = FindSourceFileDB(DirDB, path, NULL); }
if (pfrPch != NULL) { FILEREC *pfrRoot; SOURCEREC *psr = NULL;
BOOL fCase1; BOOL fCase2; BOOL fCase3; BOOL fNeedCompile; BOOL fCheckDepends;
// Remote directory PCH files can't be found here
if (pfrPch->Dir == DirDB) { psr = FindSourceDB(pds->psrSourcesList[0], pfrPch); assert(psr != NULL); psr->SrcFlags |= SOURCEDB_PCH; }
Target->pfrCompiland = pfrPch; assert((pfrRoot = NULL) == NULL); // assign NULL
fNeedCompile = FALSE; fCheckDepends = FALSE;
switch(0) { default: fCase1 = (fStatusTree && (fCheckDepends=TRUE) && CheckDependencies(Target, pfrPch, TRUE, &pfrRoot)); if ( fCase1 ) { fNeedCompile = TRUE; break; } fCase2 = (Target->DateTime == 0); if ( fCase2 ) { fNeedCompile = TRUE; break; } fCase3 = (!fStatusTree && (fCheckDepends=TRUE) && CheckDependencies(Target, pfrPch, TRUE, &pfrRoot)); if ( fCase3 ) { fNeedCompile = TRUE; break; } break; }
if (( fCheckIncludePaths ) && ( ! fCheckDepends )) { CheckDependencies(Target, pfrPch, TRUE, &pfrRoot); }
if (fNeedCompile) {
if (psr != NULL) { if (fWhyBuild) { BuildMsgRaw("\n"); if (fCase1) { BuildMsgRaw("Compiling %s because (Case 1) *1\n", psr->pfrSource->Name); } else if (fCase2) { BuildMsgRaw("Compiling %s because Target date == 0 (Target->Compiland=%s) *1\n", psr->pfrSource->Name, Target->pfrCompiland->Name); } else if (fCase3) { BuildMsgRaw("Compiling %s because (Case 3) *1\n", psr->pfrSource->Name); } }
psr->SrcFlags |= SOURCEDB_COMPILE_NEEDED; } else { if (fWhyBuild) { BuildMsgRaw("\n"); BuildMsgRaw("Compiling %s because Target date == 0 (Target->Compiland=%s) *1\n", Target->Name, Target->pfrCompiland->Name); } }
pfrPch->Dir->DirFlags |= DIRDB_COMPILENEEDED; DateTimePch = ULONG_MAX; // always out of date
if (fKeep) { Target->DateTime = 0; // don't delete pch target
} } else { // else it exists and is up to date...
Target->DateTime = 0; // don't delete pch target
}
// No cycle possible at the root of the tree.
assert(pfrRoot == NULL); } else if (DEBUG_1) { BuildError("Cannot locate precompiled header file: %s.\n", path); } }
//
// Target->DateTime will be zero if the file is up to date (or we
// don't want to delete it). If Target->DateTime is non-zero,
// delete the .pch and corresponding .obj file so they will be
// rebuilt.
//
if (Target->DateTime != 0) { DeleteSingleFile(NULL, Target->Name, FALSE); if (DirDB->PchObj != NULL) { ExpandObjAsterisk( path, DirDB->PchObj, TargetMachines[i]->ObjectDirectory); DeleteSingleFile(NULL, path, FALSE); } else { p = strrchr(Target->Name, '.'); if (p != NULL && strcmp(p, ".pch") == 0) { strcpy(p, ".obj"); DeleteSingleFile(NULL, Target->Name, FALSE); } } } FreeMem(&Target, MT_TARGET); }
//
// Check to see which files given in the SOURCES macro need to be
// rebuilt, and delete their targets (.obj) if they're out of date.
//
ProcessSourceList:
apsr[1] = pds->psrSourcesList[TargetToPossibleTarget[i] + 1];
for (ppsr = apsr; ppsr < apsr + (sizeof(apsr)/sizeof(*apsr)); ppsr++) { SOURCEREC *psr;
if (*ppsr == NULL) { continue; }
for (psr = *ppsr; psr != NULL; psr = psr->psrNext) { FILEREC *pfr, *pfrRoot;
AssertSource(psr);
pfr = psr->pfrSource;
AssertFile(pfr);
if ((psr->SrcFlags & SOURCEDB_PCH) == 0) {
USHORT j; LONG iPass, iPassEnd;
iPass = 1; iPassEnd = 0;
if (pfr->FileFlags & FILEDB_PASS0) iPass = 0;
if ((pfr->FileFlags & FILEDB_MULTIPLEPASS) || !(pfr->FileFlags & FILEDB_PASS0)) iPassEnd = 1;
assert(iPass <= iPassEnd);
//
// If we're doing a pass zero scan and the file is
// not a pass zero file, then continue because we
// don't care about it right now.
//
if (fFirstScan && fPassZero && iPass == 1) { continue; }
//
// Don't check dependencies of pass zero files on the
// second scan, because they're all supposed to be built
// by now.
//
if (!fFirstScan && iPassEnd == 0) { continue; }
//
// If the file was created during pass zero, then make sure
// we don't think it's still missing.
//
if (!fFirstScan && (psr->SrcFlags & SOURCEDB_FILE_MISSING) && !(pfr->FileFlags & FILEDB_FILE_MISSING)) { psr->SrcFlags &= ~SOURCEDB_FILE_MISSING; }
// If the file is a multiple pass file (e.g. .asn), loop
// through both passes.
for ( ; iPass <= iPassEnd; iPass++) {
//
// If the file has multiple targets (e.g. .mc, .idl or
// .asn), then loop through all the targets.
//
for (j = 0; Target = BuildCompileTarget( pfr, pfr->Name, j, pds->ConditionalIncludes, DirDB, pds, iPass, TargetMachines[i]->ObjectDirectory, TargetMachines[i]->SourceDirectory); j++) {
BOOL fCase1; BOOL fCase2; BOOL fCase3; BOOL fCase4; BOOL fCase5; BOOL fNeedCompile; BOOL fCheckDepends;
if (DEBUG_4) { BuildMsgRaw(szNewLine); } assert((pfrRoot = NULL) == NULL); // assign NULL
// Decide whether the target needs to be compiled.
// Forcibly examine dependencies to get line count.
fNeedCompile = FALSE; fCheckDepends = FALSE;
switch(0) { default: fCase1 = (psr->SrcFlags & SOURCEDB_FILE_MISSING); if ( fCase1 ) { fNeedCompile = TRUE; break; } fCase2 = (fStatusTree && (fCheckDepends=TRUE) && CheckDependencies(Target, pfr, TRUE, &pfrRoot)); if ( fCase2 ) { fNeedCompile = TRUE; break; } fCase3 = (Target->DateTime == 0); if ( fCase3 ) { fNeedCompile = TRUE; break; } fCase4 = ((pfr->FileFlags & FILEDB_C) && Target->DateTime < DateTimePch); if ( fCase4 ) { fNeedCompile = TRUE; break; } fCase5 = (!fStatusTree && (fCheckDepends=TRUE) && CheckDependencies(Target, pfr, TRUE, &pfrRoot)); if ( fCase5 ) { fNeedCompile = TRUE; break; } break; }
if (( fCheckIncludePaths ) && ( ! fCheckDepends )) { CheckDependencies(Target, pfr, TRUE, &pfrRoot); }
if ( fNeedCompile ) { if (fWhyBuild) { BuildMsgRaw("\n"); if (fCase1) { BuildMsgRaw("Compiling %s because filename is missing from build database *2\n", psr->pfrSource->Name); } else if (fCase2) { BuildMsgRaw("Compiling %s because (Case 2) *2\n", psr->pfrSource->Name); } else if (fCase3) { BuildMsgRaw("Compiling %s because Target date == 0 *2\n", psr->pfrSource->Name); } else if (fCase4) { BuildMsgRaw("Compiling %s because C file is later earlier than pch *2\n", psr->pfrSource->Name); } else if (fCase5) { BuildMsgRaw("Compiling %s because (Case 5) *2\n", psr->pfrSource->Name); } }
psr->SrcFlags |= SOURCEDB_COMPILE_NEEDED;
if (pfr->FileFlags & FILEDB_PASS0) { DirDB->DirFlags |= DIRDB_PASS0NEEDED; } else DirDB->DirFlags |= DIRDB_COMPILENEEDED;
if (Target->DateTime != 0 && !fKeep) { DeleteSingleFile(NULL, Target->Name, FALSE); }
FreeMem(&Target, MT_TARGET);
if (j != 0) { //
// Delete the 'rule target' so nmake
// doesn't complain about "don't know how
// to make ..."
//
Target = BuildCompileTarget( pfr, pfr->Name, 0, pds->ConditionalIncludes, DirDB, pds, iPass, TargetMachines[i]->ObjectDirectory, TargetMachines[i]->SourceDirectory); if (Target) { DeleteSingleFile( NULL, Target->Name, FALSE);
FreeMem(&Target, MT_TARGET); } }
// No need to check other targets,
// we know they all will be rebuilt.
break; }
// No cycle possible at the root of the tree.
assert(pfrRoot == NULL); FreeMem(&Target, MT_TARGET); } } } if (fClean || (psr->SrcFlags & SOURCEDB_COMPILE_NEEDED)) { ULONG cline;
if (++idScan == 0) { ++idScan; // skip zero
}
if (fFirstScan && (pfr->FileFlags & FILEDB_PASS0)) { cline = CountSourceLines(idScan, pfr); DirDB->PassZeroLines += cline; DirDB->CountOfPassZeroFiles++; }
// For a multiple pass file, we really need to count the
// lines in the file compiled duing pass1 (and generated
// during pass 0). Instead, we just count the pass 0
// source file all over again. It's cheap, but the line
// count is inaccurate.
if (!fPassZero && ((pfr->FileFlags & FILEDB_MULTIPLEPASS) || !(pfr->FileFlags & FILEDB_PASS0))) { cline = CountSourceLines(idScan, pfr); DirDB->SourceLinesToCompile += cline; DirDB->CountOfFilesToCompile++; } } } } } }
//+---------------------------------------------------------------------------
//
// Function: ScanSourceDirectories
//
// Synopsis: Scan a source directory to determine what files it
// contains, whether it should be compiled or linked, and
// whether it has subdirectories that we should process.
//
// Arguments: [DirName] -- Directory to scan
//
//----------------------------------------------------------------------------
VOID ScanSourceDirectories(LPSTR DirName) { char path[DB_MAX_PATH_LENGTH]; PDIRREC DirDB; DIRSUP *pds = NULL; LPSTR SavedCurrentDirectory; BOOL DirsPresent; ULONG DateTimeSources = 0; UINT i;
if (DEBUG_4) { BuildMsgRaw( "ScanSourceDirectories(%s) level = %d\n", DirName, RecurseLevel); }
// Change to the given directory
SavedCurrentDirectory = PushCurrentDirectory(DirName);
// Process all the files in this directory
DirDB = ScanDirectory(DirName);
AssertOptionalDir(DirDB); if (fCleanRestart && DirDB != NULL && !strcmp(DirDB->Name, RestartDir)) { fCleanRestart = FALSE; fClean = fRestartClean; fCleanLibs = fRestartCleanLibs; }
if (!DirDB || !(DirDB->DirFlags & (DIRDB_DIRS | DIRDB_SOURCES))) { PopCurrentDirectory(SavedCurrentDirectory); return; }
if (fShowTree && !(DirDB->DirFlags & DIRDB_SHOWN)) { AddShowDir(DirDB); }
if (DirDB->DirFlags & DIRDB_SOURCES) { BOOL fSourcesRead = TRUE;
SetObjDir((DirDB->DirFlags & DIRDB_CHECKED_ALT_DIR) != 0);
//
// This directory contains a SOURCES file
//
if (fFirstScan) { AllocMem(sizeof(DIRSUP), &pds, MT_DIRSUP); memset(pds, 0, sizeof(*pds)); fSourcesRead = ReadSourcesFile(DirDB, pds, &DateTimeSources);
DirDB->pds = pds; } else { pds = DirDB->pds;
assert(pds);
DateTimeSources = pds->DateTimeSources;
//
// We need to rebuild the sources list because
// the previous scan was probably not complete.
//
if (pds) PostProcessSources(DirDB, pds);
}
assert(pds);
if (DEBUG_4) { BuildMsgRaw("ScanSourceDirectories(%s) SOURCES\n", DirName); }
ScanFlagsCurrent = 0; CountIncludeDirs = CountSystemIncludeDirs;
// Scan the include environments in the order that MAKEFILE.DEF
// processes them. This order is:
//
// 1) Sources variable INCLUDE
// 2) Cairo/Chicago directories
// 3) System includes
// 4) UMTYPE-derived includes
//
// The subtlety is that we must do this in the reverse order
// since each of the processing routines pushes search directories
// onto the HEAD of the include search list.
//
// Note: we come in here with the system includes already set.
// There's no way to stick the UMTYPE-derived ones ahead of the
// system includes
// 4) UMTYPE-derived includes
if (pds->TestType != NULL && !strcmp(pds->TestType, "os2")) { ScanGlobalIncludeDirectory(pszIncCrt); ScanGlobalIncludeDirectory(pszIncOs2); ScanFlagsCurrent |= SCANFLAGS_OS2; } else if (pds->TestType != NULL && !strcmp(pds->TestType, "posix")) { ScanGlobalIncludeDirectory(pszIncPosix); ScanFlagsCurrent |= SCANFLAGS_POSIX; } else { ScanGlobalIncludeDirectory(pszIncCrt); ScanFlagsCurrent |= SCANFLAGS_CRT; }
if (DirDB->DirFlags & DIRDB_CHICAGO_INCLUDES) { ScanGlobalIncludeDirectory(pszIncChicago); ScanFlagsCurrent |= SCANFLAGS_CHICAGO; }
// 1) Sources variable INCLUDE
if (pds->LocalIncludePath) {
ScanIncludeEnv(pds->LocalIncludePath); }
DirsPresent = FALSE;
} else if (DirDB->DirFlags & DIRDB_DIRS) { //
// This directory contains a DIRS or MYDIRS file
//
DirsPresent = ReadDirsFile(DirDB);
if (DEBUG_4) { BuildMsgRaw("ScanSourceDirectories(%s) DIRS\n", DirName); } }
if (!fQuicky || (fQuickZero && fFirstScan)) { if (!RecurseLevel) { BuildError( "Examining %s directory%s for %s.%s\n", DirDB->Name, DirsPresent? " tree" : "", fLinkOnly? "targets to link" : "files to compile", fFirstScan ? "" : " (2nd Pass)" ); } ClearLine(); BuildMsgRaw(" %s ", DirDB->Name); fLineCleared = FALSE; if (fDebug || !(BOOL) _isatty(_fileno(stderr))) { BuildMsgRaw(szNewLine); fLineCleared = TRUE; } }
if (!fLinkOnly) {
if (DirDB->DirFlags & DIRDB_SOURCESREAD) { //
// Determine what files need to be compiled
//
ProcessSourceDependencies(DirDB, pds, DateTimeSources); } else if (fFirstScan && DirsPresent && (DirDB->DirFlags & DIRDB_MAKEFIL0)) { DirDB->DirFlags |= ((fSemiQuicky && (!fQuickZero || !fFirstScan)) ? DIRDB_COMPILENEEDED : DIRDB_PASS0NEEDED); } else if (DirsPresent && (DirDB->DirFlags & DIRDB_MAKEFIL1)) { DirDB->DirFlags |= DIRDB_COMPILENEEDED; }
if (fFirstScan && (DirDB->DirFlags & DIRDB_PASS0NEEDED)) { if (CountPassZeroDirs >= MAX_BUILD_DIRECTORIES) { BuildError( "%s: Ignoring PassZero Directory table overflow, %u " "entries allowed\n", DirDB->Name, MAX_BUILD_DIRECTORIES); } else { //
// This directory needs to be compiled in pass zero. Add it
// to the list.
//
PassZeroDirs[CountPassZeroDirs++] = DirDB; }
if (fQuicky && !fQuickZero) { if (!(fSemiQuicky && (DirDB->DirFlags & DIRDB_COMPILENEEDED))) { // For -Z with compile needed anyway, CompileSourceDirectories do it.
CompilePassZeroDirectories(); } CountPassZeroDirs = 0; } else { if (fFirstScan) { fPassZero = TRUE; // Limits scanning during pass zero.
}
if (DirDB->CountOfPassZeroFiles) { if (fLineCleared) { BuildMsgRaw(" %s ", DirDB->Name); } BuildMsgRaw( "- %d Pass Zero files (%s lines)\n", DirDB->CountOfPassZeroFiles, FormatNumber(DirDB->PassZeroLines)); } } }
if ((DirDB->DirFlags & DIRDB_COMPILENEEDED) && (!fFirstScan || !fPassZero)) {
if (CountCompileDirs >= MAX_BUILD_DIRECTORIES) { BuildError( "%s: Ignoring Compile Directory table overflow, %u " "entries allowed\n", DirDB->Name, MAX_BUILD_DIRECTORIES); } else { //
// This directory needs to be compiled. Add it to the list.
//
CompileDirs[CountCompileDirs++] = DirDB; }
if (fQuicky && (!fQuickZero || !fFirstScan)) { CompileSourceDirectories(); CountCompileDirs = 0; } else if (DirDB->CountOfFilesToCompile) { if (fLineCleared) { BuildMsgRaw(" %s ", DirDB->Name); } BuildMsgRaw( "- %d source files (%s lines)\n", DirDB->CountOfFilesToCompile, FormatNumber(DirDB->SourceLinesToCompile)); } } }
if (DirsPresent && (DirDB->DirFlags & DIRDB_MAKEFILE)) { DirDB->DirFlags |= DIRDB_LINKNEEDED | DIRDB_FORCELINK; } else if (DirDB->DirFlags & DIRDB_TARGETFILES) { DirDB->DirFlags |= DIRDB_LINKNEEDED | DIRDB_FORCELINK; }
if ((DirDB->DirFlags & DIRDB_LINKNEEDED) && (!fQuicky || fSemiQuicky)) { if (CountLinkDirs >= MAX_BUILD_DIRECTORIES) { BuildError( "%s: Ignoring Link Directory table overflow, %u entries allowed\n", DirDB->Name, MAX_BUILD_DIRECTORIES); } else { LinkDirs[CountLinkDirs++] = DirDB; } } if ((DirDB->DirFlags & DIRDB_SOURCESREAD) && !fFirstScan) { FreeDirSupData(pds); // free data that are no longer needed
FreeMem(&pds, MT_DIRSUP); DirDB->pds = NULL; }
//
// Recurse into subdirectories
//
if (DirsPresent) { for (i = 1; i <= DirDB->CountSubDirs; i++) { FILEREC *FileDB, **FileDBNext;
FileDBNext = &DirDB->Files; while (FileDB = *FileDBNext) { if (FileDB->SubDirIndex == (USHORT) i) { GetCurrentDirectory(DB_MAX_PATH_LENGTH, path); strcat(path, "\\"); strcat(path, FileDB->Name); DirDB->RecurseLevel = (USHORT) ++RecurseLevel; ScanSourceDirectories(path); RecurseLevel--; break; } FileDBNext = &FileDB->Next; } } }
if (((fQuickZero && fFirstScan) || (!fQuicky)) && !RecurseLevel) { ClearLine(); }
PopCurrentDirectory(SavedCurrentDirectory); }
//+---------------------------------------------------------------------------
//
// Function: CompilePassZeroDirectories
//
// Synopsis: Spawns the compiler on the directories in the PassZeroDirs
// array.
//
// Arguments: (none)
//
//----------------------------------------------------------------------------
VOID CompilePassZeroDirectories( VOID ) { PDIRREC DirDB; LPSTR SavedCurrentDirectory; UINT i; PCHAR s;
StartElapsedTime(); for (i = 0; i < CountPassZeroDirs; i++) {
DirDB = PassZeroDirs[ i ]; AssertDir(DirDB);
if (fQuicky && !fSemiQuicky) s = "Compiling and linking"; else s = "Building generated files in";
BuildMsg("%s %s\n", s, DirDB->Name); LogMsg("%s %s%s\n", s, DirDB->Name, szAsterisks);
if ((fQuickZero && fFirstScan) || !fQuicky) { SavedCurrentDirectory = PushCurrentDirectory( DirDB->Name ); }
if (DirDB->DirFlags & DIRDB_DIRS) { if (DirDB->DirFlags & DIRDB_MAKEFIL0) { strcpy( MakeParametersTail, " -f makefil0." ); strcat( MakeParametersTail, " NOLINK=1" ); if (fClean) { strcat( MakeParametersTail, " clean" ); }
if (fQuery) { BuildErrorRaw("'%s %s'\n", MakeProgram, MakeParameters); } else { if (DEBUG_1) { BuildMsg( "Executing: %s %s\n", MakeProgram, MakeParameters); }
CurrentCompileDirDB = NULL; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram(MakeProgram, MakeParameters, MakeTargets, TRUE); } } } else { strcpy(MakeParametersTail, " NTTEST="); if (DirDB->KernelTest) { strcat(MakeParametersTail, DirDB->KernelTest); }
strcat(MakeParametersTail, " UMTEST="); if (DirDB->UserTests) { strcat(MakeParametersTail, DirDB->UserTests); }
if (DirDB->DirFlags & DIRDB_CHECKED_ALT_DIR) { strcat(MakeParametersTail, szCheckedAltDir); } if (fQuicky && !fSemiQuicky) { if (DirDB->DirFlags & DIRDB_DLLTARGET) { strcat(MakeParametersTail, " MAKEDLL=1"); } ProcessLinkTargets(DirDB, NULL); } else { strcat( MakeParametersTail, " NOLINK=1 PASS0ONLY=1"); }
if (fQuery) { BuildErrorRaw( "'%s %s%s'\n", MakeProgram, MakeParameters, MakeTargets); } else { if ((DirDB->DirFlags & DIRDB_SYNCHRONIZE_DRAIN) && (fParallel)) { //
// Wait for all threads to complete before
// trying to compile this directory.
//
WaitForParallelThreads(); } if (DEBUG_1) { BuildMsg("Executing: %s %s%s\n", MakeProgram, MakeParameters, MakeTargets); } CurrentCompileDirDB = DirDB; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram( MakeProgram, MakeParameters, MakeTargets, (DirDB->DirFlags & DIRDB_SYNCHRONIZE_BLOCK) != 0); } } PrintElapsedTime(); if ((fQuickZero && fFirstScan) || !fQuicky) { PopCurrentDirectory(SavedCurrentDirectory); }
DirDB->DirFlags &= ~DIRDB_PASS0NEEDED; DirDB->CountOfPassZeroFiles = 0; DirDB->PassZeroLines = 0; } }
//+---------------------------------------------------------------------------
//
// Function: CompileSourceDirectories
//
// Synopsis: Spawns the compiler on the directories in the CompileDirs
// array.
//
// Arguments: (none)
//
//----------------------------------------------------------------------------
VOID CompileSourceDirectories( VOID ) { PDIRREC DirDB; LPSTR SavedCurrentDirectory; UINT i,j; PCHAR s; char path[DB_MAX_PATH_LENGTH];
StartElapsedTime(); for (i = 0; i < CountCompileDirs; i++) {
DirDB = CompileDirs[ i ];
AssertDir(DirDB);
if (fQuicky && !fSemiQuicky) { s = "Compiling and linking"; } else { s = "Compiling"; } BuildMsg("%s %s directory\n", s, DirDB->Name); LogMsg("%s %s directory%s\n", s, DirDB->Name, szAsterisks);
if (!fQuicky || (fQuickZero && (!fFirstScan || !RecurseLevel))) { SavedCurrentDirectory = PushCurrentDirectory( DirDB->Name ); if (fQuickZero && !RecurseLevel && fFirstScan) { GenerateObjectsDotMac(DirDB, DirDB->pds, DirDB->pds->DateTimeSources); } }
if (DirDB->DirFlags & DIRDB_DIRS) { if ((DirDB->DirFlags & DIRDB_SYNCHRONIZE_DRAIN) && (fParallel)) { //
// Wait for all threads to complete before
// trying to compile this directory.
//
WaitForParallelThreads(); } if (fSemiQuicky && (DirDB->DirFlags & DIRDB_MAKEFIL0)) { strcpy( MakeParametersTail, " -f makefil0." ); strcat( MakeParametersTail, " NOLINK=1" ); if (fClean) { strcat( MakeParametersTail, " clean" ); }
if (fQuery) { BuildErrorRaw("'%s %s'\n", MakeProgram, MakeParameters); } else { if (DEBUG_1) { BuildMsg( "Executing: %s %s\n", MakeProgram, MakeParameters); }
CurrentCompileDirDB = NULL; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram(MakeProgram, MakeParameters, MakeTargets, TRUE); } }
if (DirDB->DirFlags & DIRDB_MAKEFIL1) { strcpy( MakeParametersTail, " -f makefil1." ); strcat( MakeParametersTail, " NOLINK=1 NOPASS0=1" ); if (fClean) { strcat( MakeParametersTail, " clean" ); }
if (fQuery) { BuildErrorRaw("'%s %s'\n", MakeProgram, MakeParameters); } else { if (DEBUG_1) { BuildMsg( "Executing: %s %s\n", MakeProgram, MakeParameters); }
CurrentCompileDirDB = NULL; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram(MakeProgram, MakeParameters, MakeTargets, TRUE); } } } else { strcpy(MakeParametersTail, " NTTEST="); if (DirDB->KernelTest) { strcat(MakeParametersTail, DirDB->KernelTest); }
strcat(MakeParametersTail, " UMTEST="); if (DirDB->UserTests) { strcat(MakeParametersTail, DirDB->UserTests); }
if (fQuicky && DirDB->PchObj) { for (j = 0; j < CountTargetMachines; j++) { FormatLinkTarget( path, TargetMachines[j]->ObjectDirectory, DirDB->TargetPath, DirDB->PchObj, "");
if (ProbeFile( NULL, path ) != -1) { //
// the pch.obj file is present so we therefore
// must do this incremental build without pch
//
strcat( MakeParametersTail, " NTNOPCH=yes" ); break; } } }
if (DirDB->DirFlags & DIRDB_CHECKED_ALT_DIR) { strcat(MakeParametersTail, szCheckedAltDir); } if (fQuicky && !fSemiQuicky) { if (DirDB->DirFlags & DIRDB_DLLTARGET) { strcat(MakeParametersTail, " MAKEDLL=1"); } ProcessLinkTargets(DirDB, NULL); } else if (fQuicky && fSemiQuicky) { strcat(MakeParametersTail, " NOLINK=1"); } else { strcat(MakeParametersTail, " NOLINK=1 NOPASS0=1"); }
if (fQuery) { BuildErrorRaw( "'%s %s%s'\n", MakeProgram, MakeParameters, MakeTargets); } else { if ((DirDB->DirFlags & DIRDB_SYNCHRONIZE_DRAIN) && (fParallel)) { //
// Wait for all threads to complete before
// trying to compile this directory.
//
WaitForParallelThreads(); } if (DEBUG_1) { BuildMsg("Executing: %s %s%s\n", MakeProgram, MakeParameters, MakeTargets); } CurrentCompileDirDB = DirDB; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram( MakeProgram, MakeParameters, MakeTargets, (DirDB->DirFlags & DIRDB_SYNCHRONIZE_BLOCK) != 0); } } PrintElapsedTime(); if (!fQuicky || (fQuickZero && (!fFirstScan || !RecurseLevel))) { PopCurrentDirectory(SavedCurrentDirectory); } } }
static CountLinkTargets;
//+---------------------------------------------------------------------------
//
// Function: LinkSourceDirectories
//
// Synopsis: Link the directories given in the LinkDirs array. This is
// done by passing LINKONLY=1 to nmake.
//
// Arguments: (none)
//
//----------------------------------------------------------------------------
VOID LinkSourceDirectories(VOID) { PDIRREC DirDB; LPSTR SavedCurrentDirectory; UINT i;
CountLinkTargets = 0; StartElapsedTime(); for (i = 0; i < CountLinkDirs; i++) { DirDB = LinkDirs[ i ]; AssertDir(DirDB); SavedCurrentDirectory = PushCurrentDirectory(DirDB->Name);
//
// Deletes link targets as necessary
//
ProcessLinkTargets(DirDB, SavedCurrentDirectory);
PopCurrentDirectory(SavedCurrentDirectory); }
if (fPause && !fMTScriptSync) { BuildMsg("Press enter to continue with linking (or 'q' to quit)..."); if (getchar() == 'q') { return; } }
for (i = 0; i < CountLinkDirs; i++) { DirDB = LinkDirs[i];
if (!fMTScriptSync && (DirDB->DirFlags & DIRDB_COMPILEERRORS) && (DirDB->DirFlags & DIRDB_FORCELINK) == 0) {
BuildMsg("Compile errors: not linking %s directory\n", DirDB->Name); LogMsg( "Compile errors: not linking %s directory%s\n", DirDB->Name, szAsterisks); continue; }
SavedCurrentDirectory = PushCurrentDirectory(DirDB->Name);
BuildMsg("Linking %s directory\n", DirDB->Name); LogMsg ("Linking %s directory%s\n", DirDB->Name, szAsterisks);
strcpy(MakeParametersTail, " LINKONLY=1 NOPASS0=1"); strcat(MakeParametersTail, " NTTEST="); if (DirDB->KernelTest) { strcat(MakeParametersTail, DirDB->KernelTest); }
strcat(MakeParametersTail, " UMTEST="); if (DirDB->UserTests) { strcat(MakeParametersTail, DirDB->UserTests); }
if (DirDB->DirFlags & DIRDB_CHECKED_ALT_DIR) { strcat(MakeParametersTail, szCheckedAltDir); }
if (DirDB->DirFlags & DIRDB_DLLTARGET) { strcat(MakeParametersTail, " MAKEDLL=1"); }
if ((DirDB->DirFlags & DIRDB_DIRS) && (DirDB->DirFlags & DIRDB_MAKEFILE) && fClean) { strcat(MakeParametersTail, " clean"); }
if (fQuery) { BuildErrorRaw( "'%s %s%s'\n", MakeProgram, MakeParameters, MakeTargets); } else { if ((DirDB->DirFlags & DIRDB_SYNCHRONIZE_DRAIN) && (fParallel) && (fSyncLink)) { //
// Wait for all threads to complete before
// trying to compile this directory.
//
WaitForParallelThreads(); } if (DEBUG_1) { BuildMsg("Executing: %s %s%s\n", MakeProgram, MakeParameters, MakeTargets); }
CurrentCompileDirDB = NULL; RecurseLevel = DirDB->RecurseLevel; ExecuteProgram(MakeProgram, MakeParameters, MakeTargets, (fSyncLink) && (DirDB->DirFlags & DIRDB_SYNCHRONIZE_BLOCK) != 0); } PopCurrentDirectory(SavedCurrentDirectory); PrintElapsedTime(); } }
//+---------------------------------------------------------------------------
//
// Function: GetTargetData
//
// Synopsis: Searches aTargetInfo for an entry corresponding to the given
// extension.
//
// Arguments: [ext] -- Extension to look up (including '.').
// [iPass] -- 0 for pass zero; 1 for pass 1
// [index] -- Index used to differentiate multiple targets
// [usMidlFlag] -- Indicates which set of MIDL targets should
// be used for MIDL source files.
//
// Returns: A TARGETDATA for the given extension and index. NULL if
// Index is invalid.
//
// History: 29-Jul-94 LyleC Created
//
// Notes: If ext is not found in the aTargetInfo array, then a default
// TARGETINFO is used which maps the extension to obj\*\.obj.
//
//----------------------------------------------------------------------------
LPTARGETDATA GetTargetData(LPSTR ext, LONG iPass, USHORT index, ULONG usMidlIndex) { int i; OBJECTTARGETINFO **aTargetInfo; int cTargetInfo;
if (!ext || (ext[0] == '\0') || (ext[1] == '\0')) return &DefaultData;
if ((ext[1] == aMidlTargetInfo0[usMidlIndex]->pszSourceExt[1]) && (strcmp(ext, aMidlTargetInfo0[usMidlIndex]->pszSourceExt) == 0)) { if (index >= aMidlTargetInfo0[usMidlIndex]->NumData) return NULL;
return &(aMidlTargetInfo0[usMidlIndex]->Data[index]); }
assert(iPass == 0 || iPass == 1); cTargetInfo = aTargetArray[iPass].cTargetInfo; aTargetInfo = aTargetArray[iPass].aTargetInfo;
for (i = 0; i < cTargetInfo; i++) { if ((ext[1] == aTargetInfo[i]->pszSourceExt[1]) && (strcmp(ext, aTargetInfo[i]->pszSourceExt) == 0)) { if (index >= aTargetInfo[i]->NumData) return NULL;
return(&aTargetInfo[i]->Data[index]); } }
if (index) return NULL;
return &DefaultData; }
//+---------------------------------------------------------------------------
//
// Function: BuildCompileTarget
//
// Synopsis: Fills a TARGET struct with data about the target of a given
// source file.
//
// Arguments: [pfr] -- FileRec of source file
// [pszfile] -- Path of source file (compiland)
// [TargetIndex] -- Which target for a source file
// with multiple targets.
// [pszConditionalIncludes] -- List of conditional includes
// [pdrBuild] -- Build directory (with source file)
// [iPass] -- 0 for pass zero; 1 for pass 1
// [ppszObjectDir] -- Names of target object directories
// [pszSourceDir] -- Name of machine specific source dir
//
// Returns: A filled TARGET struct. NULL if TargetIndex is an invalid
// value for the given file type.
//
// Notes: If [pfr] is NULL, then [pszfile] is not modified and is
// used as the full pathname of the target file.
// [pszObjectDir] is ignored in this case. if [pfr] is not
// NULL, the filename component of [pszfile] is taken, its
// extension is modified, and it is appended to [pszObjectDir]
// to obtain the pathname of the target. The other data is
// used to fill in the rest of the TARGET struct in all cases.
//
// For source files with multiple targets, use the TargetIndex
// parameter to indicate which target you want the path of. For
// instance, .idl files have two targets, so a TargetIndex of 0
// will return the .h target and TargetIndex=1 will return the
// .c target. A TargetIndex of 2 or above in this case will
// return NULL. TargetIndex is ignored if [pfr] is NULL.
//
//----------------------------------------------------------------------------
TARGET * BuildCompileTarget( FILEREC *pfr, LPSTR pszfile, USHORT TargetIndex, LPSTR pszConditionalIncludes, DIRREC *pdrBuild, DIRSUP *pdsBuild, LONG iPass, LPSTR *ppszObjectDir, LPSTR pszSourceDir) { LPSTR p, p1; PTARGET Target; char path[DB_MAX_PATH_LENGTH]; LPTARGETDATA pData;
p = pszfile; if (pfr != NULL) { p1 = p; while (*p) { if (*p++ == '\\') { p1 = p; // point to last component of pathname
} }
sprintf(path, "%s", p1);
p = strrchr(path, '.');
pData = GetTargetData(p, iPass, TargetIndex, pdsBuild->IdlType);
if (!pData) { if (DEBUG_1) { BuildMsg( "BuildCompileTarget(\"%s\"[%u][%u], \"%s\") -> NULL\n", pszfile, iPass, TargetIndex, ppszObjectDir[iObjectDir]); } return NULL; }
assert(pdsBuild);
switch (pData->ObjectDirFlag) { case TD_OBJECTDIR: p = ppszObjectDir[iObjectDir]; break;
case TD_PASS0HDRDIR: p = pdsBuild->PassZeroHdrDir; break;
p = pdsBuild->PassZeroSrcDir1; break;
case TD_MCSOURCEDIR: case TD_PASS0DIR1: p = pdsBuild->PassZeroSrcDir1; break;
case TD_PASS0DIR2: p = pdsBuild->PassZeroSrcDir2; break;
default: assert(0 && "Invalid ObjectDirFlag"); break; }
if (!p) { // Make sure path ends in a period
sprintf(path, "%s.", p1); } else if (p[0] == '.' && p[1] == '\0') { strcpy(path, p1); } else { sprintf(path, "%s\\%s", p, p1); }
p = strrchr(path, '.');
if (p) { strcpy(p, pData->pszTargetExt); }
p = path; }
AllocMem(sizeof(TARGET) + strlen(p), &Target, MT_TARGET); strcpy(Target->Name, p); Target->pdrBuild = pdrBuild; Target->DateTime = (*pDateTimeFile)(NULL, p); Target->pfrCompiland = pfr; Target->pszSourceDirectory = pszSourceDir; Target->ConditionalIncludes = pszConditionalIncludes; Target->DirFlags = pdrBuild->DirFlags; if (DEBUG_1) { BuildMsg( "BuildCompileTarget(\"%s\"[%u][%u], \"%s\") -> \"%s\"\n", pszfile, iPass, TargetIndex, ppszObjectDir[iObjectDir], Target->Name); } if (Target->DateTime == 0) { if (fShowOutOfDateFiles) { BuildError("%s target is missing.\n", Target->Name); } } return(Target); }
//+---------------------------------------------------------------------------
//
// Function: FormatLinkTarget
//
// Synopsis: Builds a link target path name.
//
// Arguments: [path] -- Place to put constructed name
// [ObjectDirectory] -- e.g. "obj\i386"
// [TargetPath] -- Path (w/o platfrom spec. name) for target
// [TargetName] -- Base name of target
// [TargetExt] -- Extension of target
//
// Notes: Sample input: (path, "obj\i386", "..\obj", "foobar", ".dll")
//
// output: path = "..\obj\i386\foobar.dll"
//
//----------------------------------------------------------------------------
VOID FormatLinkTarget( LPSTR path, LPSTR *ObjectDirectory, LPSTR TargetPath, LPSTR TargetName, LPSTR TargetExt) { LPSTR p, p1;
p = ObjectDirectory[iObjectDir]; assert(strncmp(pszObjDirSlash, p, strlen(pszObjDirSlash)) == 0); p1 = p + strlen(p); while (p1 > p) { if (*--p1 == '\\') { p1++; break; } } sprintf(path, "%s\\%s\\%s%s", TargetPath, p1, TargetName, TargetExt); }
//+---------------------------------------------------------------------------
//
// Function: ProcessLinkTargets
//
// Synopsis: Deletes link targets for the given directory (.lib & .dll)
//
// Arguments: [DirDB] -- Directory to process
// [CurrentDirectory] -- Current directory
//
//----------------------------------------------------------------------------
VOID ProcessLinkTargets(PDIRREC DirDB, LPSTR CurrentDirectory) { UINT i; char path[DB_MAX_PATH_LENGTH];
AssertDir(DirDB); for (i = 0; i < CountTargetMachines; i++) { //
// Delete 'special' link targets
//
if (DirDB->KernelTest) { FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, pszObjDir, DirDB->KernelTest, ".exe"); if (fClean && !fKeep && fFirstScan) { DeleteSingleFile(NULL, path, FALSE); } } else { UINT j;
for (j = 0; j < 2; j++) { LPSTR pNextName;
pNextName = j == 0? DirDB->UserAppls : DirDB->UserTests; if (pNextName != NULL) { char name[256];
while (SplitToken(name, '*', &pNextName)) { FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, pszObjDir, name, ".exe");
if (fClean && !fKeep && fFirstScan) { DeleteSingleFile(NULL, path, FALSE); } } } } }
if (DirDB->TargetPath != NULL && DirDB->TargetName != NULL && DirDB->TargetExt != NULL && strcmp(DirDB->TargetExt, ".lib")) {
FormatLinkTarget( path, TargetMachines[i]->ObjectDirectory, DirDB->TargetPath, DirDB->TargetName, DirDB->TargetExt);
if (fClean && !fKeep && fFirstScan) { DeleteSingleFile(NULL, path, FALSE); } } if (DirDB->DirFlags & DIRDB_DIRS) { if (fDebug && (DirDB->DirFlags & DIRDB_MAKEFILE)) { BuildError( "%s\\makefile. unexpected in directory with DIRS file\n", DirDB->Name); } if ((DirDB->DirFlags & DIRDB_SOURCES)) { BuildError( "%s\\sources. unexpected in directory with DIRS file\n", DirDB->Name); BuildError("Ignoring %s\\sources.\n", DirDB->Name); DirDB->DirFlags &= ~DIRDB_SOURCES; } } } }
//+---------------------------------------------------------------------------
//
// Function: IncludeError
//
// Synopsis: Print out the name of an include file and an error message
// to the screen.
//
// Arguments: [pt] -- Target of the file which includes the include
// file or [pfr].
// [pfr] -- File which includes the include file
// [pir] -- Include file at issue
// [pszError] -- Error string
//
// Notes: If [pt]->pfrCompiland and [pfr] are different, then the names
// of both are printed.
//
//----------------------------------------------------------------------------
VOID IncludeError(TARGET *pt, FILEREC *pfr, INCLUDEREC *pir, LPSTR pszError) { char c1, c2;
AssertFile(pfr); AssertInclude(pir); if (pir->IncFlags & INCLUDEDB_LOCAL) { c1 = c2 = '"'; } else { c1 = '<'; c2 = '>'; } BuildError("%s\\%s: ", pt->pfrCompiland->Dir->Name, pt->pfrCompiland->Name); if (pt->pfrCompiland != pfr) { if (pt->pfrCompiland->Dir != pfr->Dir) { BuildErrorRaw("%s\\", pfr->Dir->Name); } BuildErrorRaw("%s: ", pfr->Name); } BuildErrorRaw("%s %c%s%c\n", pszError, c1, pir->Name, c2); }
//+---------------------------------------------------------------------------
//
// Function: IsConditionalInc
//
// Synopsis: Returns TRUE if the given filename is a conditional include
// for this directory. (As given by the CONDITIONAL_INCLUDES
// macro).
//
// Arguments: [pszFile] -- Name of file to check
// [pt] -- Target struct giving list of conditional includes
//
// Returns: TRUE if it's a conditional include
//
//----------------------------------------------------------------------------
BOOL IsConditionalInc(LPSTR pszFile, TARGET *pt) { AssertPathString(pszFile);
if (pt->ConditionalIncludes != NULL) { LPSTR p; char name[DB_MAX_PATH_LENGTH];
p = pt->ConditionalIncludes; while (SplitToken(name, ' ', &p)) { if (strcmp(name, pszFile) == 0) { return(TRUE); } } } return(FALSE); }
//+---------------------------------------------------------------------------
//
// Function: IsExcludedInc
//
// Synopsis: Returns TRUE if the given file is listed in the ExcludeIncs
// array.
//
// Arguments: [pszFile] -- File to check
//
//----------------------------------------------------------------------------
BOOL IsExcludedInc(LPSTR pszFile) { ULONG i;
AssertPathString(pszFile); for (i = 0; i < CountExcludeIncs; i++) { if (!strcmp(pszFile, ExcludeIncs[i])) { return(TRUE); } } return(FALSE); }
//+---------------------------------------------------------------------------
//
// Function: CheckDependencies
//
// Synopsis: Process dependencies to see if a target is out of date
//
// Arguments: [Target] -- Target to check date on
// [FileDB] -- File which makes [Target]
// [CheckDate] -- If FALSE, then the date check is bypassed.
// [ppfrRoot] -- Returns a cycle root if a cycle is encountered.
// Used only during recursion.
//
// Returns: TRUE if [Target] is out of date w/r/t [FileDB]
//
//----------------------------------------------------------------------------
BOOL CheckDependencies( PTARGET Target, FILEREC *FileDB, BOOL CheckDate, FILEREC **ppfrRoot) { BOOL fOutOfDate; BOOL CheckVersion; static ULONG ChkRecursLevel = 0;
*ppfrRoot = NULL; ChkRecursLevel++;
assert(FileDB != NULL); // NULL FileDB should never happen.
AssertFile(FileDB);
if (FileDB->fDependActive) {
// We have detected a loop in the graph of include files.
// Just return, to terminate the recursion.
if (DEBUG_1) { BuildMsgRaw( "ChkDepend(%s, %s, %u) %s\n", Target->Name, FileDB->Name, CheckDate, "Target Match, *** ASSUME UP TO DATE ***"); } if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x %s\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTime, "Target Match"); } *ppfrRoot = FileDB; ChkRecursLevel--; return(FALSE); } if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "++", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTime); }
// We've decided to process this file:
FileDB->fDependActive = TRUE; CheckVersion = fEnableVersionCheck; fOutOfDate = FALSE;
if (FileDB->GlobalSequence != GlobalSequence || FileDB->LocalSequence != LocalSequence) { if (FileDB->GlobalSequence != 0 || FileDB->LocalSequence != 0) { if (DEBUG_1) { BuildError( "Include Sequence %hu/%hu -> %hu/%hu\n", FileDB->LocalSequence, FileDB->GlobalSequence, LocalSequence, GlobalSequence); } if (fDebug & 16) { PrintFileDB(stderr, FileDB, 0); } UnsnapIncludeFiles( FileDB, (FileDB->Dir->DirFlags & DIRDB_GLOBAL_INCLUDES) == 0 || FileDB->GlobalSequence != GlobalSequence); } FileDB->GlobalSequence = GlobalSequence; FileDB->LocalSequence = LocalSequence; FileDB->DateTimeTree = 0; }
if (DEBUG_1) { BuildMsgRaw( "ChkDepend(%s, %s, %u)\n", Target->Name, FileDB->Name, CheckDate); }
if (CheckDate && (FileDB->FileFlags & FILEDB_HEADER) && FileDB->DateTimeTree == 0 && IsExcludedInc(FileDB->Name)) {
if (DEBUG_1) { BuildMsg("Skipping date check for %s\n", FileDB->Name); } CheckVersion = FALSE; FileDB->DateTimeTree = 1; // never out of date
}
if (FileDB->IncludeFiles == NULL && FileDB->DateTimeTree == 0) { FileDB->DateTimeTree = FileDB->DateTime; if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "t<-f", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTime); } } if (CheckDate && (Target->DateTime < FileDB->DateTime || Target->DateTime < FileDB->DateTimeTree)) { if (Target->DateTime != 0) { if (DEBUG_1 || fShowOutOfDateFiles) { BuildMsg("%s is out of date with respect to %s\\%s.\n", Target->Name, FileDB->NewestDependency->Dir->Name, FileDB->NewestDependency->Name); } } fOutOfDate = TRUE; }
//
// If FileDB->DateTimeTree is non-zero, then the field is equal to the
// newest DateTime of this file or any of its dependants, so we don't
// need to go through the dependency tree again.
//
if (FileDB->DateTimeTree == 0) { INCLUDEREC *IncludeDB, **IncludeDBNext, **ppirTree;
//
// Find the file records for all include files so that after cycles are
// collapsed, we won't attempt to lookup an include file relative to
// the wrong directory.
//
ppirTree = &FileDB->IncludeFilesTree; for (IncludeDBNext = &FileDB->IncludeFiles; (IncludeDB = *IncludeDBNext) != NULL; IncludeDBNext = &IncludeDB->Next) {
AssertInclude(IncludeDB); AssertCleanTree(IncludeDB, FileDB); IncludeDB->IncFlags |= INCLUDEDB_SNAPPED; if (IncludeDB->pfrInclude == NULL) { IncludeDB->pfrInclude = FindIncludeFileDB( FileDB, Target->pfrCompiland, Target->pdrBuild, Target->pszSourceDirectory, IncludeDB); AssertOptionalFile(IncludeDB->pfrInclude); if (IncludeDB->pfrInclude != NULL && (IncludeDB->pfrInclude->Dir->DirFlags & DIRDB_GLOBAL_INCLUDES)) { IncludeDB->IncFlags |= INCLUDEDB_GLOBAL; }
} if (IncludeDB->pfrInclude == NULL) { if (!IsConditionalInc(IncludeDB->Name, Target)) { if (DEBUG_1 || !(IncludeDB->IncFlags & INCLUDEDB_MISSING)) { IncludeError( Target, FileDB, IncludeDB, "cannot find include file"); IncludeDB->IncFlags |= INCLUDEDB_MISSING; } } else if (DEBUG_1) { IncludeError( Target, FileDB, IncludeDB, "Skipping missing conditional include file"); } continue; } *ppirTree = IncludeDB; ppirTree = &IncludeDB->NextTree; } *ppirTree = NULL; // truncate any links from previous sequence
FileDB->DateTimeTree = FileDB->DateTime;
//
// Walk through the dynamic list.
//
rescan: for (IncludeDBNext = &FileDB->IncludeFilesTree; (IncludeDB = *IncludeDBNext) != NULL; IncludeDBNext = &IncludeDB->NextTree) {
AssertInclude(IncludeDB); if (DEBUG_2) { BuildMsgRaw( "%lu-%hu/%hu %s %*s%-10s %*s%s\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->pfrCompiland->Name, (ChkRecursLevel - 1) * 2, "", IncludeDB->Name, max(0, 12 - ((int)ChkRecursLevel - 1) * 2), "", IncludeDB->pfrInclude != NULL? IncludeDB->pfrInclude->Dir->Name : "not found"); }
//
// tommcg 5/21/98
//
// If included file is not in "sanctioned" path, warn about it.
// Sanctioned paths are set in an environment variable named
// BUILD_ACCEPTABLE_INCLUDES which can contain wildcards and look
// something like this:
//
// *\nt\public\*;*\nt\private\inc\*;*\..\inc\*;*\..\include\*
//
if (( fCheckIncludePaths ) && ( IncludeDB->pfrInclude != NULL )) {
CheckIncludeForWarning( Target->pfrCompiland->Dir->Name, Target->pfrCompiland->Name, FileDB->Dir->Name, FileDB->Name, IncludeDB->pfrInclude->Dir->Name, IncludeDB->pfrInclude->Name ); }
assert(IncludeDB->IncFlags & INCLUDEDB_SNAPPED); if (IncludeDB->pfrInclude != NULL) { if (fEnableVersionCheck) { CheckDate = (IncludeDB->pfrInclude->Version == 0); }
if (IncludeDB->Version != IncludeDB->pfrInclude->Version) { if (CheckVersion) { if (DEBUG_1 || fShowOutOfDateFiles) { BuildError( "%s (v%d) is out of date with " "respect to %s\\%s (v%d).\n", FileDB->Name, IncludeDB->Version, IncludeDB->pfrInclude->Dir->Name, IncludeDB->pfrInclude->Name, IncludeDB->pfrInclude->Version); } FileDB->DateTimeTree = ULONG_MAX; // always out of date
fOutOfDate = TRUE; } else if (!fClean && fEnableVersionCheck && !fSilent) { BuildError( "%s - #include %s (v%d updated to v%d)\n", FileDB->Name, IncludeDB->pfrInclude->Name, IncludeDB->Version, IncludeDB->pfrInclude->Version); } IncludeDB->Version = IncludeDB->pfrInclude->Version; AllDirsModified = TRUE; } if (CheckDependencies(Target, IncludeDB->pfrInclude, CheckDate, ppfrRoot)) { fOutOfDate = TRUE;
// No cycle possible if recursive call returned TRUE.
assert(*ppfrRoot == NULL); }
// if the include file is involved in a cycle, unwind the
// recursion up to the root of the cycle while collpasing
// the cycle, then process the tree again from cycle root.
else if (*ppfrRoot != NULL) {
AssertFile(*ppfrRoot);
// Don't say the file is out of date, yet.
fOutOfDate = FALSE;
// Remove the current include file record from the list,
// because it participates in the cycle.
*IncludeDBNext = IncludeDB->NextTree; if (IncludeDB->IncFlags & INCLUDEDB_CYCLEROOT) { RemoveFromCycleRoot(IncludeDB, FileDB); } IncludeDB->NextTree = NULL; IncludeDB->IncFlags |= INCLUDEDB_CYCLEORPHAN;
// If the included file is not the cycle root, add the
// cycle root to the included file's include file list.
if (*ppfrRoot != IncludeDB->pfrInclude) { LinkToCycleRoot(IncludeDB, *ppfrRoot); }
if (*ppfrRoot == FileDB) {
// We're at the cycle root; clear the root pointer.
// Then go rescan the list.
*ppfrRoot = NULL; if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x %s\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "^^", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTime, "ReScan"); BuildMsgRaw("^^\n"); } goto rescan; }
// Merge the list for the file involved in the
// cycle into the root file's include list.
MergeIncludeFiles( *ppfrRoot, FileDB->IncludeFilesTree, FileDB); FileDB->IncludeFilesTree = NULL;
// Return immediately and reprocess the flattened
// tree, which now excludes the include files
// directly involved in the cycle. First, make
// sure the files removed from the cycle have their file
// (not tree) time stamps reflected in the cycle root.
if ((*ppfrRoot)->DateTimeTree < FileDB->DateTime) { (*ppfrRoot)->DateTimeTree = FileDB->DateTime; (*ppfrRoot)->NewestDependency = FileDB;
if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "t<-c", ChkRecursLevel, szRecurse, (*ppfrRoot)->Name, CheckDate, (*ppfrRoot)->DateTimeTree); } } break; }
//
// Propagate newest time up through the dependency tree.
// This way, each parent will have the date of its newest
// dependent, so we don't have to check through the whole
// dependency tree for each file more than once.
//
// Note that similar behavior has not been enabled for
// version checking.
//
if (FileDB->DateTimeTree < IncludeDB->pfrInclude->DateTimeTree) { FileDB->DateTimeTree = IncludeDB->pfrInclude->DateTimeTree; FileDB->NewestDependency = IncludeDB->pfrInclude->NewestDependency;
if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "t<-s", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTimeTree); } } } else { //
// Couldn't find the FILEDB for the include file, but this
// could be because the file is 'rcinclude'd, or 'importlib'd
// and isn't considered a source file. In this case, just get
// the timestamp on the file if possible.
//
// Time will be zero if the file is not found.
//
ULONG Time = (*pDateTimeFile)(NULL, IncludeDB->Name); if (FileDB->DateTimeTree < Time) { FileDB->DateTimeTree = Time; //
// Since we don't have a FILEDB for this dependency, just
// set the pointer to itself and print a message.
//
FileDB->NewestDependency = FileDB;
if (DEBUG_1 || fShowOutOfDateFiles) { BuildError( "%s (v%d) is out of date with respect to %s.\n", FileDB->Name, IncludeDB->Version, IncludeDB->Name); }
if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "t<-s", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTimeTree); }
} } } } if (DEBUG_4) { BuildMsgRaw( "%lu-%hu/%hu: ChkDepend(%s %x, %4s%.*s%s, %u) %x %s\n", ChkRecursLevel, LocalSequence, GlobalSequence, Target->Name, Target->DateTime, "--", ChkRecursLevel, szRecurse, FileDB->Name, CheckDate, FileDB->DateTimeTree, *ppfrRoot != NULL? "Collapse Cycle" : fOutOfDate? "OUT OF DATE" : "up-to-date"); } assert(FileDB->fDependActive); FileDB->fDependActive = FALSE; ChkRecursLevel--; return(fOutOfDate); }
//+---------------------------------------------------------------------------
//
// Function: PickFirst
//
// Synopsis: When called iteratively, the set of returned values is
// effectively a merge sort of the two source lists.
//
// Effects: The pointers given in [ppsr1] and [ppsr2] are modified to point
// to the next appropriate item in the list.
//
// Arguments: [ppsr1] -- First SOURCEREC list
// [ppsr2] -- Second SOURCEREC list
//
// Returns: The appropriate next item from either [ppsr1] or [ppsr2]
//
// Notes: [ppsr1] and [ppsr2] should each be appropriately sorted.
//
// InsertSourceDB maintains a sort order for PickFirst() based first on the
// filename extension, then on the subdirectory mask. Two exceptions to the
// alphabetic sort are:
// - No extension sorts last.
// - .rc extension sorts first.
//
//----------------------------------------------------------------------------
#define PF_FIRST -1
#define PF_SECOND 1
SOURCEREC * PickFirst(SOURCEREC **ppsr1, SOURCEREC **ppsr2) { SOURCEREC **ppsr; SOURCEREC *psr; int r = 0;
AssertOptionalSource(*ppsr1); AssertOptionalSource(*ppsr2); if (*ppsr1 == NULL) { if (*ppsr2 == NULL) { return(NULL); // both lists NULL -- no more
} r = PF_SECOND; // 1st is NULL -- return 2nd
} else if (*ppsr2 == NULL) { r = PF_FIRST; // 2nd is NULL -- return 1st
} else { LPSTR pszext1, pszext2;
pszext1 = strrchr((*ppsr1)->pfrSource->Name, '.'); pszext2 = strrchr((*ppsr2)->pfrSource->Name, '.'); if (pszext1 == NULL) { r = PF_SECOND; // 1st has no extension -- return 2nd
} else if (pszext2 == NULL) { r = PF_FIRST; // 2nd has no extension -- return 1st
} else if (strcmp(pszext1, ".rc") == 0) { r = PF_FIRST; // 1st is .rc -- return 1st
} else if (strcmp(pszext2, ".rc") == 0) { r = PF_SECOND; // 2nd is .rc -- return 2nd
} else { r = strcmp(pszext1, pszext2); if (r == 0 && (*ppsr1)->SourceSubDirMask != (*ppsr2)->SourceSubDirMask) { if ((*ppsr1)->SourceSubDirMask > (*ppsr2)->SourceSubDirMask) { r = PF_FIRST; // 2nd subdir after 1st -- return 1st
} else { r = PF_SECOND; // 1st subdir after 2nd -- return 2nd
} } } } if (r <= 0) { ppsr = ppsr1; } else { ppsr = ppsr2; } psr = *ppsr; *ppsr = psr->psrNext; return(psr); }
//+---------------------------------------------------------------------------
//
// Function: WriteObjectsDefinition
//
// Synopsis: Writes out a single platform-specific section of the
// _objects.mac file.
//
// Arguments: [OutFileHandle] -- File handle to write to
// [psrCommon] -- List of common source files
// [psrMachine] -- List of machine-specific source files
// [DirDB] -- directory record
// [ObjectVariable] -- e.g. 386_SOURCES
// [ObjectDirectory] -- name of machine obj dir (e.g. obj\i386)
//
// Returns:
//
// History: 26-Jul-94 LyleC Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID WriteObjectsDefinition( FILE *OutFileHandle, SOURCEREC *psrMachine, DIRSUP *pds, LPSTR ObjectVariable, LPSTR ObjectDirectory, LPSTR DirName ) { LPSTR pbuf; LPSTR pszextsrc; LPSTR pszextdir; LPTARGETDATA pData; SOURCEREC *psrComCopy; SOURCEREC *psrMachCopy; SOURCEREC *psrCommon = pds->psrSourcesList[0];
SOURCEREC *psr; USHORT i; LONG iPass;
//
// We loop twice - the first time writing out the non-pass-zero files
// to the ObjectVariable, the second time writing out pass zero
// files to the PASS0_ObjectVariable.
//
for (iPass = 1; iPass >= 0; iPass--) { pbuf = BigBuf;
pbuf[0] = '\0'; if (iPass == 0) { strcpy(pbuf, "PASS0_"); } strcat(pbuf, ObjectVariable); strcat(pbuf, "="); pbuf += strlen(pbuf);
psrComCopy = psrCommon; psrMachCopy = psrMachine;
while ((psr = PickFirst(&psrComCopy, &psrMachCopy)) != NULL) {
AssertSource(psr); if ((psr->SrcFlags & SOURCEDB_SOURCES_LIST) == 0) { continue; }
// if pass 0 macro and not a pass 0 file, skip it.
if (iPass == 0 && !(psr->pfrSource->FileFlags & FILEDB_PASS0)) continue;
// if pass 1 macro and not a pass 1 file, skip it.
if (iPass == 1 && (psr->pfrSource->FileFlags & FILEDB_PASS0) && !(psr->pfrSource->FileFlags & FILEDB_MULTIPLEPASS)) continue;
pszextsrc = strrchr(psr->pfrSource->Name, '.');
if (!pszextsrc) { BuildError("Bad sources extension: %s\n", psr->pfrSource->Name); continue; }
i = 0; while (pData = GetTargetData(pszextsrc, iPass, i, pds->IdlType)) { if (pData == &DefaultData) { //
// Check for implicitly 'known' extensions...
//
switch (pszextsrc[1]) { case 'f': // Fortran
case 'h': // Header file ?
case 'p': // Pascal
BuildError( "%s: Interesting sources extension: %s\n", DirName, psr->pfrSource->Name); // FALL THROUGH
case 'a': // Assembly file (.asm)
case 'c': // C file (.c or .cxx)
case 's': // Assembly file (.s)
break;
default: BuildError("Bad sources extension: %s\n", psr->pfrSource->Name); } }
switch (pData->ObjectDirFlag) { case TD_OBJECTDIR: pszextdir = ObjectDirectory; break;
case TD_PASS0HDRDIR: pszextdir = "$(PASS0_HEADERDIR)"; break;
case TD_MCSOURCEDIR: pszextdir = "$(MC_SOURCEDIR)"; break;
case TD_PASS0DIR1: pszextdir = pds->PassZeroSrcDir1; break;
case TD_PASS0DIR2: pszextdir = pds->PassZeroSrcDir2; break;
default: assert(0 && "Invalid ObjectDirFlag"); break; } assert(pszextdir); assert(pData->pszTargetExt);
sprintf( pbuf, " \\\r\n %s\\%.*s%s", pszextdir, pszextsrc - psr->pfrSource->Name, psr->pfrSource->Name, pData->pszTargetExt); pbuf += strlen(pbuf);
i++; } } strcpy(pbuf, "\r\n\r\n"); pbuf += 4;
fwrite(BigBuf, 1, (UINT) (pbuf - BigBuf), OutFileHandle); } }
DWORD CreateDirectoriesOnPath( LPTSTR pszPath, LPSECURITY_ATTRIBUTES psa) { DWORD dwErr = ERROR_SUCCESS;
if (pszPath && *pszPath) { LPTSTR pch = pszPath;
// If the path is a UNC path, we need to skip the \\server\share
// portion.
//
if ((TEXT('\\') == *pch) && (TEXT('\\') == *(pch+1))) { // pch now pointing at the server name. Skip to the backslash
// before the share name.
//
pch += 2; while (*pch && (TEXT('\\') != *pch)) { pch++; }
if (!*pch) { // Just the \\server was specified. This is bogus.
//
return ERROR_INVALID_PARAMETER; }
// pch now pointing at the backslash before the share name.
// Skip to the backslash that should come after the share name.
//
pch++; while (*pch && (TEXT('\\') != *pch)) { pch++; }
if (!*pch) { // Just the \\server\share was specified. No subdirectories
// to create.
//
return ERROR_SUCCESS; } }
// Loop through the path.
//
for (; *pch; pch++) { // Stop at each backslash and make sure the path
// is created to that point. Do this by changing the
// backslash to a null-terminator, calling CreateDirecotry,
// and changing it back.
//
if (TEXT('\\') == *pch) { BOOL fOk;
*pch = 0; fOk = CreateDirectory (pszPath, psa); *pch = TEXT('\\');
// Any errors other than path alredy exists and we should
// bail out. We also get access denied when trying to
// create a root drive (i.e. c:) so check for this too.
//
if (!fOk) { dwErr = GetLastError (); if (ERROR_ALREADY_EXISTS == dwErr) { dwErr = ERROR_SUCCESS; } else if ((ERROR_ACCESS_DENIED == dwErr) && (pch - 1 > pszPath) && (TEXT(':') == *(pch - 1))) { dwErr = ERROR_SUCCESS; } else { break; } } } }
if (ERROR_ALREADY_EXISTS == dwErr) { dwErr = ERROR_SUCCESS; }
if (ERROR_SUCCESS == dwErr) { // All dirs up to the last are created. Make the last one also.
if (CreateDirectory(pszPath, psa)) { dwErr = GetLastError (); if (ERROR_ALREADY_EXISTS == dwErr) { dwErr = ERROR_SUCCESS; } } } }
return dwErr; }
//+---------------------------------------------------------------------------
//
// Function: CreateBuildDirectory
//
// Synopsis: Creates a directory to hold generate object files. SET the
// FILE_ATTRIBUTE_ARCHIVE bit for the directory, since there is nothing
// to backup. We use SET since the default setting for a new directory
// is clear. Go figure. DOS was such a well planned product.
//
// Arguments: [Name] -- Directory to create
//
// Returns: TRUE if directory already exists or was created successfully.
// FALSE otherwise.
//----------------------------------------------------------------------------
BOOL CreateBuildDirectory(LPSTR Name) { DWORD Attributes;
Attributes = GetFileAttributes(Name); if (Attributes == -1) { CreateDirectoriesOnPath(Name, NULL); Attributes = GetFileAttributes(Name); }
if (Attributes != -1 && ((Attributes & FILE_ATTRIBUTE_ARCHIVE) == 0)) { SetFileAttributes(Name, Attributes | FILE_ATTRIBUTE_ARCHIVE); }
return((BOOL)(Attributes != -1)); }
//+---------------------------------------------------------------------------
//
// Function: CreatedBuildFile
//
// Synopsis: Called whenever BUILD creates a file. Clears the FILE_ATTRIBUTE_ARCHIVE
// bit for the file, since there is nothing to backup with a generated file.
//
// Arguments: [DirName] -- DIRDB for directory
// [FileName] -- file name path relative to DirName
//
//----------------------------------------------------------------------------
VOID CreatedBuildFile(LPSTR DirName, LPSTR FileName) { char Name[ DB_MAX_PATH_LENGTH * 2 + 1]; // ensure we have enough space for "DirName" + "\\" + "FileName"
DWORD Attributes;
strcpy(Name, DirName); if (Name[0] != '\0') { strcat(Name, "\\"); } strcat(Name, FileName);
Attributes = GetFileAttributes(Name); if (Attributes != -1 && (Attributes & FILE_ATTRIBUTE_ARCHIVE)) { SetFileAttributes(Name, Attributes & ~FILE_ATTRIBUTE_ARCHIVE); } return; }
//+---------------------------------------------------------------------------
//
// Function: GenerateObjectsDotMac
//
// Synopsis: Creates the _objects.mac file containing info for all platforms
//
// Arguments: [DirDB] -- Directory to create file for
// [pds] -- Supplementary information on [DirDB]
// [DateTimeSources] -- Timestamp of the SOURCES file
//
//----------------------------------------------------------------------------
VOID GenerateObjectsDotMac(DIRREC *DirDB, DIRSUP *pds, ULONG DateTimeSources) { FILE *OutFileHandle; UINT i; ULONG ObjectsDateTime;
CreateBuildDirectory("obj"); if (strcmp(pszObjDir, "obj") != 0) { if (ProbeFile(".", pszObjDir) == -1) { CreateDirectory(pszObjDir, NULL); } } for (i = 0; i < CountTargetMachines; i++) { assert(strncmp( pszObjDirSlash, TargetMachines[i]->ObjectDirectory[iObjectDir], strlen(pszObjDirSlash)) == 0); CreateBuildDirectory(TargetMachines[i]->ObjectDirectory[iObjectDir]); }
if (ObjectsDateTime = (*pDateTimeFile)(DirDB->Name, "obj\\_objects.mac")) {
if (DateTimeSources == 0) { BuildError("%s: no sources timestamp\n", DirDB->Name); }
if (ObjectsDateTime >= DateTimeSources) { if (!fForce) { return; } } } if (!MyOpenFile(DirDB->Name, "obj\\_objects.mac", "wb", &OutFileHandle, TRUE)) { return; }
if ((DirDB->DirFlags & DIRDB_SOURCES_SET) == 0) { BuildError("Missing SOURCES= definition in %s\n", DirDB->Name); } else { for (i = 0; i < MAX_TARGET_MACHINES; i++) { WriteObjectsDefinition( OutFileHandle, pds->psrSourcesList[i + 1], pds, PossibleTargetMachines[i]->ObjectVariable, PossibleTargetMachines[i]->ObjectMacro, DirDB->Name); } } fclose(OutFileHandle); CreatedBuildFile(DirDB->Name, "obj\\_objects.mac");
//
// If the _objects.mac file was generated during the first pass, then we
// want to regenerate it during the second scan because the first scan
// wasn't complete and _objects.mac may not be correct for non-pass-zero
// files. We do this by setting the timestamp back to the old time.
//
if (fFirstScan && fPassZero) { HANDLE hf; FILETIME ft;
hf = CreateFile("obj\\_objects.mac", GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, (HANDLE)NULL);
if (hf != INVALID_HANDLE_VALUE) { ULONG time;
if (ObjectsDateTime) { time = ObjectsDateTime; } else if (DateTimeSources) { //
// All we care about is that time time stamp on _objects.mac
// is less than that of the sources file so it will get
// regenerated during the second scan.
//
time = DateTimeSources; if (LOWORD(time) != 0) time &= 0xFFFF0000; // 00:00:00 on the same date
else time = 0x1421A000; // 12:00:00 1/1/1990
} else { time = 0x1421A000; // 12:00:00 1/1/1990
}
DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &ft);
SetFileTime(hf, (LPFILETIME)NULL, (LPFILETIME)NULL, &ft);
CloseHandle(hf); } } }
|