From 831a86599ecd6d5ddf3f691fc94cf9c0effda50b Mon Sep 17 00:00:00 2001 From: WalterPlinge <22519813+WalterPlinge@users.noreply.github.com> Date: Wed, 25 May 2022 02:00:13 +0100 Subject: [PATCH 1/4] Add fallback build paths search using environment variables --- src/build_settings.cpp | 219 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 4 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b458d8308..4d560bc00 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1186,7 +1186,201 @@ void init_build_context(TargetMetrics *cross_target) { // NOTE(IC): In order to find Visual C++ paths without relying on environment variables. // NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`. #include "microsoft_craziness.h" -#endif + +// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both +// official and portable installations (like mmozeiko's portable msvc script). This will only use +// the first paths it finds, and won't overwrite any values that `result` already has. +bool find_portable_msvc_installation(gbAllocator allocator, Find_Result_Utf8 *result) { + if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) { + return false; + } + + bool sdk_found = false; + if(result->windows_sdk_root.len > 0 + && result->windows_sdk_um_library_path.len > 0 + && result->windows_sdk_ucrt_library_path.len > 0) { + sdk_found = true; + } + + // We can find windows sdk using the following combination of env vars: + // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion) + if (!sdk_found) { + // These appear to be suitable env vars used by Visual Studio + char const *win_sdk_ver_env = gb_get_env("WindowsSDKVersion", allocator); + char const *win_sdk_lib_env = gb_get_env("WindowsSDKLibVersion", allocator); + char const *win_sdk_dir_env = gb_get_env("WindowsSdkDir", allocator); + char const *crt_sdk_dir_env = gb_get_env("UniversalCRTSdkDir", allocator); + defer (gb_free(allocator, (void*)win_sdk_ver_env)); + defer (gb_free(allocator, (void*)win_sdk_lib_env)); + defer (gb_free(allocator, (void*)win_sdk_dir_env)); + defer (gb_free(allocator, (void*)crt_sdk_dir_env)); + + // NOTE(WalterPlinge): If any combination is found, let's just assume they are correct + if ((win_sdk_ver_env || win_sdk_lib_env) && (win_sdk_dir_env || crt_sdk_dir_env)) { + //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue + String dir = win_sdk_dir_env + ? make_string_c(win_sdk_dir_env) + : make_string_c(crt_sdk_dir_env); + String ver = win_sdk_ver_env + ? make_string_c(win_sdk_ver_env) + : make_string_c(win_sdk_lib_env); + + // These have trailing '\' as we are just composing the path + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("um\\x64\\") + : make_string_c("um\\x86\\"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("ucrt\\x64\\") + : make_string_c("ucrt\\x86\\"); + + result->windows_sdk_root = concatenate3_strings(allocator, dir, make_string_c("Lib\\"), ver); + result->windows_sdk_um_library_path = concatenate_strings(allocator, result->windows_sdk_root, um_dir); + result->windows_sdk_ucrt_library_path = concatenate_strings(allocator, result->windows_sdk_root, ucrt_dir); + + sdk_found = true; + } + } + + // If we haven't found it yet, we can loop through LIB for specific folders + //? This may not be robust enough using `um\x64` and `ucrt\x64` + if (!sdk_found) { + char const *lib_env = gb_get_env("LIB", allocator); + defer (gb_free(allocator, (void*)lib_env)); + if (lib_env) { + String lib = make_string_c(lib_env); + + // NOTE(WalterPlinge): I don't know if there's a chance for the LIB variable + // to be set without a trailing '\' (apart from manually), so we can just + // check paths without it (see use of `String end` in the loop below) + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("um\\x64") + : make_string_c("um\\x86"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("ucrt\\x64") + : make_string_c("ucrt\\x86"); + + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c <= lib.len; c += 1) { + if (c != lib.len && lib[c] != ';') { + continue; + } + hi = c; + String dir = substring(lib, lo, hi); + defer (lo = hi + 1); + + // Remove the last slash so we can match with the strings above + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); + + // Find one and we can make the other + if (string_ends_with(end, um_dir)) { + result->windows_sdk_um_library_path = concatenate_strings(allocator, end, make_string_c("\\")); + break; + } else if (string_ends_with(end, ucrt_dir)) { + result->windows_sdk_ucrt_library_path = concatenate_strings(allocator, end, make_string_c("\\")); + break; + } + } + + // Get the root from the one we found, and make the other + if (result->windows_sdk_um_library_path.len > 0) { + result->windows_sdk_root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); + result->windows_sdk_ucrt_library_path = concatenate3_strings(allocator, result->windows_sdk_root, ucrt_dir, make_string_c("\\")); + } else if (result->windows_sdk_ucrt_library_path.len > 0) { + result->windows_sdk_root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); + result->windows_sdk_um_library_path = concatenate3_strings(allocator, result->windows_sdk_root, um_dir, make_string_c("\\")); + } + + if (result->windows_sdk_root.len > 0) { + sdk_found = true; + } + } + } + + // NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was + // installed using mmozeiko's portable msvc script, which uses the windows 10 sdk. + // This may need to be changed later if it ends up causing problems. + if (sdk_found && result->windows_sdk_version == 0) { + result->windows_sdk_version = 10; + } + + bool vs_found = false; + if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) { + vs_found = true; + } + + // We can find visual studio using VCToolsInstallDir + if (!vs_found) { + char const *vctid_env = gb_get_env("VCToolsInstallDir", allocator); + defer (gb_free(allocator, (void*)vctid_env)); + if (vctid_env) { + String vctid = make_string_c(vctid_env); + String exe = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("bin\\Hostx64\\x64\\") + : make_string_c("bin\\Hostx86\\x86\\"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("lib\\x64\\") + : make_string_c("lib\\x86\\"); + result->vs_exe_path = concatenate_strings(allocator, vctid, exe); + result->vs_library_path = concatenate_strings(allocator, vctid, lib); + vs_found = true; + } + } + + // If we haven't found it yet, we can loop through Path for specific folders + if (!vs_found) { + char const *path_env = gb_get_env("Path", allocator); + defer (gb_free(allocator, (void*)path_env)); + if (path_env) { + String path = make_string_c(path_env); + + String exe = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("bin\\Hostx64\\x64") + : make_string_c("bin\\Hostx86\\x86"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("lib\\x64") + : make_string_c("lib\\x86"); + + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c <= path.len; c += 1) { + if (c != path.len && path[c] != ';') { + continue; + } + + hi = c; + String dir = substring(path, lo, hi); + defer (lo = hi + 1); + + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); + + // check if cl.exe and link.exe exist in this folder + String cl = concatenate_strings(allocator, end, make_string_c("\\cl.exe")); + String link = concatenate_strings(allocator, end, make_string_c("\\link.exe")); + defer (gb_free(allocator, cl.text)); + defer (gb_free(allocator, link.text)); + + if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) { + continue; + } + + String root = substring(end, 0, end.len - exe.len); + + result->vs_exe_path = concatenate_strings(allocator, end, make_string_c("\\")); + result->vs_library_path = concatenate3_strings(allocator, root, lib, make_string_c("\\")); + + vs_found = true; + } + } + } + + return sdk_found && vs_found; +} +#endif defined(GB_SYSTEM_WINDOWS) // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. // We've previously called `parse_build_flags`, so `out_filepath` should be set. @@ -1227,11 +1421,28 @@ bool init_build_paths(String init_filename) { if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) { // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); + bool all_found = + find_result.windows_sdk_root.len > 0 && + find_result.windows_sdk_um_library_path.len > 0 && + find_result.windows_sdk_ucrt_library_path.len > 0 && + find_result.vs_exe_path.len > 0 && + find_result.vs_library_path.len > 0; - if (find_result.windows_sdk_version == 0) { - gb_printf_err("Windows SDK not found.\n"); - return false; + if (find_result.windows_sdk_version == 0 || !all_found) { + if (!find_portable_msvc_installation(ha, &find_result)) { + gb_printf_err("Windows SDK not found.\n"); + return false; + } } +#if 0 + printf("windows_sdk_root: %.*s\n", LIT(find_result.windows_sdk_root)); + printf("windows_sdk_um_library_path: %.*s\n", LIT(find_result.windows_sdk_um_library_path)); + printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(find_result.windows_sdk_ucrt_library_path)); + printf("vs_exe_path: %.*s\n", LIT(find_result.vs_exe_path)); + printf("vs_library_path: %.*s\n", LIT(find_result.vs_library_path)); + + gb_exit(1); +#endif if (find_result.windows_sdk_um_library_path.len > 0) { GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); From 8fcf2f5dca8cfcb8eb114bb9efbd6582ed0a2f9a Mon Sep 17 00:00:00 2001 From: WalterPlinge <22519813+WalterPlinge@users.noreply.github.com> Date: Wed, 25 May 2022 02:10:34 +0100 Subject: [PATCH 2/4] a little cleanup --- src/build_settings.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4d560bc00..19de16f93 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1190,7 +1190,7 @@ void init_build_context(TargetMetrics *cross_target) { // NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both // official and portable installations (like mmozeiko's portable msvc script). This will only use // the first paths it finds, and won't overwrite any values that `result` already has. -bool find_portable_msvc_installation(gbAllocator allocator, Find_Result_Utf8 *result) { +bool find_msvc_install_from_env_vars(gbAllocator allocator, Find_Result_Utf8 *result) { if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) { return false; } @@ -1380,7 +1380,7 @@ bool find_portable_msvc_installation(gbAllocator allocator, Find_Result_Utf8 *re return sdk_found && vs_found; } -#endif defined(GB_SYSTEM_WINDOWS) +#endif // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. // We've previously called `parse_build_flags`, so `out_filepath` should be set. @@ -1429,20 +1429,11 @@ bool init_build_paths(String init_filename) { find_result.vs_library_path.len > 0; if (find_result.windows_sdk_version == 0 || !all_found) { - if (!find_portable_msvc_installation(ha, &find_result)) { + if (!find_msvc_install_from_env_vars(ha, &find_result)) { gb_printf_err("Windows SDK not found.\n"); return false; } } -#if 0 - printf("windows_sdk_root: %.*s\n", LIT(find_result.windows_sdk_root)); - printf("windows_sdk_um_library_path: %.*s\n", LIT(find_result.windows_sdk_um_library_path)); - printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(find_result.windows_sdk_ucrt_library_path)); - printf("vs_exe_path: %.*s\n", LIT(find_result.vs_exe_path)); - printf("vs_library_path: %.*s\n", LIT(find_result.vs_library_path)); - - gb_exit(1); -#endif if (find_result.windows_sdk_um_library_path.len > 0) { GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); From 209a1556083e80141da0e4e2142af104d9de3a27 Mon Sep 17 00:00:00 2001 From: WalterPlinge <22519813+WalterPlinge@users.noreply.github.com> Date: Wed, 25 May 2022 14:51:37 +0100 Subject: [PATCH 3/4] fix a double free bug --- src/build_settings.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 19de16f93..f9c417ce2 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1285,11 +1285,14 @@ bool find_msvc_install_from_env_vars(gbAllocator allocator, Find_Result_Utf8 *re } // Get the root from the one we found, and make the other + // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free if (result->windows_sdk_um_library_path.len > 0) { - result->windows_sdk_root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); + String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); + result->windows_sdk_root = copy_string(allocator, root); result->windows_sdk_ucrt_library_path = concatenate3_strings(allocator, result->windows_sdk_root, ucrt_dir, make_string_c("\\")); } else if (result->windows_sdk_ucrt_library_path.len > 0) { - result->windows_sdk_root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); + String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); + result->windows_sdk_root = copy_string(allocator, root); result->windows_sdk_um_library_path = concatenate3_strings(allocator, result->windows_sdk_root, um_dir, make_string_c("\\")); } From 92ed9e0b94c36e9191126e040c30d49be624a341 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 27 May 2022 23:48:31 +0200 Subject: [PATCH 4/4] Refactor Walter's PR. --- src/build_settings.cpp | 212 +------------------------------------- src/microsoft_craziness.h | 204 ++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 209 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 202f2fd50..e9f5f2099 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1186,203 +1186,6 @@ void init_build_context(TargetMetrics *cross_target) { // NOTE(IC): In order to find Visual C++ paths without relying on environment variables. // NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`. #include "microsoft_craziness.h" - -// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both -// official and portable installations (like mmozeiko's portable msvc script). This will only use -// the first paths it finds, and won't overwrite any values that `result` already has. -bool find_msvc_install_from_env_vars(gbAllocator allocator, Find_Result_Utf8 *result) { - if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) { - return false; - } - - bool sdk_found = false; - if(result->windows_sdk_root.len > 0 - && result->windows_sdk_um_library_path.len > 0 - && result->windows_sdk_ucrt_library_path.len > 0) { - sdk_found = true; - } - - // We can find windows sdk using the following combination of env vars: - // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion) - if (!sdk_found) { - // These appear to be suitable env vars used by Visual Studio - char const *win_sdk_ver_env = gb_get_env("WindowsSDKVersion", allocator); - char const *win_sdk_lib_env = gb_get_env("WindowsSDKLibVersion", allocator); - char const *win_sdk_dir_env = gb_get_env("WindowsSdkDir", allocator); - char const *crt_sdk_dir_env = gb_get_env("UniversalCRTSdkDir", allocator); - defer (gb_free(allocator, (void*)win_sdk_ver_env)); - defer (gb_free(allocator, (void*)win_sdk_lib_env)); - defer (gb_free(allocator, (void*)win_sdk_dir_env)); - defer (gb_free(allocator, (void*)crt_sdk_dir_env)); - - // NOTE(WalterPlinge): If any combination is found, let's just assume they are correct - if ((win_sdk_ver_env || win_sdk_lib_env) && (win_sdk_dir_env || crt_sdk_dir_env)) { - //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue - String dir = win_sdk_dir_env - ? make_string_c(win_sdk_dir_env) - : make_string_c(crt_sdk_dir_env); - String ver = win_sdk_ver_env - ? make_string_c(win_sdk_ver_env) - : make_string_c(win_sdk_lib_env); - - // These have trailing '\' as we are just composing the path - String um_dir = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("um\\x64\\") - : make_string_c("um\\x86\\"); - String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("ucrt\\x64\\") - : make_string_c("ucrt\\x86\\"); - - result->windows_sdk_root = concatenate3_strings(allocator, dir, make_string_c("Lib\\"), ver); - result->windows_sdk_um_library_path = concatenate_strings(allocator, result->windows_sdk_root, um_dir); - result->windows_sdk_ucrt_library_path = concatenate_strings(allocator, result->windows_sdk_root, ucrt_dir); - - sdk_found = true; - } - } - - // If we haven't found it yet, we can loop through LIB for specific folders - //? This may not be robust enough using `um\x64` and `ucrt\x64` - if (!sdk_found) { - char const *lib_env = gb_get_env("LIB", allocator); - defer (gb_free(allocator, (void*)lib_env)); - if (lib_env) { - String lib = make_string_c(lib_env); - - // NOTE(WalterPlinge): I don't know if there's a chance for the LIB variable - // to be set without a trailing '\' (apart from manually), so we can just - // check paths without it (see use of `String end` in the loop below) - String um_dir = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("um\\x64") - : make_string_c("um\\x86"); - String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("ucrt\\x64") - : make_string_c("ucrt\\x86"); - - isize lo = {0}; - isize hi = {0}; - for (isize c = 0; c <= lib.len; c += 1) { - if (c != lib.len && lib[c] != ';') { - continue; - } - hi = c; - String dir = substring(lib, lo, hi); - defer (lo = hi + 1); - - // Remove the last slash so we can match with the strings above - String end = dir[dir.len - 1] == '\\' - ? substring(dir, 0, dir.len - 1) - : substring(dir, 0, dir.len); - - // Find one and we can make the other - if (string_ends_with(end, um_dir)) { - result->windows_sdk_um_library_path = concatenate_strings(allocator, end, make_string_c("\\")); - break; - } else if (string_ends_with(end, ucrt_dir)) { - result->windows_sdk_ucrt_library_path = concatenate_strings(allocator, end, make_string_c("\\")); - break; - } - } - - // Get the root from the one we found, and make the other - // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free - if (result->windows_sdk_um_library_path.len > 0) { - String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); - result->windows_sdk_root = copy_string(allocator, root); - result->windows_sdk_ucrt_library_path = concatenate3_strings(allocator, result->windows_sdk_root, ucrt_dir, make_string_c("\\")); - } else if (result->windows_sdk_ucrt_library_path.len > 0) { - String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); - result->windows_sdk_root = copy_string(allocator, root); - result->windows_sdk_um_library_path = concatenate3_strings(allocator, result->windows_sdk_root, um_dir, make_string_c("\\")); - } - - if (result->windows_sdk_root.len > 0) { - sdk_found = true; - } - } - } - - // NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was - // installed using mmozeiko's portable msvc script, which uses the windows 10 sdk. - // This may need to be changed later if it ends up causing problems. - if (sdk_found && result->windows_sdk_version == 0) { - result->windows_sdk_version = 10; - } - - bool vs_found = false; - if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) { - vs_found = true; - } - - // We can find visual studio using VCToolsInstallDir - if (!vs_found) { - char const *vctid_env = gb_get_env("VCToolsInstallDir", allocator); - defer (gb_free(allocator, (void*)vctid_env)); - if (vctid_env) { - String vctid = make_string_c(vctid_env); - String exe = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("bin\\Hostx64\\x64\\") - : make_string_c("bin\\Hostx86\\x86\\"); - String lib = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("lib\\x64\\") - : make_string_c("lib\\x86\\"); - result->vs_exe_path = concatenate_strings(allocator, vctid, exe); - result->vs_library_path = concatenate_strings(allocator, vctid, lib); - vs_found = true; - } - } - - // If we haven't found it yet, we can loop through Path for specific folders - if (!vs_found) { - char const *path_env = gb_get_env("Path", allocator); - defer (gb_free(allocator, (void*)path_env)); - if (path_env) { - String path = make_string_c(path_env); - - String exe = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("bin\\Hostx64\\x64") - : make_string_c("bin\\Hostx86\\x86"); - String lib = build_context.metrics.arch == TargetArch_amd64 - ? make_string_c("lib\\x64") - : make_string_c("lib\\x86"); - - isize lo = {0}; - isize hi = {0}; - for (isize c = 0; c <= path.len; c += 1) { - if (c != path.len && path[c] != ';') { - continue; - } - - hi = c; - String dir = substring(path, lo, hi); - defer (lo = hi + 1); - - String end = dir[dir.len - 1] == '\\' - ? substring(dir, 0, dir.len - 1) - : substring(dir, 0, dir.len); - - // check if cl.exe and link.exe exist in this folder - String cl = concatenate_strings(allocator, end, make_string_c("\\cl.exe")); - String link = concatenate_strings(allocator, end, make_string_c("\\link.exe")); - defer (gb_free(allocator, cl.text)); - defer (gb_free(allocator, link.text)); - - if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) { - continue; - } - - String root = substring(end, 0, end.len - exe.len); - - result->vs_exe_path = concatenate_strings(allocator, end, make_string_c("\\")); - result->vs_library_path = concatenate3_strings(allocator, root, lib, make_string_c("\\")); - - vs_found = true; - } - } - } - - return sdk_found && vs_found; -} #endif // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. @@ -1426,18 +1229,9 @@ bool init_build_paths(String init_filename) { Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); defer (mc_free_all()); - bool all_found = - find_result.windows_sdk_root.len > 0 && - find_result.windows_sdk_um_library_path.len > 0 && - find_result.windows_sdk_ucrt_library_path.len > 0 && - find_result.vs_exe_path.len > 0 && - find_result.vs_library_path.len > 0; - - if (find_result.windows_sdk_version == 0 || !all_found) { - if (!find_msvc_install_from_env_vars(ha, &find_result)) { - gb_printf_err("Windows SDK not found.\n"); - return false; - } + if (find_result.windows_sdk_version == 0) { + gb_printf_err("Windows SDK not found.\n"); + return false; } if (!build_context.use_lld && find_result.vs_exe_path.len == 0) { diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index 98707a4a1..f5222e943 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -87,6 +87,11 @@ String mc_concat(String a, String b, String c) { return concatenate3_strings(mc_allocator, a, b, c); } +String mc_get_env(String key) { + char const * value = gb_get_env((char const *)key.text, mc_allocator); + return make_string_c(value); +} + void mc_free(String str) { gb_free(mc_allocator, str.text); } @@ -549,6 +554,194 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8 return false; } +// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both +// official and portable installations (like mmozeiko's portable msvc script). This will only use +// the first paths it finds, and won't overwrite any values that `result` already has. +bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) { + if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) { + return false; + } + + // We can find windows sdk using the following combination of env vars: + // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion) + bool sdk_found = false; + + // These appear to be suitable env vars used by Visual Studio + String win_sdk_ver_env = mc_get_env(str_lit("WindowsSDKVersion")); + String win_sdk_lib_env = mc_get_env(str_lit("WindowsSDKLibVersion")); + String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir")); + String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir")); + + defer ({ + mc_free(win_sdk_ver_env); + mc_free(win_sdk_lib_env); + mc_free(win_sdk_dir_env); + mc_free(crt_sdk_dir_env); + }); + + // NOTE(WalterPlinge): If any combination is found, let's just assume they are correct + if ((win_sdk_ver_env.len || win_sdk_lib_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) { + //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue + String dir = win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env; + String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_env; + + // These have trailing '\' as we are just composing the path + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("um\\x64\\") + : str_lit("um\\x86\\"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("ucrt\\x64\\") + : str_lit("ucrt\\x86\\"); + + result->windows_sdk_root = mc_concat(dir, str_lit("Lib\\"), ver); + result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir); + result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir); + + sdk_found = true; + } + + // If we haven't found it yet, we can loop through LIB for specific folders + //? This may not be robust enough using `um\x64` and `ucrt\x64` + if (!sdk_found) { + char const *lib_env = gb_get_env("LIB", mc_allocator); + defer (gb_free(mc_allocator, (void*)lib_env)); + if (lib_env) { + String lib = make_string_c(lib_env); + + // NOTE(WalterPlinge): I don't know if there's a chance for the LIB variable + // to be set without a trailing '\' (apart from manually), so we can just + // check paths without it (see use of `String end` in the loop below) + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("um\\x64") + : make_string_c("um\\x86"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? make_string_c("ucrt\\x64") + : make_string_c("ucrt\\x86"); + + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c <= lib.len; c += 1) { + if (c != lib.len && lib[c] != ';') { + continue; + } + hi = c; + String dir = substring(lib, lo, hi); + defer (lo = hi + 1); + + // Remove the last slash so we can match with the strings above + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); + + // Find one and we can make the other + if (string_ends_with(end, um_dir)) { + result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\")); + break; + } else if (string_ends_with(end, ucrt_dir)) { + result->windows_sdk_ucrt_library_path = mc_concat(end, str_lit("\\")); + break; + } + } + + // Get the root from the one we found, and make the other + // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free + if (result->windows_sdk_um_library_path.len > 0) { + String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); + result->windows_sdk_root = copy_string(mc_allocator, root); + result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir, str_lit("\\")); + } else if (result->windows_sdk_ucrt_library_path.len > 0) { + String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); + result->windows_sdk_root = copy_string(mc_allocator, root); + result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir, str_lit("\\")); + } + + if (result->windows_sdk_root.len > 0) { + sdk_found = true; + } + } + } + + // NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was + // installed using mmozeiko's portable msvc script, which uses the windows 10 sdk. + // This may need to be changed later if it ends up causing problems. + if (sdk_found && result->windows_sdk_version == 0) { + result->windows_sdk_version = 10; + } + + bool vs_found = false; + if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) { + vs_found = true; + } + + // We can find visual studio using VCToolsInstallDir + if (!vs_found) { + String vctid = mc_get_env(str_lit("VCToolsInstallDir")); + defer (mc_free(vctid)); + + if (vctid.len) { + String exe = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("bin\\Hostx64\\x64\\") + : str_lit("bin\\Hostx86\\x86\\"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("lib\\x64\\") + : str_lit("lib\\x86\\"); + + result->vs_exe_path = mc_concat(vctid, exe); + result->vs_library_path = mc_concat(vctid, lib); + vs_found = true; + } + } + + // If we haven't found it yet, we can loop through Path for specific folders + if (!vs_found) { + String path = mc_get_env(str_lit("Path")); + defer (mc_free(path)); + + if (path.len) { + String exe = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("bin\\Hostx64\\x64") + : str_lit("bin\\Hostx86\\x86"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("lib\\x64") + : str_lit("lib\\x86"); + + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c <= path.len; c += 1) { + if (c != path.len && path[c] != ';') { + continue; + } + + hi = c; + String dir = substring(path, lo, hi); + defer (lo = hi + 1); + + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); + + // check if cl.exe and link.exe exist in this folder + String cl = mc_concat(end, str_lit("\\cl.exe")); + String link = mc_concat(end, str_lit("\\link.exe")); + defer (mc_free(cl)); + defer (mc_free(link)); + + if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) { + continue; + } + + String root = substring(end, 0, end.len - exe.len); + result->vs_exe_path = mc_concat(end, str_lit("\\")); + result->vs_library_path = mc_concat(root, lib, str_lit("\\")); + + vs_found = true; + } + } + } + + return sdk_found && vs_found; +} + Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() { Find_Result_Utf8 r = {}; find_windows_kit_root(&r); @@ -565,6 +758,17 @@ Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() { find_visual_studio_by_fighting_through_microsoft_craziness(&r); + bool all_found = + r.windows_sdk_root.len > 0 && + r.windows_sdk_um_library_path.len > 0 && + r.windows_sdk_ucrt_library_path.len > 0 && + r.vs_exe_path.len > 0 && + r.vs_library_path.len > 0; + + if (!all_found && !find_msvc_install_from_env_vars(&r)) { + return {}; + } + #if 0 printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root)); printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path));