#define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #define Assert(c) do { if (!(c)) { fprintf(stderr, "Assertion failed: %s at %s:%d\n", #c, __FILE__, __LINE__); abort(); } } while(0) typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; typedef int32_t int32; typedef int64_t int64; typedef struct FileNode { char path[512]; struct FileNode* next; } FileNode; typedef struct FileList { FileNode* first; FileNode* last; int32 count; } FileList; typedef struct ProjectInfo { char name[64]; char guid[64]; FileList c_files; FileList h_files; } ProjectInfo; static void file_list_push(FileList* list, const char* path) { Assert(list != NULL); Assert(path != NULL); FileNode* node = (FileNode*)malloc(sizeof(FileNode)); Assert(node != NULL); strncpy_s(node->path, sizeof(node->path), path, _TRUNCATE); node->next = NULL; if (list->last == NULL) { list->first = node; list->last = node; } else { list->last->next = node; list->last = node; } list->count++; } static void file_list_free(FileList* list) { Assert(list != NULL); FileNode* curr = list->first; while (curr != NULL) { FileNode* next = curr->next; free(curr); curr = next; } list->first = NULL; list->last = NULL; list->count = 0; } static void normalize_path_slashes_forward(char* path) { Assert(path != NULL); for (int32 i = 0; path[i] != '\0'; i++) { if (path[i] == '\\') { path[i] = '/'; } } } static void normalize_path_slashes_backward(char* path) { Assert(path != NULL); for (int32 i = 0; path[i] != '\0'; i++) { if (path[i] == '/') { path[i] = '\\'; } } } static bool str_ends_with_no_case(const char* str, const char* suffix) { Assert(str != NULL); Assert(suffix != NULL); size_t str_len = strlen(str); size_t suffix_len = strlen(suffix); if (suffix_len > str_len) { return false; } const char* p = str + (str_len - suffix_len); return (_stricmp(p, suffix) == 0); } static bool is_c_source_file(const char* path) { Assert(path != NULL); return str_ends_with_no_case(path, ".c") || str_ends_with_no_case(path, ".cpp"); } static bool is_c_header_file(const char* path) { Assert(path != NULL); return str_ends_with_no_case(path, ".h") || str_ends_with_no_case(path, ".hpp"); } static void find_files_recursive(const char* dir_path, FileList* out_list, bool include_headers) { Assert(dir_path != NULL); Assert(out_list != NULL); char search_pattern[512]; snprintf(search_pattern, sizeof(search_pattern), "%s\\*", dir_path); WIN32_FIND_DATAA find_data; HANDLE find_handle = FindFirstFileA(search_pattern, &find_data); if (find_handle == INVALID_HANDLE_VALUE) { return; } do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } char item_path[512]; snprintf(item_path, sizeof(item_path), "%s\\%s", dir_path, find_data.cFileName); if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { find_files_recursive(item_path, out_list, include_headers); } else { if (is_c_source_file(item_path) || (include_headers && is_c_header_file(item_path))) { char clean_path[512]; strncpy_s(clean_path, sizeof(clean_path), item_path, _TRUNCATE); normalize_path_slashes_forward(clean_path); file_list_push(out_list, clean_path); } } } while (FindNextFileA(find_handle, &find_data)); FindClose(find_handle); } static void generate_deterministic_guid(const char* input, char* out_guid, size_t out_guid_size) { Assert(input != NULL); Assert(out_guid != NULL); Assert(out_guid_size >= 37); uint64 hash1 = 14695981039346656037ULL; uint64 hash2 = 1099511628211ULL; for (const char* c = input; *c != '\0'; ++c) { hash1 ^= (uint64)(*c); hash1 *= 1099511628211ULL; hash2 ^= (uint64)(*c); hash2 *= 137ULL; } snprintf(out_guid, out_guid_size, "%08X-%04X-%04X-%04X-%012llX", (uint32)(hash1 >> 32), (uint32)((hash1 >> 16) & 0xFFFF), (uint32)(hash1 & 0xFFFF), (uint32)((hash2 >> 48) & 0xFFFF), (uint64)(hash2 & 0xFFFFFFFFFFFFULL)); } static bool write_entire_file(const char* path, const char* buffer, size_t size) { Assert(path != NULL); Assert(buffer != NULL); HANDLE file_handle = CreateFileA(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { fprintf(stderr, "[ERROR] Could not open file for writing: %s\n", path); return false; } DWORD bytes_written = 0; BOOL success = WriteFile(file_handle, buffer, (DWORD)size, &bytes_written, NULL); CloseHandle(file_handle); return (success && bytes_written == (DWORD)size); } static void get_normalized_cwd(char* out_cwd, size_t out_cwd_size, char* out_real_cwd, size_t out_real_size) { Assert(out_cwd != NULL); Assert(out_cwd_size > 0); GetCurrentDirectoryA((DWORD)out_cwd_size, out_cwd); normalize_path_slashes_forward(out_cwd); if (out_real_cwd != NULL && out_real_size > 0) { out_real_cwd[0] = '\0'; 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[512]; DWORD len = GetFinalPathNameByHandleA(dir_handle, real_path, sizeof(real_path), FILE_NAME_NORMALIZED); CloseHandle(dir_handle); if (len > 0 && len < sizeof(real_path)) { const char* p = real_path; if (strncmp(p, "\\\\?\\", 4) == 0) { p += 4; } strncpy_s(out_real_cwd, out_real_size, p, _TRUNCATE); normalize_path_slashes_forward(out_real_cwd); } } } } static int32 get_layer_order_from_main(char layers[32][64]) { int32 count = 0; FILE* file = fopen("Juliet/appbin/appbin_main.c", "rb"); if (file != NULL) { char line[512]; while (fgets(line, sizeof(line), file) != NULL) { char* inc_pos = strstr(line, "#include"); if (inc_pos != NULL) { char* quote_start = strchr(inc_pos, '"'); if (quote_start != NULL) { quote_start++; char* slash = strchr(quote_start, '/'); if (slash == NULL) { slash = strchr(quote_start, '\\'); } if (slash != NULL) { size_t layer_len = (size_t)(slash - quote_start); if (layer_len > 0 && layer_len < 64) { char layer_name[64]; memcpy(layer_name, quote_start, layer_len); layer_name[layer_len] = '\0'; char* quote_end = strchr(slash, '"'); if (quote_end != NULL && strstr(slash, "_inc.h") != NULL) { bool already_added = false; for (int32 i = 0; i < count; ++i) { if (_stricmp(layers[i], layer_name) == 0) { already_added = true; break; } } if (!already_added && count < 32) { strncpy_s(layers[count], sizeof(layers[count]), layer_name, _TRUNCATE); count++; } } } } } } } fclose(file); } // Always ensure "core" is present bool has_core = false; for (int32 i = 0; i < count; ++i) { if (_stricmp(layers[i], "core") == 0) { has_core = true; break; } } if (!has_core && count < 32) { for (int32 i = count; i > 0; --i) { strncpy_s(layers[i], sizeof(layers[i]), layers[i - 1], _TRUNCATE); } strncpy_s(layers[0], sizeof(layers[0]), "core", _TRUNCATE); count++; } // Discover any other directories in Juliet/ that have _inc.h but weren't in appbin_main.c yet WIN32_FIND_DATAA find_data; HANDLE find_handle = FindFirstFileA("Juliet\\*", &find_data); if (find_handle != INVALID_HANDLE_VALUE) { do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (_stricmp(find_data.cFileName, "src") == 0 || _stricmp(find_data.cFileName, "include") == 0 || _stricmp(find_data.cFileName, "appbin") == 0) { continue; } bool already = false; for (int32 i = 0; i < count; ++i) { if (_stricmp(layers[i], find_data.cFileName) == 0) { already = true; break; } } if (!already && count < 32) { strncpy_s(layers[count], sizeof(layers[count]), find_data.cFileName, _TRUNCATE); count++; } } } while (FindNextFileA(find_handle, &find_data)); FindClose(find_handle); } return count; } static int32 find_layer_index_for_file(const char* file_path, char layers[32][64], int32 layer_count) { Assert(file_path != NULL); if (strstr(file_path, "win32/core") != NULL || strstr(file_path, "win32\\core") != NULL) { for (int32 i = 0; i < layer_count; ++i) { if (_stricmp(layers[i], "core") == 0) { return i; } } } for (int32 i = 0; i < layer_count; ++i) { char prefix1[128]; char prefix2[128]; snprintf(prefix1, sizeof(prefix1), "Juliet/%s/", layers[i]); snprintf(prefix2, sizeof(prefix2), "Juliet\\%s\\", layers[i]); if (_strnicmp(file_path, prefix1, strlen(prefix1)) == 0 || _strnicmp(file_path, prefix2, strlen(prefix2)) == 0) { return i; } } return -1; } static void get_preceding_includes_from_inc_h(const char* layer_name, const char* target_file_path, char* out_flags, size_t out_flags_size) { Assert(layer_name != NULL); Assert(target_file_path != NULL); Assert(out_flags != NULL); char inc_h_path[512]; snprintf(inc_h_path, sizeof(inc_h_path), "Juliet/%s/%s_inc.h", layer_name, layer_name); FILE* file = fopen(inc_h_path, "rb"); if (file == NULL) { return; } char line[512]; while (fgets(line, sizeof(line), file) != NULL) { char* hash = strchr(line, '#'); if (hash == NULL) { continue; } char* inc_pos = strstr(hash, "include"); if (inc_pos == NULL) { continue; } char* quote_start = strchr(inc_pos, '"'); if (quote_start == NULL) { continue; } quote_start++; char* quote_end = strchr(quote_start, '"'); if (quote_end == NULL) { continue; } size_t inc_file_len = (size_t)(quote_end - quote_start); if (inc_file_len == 0 || inc_file_len >= 256) { continue; } char inc_file[256]; memcpy(inc_file, quote_start, inc_file_len); inc_file[inc_file_len] = '\0'; normalize_path_slashes_forward(inc_file); char full_inc_path[512]; if (strstr(inc_file, "/") != NULL) { snprintf(full_inc_path, sizeof(full_inc_path), "Juliet/%s", inc_file); } else { snprintf(full_inc_path, sizeof(full_inc_path), "Juliet/%s/%s", layer_name, inc_file); } normalize_path_slashes_forward(full_inc_path); char expected_suffix[256]; snprintf(expected_suffix, sizeof(expected_suffix), "/%s", inc_file); if (_stricmp(full_inc_path, target_file_path) == 0 || str_ends_with_no_case(target_file_path, expected_suffix)) { break; } char flag[512]; if (strstr(inc_file, "/") != NULL) { snprintf(flag, sizeof(flag), " \"/FI%s\",\n", inc_file); } else { snprintf(flag, sizeof(flag), " \"/FI%s/%s\",\n", layer_name, inc_file); } strncat_s(out_flags, out_flags_size, flag, _TRUNCATE); } fclose(file); } static void generate_compile_commands(void) { char cwd[512]; char real_cwd[512]; get_normalized_cwd(cwd, sizeof(cwd), real_cwd, sizeof(real_cwd)); size_t buf_cap = 8 * 1024 * 1024; char* json_buf = (char*)malloc(buf_cap); Assert(json_buf != NULL); int32 offset = 0; offset += snprintf(json_buf + offset, buf_cap - offset, "[\n"); bool is_first = true; char layers[32][64]; int32 layer_count = get_layer_order_from_main(layers); // 1. Scan new Juliet modules (core, render, ui, appbin, etc.) WIN32_FIND_DATAA find_data; HANDLE find_handle = FindFirstFileA("Juliet\\*", &find_data); if (find_handle != INVALID_HANDLE_VALUE) { do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (_stricmp(find_data.cFileName, "src") == 0 || _stricmp(find_data.cFileName, "include") == 0) { continue; } char module_dir[512]; snprintf(module_dir, sizeof(module_dir), "Juliet\\%s", find_data.cFileName); FileList module_files = {0}; find_files_recursive(module_dir, &module_files, true); for (FileNode* node = module_files.first; node != NULL; node = node->next) { char forced_inc_flags[1024] = ""; int32 file_layer_idx = find_layer_index_for_file(node->path, layers, layer_count); if (file_layer_idx >= 0 && _stricmp(layers[file_layer_idx], "appbin") != 0) { for (int32 i = 0; i <= file_layer_idx; ++i) { if (i < file_layer_idx) { char inc_disk_path[512]; snprintf(inc_disk_path, sizeof(inc_disk_path), "Juliet\\%s\\%s_inc.h", layers[i], layers[i]); if (GetFileAttributesA(inc_disk_path) != INVALID_FILE_ATTRIBUTES) { char flag[128]; snprintf(flag, sizeof(flag), " \"/FI%s/%s_inc.h\",\n", layers[i], layers[i]); strncat_s(forced_inc_flags, sizeof(forced_inc_flags), flag, _TRUNCATE); } } else { if (is_c_source_file(node->path)) { char inc_disk_path[512]; snprintf(inc_disk_path, sizeof(inc_disk_path), "Juliet\\%s\\%s_inc.h", layers[i], layers[i]); if (GetFileAttributesA(inc_disk_path) != INVALID_FILE_ATTRIBUTES) { char flag[128]; snprintf(flag, sizeof(flag), " \"/FI%s/%s_inc.h\",\n", layers[i], layers[i]); strncat_s(forced_inc_flags, sizeof(forced_inc_flags), flag, _TRUNCATE); } } else if (!str_ends_with_no_case(node->path, "_inc.h")) { get_preceding_includes_from_inc_h(layers[i], node->path, forced_inc_flags, sizeof(forced_inc_flags)); } } } } const char* dirs_to_emit[2] = { cwd, (real_cwd[0] && _stricmp(cwd, real_cwd) != 0) ? real_cwd : NULL }; for (int32 d = 0; d < 2; d++) { if (dirs_to_emit[d] == NULL) { continue; } if (!is_first) { offset += snprintf(json_buf + offset, buf_cap - offset, ",\n"); } is_first = false; char full_file_path[512]; snprintf(full_file_path, sizeof(full_file_path), "%s/%s", dirs_to_emit[d], node->path); offset += snprintf(json_buf + offset, buf_cap - offset, " {\n" " \"directory\": \"%s\",\n" " \"file\": \"%s\",\n" " \"arguments\": [\n" " \"clang-cl.exe\",\n" " \"--driver-mode=cl\",\n" " \"/nologo\",\n" " \"/clang:-std=c11\",\n" " \"/W4\",\n" " \"/Wno-unused-function\",\n" " \"/clang:-Wno-initializer-overrides\",\n" " \"/utf-8\",\n" " \"/DBUILD_DEBUG=1\",\n" " \"/IJuliet\",\n" "%s" " \"-c\",\n" " \"%s\"\n" " ]\n" " }", dirs_to_emit[d], full_file_path, forced_inc_flags, full_file_path); } } file_list_free(&module_files); } } while (FindNextFileA(find_handle, &find_data)); FindClose(find_handle); } // 2. Scan legacy directories so existing files keep working with Neovim LSP const char* legacy_dirs[] = { "Juliet/src", "Game", "JulietApp", "JulietShaderCompiler", "External/imgui" }; for (size_t l = 0; l < sizeof(legacy_dirs) / sizeof(legacy_dirs[0]); l++) { char search_path[512]; strncpy_s(search_path, sizeof(search_path), legacy_dirs[l], _TRUNCATE); normalize_path_slashes_backward(search_path); DWORD attrs = GetFileAttributesA(search_path); if (attrs == INVALID_FILE_ATTRIBUTES || !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { continue; } FileList legacy_files = {0}; find_files_recursive(search_path, &legacy_files, false); for (FileNode* node = legacy_files.first; node != NULL; node = node->next) { const char* dirs_to_emit[2] = { cwd, (real_cwd[0] && _stricmp(cwd, real_cwd) != 0) ? real_cwd : NULL }; for (int32 d = 0; d < 2; d++) { if (dirs_to_emit[d] == NULL) { continue; } if (!is_first) { offset += snprintf(json_buf + offset, buf_cap - offset, ",\n"); } is_first = false; char full_file_path[512]; snprintf(full_file_path, sizeof(full_file_path), "%s/%s", dirs_to_emit[d], node->path); const char* lang_flag = str_ends_with_no_case(full_file_path, ".c") ? "/clang:-std=c99" : "/std:c++20"; offset += snprintf(json_buf + offset, buf_cap - offset, " {\n" " \"directory\": \"%s\",\n" " \"file\": \"%s\",\n" " \"arguments\": [\n" " \"clang-cl.exe\",\n" " \"--driver-mode=cl\",\n" " \"/nologo\",\n" " \"%s\",\n" " \"/W4\",\n" " \"/utf-8\",\n" " \"/DJULIET_WIN32\",\n" " \"/DUNICODE\",\n" " \"/D_UNICODE\",\n" " \"/DWIN32_LEAN_AND_MEAN\",\n" " \"/D_CRT_SECURE_NO_WARNINGS\",\n" " \"/DDEBUG\",\n" " \"/DJULIET_DEBUG\",\n" " \"/DPROFILING_ENABLED\",\n" " \"/DJULIET_ENABLE_IMGUI\",\n" " \"/DIMGUI_DEFINE_MATH_OPERATORS\",\n" " \"/DJULIET_EXPORT\",\n" " \"/DIMGUI_API=__declspec(dllexport)\",\n" " \"/IJuliet/include\",\n" " \"/IJuliet/src\",\n" " \"/IJuliet\",\n" " \"/IGame\",\n" " \"/IExternal/imgui\",\n" " \"/IExternal/imgui/backends\",\n" " \"/FICore/PCH.h\",\n" " \"-c\",\n" " \"%s\"\n" " ]\n" " }", dirs_to_emit[d], full_file_path, lang_flag, full_file_path); } } file_list_free(&legacy_files); } offset += snprintf(json_buf + offset, buf_cap - offset, "\n]\n"); bool success = write_entire_file("compile_commands.json", json_buf, offset); if (success) { printf("[SUCCESS] Generated compile_commands.json\n"); } free(json_buf); } static void generate_vs_files(void) { CreateDirectoryA("vs", NULL); // Discover modules in Juliet/ ProjectInfo appbin_project = {0}; strncpy_s(appbin_project.name, sizeof(appbin_project.name), "appbin", _TRUNCATE); generate_deterministic_guid(appbin_project.name, appbin_project.guid, sizeof(appbin_project.guid)); // Gather all new module files for appbin project WIN32_FIND_DATAA find_data; HANDLE find_handle = FindFirstFileA("Juliet\\*", &find_data); if (find_handle != INVALID_HANDLE_VALUE) { do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (_stricmp(find_data.cFileName, "src") == 0 || _stricmp(find_data.cFileName, "include") == 0) { continue; } char module_dir[512]; snprintf(module_dir, sizeof(module_dir), "Juliet\\%s", find_data.cFileName); FileList all_files = {0}; find_files_recursive(module_dir, &all_files, true); for (FileNode* node = all_files.first; node != NULL; node = node->next) { if (is_c_source_file(node->path)) { file_list_push(&appbin_project.c_files, node->path); } else if (is_c_header_file(node->path)) { file_list_push(&appbin_project.h_files, node->path); } } file_list_free(&all_files); } } while (FindNextFileA(find_handle, &find_data)); FindClose(find_handle); } // 1. Generate vs/appbin.vcxproj size_t buf_cap = 2 * 1024 * 1024; char* vcxproj_buf = (char*)malloc(buf_cap); Assert(vcxproj_buf != NULL); int32 offset = 0; offset += snprintf(vcxproj_buf + offset, buf_cap - offset, "\n" "\n" " \n" " \n" " Debug\n" " x64\n" " \n" " \n" " Release\n" " x64\n" " \n" " \n" " \n" " {%s}\n" " MakeFileProj\n" " appbin\n" " \n" " \n" " \n" " Makefile\n" " true\n" " v143\n" " \n" " \n" " Makefile\n" " false\n" " v143\n" " \n" " \n" " \n" " \n" " cd $(SolutionDir)..\\ & new_build.bat appbin debug\n" " cd $(SolutionDir)..\\ & new_build.bat clean & new_build.bat appbin debug\n" " cd $(SolutionDir)..\\ & new_build.bat clean\n" " $(SolutionDir)..\\build\\appbin.exe\n" " $(SolutionDir)..\\Juliet;$(NMakeIncludeSearchPath)\n" " BUILD_DEBUG=1;$(NMakePreprocessorDefinitions)\n" " \n" " \n" " cd $(SolutionDir)..\\ & new_build.bat appbin release\n" " cd $(SolutionDir)..\\ & new_build.bat clean & new_build.bat appbin release\n" " cd $(SolutionDir)..\\ & new_build.bat clean\n" " $(SolutionDir)..\\build\\appbin.exe\n" " $(SolutionDir)..\\Juliet;$(NMakeIncludeSearchPath)\n" " BUILD_DEBUG=0;$(NMakePreprocessorDefinitions)\n" " \n" " \n", appbin_project.guid); for (FileNode* node = appbin_project.c_files.first; node != NULL; node = node->next) { char backslash_path[512]; strncpy_s(backslash_path, sizeof(backslash_path), node->path, _TRUNCATE); normalize_path_slashes_backward(backslash_path); offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n", backslash_path); } offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n \n"); for (FileNode* node = appbin_project.h_files.first; node != NULL; node = node->next) { char backslash_path[512]; strncpy_s(backslash_path, sizeof(backslash_path), node->path, _TRUNCATE); normalize_path_slashes_backward(backslash_path); offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n", backslash_path); } offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n"); if (GetFileAttributesA("Natvis") != INVALID_FILE_ATTRIBUTES) { offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n" " \n" " \n" " \n"); } offset += snprintf(vcxproj_buf + offset, buf_cap - offset, " \n" "\n"); write_entire_file("vs\\appbin.vcxproj", vcxproj_buf, offset); printf("[SUCCESS] Generated vs\\appbin.vcxproj\n"); free(vcxproj_buf); // 2. Generate vs/appbin.vcxproj.filters char* filters_buf = (char*)malloc(buf_cap); Assert(filters_buf != NULL); int32 f_offset = 0; f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, "\n" "\n" " \n" " \n" " \n" " \n" " \n"); for (FileNode* node = appbin_project.c_files.first; node != NULL; node = node->next) { char backslash_path[512]; strncpy_s(backslash_path, sizeof(backslash_path), node->path, _TRUNCATE); normalize_path_slashes_backward(backslash_path); const char* filter = strstr(node->path, "core/") ? "core" : "appbin"; f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, " \n" " %s\n" " \n", backslash_path, filter); } f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, " \n \n"); for (FileNode* node = appbin_project.h_files.first; node != NULL; node = node->next) { char backslash_path[512]; strncpy_s(backslash_path, sizeof(backslash_path), node->path, _TRUNCATE); normalize_path_slashes_backward(backslash_path); const char* filter = strstr(node->path, "core/") ? "core" : "appbin"; f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, " \n" " %s\n" " \n", backslash_path, filter); } f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, " \n"); if (GetFileAttributesA("Natvis") != INVALID_FILE_ATTRIBUTES) { f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, " \n" " \n" " \n" " \n" " \n" " Natvis\n" " \n" " \n" " Natvis\n" " \n" " \n"); } f_offset += snprintf(filters_buf + f_offset, buf_cap - f_offset, "\n"); write_entire_file("vs\\appbin.vcxproj.filters", filters_buf, f_offset); printf("[SUCCESS] Generated vs\\appbin.vcxproj.filters\n"); free(filters_buf); // 3. Generate vs/new_Juliet.sln char sln_buf[4096]; int32 s_offset = snprintf(sln_buf, sizeof(sln_buf), "Microsoft Visual Studio Solution File, Format Version 12.00\n" "# Visual Studio Version 17\n" "VisualStudioVersion = 17.0.31903.59\n" "MinimumVisualStudioVersion = 10.0.40219.1\n" "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"appbin\", \"appbin.vcxproj\", \"{%s}\"\n" "EndProject\n" "Global\n" " GlobalSection(SolutionConfigurationPlatforms) = preSolution\n" " Debug|x64 = Debug|x64\n" " Release|x64 = Release|x64\n" " EndGlobalSection\n" " GlobalSection(ProjectConfigurationPlatforms) = postSolution\n" " {%s}.Debug|x64.ActiveCfg = Debug|x64\n" " {%s}.Debug|x64.Build.0 = Debug|x64\n" " {%s}.Release|x64.ActiveCfg = Release|x64\n" " {%s}.Release|x64.Build.0 = Release|x64\n" " EndGlobalSection\n" " GlobalSection(SolutionProperties) = preSolution\n" " HideSolutionNode = FALSE\n" " EndGlobalSection\n" "EndGlobal\n", appbin_project.guid, appbin_project.guid, appbin_project.guid, appbin_project.guid, appbin_project.guid); write_entire_file("vs\\new_Juliet.sln", sln_buf, s_offset); printf("[SUCCESS] Generated vs\\new_Juliet.sln\n"); file_list_free(&appbin_project.c_files); file_list_free(&appbin_project.h_files); } int main(int argc, char** argv) { bool do_compile_commands = false; bool do_vs = false; if (argc <= 1) { do_compile_commands = true; do_vs = true; } else { for (int i = 1; i < argc; i++) { if (_stricmp(argv[i], "all") == 0) { do_compile_commands = true; do_vs = true; } else if (_stricmp(argv[i], "compile_commands") == 0 || _stricmp(argv[i], "json") == 0 || _stricmp(argv[i], "lsp") == 0) { do_compile_commands = true; } else if (_stricmp(argv[i], "vs") == 0 || _stricmp(argv[i], "sln") == 0 || _stricmp(argv[i], "vcxproj") == 0) { do_vs = true; } else { printf("[INFO] Unknown argument '%s', generating all by default.\n", argv[i]); do_compile_commands = true; do_vs = true; } } } if (do_compile_commands) { generate_compile_commands(); } if (do_vs) { generate_vs_files(); } return 0; }