From 5886a518402b2884681e07e4b2d270bad632b2c4 Mon Sep 17 00:00:00 2001 From: Xiaoyi Shi Date: Wed, 3 Jun 2026 17:00:00 +0800 Subject: [PATCH] rustc_session: apply -Zremap-cwd-prefix when building the file mapping `-Zremap-cwd-prefix=` was applied inside `parse_remap_path_prefix`, which resolved `std::env::current_dir()` and pushed `(cwd, to)` onto `remap_path_prefix`. That option is `[TRACKED_NO_CRATE_HASH]`, so the absolute working directory entered the incremental command-line-args hash, and building the same sources from a different directory (e.g. a Bazel sandbox, whose path contains a per-action counter) purged the whole incremental cache even though the remapped output is identical. `--remap-path-prefix` and `-Zremap-cwd-prefix` already have separate options (`remap_path_prefix` and `remap_cwd_prefix`). Keep each option holding exactly its own flag, and apply `-Zremap-cwd-prefix` in `file_path_mapping` -- i.e. when building the (untracked) applied `FilePathMapping` -- rather than when parsing/tracking options. The absolute cwd then lives only in the runtime mapping, never in a tracked field. Output is unchanged (the cwd entry is still appended last). Tracking stays sound: the cwd's effect is tracked via `remap_cwd_prefix` (the target prefix baked into output) and `working_dir` (whose `RealFileName` hash includes the real path only when it was not fully remapped, i.e. exactly when the cwd can leak into output). The stable `--remap-path-prefix` flag is unchanged in data and in tracking. A unit test guards against the cwd being merged back into `remap_path_prefix`. --- compiler/rustc_interface/src/tests.rs | 29 +++++++++++- compiler/rustc_session/src/config.rs | 44 ++++++++++++------- .../src/compiler-flags/remap-cwd-prefix.md | 6 +++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0498d835df5f5..f8b63ea8a8f13 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -25,7 +25,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib}; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts}; use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; -use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; +use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, @@ -175,6 +175,33 @@ fn test_can_print_warnings() { }); } +// `-Zremap-cwd-prefix` must not be merged into the tracked `remap_path_prefix` option: +// storing the absolute cwd there invalidates the incremental cache across build +// directories (see #132132). It must instead be applied via `file_path_mapping`. +#[test] +fn test_remap_cwd_prefix_not_in_remap_path_prefix() { + sess_and_cfg( + &["--remap-path-prefix=/explicit=mapped", "-Zremap-cwd-prefix=cwd-mapped"], + |sess, _cfg| { + // The tracked option holds only the explicit `--remap-path-prefix` entry. + assert_eq!( + sess.opts.remap_path_prefix, + vec![(PathBuf::from("/explicit"), PathBuf::from("mapped"))], + ); + + // ... but the cwd remapping is still applied via `file_path_mapping`. + let cwd = std::env::current_dir().unwrap(); + let remapped = sess + .opts + .file_path_mapping() + .to_real_filename(&RealFileName::empty(), cwd.join("foo.rs")) + .path(RemapPathScopeComponents::DEBUGINFO) + .to_path_buf(); + assert_eq!(remapped, PathBuf::from("cwd-mapped/foo.rs")); + }, + ); +} + #[test] fn test_output_types_tracking_hash_different_paths() { let mut v1 = Options::default(); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e82f67eac5e9f..5bcfdada4ec9e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1375,9 +1375,21 @@ pub fn host_tuple() -> &'static str { fn file_path_mapping( remap_path_prefix: Vec<(PathBuf, PathBuf)>, + remap_cwd_prefix: Option<&Path>, remap_path_scope: RemapPathScopeComponents, ) -> FilePathMapping { - FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope) + // Apply `-Zremap-cwd-prefix` here rather than in `parse_remap_path_prefix`, so the + // absolute cwd is never stored in the tracked `remap_path_prefix` option (#132132). + let cwd_remap = if let Some(to) = remap_cwd_prefix + && let Ok(cwd) = std::env::current_dir() + { + Some((cwd, to.to_path_buf())) + } else { + None + }; + // The cwd remapping is appended last: `map_prefix` tries entries in reverse order, so this + // keeps `-Zremap-cwd-prefix` taking precedence over `--remap-path-prefix`, as documented. + FilePathMapping::new(remap_path_prefix.into_iter().chain(cwd_remap).collect(), remap_path_scope) } impl Default for Options { @@ -1389,7 +1401,8 @@ impl Default for Options { // to create a default working directory. let working_dir = { let working_dir = std::env::current_dir().unwrap(); - let file_mapping = file_path_mapping(Vec::new(), RemapPathScopeComponents::empty()); + let file_mapping = + file_path_mapping(Vec::new(), None, RemapPathScopeComponents::empty()); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; @@ -1450,7 +1463,11 @@ impl Options { } pub fn file_path_mapping(&self) -> FilePathMapping { - file_path_mapping(self.remap_path_prefix.clone(), self.remap_path_scope) + file_path_mapping( + self.remap_path_prefix.clone(), + self.unstable_opts.remap_cwd_prefix.as_deref(), + self.remap_path_scope, + ) } /// Returns `true` if there will be an output file generated. @@ -2384,9 +2401,8 @@ pub fn parse_externs( fn parse_remap_path_prefix( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, - unstable_opts: &UnstableOptions, ) -> Vec<(PathBuf, PathBuf)> { - let mut mapping: Vec<(PathBuf, PathBuf)> = matches + matches .opt_strs("remap-path-prefix") .into_iter() .map(|remap| match remap.rsplit_once('=') { @@ -2395,15 +2411,7 @@ fn parse_remap_path_prefix( } Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) - .collect(); - match &unstable_opts.remap_cwd_prefix { - Some(to) => match std::env::current_dir() { - Ok(cwd) => mapping.push((cwd, to.clone())), - Err(_) => (), - }, - None => (), - }; - mapping + .collect() } fn parse_logical_env( @@ -2661,7 +2669,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let externs = parse_externs(early_dcx, matches, &unstable_opts); - let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); + let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches); let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts); let pretty = parse_pretty(early_dcx, &unstable_opts); @@ -2729,7 +2737,11 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M early_dcx.early_fatal(format!("Current directory is invalid: {e}")); }); - let file_mapping = file_path_mapping(remap_path_prefix.clone(), remap_path_scope); + let file_mapping = file_path_mapping( + remap_path_prefix.clone(), + unstable_opts.remap_cwd_prefix.as_deref(), + remap_path_scope, + ); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; diff --git a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md index 3890a12b7e684..bcf526694ad94 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md @@ -16,6 +16,12 @@ directory from build output, while allowing the command line to be universally reproducible, such that the same execution will work on all machines, regardless of build environment. +Unlike passing the equivalent mapping through `--remap-path-prefix`, the current +working directory does not take part in incremental compilation's dependency +tracking. Building the same sources from different directories (for example, a +sandboxed or per-build checkout path) therefore reuses the incremental cache +rather than invalidating it. + ## Example ```sh # This would produce an absolute path to main.rs in build outputs of