Fixing adaptive incremental build.
Moved more functions into engine.cpp
This commit is contained in:
+104
-83
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user