use anyhow::Result;
use fs_err as fs;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;
use which::which_in;
pub fn setup_env_path(cache_dir: &Path) -> Result<OsString> {
let env_path = env::var("PATH").unwrap_or_default();
let mut env_paths: Vec<_> = env::split_paths(&env_path).collect();
if cfg!(target_os = "macos") {
let usr_llvm = "/usr/local/opt/llvm/bin".into();
let opt_llvm = "/opt/homebrew/opt/llvm/bin".into();
if cfg!(target_arch = "x86_64")
&& Path::new(&usr_llvm).is_dir()
&& !env_paths.contains(&usr_llvm)
{
env_paths.insert(0, usr_llvm);
} else if cfg!(target_arch = "aarch64")
&& Path::new(&opt_llvm).is_dir()
&& !env_paths.contains(&opt_llvm)
{
env_paths.insert(0, opt_llvm);
}
}
env_paths.push(cache_dir.to_path_buf());
Ok(env::join_paths(env_paths)?)
}
pub fn setup_llvm_tools(env_path: &OsStr, cache_dir: &Path) -> Result<()> {
symlink_llvm_tool("rust-lld", "lld-link", env_path, cache_dir)?;
symlink_llvm_tool("llvm-ar", "llvm-lib", env_path, cache_dir)?;
symlink_llvm_tool("llvm-ar", "llvm-dlltool", env_path, cache_dir)?;
Ok(())
}
pub fn setup_target_compiler_and_linker_env(cmd: &mut Command, env_target: &str, compiler: &str) {
cmd.env("TARGET_CC", compiler);
cmd.env("TARGET_CXX", compiler);
cmd.env(format!("CC_{}", env_target), compiler);
cmd.env(format!("CXX_{}", env_target), compiler);
cmd.env("TARGET_AR", "llvm-lib");
cmd.env(format!("AR_{}", env_target), "llvm-lib");
cmd.env(
format!("CARGO_TARGET_{}_LINKER", env_target.to_uppercase()),
"lld-link",
);
}
pub fn setup_cmake_env(cmd: &mut Command, target: &str, toolchain_path: PathBuf) {
let env_target = target.to_lowercase().replace('-', "_");
cmd.env("CMAKE_GENERATOR", "Ninja")
.env("CMAKE_SYSTEM_NAME", "Windows")
.env(
format!("CMAKE_TOOLCHAIN_FILE_{}", env_target),
toolchain_path,
);
}
pub fn rustc_target_bin_dir() -> Result<PathBuf> {
let output = Command::new("rustc")
.args(["--print", "target-libdir"])
.output()?;
let stdout = String::from_utf8(output.stdout)?;
let lib_dir = Path::new(&stdout);
let bin_dir = lib_dir.parent().unwrap().join("bin");
Ok(bin_dir)
}
fn symlink_llvm_tool(
tool: &str,
link_name: &str,
env_path: &OsStr,
cache_dir: &Path,
) -> Result<()> {
if which_in(link_name, Some(env_path), env::current_dir()?).is_err() {
let bin_dir = rustc_target_bin_dir()?;
let rust_tool = bin_dir.join(tool);
if rust_tool.exists() {
#[cfg(windows)]
{
let symlink = cache_dir.join(format!("{}.exe", link_name));
if symlink.is_symlink() || symlink.is_file() {
fs::remove_file(&symlink)?;
}
fs_err::os::windows::fs::symlink_file(rust_tool, symlink)?;
}
#[cfg(unix)]
{
let symlink = cache_dir.join(link_name);
if symlink.is_symlink() || symlink.is_file() {
fs::remove_file(&symlink)?;
}
fs_err::os::unix::fs::symlink(rust_tool, symlink)?;
}
}
}
Ok(())
}
#[cfg(target_family = "unix")]
pub fn adjust_canonicalization(p: String) -> String {
p
}
#[cfg(target_os = "windows")]
pub fn adjust_canonicalization(p: String) -> String {
const VERBATIM_PREFIX: &str = r#"\\?\"#;
if let Some(p) = p.strip_prefix(VERBATIM_PREFIX) {
p.to_string()
} else {
p
}
}
pub fn default_build_target_from_config(workdir: &Path) -> Result<Option<String>> {
let output = Command::new("cargo")
.current_dir(workdir)
.args([
"config",
"get",
"-Z",
"unstable-options",
"--format",
"json-value",
"build.target",
])
.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly")
.output()?;
if !output.status.success() {
return Ok(None);
}
let stdout = String::from_utf8(output.stdout)?;
let target = stdout.trim().trim_matches('"');
Ok(Some(target.to_string()))
}
pub fn get_rustflags(workdir: &Path, target: &str) -> Result<Option<cargo_config2::Flags>> {
let cargo_config = cargo_config2::Config::load_with_cwd(workdir)?;
let rustflags = cargo_config.rustflags(target)?;
Ok(rustflags)
}
pub fn is_static_crt_enabled(workdir: &Path, target: &str) -> Result<bool> {
if let Ok(rustflags) = env::var("RUSTFLAGS") {
if rustflags.contains("+crt-static") {
return Ok(true);
}
}
if let Some(flags) = get_rustflags(workdir, target)? {
for flag in &flags.flags {
if flag.contains("+crt-static") {
return Ok(true);
}
}
}
Ok(false)
}
pub fn http_agent() -> Result<ureq::Agent> {
#[cfg(feature = "native-tls")]
{
let builder = ureq::config::Config::builder();
let agent = builder
.tls_config(
ureq::tls::TlsConfig::builder()
.provider(ureq::tls::TlsProvider::NativeTls)
.root_certs(ureq::tls::RootCerts::PlatformVerifier)
.build(),
)
.build()
.new_agent();
return Ok(agent);
}
#[cfg(all(not(feature = "native-tls"), feature = "rustls-tls"))]
{
let builder = ureq::config::Config::builder();
let agent = builder
.tls_config(
ureq::tls::TlsConfig::builder()
.provider(ureq::tls::TlsProvider::Rustls)
.root_certs(ureq::tls::RootCerts::PlatformVerifier)
.build(),
)
.build()
.new_agent();
Ok(agent)
}
#[cfg(not(any(feature = "native-tls", feature = "rustls-tls")))]
{
let agent = ureq::agent();
return Ok(agent);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_is_static_crt_enabled_env_var() {
unsafe {
env::set_var("RUSTFLAGS", "-C target-feature=+crt-static");
}
let result = is_static_crt_enabled(Path::new("."), "x86_64-pc-windows-msvc");
assert!(result.is_ok());
assert!(result.unwrap());
unsafe {
env::set_var("RUSTFLAGS", "-C opt-level=3");
}
let result = is_static_crt_enabled(Path::new("."), "x86_64-pc-windows-msvc");
assert!(result.is_ok());
assert!(!result.unwrap());
unsafe {
env::remove_var("RUSTFLAGS");
}
}
#[test]
fn test_is_static_crt_enabled_no_flags() {
unsafe {
env::remove_var("RUSTFLAGS");
}
let result = is_static_crt_enabled(Path::new("."), "x86_64-pc-windows-msvc");
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_is_static_crt_enabled_config_file() {
let test_dir = std::env::temp_dir().join("test_cargo_config");
std::fs::create_dir_all(&test_dir).unwrap();
std::fs::create_dir_all(test_dir.join(".cargo")).unwrap();
let config_content = r#"[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]"#;
std::fs::write(test_dir.join(".cargo/config.toml"), config_content).unwrap();
unsafe {
env::remove_var("RUSTFLAGS");
}
let result = is_static_crt_enabled(&test_dir, "x86_64-pc-windows-msvc");
assert!(result.is_ok());
assert!(result.unwrap());
std::fs::remove_dir_all(&test_dir).unwrap();
}
}