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

3474 lines
115 KiB
C

//+---------------------------------------------------------------------------
//
// 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);
}
}
}