Skip to content

Commit

Permalink
Support finding windows tools on non-Windows host
Browse files Browse the repository at this point in the history
  • Loading branch information
cavivie committed Jan 25, 2024
1 parent 8cf5455 commit 3162fbe
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 51 deletions.
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3559,7 +3559,6 @@ impl Tool {
Self::with_features(path, clang_driver, false, cargo_output)
}

#[cfg(windows)]
/// Explicitly set the `ToolFamily`, skipping name-based detection.
fn with_family(path: PathBuf, family: ToolFamily) -> Self {
Self {
Expand Down
160 changes: 110 additions & 50 deletions src/windows_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,39 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper module to probe the Windows Registry when looking for
//! windows-specific tools.
//! A helper module to looking for windows-specific tools:
//! 1. On Windows host, probe the Windows Registry if needed;
//! 2. On non-Windows host, check specified environment variables.
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::upper_case_acronyms)]

use std::process::Command;

use crate::Tool;
#[cfg(windows)]
use crate::ToolFamily;

#[cfg(windows)]
const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };

/// Attempts to find a tool within an MSVC installation using the Windows
/// registry as a point to search from.
#[derive(Copy, Clone)]
struct TargetArch<'a>(pub &'a str);

impl PartialEq<&str> for TargetArch<'_> {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}

impl<'a> From<TargetArch<'a>> for &'a str {
fn from(target: TargetArch<'a>) -> Self {
target.0
}
}

/// Attempts to find a tool within an MSVC installation:
/// 1. On Windows host, using the Windows registry as a point to search from;
/// 2. On non-Windows host, using related environment variables to search from.
///
///
/// The `target` argument is the target that the tool should work for (e.g.
/// compile or link for) and the `tool` argument is the tool to find (e.g.
Expand All @@ -41,13 +58,6 @@ pub fn find(target: &str, tool: &str) -> Option<Command> {
/// Similar to the `find` function above, this function will attempt the same
/// operation (finding a MSVC tool in a local install) but instead returns a
/// `Tool` which may be introspected.
#[cfg(not(windows))]
pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
None
}

/// Documented above.
#[cfg(windows)]
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// This logic is all tailored for MSVC, if we're not that then bail out
// early.
Expand All @@ -56,15 +66,16 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}

// Split the target to get the arch.
let target = impl_::TargetArch(target.split_once('-')?.0);
let target = TargetArch(target.split_once('-')?.0);

// Looks like msbuild isn't located in the same location as other tools like
// cl.exe and lib.exe. To handle this we probe for it manually with
// dedicated registry keys.
// cl.exe and lib.exe.
if tool.contains("msbuild") {
return impl_::find_msbuild(target);
}

