--- a/cargo/private/cargo_build_script_runner/bin.rs +++ b/cargo/private/cargo_build_script_runner/bin.rs @@ -24,6 +24,85 @@ 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 +110,11 @@ 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, @@ -55,7 +135,8 @@ 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)); @@ -89,11 +170,12 @@ fn run_buildrs() -> Result<(), String> { let working_directory = resolve_rundir(&rundir, &exec_root, &manifest_dir)?; - let mut command = Command::new(exec_root.join(progname)); + let mut command = Command::new(exec_root_join(&exec_root, &progname)); command - .current_dir(&working_directory) + .current_dir(shorten_windows_path(&working_directory)) .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"); @@ -123,7 +205,7 @@ 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())); } } @@ -139,7 +221,7 @@ 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())); } } @@ -257,7 +339,8 @@ fn resolve_rundir(rundir: &str, exec_root: &Path, manifest_dir: &Path) -> Result if rundir.is_empty() { return Ok(manifest_dir.to_owned()); } - let rundir_path = Path::new(rundir); + let rundir = normalize_windows_relative_path(rundir); + let rundir_path = Path::new(&rundir); if rundir_path.is_absolute() { return Err(format!("rundir must be empty (to run in manifest path) or relative path (relative to exec root), but was {:?}", rundir)); } diff --git a/cargo/private/cargo_build_script_runner/cargo_manifest_dir.rs b/cargo/private/cargo_build_script_runner/cargo_manifest_dir.rs --- a/cargo/private/cargo_build_script_runner/cargo_manifest_dir.rs +++ b/cargo/private/cargo_build_script_runner/cargo_manifest_dir.rs @@ -282,7 +282,7 @@ /// been copied into the runfiles directory. fn drain_runfiles_dir_windows(&self) -> Result<(), String> { for dest in self.runfiles.values() { - if !self + if self .filename_suffixes_to_retain .iter() .any(|suffix| dest.ends_with(suffix)) { continue; }