mod setup;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::error::Result;
use crate::installer::InstallInfo;
use crate::version::Architecture;
pub use setup::{
apply_environment, generate_activation_script, generate_all_activation_scripts,
save_activation_script, setup_environment,
};
#[cfg(windows)]
pub use setup::write_to_registry;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MsvcEnvironment {
pub vc_install_dir: PathBuf,
pub vc_tools_install_dir: PathBuf,
pub vc_tools_version: String,
pub windows_sdk_dir: PathBuf,
pub windows_sdk_version: String,
pub include_paths: Vec<PathBuf>,
pub lib_paths: Vec<PathBuf>,
pub bin_paths: Vec<PathBuf>,
pub arch: Architecture,
pub host_arch: Architecture,
}
impl MsvcEnvironment {
pub fn from_install_info(
msvc_info: &InstallInfo,
sdk_info: Option<&InstallInfo>,
host_arch: Architecture,
) -> Result<Self> {
let base_dir = msvc_info
.install_path
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.parent())
.and_then(|p| p.parent())
.map(|p| p.to_path_buf())
.unwrap_or_else(|| msvc_info.install_path.clone());
let vc_install_dir = base_dir.join("VC");
let vc_tools_install_dir = msvc_info.install_path.clone();
let vc_tools_version = msvc_info.version.clone();
let (windows_sdk_dir, windows_sdk_version) = if let Some(sdk) = sdk_info {
(sdk.install_path.clone(), sdk.version.clone())
} else {
(
base_dir.join("Windows Kits").join("10"),
"10.0.22621.0".to_string(),
)
};
let arch = msvc_info.arch;
let include_paths = Self::build_include_paths(
&vc_tools_install_dir,
&windows_sdk_dir,
&windows_sdk_version,
);
let lib_paths = Self::build_lib_paths(
&vc_tools_install_dir,
&windows_sdk_dir,
&windows_sdk_version,
arch,
);
let bin_paths = Self::build_bin_paths(
&vc_tools_install_dir,
&windows_sdk_dir,
&windows_sdk_version,
host_arch,
arch,
);
Ok(Self {
vc_install_dir,
vc_tools_install_dir,
vc_tools_version,
windows_sdk_dir,
windows_sdk_version,
include_paths,
lib_paths,
bin_paths,
arch,
host_arch,
})
}
fn build_include_paths(vc_tools_dir: &Path, sdk_dir: &Path, sdk_version: &str) -> Vec<PathBuf> {
vec![
vc_tools_dir.join("include"),
sdk_dir.join("Include").join(sdk_version).join("ucrt"),
sdk_dir.join("Include").join(sdk_version).join("shared"),
sdk_dir.join("Include").join(sdk_version).join("um"),
sdk_dir.join("Include").join(sdk_version).join("winrt"),
sdk_dir.join("Include").join(sdk_version).join("cppwinrt"),
]
}
fn build_lib_paths(
vc_tools_dir: &Path,
sdk_dir: &Path,
sdk_version: &str,
arch: Architecture,
) -> Vec<PathBuf> {
let arch_str = arch.to_string();
vec![
vc_tools_dir.join("lib").join(&arch_str),
sdk_dir
.join("Lib")
.join(sdk_version)
.join("ucrt")
.join(&arch_str),
sdk_dir
.join("Lib")
.join(sdk_version)
.join("um")
.join(&arch_str),
]
}
fn build_bin_paths(
vc_tools_dir: &Path,
sdk_dir: &Path,
sdk_version: &str,
host_arch: Architecture,
target_arch: Architecture,
) -> Vec<PathBuf> {
let host_dir = host_arch.msvc_host_dir();
let target_dir = target_arch.msvc_target_dir();
vec![
vc_tools_dir.join("bin").join(host_dir).join(target_dir),
sdk_dir
.join("bin")
.join(sdk_version)
.join(target_arch.to_string()),
]
}
pub fn has_cl_exe(&self) -> bool {
self.bin_paths.iter().any(|p| p.join("cl.exe").exists())
}
pub fn cl_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("cl.exe"))
.find(|p| p.exists())
}
pub fn link_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("link.exe"))
.find(|p| p.exists())
}
pub fn lib_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("lib.exe"))
.find(|p| p.exists())
}
pub fn ml64_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("ml64.exe"))
.find(|p| p.exists())
}
pub fn nmake_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("nmake.exe"))
.find(|p| p.exists())
}
pub fn rc_exe_path(&self) -> Option<PathBuf> {
self.bin_paths
.iter()
.map(|p| p.join("rc.exe"))
.find(|p| p.exists())
}
pub fn tool_paths(&self) -> ToolPaths {
ToolPaths {
cl: self.cl_exe_path(),
link: self.link_exe_path(),
lib: self.lib_exe_path(),
ml64: self.ml64_exe_path(),
nmake: self.nmake_exe_path(),
rc: self.rc_exe_path(),
}
}
pub fn include_path_string(&self) -> String {
self.include_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";")
}
pub fn lib_path_string(&self) -> String {
self.lib_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";")
}
pub fn bin_path_string(&self) -> String {
self.bin_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";")
}
pub fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"vc_install_dir": self.vc_install_dir,
"vc_tools_install_dir": self.vc_tools_install_dir,
"vc_tools_version": self.vc_tools_version,
"windows_sdk_dir": self.windows_sdk_dir,
"windows_sdk_version": self.windows_sdk_version,
"include_paths": self.include_paths,
"lib_paths": self.lib_paths,
"bin_paths": self.bin_paths,
"arch": self.arch.to_string(),
"host_arch": self.host_arch.to_string(),
"tools": {
"cl": self.cl_exe_path(),
"link": self.link_exe_path(),
"lib": self.lib_exe_path(),
"ml64": self.ml64_exe_path(),
"nmake": self.nmake_exe_path(),
"rc": self.rc_exe_path(),
}
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolPaths {
pub cl: Option<PathBuf>,
pub link: Option<PathBuf>,
pub lib: Option<PathBuf>,
pub ml64: Option<PathBuf>,
pub nmake: Option<PathBuf>,
pub rc: Option<PathBuf>,
}
pub fn get_env_vars(env: &MsvcEnvironment) -> HashMap<String, String> {
let mut vars = HashMap::new();
vars.insert(
"VCINSTALLDIR".to_string(),
env.vc_install_dir.display().to_string(),
);
vars.insert(
"VCToolsInstallDir".to_string(),
env.vc_tools_install_dir.display().to_string(),
);
vars.insert("VCToolsVersion".to_string(), env.vc_tools_version.clone());
vars.insert(
"WindowsSdkDir".to_string(),
env.windows_sdk_dir.display().to_string(),
);
vars.insert(
"WindowsSDKVersion".to_string(),
format!("{}\\", env.windows_sdk_version),
);
vars.insert(
"WindowsSdkBinPath".to_string(),
env.windows_sdk_dir
.join("bin")
.join(&env.windows_sdk_version)
.display()
.to_string(),
);
let include = env
.include_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";");
vars.insert("INCLUDE".to_string(), include);
let lib = env
.lib_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";");
vars.insert("LIB".to_string(), lib);
let path = env
.bin_paths
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(";");
vars.insert("PATH".to_string(), path);
vars.insert("Platform".to_string(), env.arch.to_string());
vars.insert("VSCMD_ARG_HOST_ARCH".to_string(), env.host_arch.to_string());
vars.insert("VSCMD_ARG_TGT_ARCH".to_string(), env.arch.to_string());
vars
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_env_vars() {
let env = MsvcEnvironment {
vc_install_dir: PathBuf::from("C:\\VC"),
vc_tools_install_dir: PathBuf::from("C:\\VC\\Tools\\MSVC\\14.40"),
vc_tools_version: "14.40.33807".to_string(),
windows_sdk_dir: PathBuf::from("C:\\Windows Kits\\10"),
windows_sdk_version: "10.0.22621.0".to_string(),
include_paths: vec![PathBuf::from("C:\\include")],
lib_paths: vec![PathBuf::from("C:\\lib")],
bin_paths: vec![PathBuf::from("C:\\bin")],
arch: Architecture::X64,
host_arch: Architecture::X64,
};
let vars = get_env_vars(&env);
assert!(vars.contains_key("VCINSTALLDIR"));
assert!(vars.contains_key("INCLUDE"));
assert!(vars.contains_key("LIB"));
assert!(vars.contains_key("PATH"));
}
}