#ifndef NOMINMAX #define NOMINMAX #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #define MAX_PATH_LEN 1024 #define MAX_FILES 4096 #define MAX_HASH_ENTRIES 512 // ============================================================================ // Arena Allocator // ============================================================================ typedef struct { char* base; size_t size; size_t offset; } Arena; static Arena g_Arena; static void ArenaInit(size_t size) { g_Arena.base = (char*)malloc(size); assert(g_Arena.base != NULL); g_Arena.size = size; g_Arena.offset = 0; } static void* ArenaPush(size_t size) { // align to 8 bytes size_t aligned_size = (size + 7) & ~7; if (g_Arena.offset + aligned_size > g_Arena.size) { printf("[ERROR] Arena out of memory!\n"); exit(1); } void* ptr = g_Arena.base + g_Arena.offset; g_Arena.offset += aligned_size; memset(ptr, 0, size); return ptr; } // ============================================================================ // String helpers // ============================================================================ static bool StringContains(const char* haystack, const char* needle) { return strstr(haystack, needle) != NULL; } static bool EndsWithNoCase(const char* str, const char* suffix) { int strLen = (int)strlen(str); int sufLen = (int)strlen(suffix); if (sufLen > strLen) return false; return _stricmp(str + strLen - sufLen, suffix) == 0; } // ============================================================================ // File I/O helpers // ============================================================================ typedef struct { char* Data; long Size; } FileContent; static FileContent ReadEntireFile(const char* path) { FileContent result = { NULL, 0 }; FILE* f = NULL; fopen_s(&f, path, "rb"); if (!f) return result; fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET); if (size > 0) { result.Data = (char*)ArenaPush(size + 1); result.Size = size; fread(result.Data, 1, size, f); result.Data[size] = '\0'; } fclose(f); return result; } static bool WriteEntireFile(const char* path, const char* data, long size) { FILE* f = NULL; fopen_s(&f, path, "wb"); if (!f) return false; size_t written = fwrite(data, 1, size, f); fclose(f); return written == (size_t)size; } static bool FileExists(const char* path) { DWORD attribs = GetFileAttributesA(path); return (attribs != INVALID_FILE_ATTRIBUTES) && !(attribs & FILE_ATTRIBUTE_DIRECTORY); } static bool DirectoryExists(const char* path) { DWORD attribs = GetFileAttributesA(path); return (attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY); } static void EnsureDirectoryExists(const char* path) { CreateDirectoryA(path, NULL); } static int64_t GetFileModTimeInt64(const char* path) { WIN32_FILE_ATTRIBUTE_DATA data; if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { ULARGE_INTEGER li; li.LowPart = data.ftLastWriteTime.dwLowDateTime; li.HighPart = data.ftLastWriteTime.dwHighDateTime; return (int64_t)li.QuadPart; } return 0; } // ============================================================================ // FNV-1a hash // ============================================================================ static uint64_t ComputeFnv1aHash(const char* data, long size) { uint64_t hash = 14695981039346656037ULL; for (long i = 0; i < size; i++) { hash ^= (uint64_t)(unsigned char)data[i]; hash *= 1099511628211ULL; } return hash; } // ============================================================================ // Hash cache // ============================================================================ typedef struct { char Path[MAX_PATH_LEN]; char HashStr[32]; } HashEntry; typedef struct { HashEntry* Entries; int Count; } HashCache; static HashCache* CreateHashCache() { HashCache* cache = (HashCache*)ArenaPush(sizeof(HashCache)); cache->Entries = (HashEntry*)ArenaPush(sizeof(HashEntry) * MAX_HASH_ENTRIES); cache->Count = 0; return cache; } static const char* HashCacheFind(const HashCache* cache, const char* path) { for (int i = 0; i < cache->Count; i++) { if (strcmp(cache->Entries[i].Path, path) == 0) { return cache->Entries[i].HashStr; } } return NULL; } static void HashCacheAdd(HashCache* cache, const char* path, const char* hash) { if (cache->Count < MAX_HASH_ENTRIES) { strncpy_s(cache->Entries[cache->Count].Path, MAX_PATH_LEN, path, _TRUNCATE); strncpy_s(cache->Entries[cache->Count].HashStr, 32, hash, _TRUNCATE); cache->Count++; } } static void LoadHashCache(const char* cachePath, HashCache* cache) { FileContent content = ReadEntireFile(cachePath); if (!content.Data) return; const char* pos = content.Data; while (*pos != '\0') { const char* lineStart = pos; while (*pos != '\0' && *pos != '\n' && *pos != '\r') pos++; int lineLen = (int)(pos - lineStart); if (lineLen > 0) { const char* sep = NULL; for (const char* c = lineStart; c < lineStart + lineLen; c++) { if (*c == '=') { sep = c; break; } } if (sep) { char key[MAX_PATH_LEN] = { 0 }; int keyLen = (int)(sep - lineStart); if (keyLen >= MAX_PATH_LEN) keyLen = MAX_PATH_LEN - 1; memcpy(key, lineStart, keyLen); int valLen = (int)((lineStart + lineLen) - (sep + 1)); char val[32] = { 0 }; if (valLen > 0 && valLen < 32) { memcpy(val, sep + 1, valLen); } HashCacheAdd(cache, key, val); } } while (*pos == '\n' || *pos == '\r') pos++; } } static void SaveHashCache(const char* cachePath, const HashCache* cache) { long bufSize = MAX_HASH_ENTRIES * (MAX_PATH_LEN + 32 + 2); char* buffer = (char*)ArenaPush(bufSize); int offset = 0; for (int i = 0; i < cache->Count; i++) { int written = sprintf_s(buffer + offset, bufSize - offset, "%s=%s\n", cache->Entries[i].Path, cache->Entries[i].HashStr); if (written > 0) offset += written; } WriteEntireFile(cachePath, buffer, offset); } // ============================================================================ // Clean #pragma message lines and compute hash for a unity file // ============================================================================ typedef struct { char HashStr[32]; bool WasCleaned; } CleanResult; static CleanResult CleanAndHashUnityFile(const char* filePath) { CleanResult result = { { 0 }, false }; FileContent content = ReadEntireFile(filePath); if (!content.Data) return result; if (StringContains(content.Data, "#pragma message")) { char* filtered = (char*)ArenaPush(content.Size + 1); long filteredSize = 0; const char* pos = content.Data; while (*pos != '\0') { const char* lineStart = pos; while (*pos != '\0' && *pos != '\n') pos++; bool skipLine = false; for (const char* c = lineStart; c < pos; c++) { if (c + 15 <= content.Data + content.Size && memcmp(c, "#pragma message", 15) == 0) { skipLine = true; break; } } if (!skipLine) { long lineLen = (long)(pos - lineStart); memcpy(filtered + filteredSize, lineStart, lineLen); filteredSize += lineLen; if (*pos == '\n') filtered[filteredSize++] = '\n'; } if (*pos == '\n') pos++; } filtered[filteredSize] = '\0'; WriteEntireFile(filePath, filtered, filteredSize); uint64_t hashVal = ComputeFnv1aHash(filtered, filteredSize); sprintf_s(result.HashStr, sizeof(result.HashStr), "%llu", hashVal); result.WasCleaned = true; } else { uint64_t hashVal = ComputeFnv1aHash(content.Data, content.Size); sprintf_s(result.HashStr, sizeof(result.HashStr), "%llu", hashVal); } return result; } // ============================================================================ // Recursive directory scan // ============================================================================ static int64_t GetNewestSourceTimeRecursive(const char* dirPath) { int64_t newest = 0; if (!DirectoryExists(dirPath)) return newest; char searchPath[MAX_PATH_LEN]; snprintf(searchPath, sizeof(searchPath), "%s\\*", dirPath); WIN32_FIND_DATAA findData; HANDLE hFind = FindFirstFileA(searchPath, &findData); if (hFind == INVALID_HANDLE_VALUE) return newest; do { if (findData.cFileName[0] == '.' && (findData.cFileName[1] == '\0' || (findData.cFileName[1] == '.' && findData.cFileName[2] == '\0'))) { continue; } char fullPath[MAX_PATH_LEN]; snprintf(fullPath, sizeof(fullPath), "%s\\%s", dirPath, findData.cFileName); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { int64_t subNewest = GetNewestSourceTimeRecursive(fullPath); if (subNewest > newest) newest = subNewest; } else { if (EndsWithNoCase(findData.cFileName, ".cpp") || EndsWithNoCase(findData.cFileName, ".h") || EndsWithNoCase(findData.cFileName, ".hpp") || EndsWithNoCase(findData.cFileName, ".inl") || EndsWithNoCase(findData.cFileName, ".c")) { ULARGE_INTEGER li; li.LowPart = findData.ftLastWriteTime.dwLowDateTime; li.HighPart = findData.ftLastWriteTime.dwHighDateTime; int64_t ft = (int64_t)li.QuadPart; if (ft > newest) newest = ft; } } } while (FindNextFileA(hFind, &findData)); FindClose(hFind); return newest; } // ============================================================================ // Collect unity files // ============================================================================ typedef struct { char (*Paths)[MAX_PATH_LEN]; int Count; } FilePathList; static FilePathList* CreateFilePathList() { FilePathList* list = (FilePathList*)ArenaPush(sizeof(FilePathList)); list->Paths = (char(*)[MAX_PATH_LEN])ArenaPush(MAX_FILES * MAX_PATH_LEN); list->Count = 0; return list; } static void FilePathListAdd(FilePathList* list, const char* path) { if (list->Count < MAX_FILES) { strncpy_s(list->Paths[list->Count], MAX_PATH_LEN, path, _TRUNCATE); list->Count++; } } static void CollectUnityFiles(const char* dirPath, FilePathList* list) { char searchPath[MAX_PATH_LEN]; snprintf(searchPath, sizeof(searchPath), "%s\\*", dirPath); WIN32_FIND_DATAA findData; HANDLE hFind = FindFirstFileA(searchPath, &findData); if (hFind == INVALID_HANDLE_VALUE) return; do { if (findData.cFileName[0] == '.' && (findData.cFileName[1] == '\0' || (findData.cFileName[1] == '.' && findData.cFileName[2] == '\0'))) { continue; } char fullPath[MAX_PATH_LEN]; snprintf(fullPath, sizeof(fullPath), "%s\\%s", dirPath, findData.cFileName); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { CollectUnityFiles(fullPath, list); } else { if (StringContains(findData.cFileName, "_Unity") && EndsWithNoCase(findData.cFileName, ".cpp")) { FilePathListAdd(list, fullPath); } } } while (FindNextFileA(hFind, &findData)); FindClose(hFind); } // ============================================================================ // Commands // ============================================================================ typedef struct { char StepName[256]; char OutputFile[MAX_PATH_LEN]; char Depends[MAX_PATH_LEN]; char Command[MAX_FILES]; } BuildStep; typedef struct { char Name[64]; char FastBuildTargets[512]; BuildStep Steps[32]; int StepCount; } BuildConfig; static int ExecuteCmd(const char* cmdLine) { STARTUPINFOA si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi = { 0 }; DWORD exitCode = 1; char* cmdBuf = (char*)ArenaPush(MAX_FILES); strcpy_s(cmdBuf, MAX_FILES, cmdLine); if (CreateProcessA(NULL, cmdBuf, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, &exitCode); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { printf("[ERROR] Failed to execute command.\n"); return 1; } return (int)exitCode; } typedef struct { char Path[MAX_PATH_LEN]; int64_t Time; } DirCacheEntry; static DirCacheEntry g_DirCache[64]; static int g_DirCacheCount = 0; static int64_t GetDirTime(const char* dir) { for (int k = 0; k < g_DirCacheCount; k++) { if (_stricmp(g_DirCache[k].Path, dir) == 0) return g_DirCache[k].Time; } int64_t t = GetNewestSourceTimeRecursive(dir); if (g_DirCacheCount < 64) { strncpy_s(g_DirCache[g_DirCacheCount].Path, MAX_PATH_LEN, dir, _TRUNCATE); g_DirCache[g_DirCacheCount].Time = t; g_DirCacheCount++; } return t; } static char g_TimingsBuf[MAX_FILES * 2]; static int g_TimingsLen = 0; static void AppendTiming(const char* stepName, double secs) { g_TimingsLen += sprintf_s(g_TimingsBuf + g_TimingsLen, sizeof(g_TimingsBuf) - g_TimingsLen, "%s|%.3fs\n", stepName, secs); } static int CommandRunPlan(int argc, char* argv[]) { if (argc < 3) { printf("[ERROR] Usage: build_system run_plan \n"); return 1; } const char* planFile = argv[2]; FileContent planContent = ReadEntireFile(planFile); if (!planContent.Data) { printf("[ERROR] Could not read build plan: %s\n", planFile); return 1; } EnsureDirectoryExists("Intermediate"); DeleteFileA("Intermediate\\step_timings.tmp"); DeleteFileA("Intermediate\\built_outputs.tmp"); LARGE_INTEGER perfStart, perfEnd, perfFreq; QueryPerformanceCounter(&perfStart); QueryPerformanceFrequency(&perfFreq); // Parse plan file BuildConfig configs[4]; memset(configs, 0, sizeof(configs)); int configCount = 0; const char* pos = planContent.Data; while (*pos != '\0') { const char* lineStart = pos; while (*pos != '\0' && *pos != '\n' && *pos != '\r') pos++; int lineLen = (int)(pos - lineStart); if (lineLen > 0) { if (lineLen > 7 && memcmp(lineStart, "CONFIG ", 7) == 0) { if (configCount < 4) { int nLen = lineLen - 7; if (nLen >= (int)sizeof(configs[configCount].Name)) nLen = (int)sizeof(configs[configCount].Name) - 1; memcpy(configs[configCount].Name, lineStart + 7, nLen); configs[configCount].Name[nLen] = '\0'; configCount++; } } else if (configCount > 0) { BuildConfig* curCfg = &configs[configCount - 1]; if (lineLen > 10 && memcmp(lineStart, "FASTBUILD ", 10) == 0) { int nLen = lineLen - 10; if (nLen >= (int)sizeof(curCfg->FastBuildTargets)) nLen = (int)sizeof(curCfg->FastBuildTargets) - 1; memcpy(curCfg->FastBuildTargets, lineStart + 10, nLen); curCfg->FastBuildTargets[nLen] = '\0'; } else if (lineLen > 5 && memcmp(lineStart, "STEP ", 5) == 0) { if (curCfg->StepCount < 32) { int nLen = lineLen - 5; if (nLen >= (int)sizeof(curCfg->Steps[curCfg->StepCount].StepName)) nLen = (int)sizeof(curCfg->Steps[curCfg->StepCount].StepName) - 1; memcpy(curCfg->Steps[curCfg->StepCount].StepName, lineStart + 5, nLen); curCfg->Steps[curCfg->StepCount].StepName[nLen] = '\0'; curCfg->StepCount++; } } else if (lineLen > 7 && memcmp(lineStart, "OUTPUT ", 7) == 0 && curCfg->StepCount > 0) { int nLen = lineLen - 7; if (nLen >= (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].OutputFile)) nLen = (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].OutputFile) - 1; memcpy(curCfg->Steps[curCfg->StepCount - 1].OutputFile, lineStart + 7, nLen); curCfg->Steps[curCfg->StepCount - 1].OutputFile[nLen] = '\0'; } else if (lineLen > 8 && memcmp(lineStart, "DEPENDS ", 8) == 0 && curCfg->StepCount > 0) { int nLen = lineLen - 8; if (nLen >= (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].Depends)) nLen = (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].Depends) - 1; memcpy(curCfg->Steps[curCfg->StepCount - 1].Depends, lineStart + 8, nLen); curCfg->Steps[curCfg->StepCount - 1].Depends[nLen] = '\0'; } else if (lineLen > 8 && memcmp(lineStart, "COMMAND ", 8) == 0 && curCfg->StepCount > 0) { int nLen = lineLen - 8; if (nLen >= (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].Command)) nLen = (int)sizeof(curCfg->Steps[curCfg->StepCount - 1].Command) - 1; memcpy(curCfg->Steps[curCfg->StepCount - 1].Command, lineStart + 8, nLen); curCfg->Steps[curCfg->StepCount - 1].Command[nLen] = '\0'; } } } while (*pos == '\n' || *pos == '\r') pos++; } bool anyBuilt = false; for (int i = 0; i < configCount; i++) { BuildConfig* cfg = &configs[i]; printf("\n==============================================================================\n"); printf("Generating Unity Files into Intermediate/ via FASTBuild [%s]...\n", cfg->Name); printf("==============================================================================\n"); if (cfg->FastBuildTargets[0] != '\0') { char fbuildCmd[MAX_PATH_LEN]; sprintf_s(fbuildCmd, sizeof(fbuildCmd), "misc\\fbuild.exe %s", cfg->FastBuildTargets); LARGE_INTEGER tStart, tEnd; QueryPerformanceCounter(&tStart); int res = ExecuteCmd(fbuildCmd); QueryPerformanceCounter(&tEnd); char stepName[MAX_PATH_LEN]; sprintf_s(stepName, sizeof(stepName), "FASTBuild Unity Gen [%s]", cfg->Name); AppendTiming(stepName, (double)(tEnd.QuadPart - tStart.QuadPart) / (double)perfFreq.QuadPart); if (res != 0) { printf("[BUILD FAILED] FASTBuild failed for %s.\n", cfg->Name); return res; } } HashCache* oldCache = CreateHashCache(); LoadHashCache("Intermediate\\.unity_hashes_cache", oldCache); HashCache* newCache = CreateHashCache(); bool unityChanged = false; FilePathList* unityFiles = CreateFilePathList(); CollectUnityFiles("Intermediate", unityFiles); if (unityFiles->Count == 0) { unityChanged = true; } else { for (int k = 0; k < unityFiles->Count; k++) { CleanResult cr = CleanAndHashUnityFile(unityFiles->Paths[k]); HashCacheAdd(newCache, unityFiles->Paths[k], cr.HashStr); const char* oldHash = HashCacheFind(oldCache, unityFiles->Paths[k]); if (oldHash == NULL || strcmp(oldHash, cr.HashStr) != 0) { unityChanged = true; } } } SaveHashCache("Intermediate\\.unity_hashes_cache", newCache); if (unityChanged) { printf("[FASTBuild] Unity structure or source file list changed.\n"); } else { printf("[FASTBuild] No Unity file structure changes detected [up-to-date].\n"); } printf("\n==============================================================================\n"); printf("Compiling Targets [%s]...\n", cfg->Name); printf("==============================================================================\n"); g_DirCacheCount = 0; bool anyNeedBuild = false; for (int j = 0; j < cfg->StepCount; j++) { BuildStep* step = &cfg->Steps[j]; int64_t maxTime = 0; const char* cur = step->Depends; while (*cur) { const char* nextPipe = strchr(cur, '|'); char dirPath[MAX_PATH_LEN] = { 0 }; if (nextPipe) { int dLen = (int)(nextPipe - cur); if (dLen >= (int)sizeof(dirPath)) dLen = (int)sizeof(dirPath) - 1; memcpy(dirPath, cur, dLen); dirPath[dLen] = '\0'; cur = nextPipe + 1; } else { strcpy_s(dirPath, sizeof(dirPath), cur); cur += strlen(cur); } if (dirPath[0] != '\0') { int64_t t = GetDirTime(dirPath); if (t > maxTime) maxTime = t; } } bool needCompile = unityChanged; if (!needCompile) { if (!FileExists(step->OutputFile)) { needCompile = true; } else { int64_t outTime = GetFileModTimeInt64(step->OutputFile); if (outTime < maxTime) needCompile = true; } } printf("\n--- %s ---\n", step->StepName); if (!needCompile) { printf("[SKIP / UP-TO-DATE] %s [Binary up to date]\n", step->OutputFile); } else { printf("[REBUILDING] Compiling %s...\n", step->OutputFile); anyNeedBuild = true; LARGE_INTEGER tStart, tEnd; QueryPerformanceCounter(&tStart); int res = ExecuteCmd(step->Command); QueryPerformanceCounter(&tEnd); AppendTiming(step->StepName, (double)(tEnd.QuadPart - tStart.QuadPart) / (double)perfFreq.QuadPart); if (res != 0) { printf("[BUILD FAILED] Command failed with exit code %d.\n", res); return res; } char builtOut[MAX_PATH_LEN + 2]; sprintf_s(builtOut, sizeof(builtOut), "%s\n", step->OutputFile); HANDLE hFile = CreateFileA("Intermediate\\built_outputs.tmp", FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD written = 0; WriteFile(hFile, builtOut, (DWORD)strlen(builtOut), &written, NULL); CloseHandle(hFile); } anyBuilt = true; } } if (!anyNeedBuild) { printf("\n[SKIP / UP-TO-DATE] All requested targets in [%s] are up-to-date.\n", cfg->Name); } } if (g_TimingsLen > 0) { WriteEntireFile("Intermediate\\step_timings.tmp", g_TimingsBuf, g_TimingsLen); } QueryPerformanceCounter(&perfEnd); double elapsedSeconds = (double)(perfEnd.QuadPart - perfStart.QuadPart) / (double)perfFreq.QuadPart; char elapsedBuf[64]; if (elapsedSeconds < 60.0) { sprintf_s(elapsedBuf, sizeof(elapsedBuf), "%.3fs", elapsedSeconds); } else { int totalMs = (int)(elapsedSeconds * 1000.0); int hours = totalMs / 3600000; int minutes = (totalMs % 3600000) / 60000; int seconds = (totalMs % 60000) / 1000; int ms = totalMs % 1000; sprintf_s(elapsedBuf, sizeof(elapsedBuf), "%02d:%02d:%02d.%03d", hours, minutes, seconds, ms); } printf("\n==============================================================================\n"); printf("[BUILD SUMMARY]\n"); printf("==============================================================================\n"); if (g_TimingsLen > 0) { printf("Step Timings:\n"); const char* pos = g_TimingsBuf; while (*pos) { const char* lineStart = pos; while (*pos && *pos != '\n') pos++; int len = (int)(pos - lineStart); if (len > 0) { const char* pipe = strchr(lineStart, '|'); if (pipe && pipe < pos) { char stepName[MAX_PATH_LEN] = { 0 }; int nameLen = (int)(pipe - lineStart); if (nameLen >= MAX_PATH_LEN) nameLen = MAX_PATH_LEN - 1; memcpy(stepName, lineStart, nameLen); char durStr[64] = { 0 }; int durLen = (int)(pos - (pipe + 1)); if (durLen >= 64) durLen = 63; memcpy(durStr, pipe + 1, durLen); printf(" - %-42s : %s\n", stepName, durStr); } } if (*pos == '\n') pos++; } printf("------------------------------------------------------------------------------\n"); } printf("Total Duration : %s\n", elapsedBuf); printf("Output Binaries:\n"); if (anyBuilt) { FileContent outputsContent = ReadEntireFile("Intermediate\\built_outputs.tmp"); if (outputsContent.Data) { const char* pos = outputsContent.Data; while (*pos) { const char* lineStart = pos; while (*pos && *pos != '\n' && *pos != '\r') pos++; int len = (int)(pos - lineStart); if (len > 0) { char filePath[MAX_PATH_LEN] = { 0 }; if (len >= MAX_PATH_LEN) len = MAX_PATH_LEN - 1; memcpy(filePath, lineStart, len); WIN32_FILE_ATTRIBUTE_DATA fileData; if (GetFileAttributesExA(filePath, GetFileExInfoStandard, &fileData)) { ULARGE_INTEGER fileSize; fileSize.LowPart = fileData.nFileSizeLow; fileSize.HighPart = fileData.nFileSizeHigh; double sizeMB = (double)fileSize.QuadPart / (1024.0 * 1024.0); printf(" - %s (%.2f MB)\n", filePath, sizeMB); } } while (*pos == '\n' || *pos == '\r') pos++; } } DeleteFileA("Intermediate\\built_outputs.tmp"); } else { printf(" - None built in this session (all requested targets up-to-date)\n"); } printf("==============================================================================\n"); printf("[ALL BUILDS SUCCEEDED] All requested targets processed successfully.\n"); printf("==============================================================================\n"); fflush(stdout); return 0; } int main(int argc, char* argv[]) { // 256 MB Bump allocator ArenaInit(256 * 1024 * 1024); if (argc < 2) { printf("Usage: build_system [args...]\n"); printf("Commands:\n"); printf(" run_plan Run the build from a generated plan file\n"); printf(" check_alive Returns 0 immediately to test execution\n"); return 1; } const char* cmd = argv[1]; if (strcmp(cmd, "check_alive") == 0) return 0; if (strcmp(cmd, "run_plan") == 0) return CommandRunPlan(argc, argv); printf("[ERROR] Unknown command: %s\n", cmd); return 1; }