diff --git a/cargo/private/cargo_build_script_runner/bin.rs b/cargo/private/cargo_build_script_runner/bin.rs index 79c6afd1a..40370c909 100644 --- a/cargo/private/cargo_build_script_runner/bin.rs +++ b/cargo/private/cargo_build_script_runner/bin.rs @@ -18,12 +18,87 @@ use std::collections::BTreeMap; use std::env; use std::fs::{create_dir_all, read_dir, read_to_string, remove_file, write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; use cargo_build_script_runner::cargo_manifest_dir::{remove_symlink, symlink, RunfilesMaker}; use cargo_build_script_runner::{BuildScriptOutput, CompileAndLinkFlags}; +#[cfg(windows)] +fn normalize_windows_relative_path(path: &str) -> String { + path.replace('/', "\\") +} + +#[cfg(not(windows))] +fn normalize_windows_relative_path(path: &str) -> String { + path.to_owned() +} + +fn exec_root_join(exec_root: &Path, relative: &str) -> PathBuf { + exec_root.join(normalize_windows_relative_path(relative)) +} + +#[cfg(windows)] +#[link(name = "Kernel32")] +unsafe extern "system" { + fn GetShortPathNameW(lpszLongPath: *const u16, lpszShortPath: *mut u16, cchBuffer: u32) -> u32; +} + +#[cfg(windows)] +fn windows_extended_length_path(path: &Path) -> PathBuf { + let path = path.as_os_str().to_string_lossy(); + if path.starts_with(r"\\?\") { + PathBuf::from(path.to_string()) + } else if let Some(stripped) = path.strip_prefix(r"\\") { + PathBuf::from(format!(r"\\?\UNC\{stripped}")) + } else { + PathBuf::from(format!(r"\\?\{path}")) + } +} + +#[cfg(windows)] +fn shorten_windows_path(path: &Path) -> PathBuf { + use std::os::windows::ffi::OsStrExt; + + let long_path = windows_extended_length_path(path); + let long_path = long_path + .as_os_str() + .encode_wide() + .chain(Some(0)) + .collect::>(); + + unsafe { + let required = GetShortPathNameW(long_path.as_ptr(), std::ptr::null_mut(), 0); + if required == 0 { + return path.to_owned(); + } + + let mut short_path = vec![0u16; required as usize]; + let written = GetShortPathNameW(long_path.as_ptr(), short_path.as_mut_ptr(), required); + if written == 0 { + path.to_owned() + } else { + let short_path = PathBuf::from( + ::from_wide( + &short_path[..written as usize], + ), + ); + PathBuf::from( + short_path + .as_os_str() + .to_string_lossy() + .trim_start_matches(r"\\?\") + .to_string(), + ) + } + } +} + +#[cfg(not(windows))] +fn shorten_windows_path(path: &Path) -> PathBuf { + path.to_owned() +} + fn run_buildrs() -> Result<(), String> { // We use exec_root.join rather than std::fs::canonicalize, to avoid resolving symlinks, as // some execution strategies and remote execution environments may use symlinks in ways which @@ -31,10 +106,12 @@ fn run_buildrs() -> Result<(), String> { // directory - resolving these may cause tools which inspect $0, or try to resolve files // relative to themselves, to fail. let exec_root = env::current_dir().expect("Failed to get current directory"); - let manifest_dir_env = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR was not set"); - let rustc_env = env::var("RUSTC").expect("RUSTC was not set"); - let manifest_dir = exec_root.join(manifest_dir_env); - let rustc = exec_root.join(&rustc_env); + let manifest_dir_env = normalize_windows_relative_path( + &env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR was not set"), + ); + let rustc_env = normalize_windows_relative_path(&env::var("RUSTC").expect("RUSTC was not set")); + let manifest_dir = exec_root_join(&exec_root, &manifest_dir_env); + let rustc = shorten_windows_path(&exec_root_join(&exec_root, &rustc_env)); let Args { progname, crate_links, @@ -52,7 +129,7 @@ fn run_buildrs() -> Result<(), String> { cargo_manifest_maker.create_runfiles_dir().unwrap(); - let out_dir_abs = exec_root.join(&out_dir); + let out_dir_abs = shorten_windows_path(&exec_root_join(&exec_root, &out_dir)); // For some reason Google's RBE does not create the output directory, force create it. create_dir_all(&out_dir_abs) .unwrap_or_else(|_| panic!("Failed to make output directory: {:?}", out_dir_abs)); @@ -84,11 +161,12 @@ fn run_buildrs() -> Result<(), String> { let target_env_vars = get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc"); - let mut command = Command::new(exec_root.join(progname)); + let mut command = Command::new(exec_root_join(&exec_root, &progname)); command - .current_dir(&manifest_dir) + .current_dir(shorten_windows_path(&manifest_dir)) .envs(target_env_vars) .env("OUT_DIR", &out_dir_abs) + .env("RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER", "1") .env("CARGO_MANIFEST_DIR", manifest_dir) .env("RUSTC", rustc) .env("RUST_BACKTRACE", "full"); @@ -118,7 +196,10 @@ fn run_buildrs() -> Result<(), String> { for tool_env_var in &["CC", "CXX", "LD"] { if let Some(tool_path) = env::var_os(tool_env_var) { - command.env(tool_env_var, exec_root.join(tool_path)); + command.env( + tool_env_var, + exec_root_join(&exec_root, &tool_path.to_string_lossy()), + ); } } @@ -134,6 +215,6 @@ fn run_buildrs() -> Result<(), String> { command.env_remove("AR"); command.env_remove("ARFLAGS"); } else { - command.env("AR", exec_root.join(ar_path)); + command.env("AR", exec_root_join(&exec_root, &ar_path.to_string_lossy())); } }