Fixing adaptive incremental build.

Moved more functions into engine.cpp
This commit is contained in:
2026-08-09 15:48:53 -04:00
parent 7c18804df6
commit 697579e72f
10 changed files with 149 additions and 159 deletions
+104 -83
View File
@@ -284,6 +284,11 @@ typedef struct
int MaxFiles;
} UnityConfig;
static int CompareFilePaths(const void* a, const void* b)
{
return strcmp((const char*)a, (const char*)b);
}
static bool GenerateUnityFiles(const UnityConfig* ucfg)
{
FilePathList* srcFiles = CreateFilePathList();
@@ -299,6 +304,9 @@ static bool GenerateUnityFiles(const UnityConfig* ucfg)
}
}
// Sort files to ensure deterministic index
qsort(srcFiles->Paths, srcFiles->Count, MAX_PATH_LEN, CompareFilePaths);
EnsureDirectoryExists(ucfg->OutputDir);
SYSTEMTIME st;
@@ -311,91 +319,99 @@ static bool GenerateUnityFiles(const UnityConfig* ucfg)
int64_t nowTime = liNow.QuadPart;
int64_t oneHour = 3600LL * 10000000LL;
FilePathList* coldFiles = CreateFilePathList();
FilePathList* hotFiles = CreateFilePathList();
for (int i = 0; i < srcFiles->Count; i++)
{
if (StringContains(srcFiles->Paths[i], "_Unity")) continue;
int64_t fileTime = GetFileModTimeInt64(srcFiles->Paths[i]);
if (nowTime - fileTime < oneHour) {
FilePathListAdd(hotFiles, srcFiles->Paths[i]);
} else {
FilePathListAdd(coldFiles, srcFiles->Paths[i]);
}
}
int unityCount = 1;
bool anyChanged = false;
char currentUnityPath[MAX_PATH_LEN];
char* memBuf = (char*)ArenaPush(1024 * 1024);
// Process cold files (MaxFiles)
int fileIdx = 0;
int memOffset = 0;
int64_t maxIncludedTime = 0;
for (int i = 0; i < coldFiles->Count; i++)
// Determine hot/cold for each file
bool* isHot = (bool*)ArenaPush(srcFiles->Count * sizeof(bool));
for (int i = 0; i < srcFiles->Count; i++)
{
char absPath[MAX_PATH_LEN];
GetFullPathNameA(coldFiles->Paths[i], MAX_PATH_LEN, absPath, NULL);
for (int k = 0; absPath[k]; k++) { if (absPath[k] == '\\') absPath[k] = '/'; }
memOffset += sprintf_s(memBuf + memOffset, (1024 * 1024) - memOffset, "#include \"%s\"\n", absPath);
fileIdx++;
int64_t fileTime = GetFileModTimeInt64(coldFiles->Paths[i]);
if (fileTime > maxIncludedTime) maxIncludedTime = fileTime;
if (fileIdx >= ucfg->MaxFiles || i == coldFiles->Count - 1)
{
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s%d.cpp", ucfg->OutputDir, ucfg->Prefix, unityCount);
FileContent exist = ReadEntireFile(currentUnityPath);
bool write = true;
if (exist.Data && exist.Size == memOffset && memcmp(exist.Data, memBuf, memOffset) == 0) write = false;
if (write) { WriteEntireFile(currentUnityPath, memBuf, memOffset); anyChanged = true; }
// Touch Unity file if included files are newer
if (!write) {
int64_t unityTime = GetFileModTimeInt64(currentUnityPath);
if (maxIncludedTime > unityTime) {
HANDLE hFile = CreateFileA(currentUnityPath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
FILETIME ft;
ULARGE_INTEGER li;
li.QuadPart = nowTime;
ft.dwLowDateTime = li.LowPart;
ft.dwHighDateTime = li.HighPart;
SetFileTime(hFile, NULL, NULL, &ft);
CloseHandle(hFile);
}
}
}
unityCount++;
fileIdx = 0;
memOffset = 0;
maxIncludedTime = 0;
isHot[i] = false;
if (StringContains(srcFiles->Paths[i], "_Unity")) continue;
int64_t fileTime = GetFileModTimeInt64(srcFiles->Paths[i]);
if (nowTime - fileTime < oneHour) {
isHot[i] = true;
}
}
// Process hot files (Standalone)
for (int i = 0; i < hotFiles->Count; i++)
int numBuckets = (srcFiles->Count + ucfg->MaxFiles - 1) / ucfg->MaxFiles;
if (numBuckets == 0) numBuckets = 1; // Always at least one bucket
for (int b = 0; b < numBuckets; b++)
{
int startIdx = b * ucfg->MaxFiles;
int endIdx = startIdx + ucfg->MaxFiles;
if (endIdx > srcFiles->Count) endIdx = srcFiles->Count;
int memOffset = 0;
int64_t maxIncludedTime = 0;
int coldCount = 0;
for (int i = startIdx; i < endIdx; i++)
{
if (isHot[i]) continue;
if (StringContains(srcFiles->Paths[i], "_Unity")) continue;
char absPath[MAX_PATH_LEN];
GetFullPathNameA(srcFiles->Paths[i], MAX_PATH_LEN, absPath, NULL);
for (int k = 0; absPath[k]; k++) { if (absPath[k] == '\\') absPath[k] = '/'; }
memOffset += sprintf_s(memBuf + memOffset, (1024 * 1024) - memOffset, "#include \"%s\"\n", absPath);
coldCount++;
int64_t fileTime = GetFileModTimeInt64(srcFiles->Paths[i]);
if (fileTime > maxIncludedTime) maxIncludedTime = fileTime;
}
if (coldCount == 0)
{
memOffset += sprintf_s(memBuf + memOffset, (1024 * 1024) - memOffset, "// Empty bucket\n");
}
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s%d.cpp", ucfg->OutputDir, ucfg->Prefix, b + 1);
FileContent exist = ReadEntireFile(currentUnityPath);
bool write = true;
if (exist.Data && exist.Size == memOffset && memcmp(exist.Data, memBuf, memOffset) == 0) write = false;
if (write) { WriteEntireFile(currentUnityPath, memBuf, memOffset); anyChanged = true; }
// Touch Unity file if included files are newer
if (!write && coldCount > 0) {
int64_t unityTime = GetFileModTimeInt64(currentUnityPath);
if (maxIncludedTime > unityTime) {
HANDLE hFile = CreateFileA(currentUnityPath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
FILETIME ft;
ULARGE_INTEGER li;
li.QuadPart = nowTime;
ft.dwLowDateTime = li.LowPart;
ft.dwHighDateTime = li.HighPart;
SetFileTime(hFile, NULL, NULL, &ft);
CloseHandle(hFile);
}
}
}
}
// Process hot files
for (int i = 0; i < srcFiles->Count; i++)
{
if (!isHot[i]) continue;
char absPath[MAX_PATH_LEN];
GetFullPathNameA(hotFiles->Paths[i], MAX_PATH_LEN, absPath, NULL);
GetFullPathNameA(srcFiles->Paths[i], MAX_PATH_LEN, absPath, NULL);
for (int k = 0; absPath[k]; k++) { if (absPath[k] == '\\') absPath[k] = '/'; }
memOffset = sprintf_s(memBuf, (1024 * 1024), "#include \"%s\"\n", absPath);
int memOffset = sprintf_s(memBuf, (1024 * 1024), "#include \"%s\"\n", absPath);
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s%d.cpp", ucfg->OutputDir, ucfg->Prefix, unityCount);
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s_Hot%d.cpp", ucfg->OutputDir, ucfg->Prefix, i);
FileContent exist = ReadEntireFile(currentUnityPath);
bool write = true;
if (exist.Data && exist.Size == memOffset && memcmp(exist.Data, memBuf, memOffset) == 0) write = false;
if (write) { WriteEntireFile(currentUnityPath, memBuf, memOffset); anyChanged = true; }
if (!write) {
int64_t fileTime = GetFileModTimeInt64(hotFiles->Paths[i]);
int64_t fileTime = GetFileModTimeInt64(srcFiles->Paths[i]);
int64_t unityTime = GetFileModTimeInt64(currentUnityPath);
if (fileTime > unityTime) {
HANDLE hFile = CreateFileA(currentUnityPath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -410,21 +426,32 @@ static bool GenerateUnityFiles(const UnityConfig* ucfg)
}
}
}
unityCount++;
}
// Delete remaining old unity files
while (true)
// Cleanup unused cold buckets
for (int b = numBuckets; b < numBuckets + 100; b++)
{
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s%d.cpp", ucfg->OutputDir, ucfg->Prefix, unityCount);
if (FileExists(currentUnityPath))
{
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s%d.cpp", ucfg->OutputDir, ucfg->Prefix, b + 1);
if (FileExists(currentUnityPath)) {
DeleteFileA(currentUnityPath);
anyChanged = true;
unityCount++;
} else {
break;
}
}
// Cleanup unused hot files
for (int i = 0; i < srcFiles->Count + 1000; i++)
{
bool shouldExist = (i < srcFiles->Count && isHot[i]);
if (!shouldExist)
{
snprintf(currentUnityPath, sizeof(currentUnityPath), "%s\\%s_Hot%d.cpp", ucfg->OutputDir, ucfg->Prefix, i);
if (FileExists(currentUnityPath)) {
DeleteFileA(currentUnityPath);
anyChanged = true;
}
}
else break;
}
return anyChanged;
@@ -1135,10 +1162,6 @@ static void GenerateProject(const char* projectName, const char* projectDir, con
int projectDirLen = (int)strlen(projectDir);
for (int i = 0; i < srcFiles->Count; i++) {
const char* path = srcFiles->Paths[i];
const char* relPath = path;
if (strncmp(path, projectDir, projectDirLen) == 0 && (path[projectDirLen] == '\\' || path[projectDirLen] == '/')) {
relPath = path + projectDirLen + 1;
}
if (EndsWithNoCase(path, ".cpp") || EndsWithNoCase(path, ".c")) {
offset += sprintf_s(buf + offset, (4 * 1024 * 1024) - offset, " <ClCompile Include=\"..\\%s\" />\n", path);
@@ -1258,7 +1281,7 @@ static void GenerateProject(const char* projectName, const char* projectDir, con
printf("Generated %s\n", filterOutPath);
}
static void GenerateSolution(const char* slnName, const char** projectNames, const char** projectDirs, const char** projectGuids, int projCount)
static void GenerateSolution(const char* slnName, const char** projectNames, const char** projectGuids, int projCount)
{
char* buf = (char*)ArenaPush(4 * 1024 * 1024);
int offset = 0;
@@ -1501,19 +1524,17 @@ static int CommandGenVS(int argc, char* argv[])
for (int i = 0; i < slnCount; i++) {
VSSolution* s = &solutions[i];
const char* pNames[16];
const char* pDirs[16];
const char* pGuids[16];
for (int j = 0; j < s->ProjCount; j++) {
pNames[j] = s->Projects[j];
for (int k = 0; k < projCount; k++) {
if (strcmp(projects[k].Name, s->Projects[j]) == 0) {
pDirs[j] = projects[k].Dir;
pGuids[j] = projects[k].Guid;
break;
}
}
}
GenerateSolution(s->Name, pNames, pDirs, pGuids, s->ProjCount);
GenerateSolution(s->Name, pNames, pGuids, s->ProjCount);
}
return 0;