//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1989 - 1994. // // File: build.c // // Contents: Parameter processing and main entry point for Build.exe // // History: 16-May-89 SteveWo Created // ... See SLM log // 26-Jul-94 LyleC Cleanup/Add Pass0 support // //---------------------------------------------------------------------------- #include "build.h" #ifndef VER_PRODUCTBUILD #define VER_PRODUCTBUILD 0 #endif static BOOL FileExistsInDirectory( LPCSTR Directory, LPCSTR FileName ) { CHAR Path[DB_MAX_PATH_LENGTH]; sprintf(Path, "%s\\%s", Directory, FileName); return _access(Path, 0) == 0; } static VOID ResolveBaseDirectory(VOID) { CHAR Candidate[DB_MAX_PATH_LENGTH]; CHAR Original[DB_MAX_PATH_LENGTH]; CHAR *Slash; LPCSTR BaseDir; DWORD Length; BaseDir = getenv("BASEDIR"); if (BaseDir && *BaseDir) { Length = GetFullPathNameA(BaseDir, sizeof(Candidate), Candidate, NULL); if (Length == 0 || Length >= sizeof(Candidate)) { BuildError("Unable to resolve BASEDIR=%s\n", BaseDir); exit(1); } } else { Length = GetCurrentDirectoryA(sizeof(Candidate), Candidate); if (Length == 0 || Length >= sizeof(Candidate)) { BuildError("Unable to resolve the current directory\n"); exit(1); } } strcpy(Original, Candidate); if (!(BaseDir && *BaseDir)) { for (;;) { if (FileExistsInDirectory(Candidate, "project.mk")) { break; } Slash = strrchr(Candidate, '\\'); if (!Slash) { strcpy(Candidate, Original); break; } if (Slash == Candidate + 2 && Candidate[1] == ':') { if (Slash[1] == '\0') { strcpy(Candidate, Original); break; } Slash[1] = '\0'; continue; } *Slash = '\0'; } } strcpy(NtRoot, Candidate); if (!SetEnvironmentVariableA("BASEDIR", NtRoot)) { BuildError("Unable to set BASEDIR=%s\n", NtRoot); exit(1); } sprintf(DbMasterName, "%s\\%s", NtRoot, DBMASTER_NAME); } static LPCSTR DefaultProcessorArchitecture(VOID) { LPCSTR Architecture = getenv("VSCMD_ARG_TGT_ARCH"); // A Visual Studio developer prompt explicitly tells us which toolchain // target is active. Prefer that for cross-target prompts. if (Architecture && *Architecture) { if (!_stricmp(Architecture, "x64") || !_stricmp(Architecture, "amd64")) return "AMD64"; if (!_stricmp(Architecture, "x86")) return "x86"; if (!_stricmp(Architecture, "ia64")) return "IA64"; if (!_stricmp(Architecture, "arm64")) return "ARM64"; } // Otherwise prefer the architecture of this build.exe. This keeps a // standalone x64 bootstrap from unexpectedly defaulting to i386 because // of inherited process-environment quirks. #if defined(_M_ARM64) return "ARM64"; #elif defined(_M_IA64) return "IA64"; #elif defined(_M_AMD64) || defined(_WIN64) return "AMD64"; #else Architecture = getenv("PROCESSOR_ARCHITECTURE"); if (Architecture && *Architecture) { return Architecture; } return "x86"; #endif } // // Target machine info: // // SourceSubDirMask, Description, Switch, MakeVariable, // SourceVariable, ObjectVariable, AssociateDirectory, // SourceDirectory, ObjectDirectory // TARGET_MACHINE_INFO i386TargetMachine = { TMIDIR_I386, "i386", "-386", "-x86", "386=1", "i386_SOURCES", "386_OBJECTS", "i386", "i386", "i386dirs", { "i386" }, DIR_INCLUDE_X86 | DIR_INCLUDE_WIN32 }; TARGET_MACHINE_INFO ia64TargetMachine = { TMIDIR_IA64, "IA64", "-ia64", "-merced", "IA64=1", "IA64_SOURCES", "IA64_OBJECTS", "ia64", "ia64", "ia64dirs", { "ia64" }, DIR_INCLUDE_IA64 | DIR_INCLUDE_RISC | DIR_INCLUDE_WIN64 }; TARGET_MACHINE_INFO Amd64TargetMachine = { TMIDIR_AMD64, "AMD64", "-amd64", "-amd64", "AMD64=1", "AMD64_SOURCES", "AMD64_OBJECTS", "amd64", "amd64", "amd64dirs", { "amd64" }, DIR_INCLUDE_AMD64 | DIR_INCLUDE_RISC | DIR_INCLUDE_WIN64 }; TARGET_MACHINE_INFO Arm64TargetMachine = { TMIDIR_ARM64, "ARM64", "-arm64", "-aarch64", "ARM64=1", "ARM64_SOURCES", "ARM64_OBJECTS", "arm64", "arm64", "arm64dirs", { "arm64" }, DIR_INCLUDE_ARM64 | DIR_INCLUDE_RISC | DIR_INCLUDE_WIN64 }; TARGET_MACHINE_INFO *PossibleTargetMachines[MAX_TARGET_MACHINES] = { &i386TargetMachine, &ia64TargetMachine, &Amd64TargetMachine, &Arm64TargetMachine }; UINT NumberCompileWarnings; UINT NumberCompileErrors; UINT NumberCompiles; UINT NumberLibraries; UINT NumberLibraryWarnings; UINT NumberLibraryErrors; UINT NumberLinks; UINT NumberLinkWarnings; UINT NumberLinkErrors; UINT NumberBinplaces; UINT NumberBinplaceWarnings; UINT NumberBinplaceErrors; // // Machine specific target dirs default. If one there is only one build // target and a target specific dirs file is selected, then this gets // filled with a pointer to the target specific dirs filename. // LPSTR pszTargetDirs = ""; #define AltDirMaxSize 10 // Maximum size for alternate obj dir name CHAR LogDirectory[DB_MAX_PATH_LENGTH] = "."; CHAR LogFileName[DB_MAX_PATH_LENGTH] = "build"; CHAR WrnFileName[DB_MAX_PATH_LENGTH] = "build"; CHAR ErrFileName[DB_MAX_PATH_LENGTH] = "build"; CHAR IncFileName[DB_MAX_PATH_LENGTH] = "build"; CHAR szObjRoot[DB_MAX_PATH_LENGTH]; CHAR *pszObjRoot; CHAR szBuildAltDir[DB_MAX_PATH_LENGTH]; CHAR *pszBuildAltDir = NULL; CHAR szObjDir[DB_MAX_PATH_LENGTH]; CHAR szObjDirSlash[DB_MAX_PATH_LENGTH]; CHAR szObjDirSlashStar[DB_MAX_PATH_LENGTH]; CHAR szObjDirD[DB_MAX_PATH_LENGTH]; CHAR szObjDirSlashD[DB_MAX_PATH_LENGTH]; CHAR szObjDirSlashStarD[DB_MAX_PATH_LENGTH]; CHAR *pszObjDir = szObjDir; CHAR *pszObjDirSlash = szObjDirSlash; CHAR *pszObjDirSlashStar = szObjDirSlashStar; CHAR *pszObjDirD = szObjDirD; BOOL fCheckedBuild = TRUE; ULONG iObjectDir = 0; BOOL fDependencySwitchUsed; BOOL fCmdLineDependencySwitchUsed; BOOL fCmdLineQuicky; BOOL fCmdLineSemiQuicky; BOOL fCmdLineQuickZero; CHAR *BuildProduct; ULONG DefaultProcesses = 0; CHAR *szBuildTag; #define MAX_ENV_ARG 512 const char szNewLine[] = "\n"; const char szUsage[] = "Usage: build [-M[ n]][-w][-e][-f][-q][-c] [-why] [-clean]\n" " [-386|-amd64|-ia64|-arm64] [-nmake arg]\n" " -M [n] parallel build (automatic up to 32 workers)\n" " -w watch tree and rebuild on changes\n" " -e write build logs\n" " -f rescan dependencies\n" " -q query only\n" " -c clean object files\n" " -why explain rebuilds\n" " -clean run project clean rules\n"; BOOL ProcessParameters(int argc, LPSTR argv[], BOOL SkipFirst); VOID GetEnvParameters( LPSTR EnvVarName, LPSTR DefaultValue, int *pargc, int maxArgc, LPSTR argv[]); VOID FreeEnvParameters(int argc, LPSTR argv[]); VOID FreeCmdStrings(VOID); VOID MungePossibleTarget( PTARGET_MACHINE_INFO pti ); VOID GetIncludePatterns( LPSTR EnvVarName, int maxArgc, LPSTR argv[]); VOID FreeIncludePatterns( int argc, LPSTR argv[]); //+--------------------------------------------------------------------------- // // Function: main // //---------------------------------------------------------------------------- int __cdecl main( int argc, LPSTR argv[] ) { char c; PDIRREC DirDB; UINT i; int EnvArgc = 0; LPSTR EnvArgv[ MAX_ENV_ARG ] = {0}; LPSTR s, s1; LPSTR PostBuildCmd; BOOL fPauseDone = FALSE; #if DBG BOOL fDebugSave; fDebug = 0; #endif { int watchResult = TryWatchMode(); if (watchResult >= 0) return watchResult; } strcpy(szObjDir, "obj"); strcpy(szObjDirSlash, "obj\\"); strcpy(szObjDirSlashStar, "obj\\*"); strcpy(szObjDirD, "objd"); strcpy(szObjDirSlashD, "objd\\"); strcpy(szObjDirSlashStarD, "objd\\*"); /* * The historical tool closed every CRT descriptor from 3 through _NFILE. * Modern MSVC treats _close() on an invalid descriptor as an invalid- * parameter failure, which can terminate the process before normal startup. * Do not sweep the CRT descriptor table; inherited handles are the caller's * responsibility and are not needed by build.exe. */ pGetFileAttributesExA = (BOOL (WINAPI *)(LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID)) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetFileAttributesExA"); if (pGetFileAttributesExA) { pDateTimeFile = DateTimeFile2; } else { pDateTimeFile = DateTimeFile; } InitializeCriticalSection(&TTYCriticalSection); s1 = getenv("COMSPEC"); if (s1) { cmdexe = s1; } else { cmdexe = "cmd.exe"; } { SYSTEMTIME st; FILETIME ft; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); FileTimeToDosDateTime( &ft, ((LPWORD)&BuildStartTime)+1, (LPWORD)&BuildStartTime ); } BigBufSize = 0xFFF0; AllocMem(BigBufSize, &BigBuf, MT_IOBUFFER); // All env parsing should happen here (after the cmd line is processed) ResolveBaseDirectory(); s = getenv("_OBJ_ROOT"); if (s) { pszObjRoot = strcpy(szObjRoot, s); } s = getenv("BUILD_ALT_DIR"); if (s) { if (strlen(s) > sizeof(szObjDir) - strlen(szObjDir) - 1) { BuildError("environment variable BUILD_ALT_DIR may not be longer than %d characters.\n", sizeof(szObjDir) - strlen(szObjDir) - 1); exit(1); } strcat(szObjDir, s); strcpy(szObjDirSlash, szObjDir); strcpy(szObjDirSlashStar, szObjDir); strcat(szObjDirSlash, "\\"); strcat(szObjDirSlashStar, "\\*"); strcat(LogFileName, s); strcat(WrnFileName, s); strcat(ErrFileName, s); strcat(IncFileName, s); BuildMsg("Object root set to: ==> obj%s\n", s); } s = getenv("NTDEBUG"); if (!s || *s == '\0' || strcmp(s, "retail") == 0 || strcmp(s, "ntsdnodbg") == 0) { fCheckedBuild = FALSE; } s = getenv("OS2_INC_PATH"); if (s) { MakeString(&pszIncOs2, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncOs2, "\\public\\sdk\\inc\\os2"); } s = getenv("POSIX_INC_PATH"); if (s) { MakeString(&pszIncPosix, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncPosix, "\\public\\sdk\\inc\\posix"); } s = getenv("CHICAGO_INC_PATH"); if (s) { MakeString(&pszIncChicago, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncChicago, "\\public\\sdk\\inc\\chicago"); } s = getenv("CRT_INC_PATH"); if (s) { MakeString(&pszIncCrt, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncCrt, "\\public\\sdk\\inc\\crt"); } s = getenv("SDK_INC_PATH"); if (s) { MakeString(&pszIncSdk, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncSdk, "\\public\\sdk\\inc"); } s = getenv("OAK_INC_PATH"); if (s) { MakeString(&pszIncOak, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncOak, "\\public\\oak\\inc"); } s = getenv("DDK_INC_PATH"); if (s) { MakeString(&pszIncDdk, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncDdk, "\\public\\ddk\\inc"); } s = getenv("WDM_INC_PATH"); if (s) { MakeString(&pszIncWdm, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncWdm, "\\public\\ddk\\inc\\wdm"); } s = getenv("PRIVATE_INC_PATH"); if (s) { MakeString(&pszIncPri, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncPri, "\\private\\inc"); } s = getenv("MFC_INCLUDES"); if (s) { MakeString(&pszIncMfc, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszIncMfc, "\\public\\sdk\\inc\\mfc42"); } s = getenv("SDK_LIB_DEST"); if (s) { MakeString(&pszSdkLibDest, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszSdkLibDest, "\\public\\sdk\\lib"); } s = getenv("DDK_LIB_DEST"); if (s) { MakeString(&pszDdkLibDest, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszDdkLibDest, "\\public\\sdk\\lib"); } s = getenv("PUBLIC_INTERNAL_PATH"); if (s) { MakeString(&pszPublicInternalPath, s, TRUE, MT_DIRSTRING); } else { MakeExpandedString(&pszPublicInternalPath, "\\public\\internal"); } szBuildTag = getenv("BUILD_TAG"); strcpy( MakeParameters, "" ); MakeParametersTail = AppendString( MakeParameters, "/c BUILDMSG=Stop.", FALSE); RecurseLevel = 0; #if DBG if ((s = getenv("BUILD_DEBUG_FLAG")) != NULL) { i = atoi(s); if (!isdigit(*s)) { i = 1; } BuildMsg("Debug Output Enabled: %u ==> %u\n", fDebug, fDebug | i); fDebug |= i; } #endif if (!(MakeProgram = getenv( "BUILD_MAKE_PROGRAM" ))) { MakeProgram = "NMAKE.EXE"; } if (s = getenv("BUILD_PATH")) { SetEnvironmentVariable("PATH", s); } if (s = getenv("COPYCMD")) { if (!strchr(s, 'y') && !strchr(s, 'Y')) { // COPYCMD is set, but /y isn't a part of it. Add /Y. s1 = malloc(strlen(s) + sizeof(" /Y") + 1); if (s1) { strcpy(s1, s); strcat(s1, " /Y"); SetEnvironmentVariable("COPYCMD", s1); } } } else { // COPYCMD not set. Do so. SetEnvironmentVariable("COPYCMD", "/Y"); } PostBuildCmd = getenv("BUILD_POST_PROCESS"); SystemIncludeEnv = getenv( "INCLUDE" ); GetCurrentDirectory( sizeof( CurrentDirectory ), CurrentDirectory ); for (i = 0; i < MAX_TARGET_MACHINES; i++) { TargetMachines[i] = NULL; TargetToPossibleTarget[i] = 0; MungePossibleTarget(PossibleTargetMachines[i]); } if (!(BuildProduct = getenv("BUILD_PRODUCT"))) { BuildProduct = ""; } if (!ProcessParameters( argc, argv, TRUE )) { fUsage = TRUE; } else { fCmdLineDependencySwitchUsed = fDependencySwitchUsed; fCmdLineQuicky = fQuicky; fCmdLineSemiQuicky = fSemiQuicky; fCmdLineQuickZero = fQuickZero; GetEnvParameters( "BUILD_DEFAULT", NULL, &EnvArgc, MAX_ENV_ARG, EnvArgv ); GetEnvParameters( "BUILD_OPTIONS", NULL, &EnvArgc, MAX_ENV_ARG, EnvArgv ); if (CountTargetMachines == 0) { LPCSTR Architecture = DefaultProcessorArchitecture(); if (!_stricmp(Architecture, "IA64")) GetEnvParameters( "BUILD_DEFAULT_TARGETS", "-ia64", &EnvArgc, MAX_ENV_ARG, EnvArgv ); else if (!_stricmp(Architecture, "ARM64")) GetEnvParameters( "BUILD_DEFAULT_TARGETS", "-arm64", &EnvArgc, MAX_ENV_ARG, EnvArgv ); else if (!_stricmp(Architecture, "AMD64") || !_stricmp(Architecture, "x64")) GetEnvParameters( "BUILD_DEFAULT_TARGETS", "-amd64", &EnvArgc, MAX_ENV_ARG, EnvArgv ); else GetEnvParameters( "BUILD_DEFAULT_TARGETS", "-386", &EnvArgc, MAX_ENV_ARG, EnvArgv ); } if (!ProcessParameters( EnvArgc, EnvArgv, FALSE )) { fUsage = TRUE; } } FreeEnvParameters(EnvArgc, EnvArgv); if (!fUsage && CountTargetMachines == 1) { LPCSTR vsTarget = getenv("VSCMD_ARG_TGT_ARCH"); if (vsTarget && *vsTarget) { LPCSTR actual = TargetMachines[0]->Description; if ((_stricmp(vsTarget, "x64") == 0 && _stricmp(actual, "amd64") != 0) || (_stricmp(vsTarget, "amd64") == 0 && _stricmp(actual, "amd64") != 0) || (_stricmp(vsTarget, "x86") == 0 && _stricmp(actual, "i386") != 0) || (_stricmp(vsTarget, "arm64") == 0 && _stricmp(actual, "arm64") != 0)) { BuildError("error: target/toolchain mismatch (%s/%s)\n", actual, vsTarget); return 1; } } } if (!fUsage && !fQuery && !SearchPathA(NULL, MakeProgram, NULL, 0, NULL, NULL)) { BuildError("error: %s missing\n", MakeProgram); return 1; } if (!fUsage && !fGenerateObjectsDotMacOnly) { if (!_stricmp(BuildProduct, "NT")) { if (fCmdLineDependencySwitchUsed) { fDependencySwitchUsed = fCmdLineDependencySwitchUsed; fQuicky = fCmdLineQuicky; fSemiQuicky = fCmdLineSemiQuicky; fQuickZero = fCmdLineQuickZero; } if (!fDependencySwitchUsed) { BuildError("(Fatal Error) One of either /D, /Z, /z, or /3 is required for NT builds\n"); exit( 1 ); } else { if (fDependencySwitchUsed == 1) { if (fQuicky) { BuildError("(Fatal Error) switch can not be used with /Z, /z, or /3\n"); exit( 1 ); } BuildMsgRaw( "BUILD: /D specified - The NT build lab will not accept build requests using this switch\n"); } if (fDependencySwitchUsed == 2){ if (fStopAfterPassZero) { BuildError("(Fatal Error) switch /0 can not be used with /z\n"); exit( 1 ); } } } } } GetIncludePatterns( "BUILD_ACCEPTABLE_INCLUDES", MAX_INCLUDE_PATTERNS, AcceptableIncludePatternList ); GetIncludePatterns( "BUILD_UNACCEPTABLE_INCLUDES", MAX_INCLUDE_PATTERNS, UnacceptableIncludePatternList ); if (( fCheckIncludePaths ) && ( AcceptableIncludePatternList[ 0 ] == NULL ) && ( UnacceptableIncludePatternList[ 0 ] == NULL )) { BuildMsgRaw( "WARNING: -# specified without BUILD_[UN]ACCEPTABLE_INCLUDES set\n" ); } if (fCleanRestart) { if (fClean) { fClean = FALSE; fRestartClean = TRUE; } else if (fCleanLibs) { fCleanLibs = FALSE; fRestartCleanLibs = TRUE; } else { BuildError("/R switch only valid with /c or /C switch.\n"); fUsage = TRUE; } } NumberProcesses = 1; if (fParallel || getenv("BUILD_MULTIPROCESSOR")) { SYSTEM_INFO SystemInfo; if (DefaultProcesses == 0) { GetSystemInfo(&SystemInfo); NumberProcesses = SystemInfo.dwNumberOfProcessors; } else { NumberProcesses = DefaultProcesses; } if (NumberProcesses == 1) { fParallel = FALSE; } else { if (NumberProcesses > 32) { if (DefaultProcesses != 0) { BuildError("error: maximum worker count is 32\n"); exit(1); } NumberProcesses = 32; } fParallel = TRUE; BuildMsg("Using %d child processes\n", NumberProcesses); } } if (fUsage) { BuildMsgRaw( "\nBUILD: Version %x.%02x.%04d\n\n", BUILD_VERSION >> 8, BUILD_VERSION & 0xFF, VER_PRODUCTBUILD); BuildMsgRaw(szUsage); } else if (CountTargetMachines != 0) { BuildError( "%s for ", fLinkOnly? "Link" : (fCompileOnly? "Compile" : "Compile and Link")); for (i = 0; i < CountTargetMachines; i++) { BuildErrorRaw(i==0? "%s" : ", %s", TargetMachines[i]->Description); AppendString( MakeTargets, TargetMachines[i]->MakeVariable, TRUE); } BuildErrorRaw(szNewLine); // // If there is one and only one build target and target dirs has // been enabled, then fill in the appropriate target dirs name. // if (CountTargetMachines == 1) { if (fTargetDirs == TRUE) { pszTargetDirs = TargetMachines[0]->TargetDirs; FileDesc[0].pszPattern = TargetMachines[0]->TargetDirs; } } if (DEBUG_1) { if (CountExcludeIncs) { BuildError("Include files that will be excluded:"); for (i = 0; i < CountExcludeIncs; i++) { BuildErrorRaw(i == 0? " %s" : ", %s", ExcludeIncs[i]); } BuildErrorRaw(szNewLine); } if (CountOptionalDirs) { BuildError("Optional Directories that will be built:"); for (i = 0; i < CountOptionalDirs; i++) { BuildErrorRaw(i == 0? " %s" : ", %s", OptionalDirs[i]); } BuildErrorRaw(szNewLine); } if (CountExcludeDirs) { BuildError("Directories that will be NOT be built:"); for (i = 0; i < CountExcludeDirs; i++) { BuildErrorRaw(i == 0? " %s" : ", %s", ExcludeDirs[i]); } BuildErrorRaw(szNewLine); } BuildMsg("MakeParameters == %s\n", MakeParameters); BuildMsg("MakeTargets == %s\n", MakeTargets); } #if DBG fDebugSave = fDebug; // fDebug = 0; #endif // // Generate the _objects.mac file if requested // if (fGenerateObjectsDotMacOnly) { DIRSUP DirSup; ULONG DateTimeSources; DirDB = ScanDirectory( CurrentDirectory ); if (DirDB && (DirDB->DirFlags & (DIRDB_DIRS | DIRDB_SOURCES))) { if (!ReadSourcesFile(DirDB, &DirSup, &DateTimeSources)) { BuildError("Current directory not a SOURCES directory.\n"); return( 1 ); } GenerateObjectsDotMac(DirDB, &DirSup, DateTimeSources); FreeDirSupData(&DirSup); ReportDirsUsage(); FreeCmdStrings(); ReportMemoryUsage(); return(0); } } if (!fQuery && fErrorLog) { strcat(LogFileName, ".log"); if (!MyOpenFile(LogDirectory, LogFileName, "wb", &LogFile, TRUE)) { BuildError("(Fatal Error) Unable to open log file\n"); exit( 1 ); } CreatedBuildFile(LogDirectory, LogFileName); strcat(WrnFileName, ".wrn"); if (!MyOpenFile(LogDirectory, WrnFileName, "wb", &WrnFile, FALSE)) { BuildError("(Fatal Error) Unable to open warning file\n"); exit( 1 ); } CreatedBuildFile(LogDirectory, WrnFileName); strcat(ErrFileName, ".err"); if (!MyOpenFile(LogDirectory, ErrFileName, "wb", &ErrFile, FALSE)) { BuildError("(Fatal Error) Unable to open error file\n"); exit( 1 ); } CreatedBuildFile(LogDirectory, ErrFileName); if ( fCheckIncludePaths ) { strcat( IncFileName, ".inc"); if (!MyOpenFile( LogDirectory, IncFileName, "wb", &IncFile, FALSE ) ) { BuildError( "(Fatal Error) Unable to open include log file\n"); exit( 1 ); } CreatedBuildFile( LogDirectory, IncFileName ); } } else { LogFile = NULL; WrnFile = NULL; ErrFile = NULL; IncFile = NULL; } s = getenv("__MTSCRIPT_ENV_ID"); if (s) { if (fDependencySwitchUsed == 2 && fPause) { BuildError("Cannot combine -z (or -2) and -p switches under MTScript"); exit(1); } // Make sure any other build.exe's that get launched as child // processes don't try to connect to the script engine. SetEnvironmentVariable("__MTSCRIPT_ENV_ID", NULL); g_hMTEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_hMTEvent != NULL) { g_hMTThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MTScriptThread, NULL, 0, &g_dwMTThreadId); if (g_hMTThread) { // Wait for the thread to tell us it's ready. WaitForSingleObject(g_hMTEvent, INFINITE); ResetEvent(g_hMTEvent); if (!g_hMTThread) { // An error occurred connecting to the script engine. // We can't continue. BuildError("Unable to connect to script engine. Exiting."); exit(2); } fMTScriptSync = TRUE; } else { BuildError("Failed to launch script handling thread! (%d)", GetLastError()); CloseHandle(g_hMTEvent); g_hMTEvent = NULL; fPause = FALSE; } } else { BuildError("Failed to create script communication event! (%d)", GetLastError()); fPause = FALSE; } } // // The user should not have CHICAGO_PRODUCT in // their environment, as it can cause problems on other machines with // other users that don't have them set. The following warning // messages are intended to alert the user to the presence of these // environment variables. // if (getenv("CHICAGO_PRODUCT") != NULL) { BuildError("CHICAGO_PRODUCT was detected in the environment.\n" ); BuildMsg(" ALL directories will be built targeting Chicago!\n" ); fChicagoProduct = TRUE; } if (!fQuicky) { LoadMasterDB(); BuildError("Computing Include file dependencies:\n"); ScanIncludeEnv(SystemIncludeEnv); ScanGlobalIncludeDirectory(pszIncMfc); ScanGlobalIncludeDirectory(pszIncOak); ScanGlobalIncludeDirectory(pszIncDdk); ScanGlobalIncludeDirectory(pszIncWdm); ScanGlobalIncludeDirectory(pszIncSdk); ScanGlobalIncludeDirectory(pszIncPri); CountSystemIncludeDirs = CountIncludeDirs; } #if DBG fDebug = fDebugSave; #endif fFirstScan = TRUE; fPassZero = FALSE; ScanSourceDirectories( CurrentDirectory ); if (!fQuicky) { if (SaveMasterDB() == FALSE) { BuildError("Unable to save the dependency database: %s\n", DbMasterName); } } c = '\n'; if (!fLinkOnly && CountPassZeroDirs) { if (!fQuicky) { TotalFilesToCompile = 0; TotalLinesToCompile = 0L; for (i=0; iCountOfPassZeroFiles; TotalLinesToCompile += DirDB->PassZeroLines; } if (CountPassZeroDirs > 1 && TotalFilesToCompile != 0 && TotalLinesToCompile != 0L) { BuildMsgRaw( "Total of %d pass zero files (%s lines) to compile in %d directories\n\n", TotalFilesToCompile, FormatNumber( TotalLinesToCompile ), CountPassZeroDirs); } } TotalFilesCompiled = 0; TotalLinesCompiled = 0L; ElapsedCompileTime = 0L; if (fPause && !fMTScriptSync) { BuildMsg("Press enter to continue with compilations (or 'q' to quit)..."); c = (char)getchar(); } if ((CountPassZeroDirs > 0) && (c == '\n')) { CompilePassZeroDirectories(); WaitForParallelThreads(); // // Rescan now that we've generated all the generated files // CountPassZeroDirs = 0; CountCompileDirs = 0; CountLinkDirs = 0; UnsnapAllDirectories(); fPassZero = FALSE; fFirstScan = FALSE; RecurseLevel = 0; if (fMTScriptSync) { WaitForResume(fPause, PE_PASS0_COMPLETE); fPauseDone = TRUE; } // This will compile directories if fQuicky is TRUE if (!fStopAfterPassZero) { ScanSourceDirectories( CurrentDirectory ); } if (!fQuicky) { if (SaveMasterDB() == FALSE) { BuildError("Unable to save the dependency database: %s\n", DbMasterName); } } } } if (fMTScriptSync && !fPauseDone) { WaitForResume(fPause, PE_PASS0_COMPLETE); } if (fStopAfterPassZero) { BuildError("Stopping after pass zero requested: Pass0 done.\n"); } if (!fStopAfterPassZero && !fLinkOnly && (c == '\n')) { if (!fQuicky) { TotalFilesToCompile = 0; TotalLinesToCompile = 0L; for (i=0; iCountOfFilesToCompile; TotalLinesToCompile += DirDB->SourceLinesToCompile; } if (CountCompileDirs > 1 && TotalFilesToCompile != 0 && TotalLinesToCompile != 0L) { BuildMsgRaw( "Total of %d source files (%s lines) to compile in %d directories\n\n", TotalFilesToCompile, FormatNumber( TotalLinesToCompile ), CountCompileDirs); } } TotalFilesCompiled = 0; TotalLinesCompiled = 0L; ElapsedCompileTime = 0L; if (fPause && !fMTScriptSync) { BuildMsg("Press enter to continue with compilations (or 'q' to quit)..."); c = (char)getchar(); } if (c == '\n') { // Does nothing if fQuicky is TRUE CompileSourceDirectories(); WaitForParallelThreads(); } } if (fMTScriptSync) { WaitForResume(fPause, PE_PASS1_COMPLETE); } if (!fStopAfterPassZero && !fCompileOnly && (c == '\n')) { LinkSourceDirectories(); WaitForParallelThreads(); } if (!fStopAfterPassZero && PostBuildCmd && !fMTScriptSync) { // If there's a post build process to invoke, do so but only if // not running under the buildcon. // PostBuildCmd is of the form // The Message is delimiated with curly brackets. ie: // POST_BUILD_PROCESS={Do randomness}randomness.cmd // would display: // // BUILD: Do randomness // // while randomness.cmd was running. The process is run synchronously and // we've still got the i/o pipes setup so any output will be logged to // build.log (and wrn/err if formated correctly) if (*PostBuildCmd == '{') { LPSTR PostBuildMessage = PostBuildCmd+1; LogMsg("Executing post build scripts %s\n", szAsterisks); while (*PostBuildCmd && *PostBuildCmd != '}') PostBuildCmd++; if (*PostBuildCmd == '}') { *PostBuildCmd = '\0'; PostBuildCmd++; BuildMsg("%s\n", PostBuildMessage); LogMsg("%s\n", PostBuildMessage); ExecuteProgram(PostBuildCmd, "", "", TRUE); } } else { ExecuteProgram(PostBuildCmd, "", "", TRUE); } } if (fShowTree) { for (i = 0; i < CountShowDirs; i++) { PrintDirDB(ShowDirs[i], 1|4); } } } else { BuildError("No target machine specified\n"); } if (!fUsage && !fQuery && fErrorLog) { ULONG cbLogMin = 32; ULONG cbWarnMin = 0; if (!fAlwaysKeepLogfile) { if (fQuicky && !fSemiQuicky && ftell(ErrFile) == 0) { cbLogMin = cbWarnMin = ULONG_MAX; } } CloseOrDeleteFile(&LogFile, LogDirectory, LogFileName, cbLogMin); CloseOrDeleteFile(&WrnFile, LogDirectory, WrnFileName, cbWarnMin); CloseOrDeleteFile(&ErrFile, LogDirectory, ErrFileName, 0L); if ( fCheckIncludePaths ) { CloseOrDeleteFile(&IncFile, LogDirectory, IncFileName, cbLogMin); } } if (NumberCompileErrors || NumberLibraryErrors || NumberLinkErrors || NumberBinplaceErrors || BuildChildFailures) { BuildError("Failed (%ld errors)\n\n", (long)(NumberCompileErrors + NumberLibraryErrors + NumberLinkErrors + NumberBinplaceErrors + BuildChildFailures)); } else { BuildError("Done\n\n"); } if (fMTScriptSync) { WaitForResume(FALSE, PE_PASS2_COMPLETE); } if (NumberCompiles) { BuildMsgRaw(" %d file%s compiled", NumberCompiles, NumberCompiles == 1 ? "" : "s"); if (NumberCompileWarnings) { BuildMsgRaw(" - %d Warning%s", NumberCompileWarnings, NumberCompileWarnings == 1 ? "" : "s"); } if (NumberCompileErrors) { BuildMsgRaw(" - %d Error%s", NumberCompileErrors, NumberCompileErrors == 1 ? "" : "s"); } if (ElapsedCompileTime) { BuildMsgRaw(" - %5ld LPS", TotalLinesCompiled / ElapsedCompileTime); } BuildMsgRaw(szNewLine); } if (NumberLibraries) { BuildMsgRaw(" %d librar%s built", NumberLibraries, NumberLibraries == 1 ? "y" : "ies"); if (NumberLibraryWarnings) { BuildMsgRaw(" - %d Warning%s", NumberLibraryWarnings, NumberLibraryWarnings == 1 ? "" : "s"); } if (NumberLibraryErrors) { BuildMsgRaw(" - %d Error%s", NumberLibraryErrors, NumberLibraryErrors == 1 ? "" : "s"); } BuildMsgRaw(szNewLine); } if (NumberLinks) { BuildMsgRaw(" %d executable%sbuilt", NumberLinks, NumberLinks == 1 ? " " : "s "); if (NumberLinkWarnings) { BuildMsgRaw(" - %d Warning%s", NumberLinkWarnings, NumberLinkWarnings == 1 ? "" : "s"); } if (NumberLinkErrors) { BuildMsgRaw(" - %d Error%s", NumberLinkErrors, NumberLinkErrors == 1 ? "" : "s"); } BuildMsgRaw(szNewLine); } if (NumberBinplaces) { BuildMsgRaw(" %d file%sbinplaced", NumberBinplaces, NumberBinplaces == 1 ? " " : "s "); if (NumberBinplaceWarnings) { BuildMsgRaw(" - %d Warning%s", NumberBinplaceWarnings, NumberBinplaceWarnings == 1 ? "" : "s"); } if (NumberBinplaceErrors) { BuildMsgRaw(" - %d Error%s", NumberBinplaceErrors, NumberBinplaceErrors == 1 ? "" : "s"); } BuildMsgRaw(szNewLine); } ReportDirsUsage(); FreeCmdStrings(); FreeIncludePatterns( MAX_INCLUDE_PATTERNS, AcceptableIncludePatternList ); FreeIncludePatterns( MAX_INCLUDE_PATTERNS, UnacceptableIncludePatternList ); ReportMemoryUsage(); ExitMTScriptThread(); if (NumberCompileErrors || NumberLibraryErrors || NumberLinkErrors || NumberBinplaceErrors || BuildChildFailures || fUsage) { return 1; } else { return( 0 ); } } VOID ReportDirsUsage( VOID ) { ULONG i; BOOLEAN fHeaderPrinted; if (!fShowUnusedDirs) { return; } fHeaderPrinted = FALSE; for (i=0; iSwitch) || !_stricmp(p, PossibleTargetMachines[i]->Switch2)) { AddTargetMachine(i); break; } } if (i < MAX_TARGET_MACHINES) { } else if (!_stricmp(p + 1, "all")) { for (i = 0; i < MAX_TARGET_MACHINES; i++) { AddTargetMachine(i); } } else if (!_stricmp(p + 1, "why")) { fWhyBuild = TRUE; } else while (c = *++p) switch (toupper( c )) { case '?': fUsage = TRUE; break; case 'C': if (!_stricmp(p, "clean")) { MakeParametersTail = AppendString(MakeParametersTail, "clean", TRUE); *p-- = '\0'; } else if (c == 'c') { fClean = TRUE; } else { Result = FALSE; } break; case 'E': fErrorLog = TRUE; break; case 'F': fForce = TRUE; break; case 'M': if (c != 'M') { Result = FALSE; break; } fParallel = TRUE; if (argc > 1 && isdigit((unsigned char)(* (argv + 1))[0])) { long n = strtol(*(argv + 1), NULL, 10); if (n < 1 || n > 32) { Result = FALSE; break; } DefaultProcesses = (ULONG)n; --argc; ++argv; } break; case 'N': if (_stricmp(p, "nmake") == 0) { if (argc > 1) { --argc; ++argv; MakeParametersTail = AppendString(MakeParametersTail, *argv, TRUE); } else { Result = FALSE; } *p-- = '\0'; } else { Result = FALSE; } break; case 'Q': fQuery = TRUE; break; case 'W': // -w is intercepted by the supervising watcher before main. Result = FALSE; break; default: BuildError("Invalid switch - /%c\n", c); Result = FALSE; break; } } else if (*p == '~') { if (CountExcludeDirs >= MAX_EXCLUDE_DIRECTORIES) { static BOOL fError = FALSE; if (!fError) { BuildError( "Exclude directory table overflow, using first %u entries\n", MAX_EXCLUDE_DIRECTORIES); fError = TRUE; } } else { MakeString( &ExcludeDirs[CountExcludeDirs++], p + 1, TRUE, MT_CMDSTRING); } } else { for (i = 0; i < MAX_TARGET_MACHINES; i++) { if (!_stricmp(p, PossibleTargetMachines[i]->MakeVariable)) { AddTargetMachine(i); break; } } if (i >= MAX_TARGET_MACHINES) { if (iscsym(*p) || *p == '.') { if (CountOptionalDirs >= MAX_OPTIONAL_DIRECTORIES) { static BOOL fError = FALSE; if (!fError) { BuildError( "Optional directory table overflow, using first %u entries\n", MAX_OPTIONAL_DIRECTORIES); fError = TRUE; } } else { MakeString( &OptionalDirs[CountOptionalDirs++], p, TRUE, MT_CMDSTRING); } } else if (!strcmp(p, "*")) { BuildAllOptionalDirs = TRUE; } else { MakeParametersTail = AppendString( MakeParametersTail, p, TRUE); } } } --argc; ++argv; } return(Result); } //+--------------------------------------------------------------------------- // // Function: GetEnvParameters // //---------------------------------------------------------------------------- VOID GetEnvParameters( LPSTR EnvVarName, LPSTR DefaultValue, int *pargc, int maxArgc, LPSTR argv[] ) { LPCSTR Value; WCHAR *CommandLine; LPWSTR *WideArgv; int WideArgc; int ValueChars; int i; static const WCHAR Prefix[] = L"build.exe "; Value = getenv(EnvVarName); if (!Value) { if (DefaultValue == NULL) { return; } Value = DefaultValue; } else if (DEBUG_1) { BuildMsg("Using %s=%s\n", EnvVarName, Value); } ValueChars = MultiByteToWideChar(CP_ACP, 0, Value, -1, NULL, 0); if (!ValueChars) { BuildError("Unable to parse %s\n", EnvVarName); exit(1); } CommandLine = (WCHAR *)malloc(sizeof(Prefix) + (ValueChars * sizeof(WCHAR))); if (!CommandLine) { BuildError("Unable to allocate command line for %s\n", EnvVarName); exit(1); } wcscpy(CommandLine, Prefix); if (!MultiByteToWideChar(CP_ACP, 0, Value, -1, CommandLine + ((sizeof(Prefix) / sizeof(Prefix[0])) - 1), ValueChars)) { free(CommandLine); BuildError("Unable to convert %s\n", EnvVarName); exit(1); } WideArgv = CommandLineToArgvW(CommandLine, &WideArgc); free(CommandLine); if (!WideArgv) { BuildError("Unable to parse %s\n", EnvVarName); exit(1); } for (i = 1; i < WideArgc; i++) { LPSTR Argument; int ArgumentBytes; LPSTR q; if (*pargc >= maxArgc) { LocalFree(WideArgv); BuildError("Too many parameters (> %d)\n", maxArgc); exit(1); } ArgumentBytes = WideCharToMultiByte(CP_ACP, 0, WideArgv[i], -1, NULL, 0, NULL, NULL); if (!ArgumentBytes) { LocalFree(WideArgv); BuildError("Unable to convert a parameter from %s\n", EnvVarName); exit(1); } Argument = (LPSTR)malloc(ArgumentBytes); if (!Argument || !WideCharToMultiByte(CP_ACP, 0, WideArgv[i], -1, Argument, ArgumentBytes, NULL, NULL)) { if (Argument) { free(Argument); } LocalFree(WideArgv); BuildError("Unable to convert a parameter from %s\n", EnvVarName); exit(1); } for (q = Argument; *q; q++) { if (*q == '#') { *q = '='; } } MakeString(&argv[*pargc], Argument, FALSE, MT_CMDSTRING); (*pargc)++; free(Argument); } LocalFree(WideArgv); } //+--------------------------------------------------------------------------- // // Function: FreeEnvParameters // //---------------------------------------------------------------------------- VOID FreeEnvParameters(int argc, LPSTR argv[]) { while (--argc >= 0) { FreeMem(&argv[argc], MT_CMDSTRING); } } //+--------------------------------------------------------------------------- // // Function: FreeCmdStrings // //---------------------------------------------------------------------------- VOID FreeCmdStrings(VOID) { #if DBG UINT i; for (i = 0; i < CountExcludeIncs; i++) { FreeMem(&ExcludeIncs[i], MT_CMDSTRING); } for (i = 0; i < CountOptionalDirs; i++) { FreeMem(&OptionalDirs[i], MT_CMDSTRING); } for (i = 0; i < CountExcludeDirs; i++) { FreeMem(&ExcludeDirs[i], MT_CMDSTRING); } // It's possible the user may have done: // = // in a sources file. Don't free mem unless it's still set... if (pszSdkLibDest) FreeMem(&pszSdkLibDest, MT_DIRSTRING); if (pszDdkLibDest) FreeMem(&pszDdkLibDest, MT_DIRSTRING); if (pszPublicInternalPath) FreeMem(&pszPublicInternalPath, MT_DIRSTRING); if (pszIncOs2) FreeMem(&pszIncOs2, MT_DIRSTRING); if (pszIncPosix) FreeMem(&pszIncPosix, MT_DIRSTRING); if (pszIncChicago) FreeMem(&pszIncChicago, MT_DIRSTRING); if (pszIncMfc) FreeMem(&pszIncMfc, MT_DIRSTRING); if (pszIncSdk) FreeMem(&pszIncSdk, MT_DIRSTRING); if (pszIncCrt) FreeMem(&pszIncCrt, MT_DIRSTRING); if (pszIncOak) FreeMem(&pszIncOak, MT_DIRSTRING); if (pszIncDdk) FreeMem(&pszIncDdk, MT_DIRSTRING); if (pszIncWdm) FreeMem(&pszIncWdm, MT_DIRSTRING); if (pszIncPri) FreeMem(&pszIncPri, MT_DIRSTRING); #endif } //+--------------------------------------------------------------------------- // // Function: MungePossibleTarget // //---------------------------------------------------------------------------- VOID MungePossibleTarget( PTARGET_MACHINE_INFO pti ) { PCHAR s; char *pszDir; if (!pti) { return; } // save "i386" string pszDir = pti->ObjectDirectory[0]; // Create "$(_OBJ_DIR)\i386" string s = malloc(12 + strlen(pszDir) + 1); if (!s) return; sprintf(s, "$(_OBJ_DIR)\\%s", pszDir); pti->ObjectMacro = s; // Create "obj$(BUILD_ALT_DIR)\i386" string for default obj dir s = malloc(strlen(szObjDir) + 1 + strlen(pszDir) + 1); if (!s) return; sprintf(s, "%s\\%s", szObjDir, pszDir); pti->ObjectDirectory[0] = s; // Create "objd\i386" string for alternate checked obj dir s = malloc(strlen(szObjDirD) + 1 + strlen(pszDir) + 1); if (!s) return; sprintf(s, "%s\\%s", szObjDirD, pszDir); pti->ObjectDirectory[1] = s; } //+--------------------------------------------------------------------------- // // Function: GetIncludePatterns // //---------------------------------------------------------------------------- VOID GetIncludePatterns( LPSTR EnvVarName, int maxArgc, LPSTR argv[] ) { LPSTR p, p1, psz; int argc; argc = 0; if ( ( p = getenv(EnvVarName ) ) == NULL ) { return; } MakeString( &psz, p, FALSE, MT_DIRSTRING ); p1 = psz; while ( *p1 ) { while ( *p1 == ';' || *p1 == ' ' ) { p1++; } p = p1; while ( *p && *p != ';' ) { p++; } if ( *p ) { *p++ = '\0'; } MakeString( &argv[argc], p1, FALSE, MT_DIRSTRING ); if ( ( argc += 1 ) == maxArgc ) { BuildError( "Too many include patterns ( > %d)\n", maxArgc ); exit( 1 ); } p1 = p; } FreeMem(&psz, MT_DIRSTRING); } //+--------------------------------------------------------------------------- // // Function: FreeIncludePatterns // //---------------------------------------------------------------------------- VOID FreeIncludePatterns(int argc, LPSTR argv[]) { while ( argc ) { if ( argv[--argc] ) { FreeMem( &argv[argc], MT_DIRSTRING ); } } }