diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..b0602b7 --- /dev/null +++ b/.clangd @@ -0,0 +1,23 @@ +CompileFlags: + Add: + - --driver-mode=cl + - -Wno-unknown-pragmas + - -Wno-unused-command-line-argument + Remove: + - /Yu* + - /Yc* + - /Fp* + - /MP + - /Zi + - /Gw + - /Gy + - /Bt+ + - /EHa- + +Diagnostics: + UnusedIncludes: None + +InlayHints: + Enabled: Yes + ParameterNames: Yes + DeducedTypes: Yes diff --git a/.gitignore b/.gitignore index 8f3ff79..90cf82b 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ launch_*.txt *.log misc/agent_error.log misc/agent_output_ship.log + +# Clangd +compile_commands.json +.cache/ diff --git a/misc/Generate.bat b/misc/Generate.bat index a08c313..22a019e 100644 --- a/misc/Generate.bat +++ b/misc/Generate.bat @@ -76,8 +76,9 @@ if %ERRORLEVEL% neq 0 ( exit /b %ERRORLEVEL% ) -echo Solutions generated successfully: +echo Solutions and LSP configuration generated successfully: echo - Juliet.sln echo - Romeo.sln echo - JulietShaderCompiler.sln +echo - compile_commands.json exit /b 0 \ No newline at end of file diff --git a/misc/build.bat b/misc/build.bat index 5d965e2..c02d2f8 100644 --- a/misc/build.bat +++ b/misc/build.bat @@ -33,6 +33,11 @@ if /i "%~1"=="gen_vs" ( set GEN_VS_PLAN=%~2 goto do_compile_build_tool ) +if /i "%~1"=="gen_compile_commands" ( + set COMMAND_GEN_COMPILE_COMMANDS=1 + set GEN_COMPILE_COMMANDS_PLAN=%~2 + goto do_compile_build_tool +) if /i "%~1"=="clean" ( echo [BUILD] Cleaning output directories... if exist Intermediate rmdir /s /q Intermediate @@ -118,6 +123,12 @@ if "!COMMAND_GEN_VS!"=="1" ( exit /b !ERRORLEVEL! ) +if "!COMMAND_GEN_COMPILE_COMMANDS!"=="1" ( + echo [BUILD] Generating compile_commands.json... + "%BUILD_TOOL%" gen_compile_commands "!GEN_COMPILE_COMMANDS_PLAN!" + exit /b !ERRORLEVEL! +) + echo [BUILD] Generating Build Plan... set PLAN_FILE=Intermediate\build_plan.txt if exist "!PLAN_FILE!" del /f /q "!PLAN_FILE!" diff --git a/misc/shell.bat b/misc/shell.bat index 0a7a8a8..c3700b3 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -4,5 +4,5 @@ pushd . call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 popd -set path=W:\Classified\Juliet\misc;%path% +set path=%~dp0;%path% set _NO_DEBUG_HEAP=1 \ No newline at end of file diff --git a/tools/build_system.c b/tools/build_system.c index fdb2b11..6e339b3 100644 --- a/tools/build_system.c +++ b/tools/build_system.c @@ -1551,163 +1551,608 @@ static void GenerateDeterministicGuid(const char* input, char* outputGuid) { hash2 & 0xFFFFFFFFFFFFULL); } -static int CommandGenVS(int argc, char* argv[]) +static void clean_include_path(const char* in_path, char* out_path, size_t out_size) { - if (argc < 3) { - printf("[ERROR] Usage: build_system gen_vs \n"); - return 1; - } - const char* planFile = argv[2]; - FileContent planContent = ReadEntireFile(planFile); - if (!planContent.Data) { - printf("[ERROR] Could not read VS plan: %s\n", planFile); - return 1; - } - - char globalInclude[1024] = {0}; - char globalDefCommon[1024] = {0}; - char globalDefDebug[1024] = {0}; - char globalDefProfile[1024] = {0}; - char globalDefRelease[1024] = {0}; - VSProject projects[16]; - int projCount = 0; - - VSSolution solutions[8]; - int slnCount = 0; + assert(in_path != NULL); + assert(out_path != NULL); + assert(out_size > 0); - const char* pos = planContent.Data; - while (*pos) { - const char* lineStart = pos; - while (*pos && *pos != '\n' && *pos != '\r') pos++; - int lineLen = (int)(pos - lineStart); - - if (lineLen > 0) { - if (lineLen > 15 && memcmp(lineStart, "GLOBAL_INCLUDE ", 15) == 0) { - int nLen = lineLen - 15; - if (nLen >= 1024) nLen = 1023; - memcpy(globalInclude, lineStart + 15, nLen); - globalInclude[nLen] = '\0'; + const char* p = in_path; + const char* sln_prefix_win = "$(SolutionDir)..\\"; + const char* sln_prefix_fwd = "$(SolutionDir)../"; + + if (strncmp(p, sln_prefix_win, 17) == 0 || strncmp(p, sln_prefix_fwd, 17) == 0) + { + p += 17; + } + else if (strncmp(p, "..\\", 3) == 0 || strncmp(p, "../", 3) == 0) + { + p += 3; + } + + while (*p == '\\' || *p == '/') + { + p++; + } + + size_t j = 0; + for (size_t i = 0; p[i] != '\0' && j + 1 < out_size; i++) + { + if (p[i] == '\\') + { + out_path[j++] = '/'; + } + else + { + out_path[j++] = p[i]; + } + } + out_path[j] = '\0'; + + while (j > 0 && out_path[j - 1] == '/') + { + out_path[--j] = '\0'; + } +} + +static void parse_semicolon_tokens(const char* input, char tokens[32][256], int* count) +{ + assert(tokens != NULL); + assert(count != NULL); + + *count = 0; + if (input == NULL || input[0] == '\0') + { + return; + } + + const char* start = input; + while (*start != '\0') + { + while (*start == ' ' || *start == '\t' || *start == ';') + { + start++; + } + if (*start == '\0') + { + break; + } + + const char* end = start; + while (*end != '\0' && *end != ';') + { + end++; + } + + int len = (int)(end - start); + while (len > 0 && (start[len - 1] == ' ' || start[len - 1] == '\t' || start[len - 1] == '\r' || start[len - 1] == '\n')) + { + len--; + } + + if (len > 0 && *count < 32) + { + int copy_len = len < 255 ? len : 255; + memcpy(tokens[*count], start, copy_len); + tokens[*count][copy_len] = '\0'; + (*count)++; + } + + start = end; + } +} + +static void generate_compile_commands( + const VSProject* projects, + int proj_count, + const char* global_include, + const char* def_common, + const char* def_debug) +{ + assert(projects != NULL); + assert(proj_count >= 0); + assert(global_include != NULL); + + char cwd[MAX_PATH_LEN]; + HANDLE dir_handle = CreateFileA(".", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (dir_handle != INVALID_HANDLE_VALUE) + { + char real_path[MAX_PATH_LEN]; + DWORD len = GetFinalPathNameByHandleA(dir_handle, real_path, MAX_PATH_LEN, FILE_NAME_NORMALIZED); + CloseHandle(dir_handle); + if (len > 0 && len < MAX_PATH_LEN) + { + const char* p = real_path; + if (strncmp(p, "\\\\?\\", 4) == 0) + { + p += 4; } - else if (lineLen > 11 && memcmp(lineStart, "DEF_COMMON ", 11) == 0) { - int nLen = lineLen - 11; - if (nLen >= 1024) nLen = 1023; - memcpy(globalDefCommon, lineStart + 11, nLen); - globalDefCommon[nLen] = '\0'; + strncpy_s(cwd, sizeof(cwd), p, _TRUNCATE); + } + else + { + GetCurrentDirectoryA((DWORD)sizeof(cwd), cwd); + } + } + else + { + GetCurrentDirectoryA((DWORD)sizeof(cwd), cwd); + } + + for (int i = 0; cwd[i] != '\0'; i++) + { + if (cwd[i] == '\\') + { + cwd[i] = '/'; + } + } + size_t cwd_len = strlen(cwd); + while (cwd_len > 0 && cwd[cwd_len - 1] == '/') + { + cwd[--cwd_len] = '\0'; + } + + size_t buffer_size = 8 * 1024 * 1024; + char* json_buf = (char*)ArenaPush(buffer_size); + int offset = 0; + + offset += sprintf_s(json_buf + offset, buffer_size - offset, "[\n"); + + bool is_first_entry = true; + + for (int i = 0; i < proj_count; i++) + { + const VSProject* p = &projects[i]; + const char* inc_str = p->IncludeSearch[0] != '\0' ? p->IncludeSearch : global_include; + + char inc_tokens[32][256]; + int inc_count = 0; + parse_semicolon_tokens(inc_str, inc_tokens, &inc_count); + + char def_common_tokens[32][256]; + int def_common_count = 0; + parse_semicolon_tokens(def_common, def_common_tokens, &def_common_count); + + char def_debug_tokens[32][256]; + int def_debug_count = 0; + parse_semicolon_tokens(def_debug, def_debug_tokens, &def_debug_count); + + char def_extra_tokens[32][256]; + int def_extra_count = 0; + parse_semicolon_tokens(p->ExtraDef, def_extra_tokens, &def_extra_count); + + bool has_imgui_api = false; + for (int d = 0; d < def_extra_count; d++) + { + if (strncmp(def_extra_tokens[d], "IMGUI_API", 9) == 0) + { + has_imgui_api = true; + break; } - else if (lineLen > 10 && memcmp(lineStart, "DEF_DEBUG ", 10) == 0) { - int nLen = lineLen - 10; - if (nLen >= 1024) nLen = 1023; - memcpy(globalDefDebug, lineStart + 10, nLen); - globalDefDebug[nLen] = '\0'; + } + + FilePathList* src_files = CreateFilePathList(); + if (p->SrcCount > 0) + { + for (int s = 0; s < p->SrcCount; s++) + { + if (EndsWithNoCase(p->Srcs[s], ".cpp") || EndsWithNoCase(p->Srcs[s], ".c")) + { + FilePathListAdd(src_files, p->Srcs[s]); + } } - else if (lineLen > 12 && memcmp(lineStart, "DEF_PROFILE ", 12) == 0) { - int nLen = lineLen - 12; - if (nLen >= 1024) nLen = 1023; - memcpy(globalDefProfile, lineStart + 12, nLen); - globalDefProfile[nLen] = '\0'; + } + else + { + CollectFiles(p->Dir, NULL, ".cpp", src_files); + CollectFiles(p->Dir, NULL, ".c", src_files); + } + + for (int f = 0; f < src_files->Count; f++) + { + const char* raw_path = src_files->Paths[f]; + char clean_rel[MAX_PATH_LEN]; + clean_include_path(raw_path, clean_rel, sizeof(clean_rel)); + + char full_path[MAX_PATH_LEN]; + if ((clean_rel[1] == ':' && clean_rel[2] == '/') || (clean_rel[0] == '/')) + { + strncpy_s(full_path, sizeof(full_path), clean_rel, _TRUNCATE); } - else if (lineLen > 12 && memcmp(lineStart, "DEF_RELEASE ", 12) == 0) { - int nLen = lineLen - 12; - if (nLen >= 1024) nLen = 1023; - memcpy(globalDefRelease, lineStart + 12, nLen); - globalDefRelease[nLen] = '\0'; + else + { + snprintf(full_path, sizeof(full_path), "%s/%s", cwd, clean_rel); } - else if (lineLen > 8 && memcmp(lineStart, "PROJECT ", 8) == 0) { - if (projCount < 16) { - VSProject* p = &projects[projCount++]; + + if (!is_first_entry) + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, ",\n"); + } + is_first_entry = false; + + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " {\n" + " \"directory\": \"%s\",\n" + " \"file\": \"%s\",\n" + " \"arguments\": [\n" + " \"clang-cl.exe\",\n" + " \"--driver-mode=cl\",\n" + " \"/nologo\",\n" + " \"/std:c++20\",\n" + " \"/W4\",\n" + " \"/utf-8\",\n", + cwd, full_path); + + for (int d = 0; d < def_common_count; d++) + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/D%s\",\n", def_common_tokens[d]); + } + + for (int d = 0; d < def_debug_count; d++) + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/D%s\",\n", def_debug_tokens[d]); + } + + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/DIMGUI_DEFINE_MATH_OPERATORS\",\n"); + + for (int d = 0; d < def_extra_count; d++) + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/D%s\",\n", def_extra_tokens[d]); + } + + if (!has_imgui_api) + { + if (strcmp(p->Name, "Juliet") == 0 || strcmp(p->Name, "ImGui") == 0) + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/DIMGUI_API=__declspec(dllexport)\",\n"); + } + else + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/DIMGUI_API=__declspec(dllimport)\",\n"); + } + } + + for (int c = 0; c < inc_count; c++) + { + char clean_inc[MAX_PATH_LEN]; + clean_include_path(inc_tokens[c], clean_inc, sizeof(clean_inc)); + if (clean_inc[0] != '\0') + { + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/I%s\",\n", clean_inc); + } + } + + if (p->PchHeader[0] != '\0') + { + char clean_pch[MAX_PATH_LEN]; + clean_include_path(p->PchHeader, clean_pch, sizeof(clean_pch)); + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"/FI%s\",\n", clean_pch); + } + + offset += sprintf_s(json_buf + offset, buffer_size - offset, + " \"-c\",\n" + " \"%s\"\n" + " ]\n" + " }", + full_path); + } + } + + offset += sprintf_s(json_buf + offset, buffer_size - offset, "\n]\n"); + + WriteEntireFile("compile_commands.json", json_buf, offset); + printf("Generated compile_commands.json\n"); +} + +static bool parse_vs_plan( + const char* plan_file, + char* global_include, + size_t global_include_size, + char* global_def_common, + size_t global_def_common_size, + char* global_def_debug, + size_t global_def_debug_size, + char* global_def_profile, + size_t global_def_profile_size, + char* global_def_release, + size_t global_def_release_size, + VSProject* projects, + int* proj_count, + VSSolution* solutions, + int* sln_count) +{ + assert(plan_file != NULL); + assert(projects != NULL); + assert(proj_count != NULL); + + FileContent plan_content = ReadEntireFile(plan_file); + if (!plan_content.Data) + { + printf("[ERROR] Could not read VS plan: %s\n", plan_file); + return false; + } + + *proj_count = 0; + if (sln_count != NULL) + { + *sln_count = 0; + } + + const char* pos = plan_content.Data; + while (*pos != '\0') + { + const char* line_start = pos; + while (*pos != '\0' && *pos != '\n' && *pos != '\r') + { + pos++; + } + int line_len = (int)(pos - line_start); + + if (line_len > 0) + { + if (line_len > 15 && memcmp(line_start, "GLOBAL_INCLUDE ", 15) == 0 && global_include != NULL) + { + int n_len = line_len - 15; + if ((size_t)n_len >= global_include_size) + { + n_len = (int)global_include_size - 1; + } + memcpy(global_include, line_start + 15, n_len); + global_include[n_len] = '\0'; + } + else if (line_len > 11 && memcmp(line_start, "DEF_COMMON ", 11) == 0 && global_def_common != NULL) + { + int n_len = line_len - 11; + if ((size_t)n_len >= global_def_common_size) + { + n_len = (int)global_def_common_size - 1; + } + memcpy(global_def_common, line_start + 11, n_len); + global_def_common[n_len] = '\0'; + } + else if (line_len > 10 && memcmp(line_start, "DEF_DEBUG ", 10) == 0 && global_def_debug != NULL) + { + int n_len = line_len - 10; + if ((size_t)n_len >= global_def_debug_size) + { + n_len = (int)global_def_debug_size - 1; + } + memcpy(global_def_debug, line_start + 10, n_len); + global_def_debug[n_len] = '\0'; + } + else if (line_len > 12 && memcmp(line_start, "DEF_PROFILE ", 12) == 0 && global_def_profile != NULL) + { + int n_len = line_len - 12; + if ((size_t)n_len >= global_def_profile_size) + { + n_len = (int)global_def_profile_size - 1; + } + memcpy(global_def_profile, line_start + 12, n_len); + global_def_profile[n_len] = '\0'; + } + else if (line_len > 12 && memcmp(line_start, "DEF_RELEASE ", 12) == 0 && global_def_release != NULL) + { + int n_len = line_len - 12; + if ((size_t)n_len >= global_def_release_size) + { + n_len = (int)global_def_release_size - 1; + } + memcpy(global_def_release, line_start + 12, n_len); + global_def_release[n_len] = '\0'; + } + else if (line_len > 8 && memcmp(line_start, "PROJECT ", 8) == 0) + { + if (*proj_count < 16) + { + VSProject* p = &projects[(*proj_count)++]; memset(p, 0, sizeof(VSProject)); - char lineBuf[1024]; - int nLen = lineLen - 8; - if (nLen >= 1024) nLen = 1023; - memcpy(lineBuf, lineStart + 8, nLen); - lineBuf[nLen] = '\0'; - int scanned = sscanf_s(lineBuf, "%s %s %s", p->Name, (unsigned)_countof(p->Name), p->Dir, (unsigned)_countof(p->Dir), p->Guid, (unsigned)_countof(p->Guid)); - if (scanned < 2) { + char line_buf[1024]; + int n_len = line_len - 8; + if (n_len >= 1024) + { + n_len = 1023; + } + memcpy(line_buf, line_start + 8, n_len); + line_buf[n_len] = '\0'; + int scanned = sscanf_s(line_buf, "%s %s %s", p->Name, (unsigned)_countof(p->Name), p->Dir, (unsigned)_countof(p->Dir), p->Guid, (unsigned)_countof(p->Guid)); + if (scanned < 2) + { strcpy_s(p->Dir, sizeof(p->Dir), p->Name); } - if (scanned < 3) { + if (scanned < 3) + { GenerateDeterministicGuid(p->Name, p->Guid); } } } - else if (lineLen > 12 && memcmp(lineStart, "PROJECT_SRC ", 12) == 0 && projCount > 0) { - VSProject* p = &projects[projCount - 1]; - if (p->SrcCount < 64) { - int nLen = lineLen - 12; - if (nLen >= 256) nLen = 255; - memcpy(p->Srcs[p->SrcCount], lineStart + 12, nLen); - p->Srcs[p->SrcCount][nLen] = '\0'; + else if (line_len > 12 && memcmp(line_start, "PROJECT_SRC ", 12) == 0 && *proj_count > 0) + { + VSProject* p = &projects[*proj_count - 1]; + if (p->SrcCount < 64) + { + int n_len = line_len - 12; + if (n_len >= 256) + { + n_len = 255; + } + memcpy(p->Srcs[p->SrcCount], line_start + 12, n_len); + p->Srcs[p->SrcCount][n_len] = '\0'; p->SrcCount++; } } - else if (lineLen > 10 && memcmp(lineStart, "EXTRA_DEF ", 10) == 0 && projCount > 0) { - VSProject* p = &projects[projCount - 1]; - int nLen = lineLen - 10; - if (nLen >= 1024) nLen = 1023; - memcpy(p->ExtraDef, lineStart + 10, nLen); - p->ExtraDef[nLen] = '\0'; + else if (line_len > 10 && memcmp(line_start, "EXTRA_DEF ", 10) == 0 && *proj_count > 0) + { + VSProject* p = &projects[*proj_count - 1]; + int n_len = line_len - 10; + if (n_len >= 1024) + { + n_len = 1023; + } + memcpy(p->ExtraDef, line_start + 10, n_len); + p->ExtraDef[n_len] = '\0'; } - else if (lineLen > 15 && memcmp(lineStart, "INCLUDE_SEARCH ", 15) == 0 && projCount > 0) { - VSProject* p = &projects[projCount - 1]; - int nLen = lineLen - 15; - if (nLen >= 1024) nLen = 1023; - memcpy(p->IncludeSearch, lineStart + 15, nLen); - p->IncludeSearch[nLen] = '\0'; + else if (line_len > 15 && memcmp(line_start, "INCLUDE_SEARCH ", 15) == 0 && *proj_count > 0) + { + VSProject* p = &projects[*proj_count - 1]; + int n_len = line_len - 15; + if (n_len >= 1024) + { + n_len = 1023; + } + memcpy(p->IncludeSearch, line_start + 15, n_len); + p->IncludeSearch[n_len] = '\0'; } - else if (lineLen > 10 && memcmp(lineStart, "DEBUG_CMD ", 10) == 0 && projCount > 0) { - VSProject* p = &projects[projCount - 1]; - int nLen = lineLen - 10; - if (nLen >= 1024) nLen = 1023; - memcpy(p->DebugCmd, lineStart + 10, nLen); - p->DebugCmd[nLen] = '\0'; + else if (line_len > 10 && memcmp(line_start, "DEBUG_CMD ", 10) == 0 && *proj_count > 0) + { + VSProject* p = &projects[*proj_count - 1]; + int n_len = line_len - 10; + if (n_len >= 1024) + { + n_len = 1023; + } + memcpy(p->DebugCmd, line_start + 10, n_len); + p->DebugCmd[n_len] = '\0'; } - else if (lineLen > 4 && memcmp(lineStart, "PCH ", 4) == 0 && projCount > 0) { - VSProject* p = &projects[projCount - 1]; - char lineBuf[1024]; - int nLen = lineLen - 4; - if (nLen >= 1024) nLen = 1023; - memcpy(lineBuf, lineStart + 4, nLen); - lineBuf[nLen] = '\0'; - sscanf_s(lineBuf, "%s %s", p->PchHeader, (unsigned)_countof(p->PchHeader), p->PchSrc, (unsigned)_countof(p->PchSrc)); + else if (line_len > 4 && memcmp(line_start, "PCH ", 4) == 0 && *proj_count > 0) + { + VSProject* p = &projects[*proj_count - 1]; + char line_buf[1024]; + int n_len = line_len - 4; + if (n_len >= 1024) + { + n_len = 1023; + } + memcpy(line_buf, line_start + 4, n_len); + line_buf[n_len] = '\0'; + sscanf_s(line_buf, "%s %s", p->PchHeader, (unsigned)_countof(p->PchHeader), p->PchSrc, (unsigned)_countof(p->PchSrc)); } - else if (lineLen > 9 && memcmp(lineStart, "SOLUTION ", 9) == 0) { - if (slnCount < 8) { - VSSolution* s = &solutions[slnCount++]; + else if (line_len > 9 && memcmp(line_start, "SOLUTION ", 9) == 0 && solutions != NULL && sln_count != NULL) + { + if (*sln_count < 8) + { + VSSolution* s = &solutions[(*sln_count)++]; memset(s, 0, sizeof(VSSolution)); - int nLen = lineLen - 9; - if (nLen >= 64) nLen = 63; - memcpy(s->Name, lineStart + 9, nLen); - s->Name[nLen] = '\0'; + int n_len = line_len - 9; + if (n_len >= 64) + { + n_len = 63; + } + memcpy(s->Name, line_start + 9, n_len); + s->Name[n_len] = '\0'; } } - else if (lineLen > 9 && memcmp(lineStart, "SLN_PROJ ", 9) == 0 && slnCount > 0) { - VSSolution* s = &solutions[slnCount - 1]; - if (s->ProjCount < 16) { - int nLen = lineLen - 9; - if (nLen >= 64) nLen = 63; - memcpy(s->Projects[s->ProjCount], lineStart + 9, nLen); - s->Projects[s->ProjCount][nLen] = '\0'; + else if (line_len > 9 && memcmp(line_start, "SLN_PROJ ", 9) == 0 && sln_count != NULL && *sln_count > 0 && solutions != NULL) + { + VSSolution* s = &solutions[*sln_count - 1]; + if (s->ProjCount < 16) + { + int n_len = line_len - 9; + if (n_len >= 64) + { + n_len = 63; + } + memcpy(s->Projects[s->ProjCount], line_start + 9, n_len); + s->Projects[s->ProjCount][n_len] = '\0'; s->ProjCount++; } } } - while (*pos == '\n' || *pos == '\r') pos++; + while (*pos == '\n' || *pos == '\r') + { + pos++; + } } - for (int i = 0; i < projCount; i++) { + return true; +} + +static int CommandGenCompileCommands(int argc, char* argv[]) +{ + if (argc < 3) + { + printf("[ERROR] Usage: build_system gen_compile_commands \n"); + return 1; + } + const char* plan_file = argv[2]; + char global_include[1024] = {0}; + char global_def_common[1024] = {0}; + char global_def_debug[1024] = {0}; + char global_def_profile[1024] = {0}; + char global_def_release[1024] = {0}; + VSProject projects[16]; + int proj_count = 0; + + if (!parse_vs_plan(plan_file, + global_include, sizeof(global_include), + global_def_common, sizeof(global_def_common), + global_def_debug, sizeof(global_def_debug), + global_def_profile, sizeof(global_def_profile), + global_def_release, sizeof(global_def_release), + projects, &proj_count, NULL, NULL)) + { + return 1; + } + + generate_compile_commands(projects, proj_count, global_include, global_def_common, global_def_debug); + return 0; +} + +static int CommandGenVS(int argc, char* argv[]) +{ + if (argc < 3) + { + printf("[ERROR] Usage: build_system gen_vs \n"); + return 1; + } + const char* plan_file = argv[2]; + char global_include[1024] = {0}; + char global_def_common[1024] = {0}; + char global_def_debug[1024] = {0}; + char global_def_profile[1024] = {0}; + char global_def_release[1024] = {0}; + VSProject projects[16]; + int proj_count = 0; + VSSolution solutions[8]; + int sln_count = 0; + + if (!parse_vs_plan(plan_file, + global_include, sizeof(global_include), + global_def_common, sizeof(global_def_common), + global_def_debug, sizeof(global_def_debug), + global_def_profile, sizeof(global_def_profile), + global_def_release, sizeof(global_def_release), + projects, &proj_count, solutions, &sln_count)) + { + return 1; + } + + for (int i = 0; i < proj_count; i++) + { VSProject* p = &projects[i]; - const char* incs = p->IncludeSearch[0] != '\0' ? p->IncludeSearch : globalInclude; - GenerateProject(p->Name, p->Dir, p->Guid, incs, p->ExtraDef, p->DebugCmd, p->Srcs, p->SrcCount, globalDefCommon, globalDefDebug, globalDefProfile, globalDefRelease, p->PchHeader, p->PchSrc); + const char* incs = p->IncludeSearch[0] != '\0' ? p->IncludeSearch : global_include; + GenerateProject(p->Name, p->Dir, p->Guid, incs, p->ExtraDef, p->DebugCmd, p->Srcs, p->SrcCount, global_def_common, global_def_debug, global_def_profile, global_def_release, p->PchHeader, p->PchSrc); } - for (int i = 0; i < slnCount; i++) { + for (int i = 0; i < sln_count; i++) + { VSSolution* s = &solutions[i]; const char* pNames[16]; const char* pGuids[16]; - for (int j = 0; j < s->ProjCount; j++) { + 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) { + for (int k = 0; k < proj_count; k++) + { + if (strcmp(projects[k].Name, s->Projects[j]) == 0) + { pGuids[j] = projects[k].Guid; break; } @@ -1715,7 +2160,8 @@ static int CommandGenVS(int argc, char* argv[]) } GenerateSolution(s->Name, pNames, pGuids, s->ProjCount); } - + + generate_compile_commands(projects, proj_count, global_include, global_def_common, global_def_debug); return 0; } @@ -1728,9 +2174,10 @@ int main(int argc, char* argv[]) { printf("Usage: build_system [args...]\n"); printf("Commands:\n"); - printf(" run_plan Run the build from a generated plan file\n"); - printf(" gen_vs Generate Visual Studio project and solution files\n"); - printf(" check_alive Returns 0 immediately to test execution\n"); + printf(" run_plan Run the build from a generated plan file\n"); + printf(" gen_vs Generate Visual Studio project, solution, and compile_commands files\n"); + printf(" gen_compile_commands Generate compile_commands.json for Neovim/clangd\n"); + printf(" check_alive Returns 0 immediately to test execution\n"); return 1; } @@ -1738,6 +2185,7 @@ int main(int argc, char* argv[]) if (strcmp(cmd, "check_alive") == 0) return 0; if (strcmp(cmd, "run_plan") == 0) return CommandRunPlan(argc, argv); if (strcmp(cmd, "gen_vs") == 0) return CommandGenVS(argc, argv); + if (strcmp(cmd, "gen_compile_commands") == 0) return CommandGenCompileCommands(argc, argv); printf("[ERROR] Unknown command: %s\n", cmd); return 1;