// Looks like devenv isn't located in the same location as other tools like
// cl.exe and lib.exe.
if tool.contains("devenv") {
return impl_::find_devenv(target);
}
Expand Down Expand Up @@ -103,17 +114,8 @@ pub enum VsVers {
///
/// This is used by the cmake crate to figure out the correct
/// generator.
#[cfg(not(windows))]
pub fn find_vs_version() -> Result<VsVers, String> {
Err("not windows".to_string())
}

/// Documented above
#[cfg(windows)]
pub fn find_vs_version() -> Result<VsVers, String> {
use std::env;

match env::var("VisualStudioVersion") {
match std::env::var("VisualStudioVersion") {
Ok(version) => match &version[..] {
"17.0" => Ok(VsVers::Vs17),
"16.0" => Ok(VsVers::Vs16),
Expand Down Expand Up @@ -157,6 +159,7 @@ pub fn find_vs_version() -> Result<VsVers, String> {
}
}

/// Windows Implementation.
#[cfg(windows)]
mod impl_ {
use crate::com;
Expand All @@ -174,24 +177,9 @@ mod impl_ {
use std::process::Command;
use std::str::FromStr;

use super::MSVC_FAMILY;
use super::{TargetArch, MSVC_FAMILY};
use crate::Tool;

#[derive(Copy, Clone)]
pub struct TargetArch<'a>(pub &'a str);

impl PartialEq<&str> for TargetArch<'_> {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}

impl<'a> From<TargetArch<'a>> for &'a str {
fn from(target: TargetArch<'a>) -> Self {
target.0
}
}

struct MsvcTool {
tool: PathBuf,
libs: Vec<PathBuf>,
Expand Down Expand Up @@ -242,7 +230,7 @@ mod impl_ {
}

/// Attempt to find the tool using environment variables set by vcvars.
pub fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
// Early return if the environment doesn't contain a VC install.
if env::var_os("VCINSTALLDIR").is_none() {
return None;
Expand Down Expand Up @@ -394,7 +382,7 @@ mod impl_ {
.collect()
}

pub fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let iter = vs15plus_instances(target)?;
iter.into_iter()
.filter_map(|instance| {
Expand Down Expand Up @@ -565,7 +553,7 @@ mod impl_ {

// For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
pub fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("14.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
add_sdks(&mut tool, target)?;
Expand Down Expand Up @@ -615,7 +603,7 @@ mod impl_ {
}

// For MSVC 12 we need to find the Windows 8.1 SDK.
pub fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("12.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
Expand All @@ -631,7 +619,7 @@ mod impl_ {
}

// For MSVC 11 we need to find the Windows 8 SDK.
pub fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("11.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
Expand Down Expand Up @@ -885,7 +873,7 @@ mod impl_ {
max_key
}

pub fn has_msbuild_version(version: &str) -> bool {
pub(super) fn has_msbuild_version(version: &str) -> bool {
match version {
"17.0" => {
find_msbuild_vs17(TargetArch("x86_64")).is_some()
Expand All @@ -912,16 +900,18 @@ mod impl_ {
}
}

pub fn find_devenv(target: TargetArch<'_>) -> Option<Tool> {
// To find devenv we probe for it manually with dedicated registry keys.
pub(super) fn find_devenv(target: TargetArch<'_>) -> Option<Tool> {
find_devenv_vs15(target)
}

fn find_devenv_vs15(target: TargetArch<'_>) -> Option<Tool> {
find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target)
}

// To find msbuild we probe for it manually with dedicated registry keys.
// see http://stackoverflow.com/questions/328017/path-to-msbuild
pub fn find_msbuild(target: TargetArch<'_>) -> Option<Tool> {
pub(super) fn find_msbuild(target: TargetArch<'_>) -> Option<Tool> {
// VS 15 (2017) changed how to locate msbuild
if let Some(r) = find_msbuild_vs17(target) {
Some(r)
Expand Down Expand Up @@ -957,3 +947,73 @@ mod impl_ {
})
}
}

/// Non-Windows Implementation.
#[cfg(not(windows))]
mod impl_ {
use std::{env, ffi::OsString};

use super::{TargetArch, MSVC_FAMILY};
use crate::Tool;

/// Finding msbuild.exe tool under unix system is not currently supported.
/// Maybe can check it using an environment variable looks like `MSBUILD_BIN`.
pub(super) fn find_msbuild(_target: TargetArch<'_>) -> Option<Tool> {
None
}

// Finding devenv.exe tool under unix system is not currently supported.
// Maybe can check it using an environment variable looks like `DEVENV_BIN`.
pub(super) fn find_devenv(_target: TargetArch<'_>) -> Option<Tool> {
None
}

/// Attempt to find the tool using environment variables set by vcvars.
pub(super) fn find_msvc_environment(tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
// Early return if the environment doesn't contain a VC install.
let _vc_install_dir = env::var_os("VCINSTALLDIR")?;
let _vs_install_dir = env::var_os("VSINSTALLDIR")?;

// Should we take the path of tool for the v[c|s] install dir?
// Both `VCINSTALLDIR` / `VSINSTALLDIR` are unused currently.

// Fallback to simply using the current environment.
env::var_os("PATH")
.and_then(|path: OsString| {
env::split_paths(&path)
.map(|p| p.join(tool))
.find(|p| p.exists())
})
.map(|path| Tool::with_family(path.into(), MSVC_FAMILY))
}

pub(super) fn find_msvc_15plus(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
None
}

// For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
pub(super) fn find_msvc_14(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
None
}

// For MSVC 12 we need to find the Windows 8.1 SDK.
pub(super) fn find_msvc_12(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
None
}

// For MSVC 11 we need to find the Windows 8 SDK.
pub(super) fn find_msvc_11(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
None
}

pub(super) fn has_msbuild_version(version: &str) -> bool {
match version {
"17.0" => false,
"16.0" => false,
"15.0" => false,
"12.0" | "14.0" => false,
_ => false,
}
}
}

0 comments on commit 3162fbe

Please sign in to comment.