Browse Source

Merge pull request #1937 from IanLilleyT/rc_fix

Find rc.exe in Windows SDK
Jeroen van Rijn 3 years ago
parent
commit
bcccc8338f
4 changed files with 201 additions and 178 deletions
  1. 4 4
      src/build_settings.cpp
  2. 5 1
      src/main.cpp
  3. 182 173
      src/microsoft_craziness.h
  4. 10 0
      src/string.cpp

+ 4 - 4
src/build_settings.cpp

@@ -204,7 +204,7 @@ enum BuildPath : u8 {
 	BuildPath_Main_Package,     // Input  Path to the package directory (or file) we're building.
 	BuildPath_Main_Package,     // Input  Path to the package directory (or file) we're building.
 	BuildPath_RC,               // Input  Path for .rc  file, can be set with `-resource:`.
 	BuildPath_RC,               // Input  Path for .rc  file, can be set with `-resource:`.
 	BuildPath_RES,              // Output Path for .res file, generated from previous.
 	BuildPath_RES,              // Output Path for .res file, generated from previous.
-	BuildPath_Win_SDK_Root,     // windows_sdk_root
+	BuildPath_Win_SDK_Bin_Path, // windows_sdk_bin_path
 	BuildPath_Win_SDK_UM_Lib,   // windows_sdk_um_library_path
 	BuildPath_Win_SDK_UM_Lib,   // windows_sdk_um_library_path
 	BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
 	BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
 	BuildPath_VS_EXE,           // vs_exe_path
 	BuildPath_VS_EXE,           // vs_exe_path
@@ -1336,7 +1336,7 @@ bool init_build_paths(String init_filename) {
 
 
 		if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
 		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.
 			// 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();
+			Find_Result find_result = find_visual_studio_and_windows_sdk();
 			defer (mc_free_all());
 			defer (mc_free_all());
 
 
 			if (find_result.windows_sdk_version == 0) {
 			if (find_result.windows_sdk_version == 0) {
@@ -1357,8 +1357,8 @@ bool init_build_paths(String init_filename) {
 			if (find_result.windows_sdk_um_library_path.len > 0) {
 			if (find_result.windows_sdk_um_library_path.len > 0) {
 				GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
 				GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
 
 
-				if (find_result.windows_sdk_root.len > 0) {
-					bc->build_paths[BuildPath_Win_SDK_Root]     = path_from_string(ha, find_result.windows_sdk_root);
+				if (find_result.windows_sdk_bin_path.len > 0) {
+					bc->build_paths[BuildPath_Win_SDK_Bin_Path]  = path_from_string(ha, find_result.windows_sdk_bin_path);
 				}
 				}
 
 
 				if (find_result.windows_sdk_um_library_path.len > 0) {
 				if (find_result.windows_sdk_um_library_path.len > 0) {

+ 5 - 1
src/main.cpp

@@ -283,6 +283,9 @@ i32 linker_stage(lbGenerator *gen) {
 			String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
 			String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
 			defer (gb_free(heap_allocator(), vs_exe_path.text));
 			defer (gb_free(heap_allocator(), vs_exe_path.text));
 
 
+			String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
+			defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
+
 			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
 			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
 			if (!build_context.use_lld) { // msvc
 			if (!build_context.use_lld) { // msvc
 				if (build_context.has_resource) {
 				if (build_context.has_resource) {
@@ -292,7 +295,8 @@ i32 linker_stage(lbGenerator *gen) {
 					defer (gb_free(heap_allocator(), res_path.text));
 					defer (gb_free(heap_allocator(), res_path.text));
 
 
 					result = system_exec_command_line_app("msvc-link",
 					result = system_exec_command_line_app("msvc-link",
-						"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+						LIT(windows_sdk_bin_path),
 						LIT(res_path),
 						LIT(res_path),
 						LIT(rc_path)
 						LIT(rc_path)
 					);
 					);

+ 182 - 173
src/microsoft_craziness.h

@@ -50,18 +50,7 @@ gb_global gbAllocator mc_allocator = heap_allocator();
 struct Find_Result {
 struct Find_Result {
 	int windows_sdk_version;   // Zero if no Windows SDK found.
 	int windows_sdk_version;   // Zero if no Windows SDK found.
 
 
-	wchar_t const *windows_sdk_root;
-	wchar_t const *windows_sdk_um_library_path;
-	wchar_t const *windows_sdk_ucrt_library_path;
-
-	wchar_t const *vs_exe_path;
-	wchar_t const *vs_library_path;
-};
-
-struct Find_Result_Utf8 {
-	int windows_sdk_version;   // Zero if no Windows SDK found.
-
-	String windows_sdk_root;
+	String windows_sdk_bin_path;
 	String windows_sdk_um_library_path;
 	String windows_sdk_um_library_path;
 	String windows_sdk_ucrt_library_path;
 	String windows_sdk_ucrt_library_path;
 
 
@@ -69,8 +58,6 @@ struct Find_Result_Utf8 {
 	String vs_library_path;
 	String vs_library_path;
 };
 };
 
 
-Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8();
-
 String mc_wstring_to_string(wchar_t const *str) {
 String mc_wstring_to_string(wchar_t const *str) {
 	return string16_to_string(mc_allocator, make_string16_c(str));
 	return string16_to_string(mc_allocator, make_string16_c(str));
 }
 }
@@ -87,6 +74,10 @@ String mc_concat(String a, String b, String c) {
 	return concatenate3_strings(mc_allocator, a, b, c);
 	return concatenate3_strings(mc_allocator, a, b, c);
 }
 }
 
 
+String mc_concat(String a, String b, String c, String d) {
+	return concatenate4_strings(mc_allocator, a, b, c, d);
+}
+
 String mc_get_env(String key) {
 String mc_get_env(String key) {
 	char const * value = gb_get_env((char const *)key.text, mc_allocator);
 	char const * value = gb_get_env((char const *)key.text, mc_allocator);
 	return make_string_c(value);
 	return make_string_c(value);
@@ -219,19 +210,19 @@ struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE I
 
 
 
 
 // The beginning of the actual code that does things.
 // The beginning of the actual code that does things.
-struct Version_Data_Utf8 {
-	i32 best_version[4];  // For Windows 8 versions, only two of these numbers are used.
+struct Version_Data {
+	i32 best_version[4];
 	String best_name;
 	String best_name;
 };
 };
 
 
-typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data_Utf8 *data);
-bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc) {
+typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data *data);
+bool mc_visit_files(String dir_name, Version_Data *data, MC_Visit_Proc proc) {
 
 
 	// Visit everything in one folder (non-recursively). If it's a directory
 	// Visit everything in one folder (non-recursively). If it's a directory
 	// that doesn't start with ".", call the visit proc on it. The visit proc
 	// that doesn't start with ".", call the visit proc on it. The visit proc
 	// will see if the filename conforms to the expected versioning pattern.
 	// will see if the filename conforms to the expected versioning pattern.
 
 
-	String wildcard_name = mc_concat(dir_name, str_lit("\\*"));
+	String wildcard_name = mc_concat(dir_name, str_lit("*"));
 	defer (mc_free(wildcard_name));
 	defer (mc_free(wildcard_name));
 
 
 	MC_Find_Data find_data;
 	MC_Find_Data find_data;
@@ -242,7 +233,7 @@ bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc
 	bool success = true;
 	bool success = true;
 	while (success) {
 	while (success) {
 		if ((find_data.file_attributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.filename[0] != '.')) {
 		if ((find_data.file_attributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.filename[0] != '.')) {
-			String full_name = mc_concat(dir_name, str_lit("\\"), find_data.filename);
+			String full_name = mc_concat(dir_name, find_data.filename);
 			defer (mc_free(full_name));
 			defer (mc_free(full_name));
 
 
 			proc(find_data.filename, full_name, data);
 			proc(find_data.filename, full_name, data);
@@ -284,7 +275,7 @@ String find_windows_kit_root(HKEY key, String const version) {
 	return value;
 	return value;
 }
 }
 
 
-void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
+void win10_best(String short_name, String full_name, Version_Data *data) {
 	// Find the Windows 10 subdirectory with the highest version number.
 	// Find the Windows 10 subdirectory with the highest version number.
 
 
 	int i0, i1, i2, i3;
 	int i0, i1, i2, i3;
@@ -304,11 +295,11 @@ void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
 
 
 	// we have to copy_string and free here because visit_files free's the full_name string
 	// we have to copy_string and free here because visit_files free's the full_name string
 	// after we execute this function, so Win*_Data would contain an invalid pointer.
 	// after we execute this function, so Win*_Data would contain an invalid pointer.
-	if (data->best_name.len > 0) mc_free(data->best_name);
+	if (data->best_name.len) mc_free(data->best_name);
 
 
 	data->best_name = copy_string(mc_allocator, full_name);
 	data->best_name = copy_string(mc_allocator, full_name);
 
 
-	if (data->best_name.len > 0) {
+	if (data->best_name.len) {
 		data->best_version[0] = i0;
 		data->best_version[0] = i0;
 		data->best_version[1] = i1;
 		data->best_version[1] = i1;
 		data->best_version[2] = i2;
 		data->best_version[2] = i2;
@@ -316,34 +307,8 @@ void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
 	}
 	}
 }
 }
 
 
-void win8_best(String short_name, String full_name, Version_Data_Utf8 *data) {
-	// Find the Windows 8 subdirectory with the highest version number.
-
-	int i0, i1;
-	auto success = sscanf_s((const char *const)short_name.text, "winv%d.%d", &i0, &i1);
-	if (success < 2) return;
-
-	if (i0 < data->best_version[0]) return;
-	else if (i0 == data->best_version[0]) {
-		if (i1 < data->best_version[1]) return;
-	}
-
-	// we have to copy_string and free here because visit_files free's the full_name string
-	// after we execute this function, so Win*_Data would contain an invalid pointer.
-	if (data->best_name.len > 0) mc_free(data->best_name);
-	data->best_name = copy_string(mc_allocator, full_name);
-
-	if (data->best_name.len > 0) {
-		data->best_version[0] = i0;
-		data->best_version[1] = i1;
-	}
-}
-
-void find_windows_kit_root(Find_Result_Utf8 *result) {
-	// Information about the Windows 10 and Windows 8 development kits
-	// is stored in the same place in the registry. We open a key
-	// to that place, first checking preferntially for a Windows 10 kit,
-	// then, if that's not found, a Windows 8 kit.
+void find_windows_kit_paths(Find_Result *result) {
+	bool sdk_found = false;
 
 
 	HKEY main_key;
 	HKEY main_key;
 
 
@@ -355,44 +320,42 @@ void find_windows_kit_root(Find_Result_Utf8 *result) {
 	// Look for a Windows 10 entry.
 	// Look for a Windows 10 entry.
 	String windows10_root = find_windows_kit_root(main_key, str_lit("KitsRoot10"));
 	String windows10_root = find_windows_kit_root(main_key, str_lit("KitsRoot10"));
 
 
-	if (windows10_root.len > 0) {
+	if (windows10_root.len) {
 		defer (mc_free(windows10_root));
 		defer (mc_free(windows10_root));
 
 
-		String windows10_lib = mc_concat(windows10_root, str_lit("Lib"));
+		String windows10_lib = mc_concat(windows10_root, str_lit("Lib\\"));
+		Version_Data data_lib = {0};
+		mc_visit_files(windows10_lib, &data_lib, win10_best);
 		defer (mc_free(windows10_lib));
 		defer (mc_free(windows10_lib));
+		defer (mc_free(data_lib.best_name));
+
+		String windows10_bin = mc_concat(windows10_root, str_lit("bin\\"));
+		Version_Data data_bin = {0};
+		mc_visit_files(windows10_bin, &data_bin, win10_best);
+		defer (mc_free(windows10_bin));
+		defer (mc_free(data_bin.best_name));
 
 
-		Version_Data_Utf8 data = {0};
-		mc_visit_files(windows10_lib, &data, win10_best);
-		if (data.best_name.len > 0) {
-			result->windows_sdk_version = 10;
-			result->windows_sdk_root    = mc_concat(data.best_name, str_lit("\\"));
-			return;
+		if (data_lib.best_name.len && data_bin.best_name.len) {
+			if (build_context.metrics.arch == TargetArch_amd64) {
+				result->windows_sdk_um_library_path   = mc_concat(data_lib.best_name, str_lit("\\um\\x64\\"));
+				result->windows_sdk_ucrt_library_path = mc_concat(data_lib.best_name, str_lit("\\ucrt\\x64\\"));
+				result->windows_sdk_bin_path          = mc_concat(data_bin.best_name, str_lit("\\x64\\"));
+				sdk_found = true;
+			} else if (build_context.metrics.arch == TargetArch_i386) {
+				result->windows_sdk_um_library_path   = mc_concat(data_lib.best_name, str_lit("\\um\\x86\\"));
+				result->windows_sdk_ucrt_library_path = mc_concat(data_lib.best_name, str_lit("\\ucrt\\x86\\"));
+				result->windows_sdk_bin_path          = mc_concat(data_bin.best_name, str_lit("\\x86\\"));
+				sdk_found = true;
+			}
 		}
 		}
-		mc_free(data.best_name);
 	}
 	}
 
 
-	// Look for a Windows 8 entry.
-	String windows8_root = find_windows_kit_root(main_key, str_lit("KitsRoot81"));
-
-	if (windows8_root.len > 0) {
-		defer (mc_free(windows8_root));
-
-		String windows8_lib = mc_concat(windows8_root, str_lit("Lib"));
-		defer (mc_free(windows8_lib));
-
-		Version_Data_Utf8 data = {0};
-		mc_visit_files(windows8_lib, &data, win8_best);
-		if (data.best_name.len > 0) {
-			result->windows_sdk_version = 8;
-			result->windows_sdk_root    = mc_concat(data.best_name, str_lit("\\"));
-			return;
-		}
-		mc_free(data.best_name);
+	if (sdk_found) {
+		result->windows_sdk_version = 10;
 	}
 	}
-	// If we get here, we failed to find anything.
 }
 }
 
 
-bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8 *result) {
+bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) {
 	// The name of this procedure is kind of cryptic. Its purpose is
 	// The name of this procedure is kind of cryptic. Its purpose is
 	// to fight through Microsoft craziness. The things that the fine
 	// to fight through Microsoft craziness. The things that the fine
 	// Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
 	// Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
@@ -555,54 +518,97 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8
 }
 }
 
 
 // NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
 // 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) {
+// official and portable installations (like mmozeiko's portable msvc script).
+void find_windows_kit_paths_from_env_vars(Find_Result *result) {
 	if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
 	if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
-		return false;
+		return;
 	}
 	}
 
 
-	// We can find windows sdk using the following combination of env vars:
-	// (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion)
-	bool sdk_found = false;
+	// We can find windows sdk lib dir using the following combination of env vars:
+	// (WindowsSdkDir or UniversalCRTSdkDir) and (WindowsSDKVersion or WindowsSDKLibVersion)
+	bool sdk_lib_found = false;
+
+	// We can find windows sdk bin dir using the following combination of env vars:
+	// (WindowsSdkVerBinPath) or ((WindowsSdkBinPath or WindowsSdkDir or UniversalCRTSdkDir) and (WindowsSDKVersion || WindowsSDKLibVersion))
+	bool sdk_bin_found = false;
 
 
 	// These appear to be suitable env vars used by Visual Studio
 	// 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_ver_env = mc_get_env(str_lit("WindowsSDKVersion"));
-	String win_sdk_lib_env = mc_get_env(str_lit("WindowsSDKLibVersion"));
+	String win_sdk_lib_ver_env = mc_get_env(str_lit("WindowsSDKLibVersion"));
 	String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir"));
 	String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir"));
 	String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir"));
 	String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir"));
+	String win_sdk_bin_path_env = mc_get_env(str_lit("WindowsSdkBinPath"));
+	String win_sdk_ver_bin_path_env = mc_get_env(str_lit("WindowsSdkVerBinPath"));
 
 
 	defer ({
 	defer ({
 		mc_free(win_sdk_ver_env);
 		mc_free(win_sdk_ver_env);
-		mc_free(win_sdk_lib_env);
+		mc_free(win_sdk_lib_ver_env);
 		mc_free(win_sdk_dir_env);
 		mc_free(win_sdk_dir_env);
 		mc_free(crt_sdk_dir_env);
 		mc_free(crt_sdk_dir_env);
+		mc_free(win_sdk_bin_path_env);
+		mc_free(win_sdk_ver_bin_path_env);
 	});
 	});
 
 
+	if (win_sdk_ver_bin_path_env.len || ((win_sdk_bin_path_env.len || win_sdk_dir_env.len || crt_sdk_dir_env.len) && (win_sdk_ver_env.len || win_sdk_lib_ver_env.len))) {
+		String bin;
+		defer (mc_free(bin));
+
+		if (win_sdk_ver_bin_path_env.len) {
+			String dir = win_sdk_ver_bin_path_env;
+
+			// Add trailing '\' in case it was missing
+			bin = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+		} else {
+			String dir = win_sdk_bin_path_env.len ? win_sdk_bin_path_env : 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_ver_env;
+
+			// Add trailing '\' in case it was missing
+			dir = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+			ver = mc_concat(ver, ver[ver.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+			defer (mc_free(dir));
+			defer (mc_free(ver));
+
+			// Append "bin" for win_sdk_dir_env and crt_sdk_dir_env
+			String dir_bin = mc_concat(dir, win_sdk_bin_path_env.len ? str_lit("") : str_lit("bin\\"));
+			defer (mc_free(dir_bin));
+
+			bin = mc_concat(dir_bin, ver);
+		}
+
+		if (build_context.metrics.arch == TargetArch_amd64) {
+			result->windows_sdk_bin_path = mc_concat(bin, str_lit("x64\\"));
+			sdk_bin_found = true;
+		} else if (build_context.metrics.arch == TargetArch_i386) {
+			result->windows_sdk_bin_path = mc_concat(bin, str_lit("x86\\"));
+			sdk_bin_found = true;
+		} 
+	}
+
 	// NOTE(WalterPlinge): If any combination is found, let's just assume they are correct
 	// 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
+	if ((win_sdk_ver_env.len || win_sdk_lib_ver_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) {
 		String dir = win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env;
 		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\\");
+		String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_ver_env;
 
 
-		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);
+		// Add trailing '\' in case it was missing
+		dir = mc_concat(dir, dir[dir.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+		ver = mc_concat(ver, ver[ver.len - 1] != '\\' ? str_lit("\\") : str_lit(""));
+		defer (mc_free(dir));
+		defer (mc_free(ver));
 
 
-		sdk_found = true;
+		if (build_context.metrics.arch == TargetArch_amd64) {
+			result->windows_sdk_um_library_path   = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("um\\x64\\"));
+			result->windows_sdk_ucrt_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("ucrt\\x64\\"));
+			sdk_lib_found = true;
+		} else if (build_context.metrics.arch == TargetArch_i386) {
+			result->windows_sdk_um_library_path   = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("um\\x86\\"));
+			result->windows_sdk_ucrt_library_path = mc_concat(dir, str_lit("Lib\\"), ver, str_lit("ucrt\\x86\\"));
+			sdk_lib_found = true;
+		}
 	}
 	}
 
 
 	// If we haven't found it yet, we can loop through LIB for specific folders
 	// 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`
 	//? This may not be robust enough using `um\x64` and `ucrt\x64`
-	if (!sdk_found) {
+	if (!sdk_lib_found) {
 		String lib = mc_get_env(str_lit("LIB"));
 		String lib = mc_get_env(str_lit("LIB"));
 		defer (mc_free(lib));
 		defer (mc_free(lib));
 
 
@@ -624,38 +630,30 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
 					continue;
 					continue;
 				}
 				}
 				hi = c;
 				hi = c;
-				String dir = substring(lib, lo, hi);
 				defer (lo = hi + 1);
 				defer (lo = hi + 1);
 
 
+				// Skip when there are two ;; in a row
+				if (lo == hi) {
+					continue;
+				}
+
+				String dir = substring(lib, lo, hi);
+
 				// Remove the last slash so we can match with the strings above
 				// Remove the last slash so we can match with the strings above
 				String end = dir[dir.len - 1] == '\\'
 				String end = dir[dir.len - 1] == '\\'
 					? substring(dir, 0, dir.len - 1)
 					? substring(dir, 0, dir.len - 1)
 					: substring(dir, 0, dir.len);
 					: substring(dir, 0, dir.len);
 
 
-				// Find one and we can make the other
 				if (string_ends_with(end, um_dir)) {
 				if (string_ends_with(end, um_dir)) {
-					result->windows_sdk_um_library_path   = mc_concat(end, str_lit("\\"));
-					break;
+					result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\"));
 				} else if (string_ends_with(end, ucrt_dir)) {
 				} else if (string_ends_with(end, ucrt_dir)) {
 					result->windows_sdk_ucrt_library_path = mc_concat(end, str_lit("\\"));
 					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;
+				if (result->windows_sdk_um_library_path.len && result->windows_sdk_ucrt_library_path.len) {
+					sdk_lib_found = true;
+					break;
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -663,33 +661,36 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
 	// NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was
 	// 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.
 	// 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.
 	// This may need to be changed later if it ends up causing problems.
-	if (sdk_found && result->windows_sdk_version == 0) {
+	if (sdk_bin_found && sdk_lib_found) {
 		result->windows_sdk_version = 10;
 		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;
+// 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.
+void find_visual_studio_paths_from_env_vars(Find_Result *result) {
+	if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
+		return;
 	}
 	}
 
 
-	// 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\\");
+	bool vs_found = false;
 
 
-			result->vs_exe_path     = mc_concat(vctid, exe);
-			result->vs_library_path = mc_concat(vctid, lib);
-			vs_found = true;
-		}
+	// We can find visual studio using VCToolsInstallDir
+	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 we haven't found it yet, we can loop through Path for specific folders
@@ -701,21 +702,32 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
 			String exe = build_context.metrics.arch == TargetArch_amd64
 			String exe = build_context.metrics.arch == TargetArch_amd64
 				? str_lit("bin\\Hostx64\\x64")
 				? str_lit("bin\\Hostx64\\x64")
 				: str_lit("bin\\Hostx86\\x86");
 				: str_lit("bin\\Hostx86\\x86");
+			// The environment variable may have an uppercase X even though the folder is lowercase
+			String exe2 = build_context.metrics.arch == TargetArch_amd64
+				? str_lit("bin\\HostX64\\x64")
+				: str_lit("bin\\HostX86\\x86");
 			String lib = build_context.metrics.arch == TargetArch_amd64
 			String lib = build_context.metrics.arch == TargetArch_amd64
 				? str_lit("lib\\x64")
 				? str_lit("lib\\x64")
 				: str_lit("lib\\x86");
 				: str_lit("lib\\x86");
 
 
 			isize lo = {0};
 			isize lo = {0};
 			isize hi = {0};
 			isize hi = {0};
-			for (isize c = 0; c < path.len; c += 1) {
-				if (path[c] != ';') {
+			for (isize c = 0; c <= path.len; c += 1) {
+				if (c != path.len && path[c] != ';') {
 					continue;
 					continue;
 				}
 				}
 
 
 				hi = c;
 				hi = c;
-				String dir = substring(path, lo, hi);
 				defer (lo = hi + 1);
 				defer (lo = hi + 1);
 
 
+				// Skip when there are two ;; in a row
+				if (lo == hi) {
+					continue;
+				}
+
+				String dir = substring(path, lo, hi);
+
+				// Remove the last slash so we can match with the strings above
 				String end = dir[dir.len - 1] == '\\'
 				String end = dir[dir.len - 1] == '\\'
 					? substring(dir, 0, dir.len - 1)
 					? substring(dir, 0, dir.len - 1)
 					: substring(dir, 0, dir.len);
 					: substring(dir, 0, dir.len);
@@ -726,7 +738,10 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
 				defer (mc_free(cl));
 				defer (mc_free(cl));
 				defer (mc_free(link));
 				defer (mc_free(link));
 
 
-				if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) {
+				if (!string_ends_with(end, exe) && !string_ends_with(end, exe2)) {
+					continue;
+				}
+				if (!gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) {
 					continue;
 					continue;
 				}
 				}
 
 
@@ -735,42 +750,36 @@ bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
 				result->vs_library_path = mc_concat(root, lib, str_lit("\\"));
 				result->vs_library_path = mc_concat(root, lib, str_lit("\\"));
 
 
 				vs_found = true;
 				vs_found = true;
+				break;
 			}
 			}
 		}
 		}
 	}
 	}
-
-	return sdk_found && vs_found;
 }
 }
 
 
-Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() {
-	Find_Result_Utf8 r = {};
-	find_windows_kit_root(&r);
+Find_Result find_visual_studio_and_windows_sdk() {
+	Find_Result r = {};
+	find_windows_kit_paths(&r);
+	find_visual_studio_by_fighting_through_microsoft_craziness(&r);
 
 
-	if (r.windows_sdk_root.len > 0) {
-		if (build_context.metrics.arch == TargetArch_amd64) {
-			r.windows_sdk_um_library_path   = mc_concat(r.windows_sdk_root, str_lit("um\\x64\\"));
-			r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x64\\"));
-		} else if (build_context.metrics.arch == TargetArch_i386) {
-			r.windows_sdk_um_library_path   = mc_concat(r.windows_sdk_root, str_lit("um\\x86\\"));
-			r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x86\\"));
-		}
-	}
+	bool sdk_found =
+		r.windows_sdk_bin_path.len          &&
+		r.windows_sdk_um_library_path.len   &&
+		r.windows_sdk_ucrt_library_path.len ;
 
 
-	find_visual_studio_by_fighting_through_microsoft_craziness(&r);
+	bool vs_found = 
+		r.vs_exe_path.len                   &&
+		r.vs_library_path.len               ;
 
 
-	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 (!sdk_found) {
+		find_windows_kit_paths_from_env_vars(&r);
+	}
 
 
-	if (!all_found) {
-		find_msvc_install_from_env_vars(&r);
+	if (!vs_found) {
+		find_visual_studio_paths_from_env_vars(&r);
 	}
 	}
 
 
 #if 0
 #if 0
-	printf("windows_sdk_root:              %.*s\n", LIT(r.windows_sdk_root));
+	printf("windows_sdk_bin_path:          %.*s\n", LIT(r.windows_sdk_bin_path));
 	printf("windows_sdk_um_library_path:   %.*s\n", LIT(r.windows_sdk_um_library_path));
 	printf("windows_sdk_um_library_path:   %.*s\n", LIT(r.windows_sdk_um_library_path));
 	printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path));
 	printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path));
 	printf("vs_exe_path:                   %.*s\n", LIT(r.vs_exe_path));
 	printf("vs_exe_path:                   %.*s\n", LIT(r.vs_exe_path));

+ 10 - 0
src/string.cpp

@@ -324,6 +324,16 @@ String concatenate3_strings(gbAllocator a, String const &x, String const &y, Str
 	data[len] = 0;
 	data[len] = 0;
 	return make_string(data, len);
 	return make_string(data, len);
 }
 }
+String concatenate4_strings(gbAllocator a, String const &x, String const &y, String const &z, String const &w) {
+	isize len = x.len+y.len+z.len+w.len;
+	u8 *data = gb_alloc_array(a, u8, len+1);
+	gb_memmove(data,                   x.text, x.len);
+	gb_memmove(data+x.len,             y.text, y.len);
+	gb_memmove(data+x.len+y.len,       z.text, z.len);
+	gb_memmove(data+x.len+y.len+z.len, w.text, w.len);
+	data[len] = 0;
+	return make_string(data, len);
+}
 
 
 String string_join_and_quote(gbAllocator a, Array<String> strings) {
 String string_join_and_quote(gbAllocator a, Array<String> strings) {
 	if (!strings.count) {
 	if (!strings.count) {