From 0d6f93fde306be154c1f0c9afe74a5910a9070f3 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Sat, 4 Aug 2018 00:41:11 +0200 Subject: [PATCH 1/4] Split MSVC 15 instance iterator retrieval into a separate function --- src/windows_registry.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/windows_registry.rs b/src/windows_registry.rs index cb35675..81f3069 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -174,7 +174,7 @@ mod impl_ { use std::io::Read; use registry::{RegistryKey, LOCAL_MACHINE}; use com; - use setup_config::{SetupConfiguration, SetupInstance}; + use setup_config::{EnumSetupInstances, SetupConfiguration, SetupInstance}; use Tool; @@ -217,11 +217,15 @@ mod impl_ { // Note that much of this logic can be found [online] wrt paths, COM, etc. // // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/ - pub fn find_msvc_15(tool: &str, target: &str) -> Option { + fn vs15_instances() -> Option { otry!(com::initialize().ok()); let config = otry!(SetupConfiguration::new().ok()); - let iter = otry!(config.enum_all_instances().ok()); + config.enum_all_instances().ok() + } + + pub fn find_msvc_15(tool: &str, target: &str) -> Option { + let iter = otry!(vs15_instances()); for instance in iter { let instance = otry!(instance.ok()); let tool = tool_from_vs15_instance(tool, target, &instance); From 123919c055631fd61edd31f517053980c16b5c67 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Sat, 4 Aug 2018 17:21:32 +0200 Subject: [PATCH 2/4] Find devenv and MSBuild via SetupConfiguration rather than registry keys After I recently uninstalled some older MSVC versions using Visual Studio Uninstaller (https://github.com/Microsoft/VisualStudioUninstaller), HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7\15.0 also was deleted in the process, even though Visual Studio 2017 *itself* wasn't, and still worked fine. Steps to reproduce: 1. Install Visual Studio 2017 Community with the *Desktop development with C++* workload on a fresh Windows system. (I tested with version 15.7.6.) 2. Confirm that the registry key points to the installation directory. 3. Run the Visual Studio Uninstaller on that same system. 4. The registry key is now gone, but Visual Studio 2017 IDE still works and can compile code. 5. The following Rust code then runs without panicking: ```rust extern crate cc; use cc::windows_registry::find_tool; fn main() { // Change this if your test system is 32-bit let target = "x86_64-pc-windows-msvc"; assert_eq!(find_tool(target, "devenv.exe").is_none(), true); assert_eq!(find_tool(target, "link.exe").is_some(), true); // msbuild.exe will fall back on find_old_msbuild(), which will // find a ersion under a different registry key. } ``` Which means that `link.exe` can still be found just fine through SetupConfiguration. This may be a bug in Visual Studio Uninstaller, but it also matches what Microsoft says about the reliability of these registry keys, and that SetupConfiguration should be preferred. (See https://developercommunity.visualstudio.com/comments/215399/view.html). --- src/windows_registry.rs | 55 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/windows_registry.rs b/src/windows_registry.rs index 81f3069..6b6203a 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -237,6 +237,30 @@ mod impl_ { None } + // It may seem that the paths to Visual Studio 2017's devenv and MSBuild + // could also be retrieved from the [registry], but SetupConfiguration's + // method seems to be [more reliable], and preferred according to Microsoft. + // + // [registry]: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 + // [more reliable]: https://github.com/alexcrichton/cc-rs/pull/331 + fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option { + otry!(vs15_instances()) + .filter_map(|instance| { + instance + .ok() + .and_then(|instance| instance.installation_path().ok()) + }) + .map(|ip| PathBuf::from(ip).join(tool)) + .map(|path| { + let mut tool = Tool::new(path); + if target.contains("x86_64") { + tool.env.push(("Platform".into(), "X64".into())); + } + tool + }) + .next() + } + fn tool_from_vs15_instance(tool: &str, target: &str, instance: &SetupInstance) -> Option { let (bin_path, host_dylib_path, lib_path, include_path) = otry!(vs15_vc_paths(target, instance)); @@ -635,19 +659,7 @@ mod impl_ { } fn find_devenv_vs15(target: &str) -> Option { - let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; - LOCAL_MACHINE - .open(key.as_ref()) - .ok() - .and_then(|key| key.query_str("15.0").ok()) - .map(|path| { - let path = PathBuf::from(path).join(r"Common7\IDE\devenv.exe"); - let mut tool = Tool::new(path); - if target.contains("x86_64") { - tool.env.push(("Platform".into(), "X64".into())); - } - tool - }) + find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target) } // see http://stackoverflow.com/questions/328017/path-to-msbuild @@ -661,22 +673,7 @@ mod impl_ { } fn find_msbuild_vs15(target: &str) -> Option { - // Seems like this could also go through SetupConfiguration, - // or that find_msvc_15 could just use this registry key - // instead of the COM interface. - let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; - LOCAL_MACHINE - .open(key.as_ref()) - .ok() - .and_then(|key| key.query_str("15.0").ok()) - .map(|path| { - let path = PathBuf::from(path).join(r"MSBuild\15.0\Bin\MSBuild.exe"); - let mut tool = Tool::new(path); - if target.contains("x86_64") { - tool.env.push(("Platform".into(), "X64".into())); - } - tool - }) + find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target) } fn find_old_msbuild(target: &str) -> Option { From 689b549fca95258b7fcfd39667a2d11c7ec1fbc5 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Sat, 4 Aug 2018 18:06:52 +0200 Subject: [PATCH 3/4] find_tool_in_vs15_path(): Filter nonexistent executables Should probably return a dedicated "uh oh, that Visual Studio installation is broken" error rather than just None, but for now, it matches the behavior of the compiler find functions. --- src/windows_registry.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows_registry.rs b/src/windows_registry.rs index 6b6203a..8c61c7a 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -251,6 +251,7 @@ mod impl_ { .and_then(|instance| instance.installation_path().ok()) }) .map(|ip| PathBuf::from(ip).join(tool)) + .filter(|ref path| path.is_file()) .map(|path| { let mut tool = Tool::new(path); if target.contains("x86_64") { From 2ddf9564b623418e3c84ce8179f816c135c25f44 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Sat, 13 Oct 2018 17:59:26 +0200 Subject: [PATCH 4/4] Fall back on finding devenv and MSBuild via the previous registry-based method --- src/windows_registry.rs | 53 +++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/windows_registry.rs b/src/windows_registry.rs index 8c61c7a..8036647 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -237,29 +237,42 @@ mod impl_ { None } - // It may seem that the paths to Visual Studio 2017's devenv and MSBuild - // could also be retrieved from the [registry], but SetupConfiguration's - // method seems to be [more reliable], and preferred according to Microsoft. + // While the paths to Visual Studio 2017's devenv and MSBuild could + // potentially be retrieved from the registry, finding them via + // SetupConfiguration has shown to be [more reliable], and is preferred + // according to Microsoft. To help head off potential regressions though, + // we keep the registry method as a fallback option. // - // [registry]: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 // [more reliable]: https://github.com/alexcrichton/cc-rs/pull/331 fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option { - otry!(vs15_instances()) - .filter_map(|instance| { - instance - .ok() - .and_then(|instance| instance.installation_path().ok()) - }) - .map(|ip| PathBuf::from(ip).join(tool)) - .filter(|ref path| path.is_file()) - .map(|path| { - let mut tool = Tool::new(path); - if target.contains("x86_64") { - tool.env.push(("Platform".into(), "X64".into())); - } - tool - }) - .next() + let mut path = match vs15_instances() { + Some(instances) => instances + .filter_map(|instance| { + instance + .ok() + .and_then(|instance| instance.installation_path().ok()) + }).map(|path| PathBuf::from(path).join(tool)) + .find(|ref path| path.is_file()), + None => None, + }; + + if path.is_none() { + let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; + path = LOCAL_MACHINE + .open(key.as_ref()) + .ok() + .and_then(|key| key.query_str("15.0").ok()) + .map(|path| PathBuf::from(path).join(tool)) + .filter(|ref path| path.is_file()); + } + + path.map(|path| { + let mut tool = Tool::new(path); + if target.contains("x86_64") { + tool.env.push(("Platform".into(), "X64".into())); + } + tool + }) } fn tool_from_vs15_instance(tool: &str, target: &str, instance: &SetupInstance) -> Option {