Rollup merge of #139550 - Urgau:rmeta-remap-path-scope, r=nnethercote

Fix `-Zremap-path-scope` rmeta handling

This PR fixes the conditional remapping (`-Zremap-path-scope`) of rmeta file paths ~~by using the `debuginfo` scope~~ by conditionally embedding the local path in addition to the remapped path.

Fixes https://github.com/rust-lang/rust/issues/139217
This commit is contained in:
Stuart Cook
2025-05-06 16:28:38 +10:00
committed by GitHub
9 changed files with 205 additions and 64 deletions

View File

@@ -551,8 +551,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
match source_file.name {
FileName::Real(ref original_file_name) => {
// FIXME: This should probably to conditionally remapped under
// a RemapPathScopeComponents but which one?
let adapted_file_name = source_map
.path_mapping()
.to_embeddable_absolute_path(original_file_name.clone(), working_directory);

View File

@@ -24,7 +24,8 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION};
use rustc_span::source_map::FilePathMapping;
use rustc_span::{
FileName, FileNameDisplayPreference, RealFileName, SourceFileHashAlgorithm, Symbol, sym,
FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName,
SourceFileHashAlgorithm, Symbol, sym,
};
use rustc_target::spec::{
FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple,
@@ -1320,6 +1321,11 @@ fn file_path_mapping(
} else {
FileNameDisplayPreference::Local
},
if unstable_opts.remap_path_scope.is_all() {
FileNameEmbeddablePreference::RemappedOnly
} else {
FileNameEmbeddablePreference::LocalAndRemapped
},
)
}

View File

@@ -224,7 +224,7 @@ pub fn with_metavar_spans<R>(f: impl FnOnce(&MetavarSpansMap) -> R) -> R {
// FIXME: We should use this enum or something like it to get rid of the
// use of magic `/rust/1.x/...` paths across the board.
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable)]
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable, Encodable)]
pub enum RealFileName {
LocalPath(PathBuf),
/// For remapped paths (namely paths into libstd that have been mapped
@@ -250,28 +250,6 @@ impl Hash for RealFileName {
}
}
// This is functionally identical to #[derive(Encodable)], with the exception of
// an added assert statement
impl<S: Encoder> Encodable<S> for RealFileName {
fn encode(&self, encoder: &mut S) {
match *self {
RealFileName::LocalPath(ref local_path) => {
encoder.emit_u8(0);
local_path.encode(encoder);
}
RealFileName::Remapped { ref local_path, ref virtual_name } => {
encoder.emit_u8(1);
// For privacy and build reproducibility, we must not embed host-dependant path
// in artifacts if they have been remapped by --remap-path-prefix
assert!(local_path.is_none());
local_path.encode(encoder);
virtual_name.encode(encoder);
}
}
}
}
impl RealFileName {
/// Returns the path suitable for reading from the file system on the local host,
/// if this information exists.
@@ -368,6 +346,16 @@ impl From<PathBuf> for FileName {
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum FileNameEmbeddablePreference {
/// If a remapped path is available, only embed the `virtual_path` and omit the `local_path`.
///
/// Otherwise embed the local-path into the `virtual_path`.
RemappedOnly,
/// Embed the original path as well as its remapped `virtual_path` component if available.
LocalAndRemapped,
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum FileNameDisplayPreference {
/// Display the path after the application of rewrite rules provided via `--remap-path-prefix`.

View File

@@ -1108,18 +1108,28 @@ pub fn get_source_map() -> Option<Arc<SourceMap>> {
pub struct FilePathMapping {
mapping: Vec<(PathBuf, PathBuf)>,
filename_display_for_diagnostics: FileNameDisplayPreference,
filename_embeddable_preference: FileNameEmbeddablePreference,
}
impl FilePathMapping {
pub fn empty() -> FilePathMapping {
FilePathMapping::new(Vec::new(), FileNameDisplayPreference::Local)
FilePathMapping::new(
Vec::new(),
FileNameDisplayPreference::Local,
FileNameEmbeddablePreference::RemappedOnly,
)
}
pub fn new(
mapping: Vec<(PathBuf, PathBuf)>,
filename_display_for_diagnostics: FileNameDisplayPreference,
filename_embeddable_preference: FileNameEmbeddablePreference,
) -> FilePathMapping {
FilePathMapping { mapping, filename_display_for_diagnostics }
FilePathMapping {
mapping,
filename_display_for_diagnostics,
filename_embeddable_preference,
}
}
/// Applies any path prefix substitution as defined by the mapping.
@@ -1217,11 +1227,13 @@ impl FilePathMapping {
) -> RealFileName {
match file_path {
// Anything that's already remapped we don't modify, except for erasing
// the `local_path` portion.
RealFileName::Remapped { local_path: _, virtual_name } => {
// the `local_path` portion (if desired).
RealFileName::Remapped { local_path, virtual_name } => {
RealFileName::Remapped {
// We do not want any local path to be exported into metadata
local_path: None,
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => local_path,
},
// We use the remapped name verbatim, even if it looks like a relative
// path. The assumption is that the user doesn't want us to further
// process paths that have gone through remapping.
@@ -1231,12 +1243,18 @@ impl FilePathMapping {
RealFileName::LocalPath(unmapped_file_path) => {
// If no remapping has been applied yet, try to do so
let (new_path, was_remapped) = self.map_prefix(unmapped_file_path);
let (new_path, was_remapped) = self.map_prefix(&unmapped_file_path);
if was_remapped {
// It was remapped, so don't modify further
return RealFileName::Remapped {
local_path: None,
virtual_name: new_path.into_owned(),
// But still provide the local path if desired
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => {
Some(unmapped_file_path)
}
},
};
}
@@ -1252,17 +1270,23 @@ impl FilePathMapping {
match working_directory {
RealFileName::LocalPath(unmapped_working_dir_abs) => {
let file_path_abs = unmapped_working_dir_abs.join(unmapped_file_path_rel);
let unmapped_file_path_abs =
unmapped_working_dir_abs.join(unmapped_file_path_rel);
// Although neither `working_directory` nor the file name were subject
// to path remapping, the concatenation between the two may be. Hence
// we need to do a remapping here.
let (file_path_abs, was_remapped) = self.map_prefix(file_path_abs);
let (file_path_abs, was_remapped) =
self.map_prefix(&unmapped_file_path_abs);
if was_remapped {
RealFileName::Remapped {
// Erase the actual path
local_path: None,
virtual_name: file_path_abs.into_owned(),
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => {
Some(unmapped_file_path_abs)
}
},
}
} else {
// No kind of remapping applied to this path, so
@@ -1271,15 +1295,20 @@ impl FilePathMapping {
}
}
RealFileName::Remapped {
local_path: _,
local_path,
virtual_name: remapped_working_dir_abs,
} => {
// If working_directory has been remapped, then we emit
// Remapped variant as the expanded path won't be valid
RealFileName::Remapped {
local_path: None,
virtual_name: Path::new(remapped_working_dir_abs)
.join(unmapped_file_path_rel),
.join(&unmapped_file_path_rel),
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => local_path
.as_ref()
.map(|local_path| local_path.join(unmapped_file_path_rel)),
},
}
}
}
@@ -1287,27 +1316,6 @@ impl FilePathMapping {
}
}
/// Expand a relative path to an absolute path **without** remapping taken into account.
///
/// The resulting `RealFileName` will have its `virtual_path` portion erased if
/// possible (i.e. if there's also a remapped path).
pub fn to_local_embeddable_absolute_path(
&self,
file_path: RealFileName,
working_directory: &RealFileName,
) -> RealFileName {
let file_path = file_path.local_path_if_available();
if file_path.is_absolute() {
// No remapping has applied to this path and it is absolute,
// so the working directory cannot influence it either, so
// we are done.
return RealFileName::LocalPath(file_path.to_path_buf());
}
debug_assert!(file_path.is_relative());
let working_directory = working_directory.local_path_if_available();
RealFileName::LocalPath(Path::new(working_directory).join(file_path))
}
/// Attempts to (heuristically) reverse a prefix mapping.
///
/// Returns [`Some`] if there is exactly one mapping where the "to" part is

View File

@@ -305,6 +305,7 @@ fn path_prefix_remapping() {
let mapping = &FilePathMapping::new(
vec![(path("abc/def"), path("foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs"));
@@ -316,6 +317,7 @@ fn path_prefix_remapping() {
let mapping = &FilePathMapping::new(
vec![(path("abc/def"), path("/foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
@@ -327,6 +329,7 @@ fn path_prefix_remapping() {
let mapping = &FilePathMapping::new(
vec![(path("/abc/def"), path("foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs"));
@@ -338,6 +341,7 @@ fn path_prefix_remapping() {
let mapping = &FilePathMapping::new(
vec![(path("/abc/def"), path("/foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
@@ -351,6 +355,7 @@ fn path_prefix_remapping_expand_to_absolute() {
let mapping = &FilePathMapping::new(
vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
@@ -448,6 +453,71 @@ fn path_prefix_remapping_expand_to_absolute() {
);
}
#[test]
fn path_prefix_remapping_expand_to_absolute_and_local() {
// "virtual" working directory is relative path
let mapping = &FilePathMapping::new(
vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::LocalAndRemapped,
);
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
local_path: Some(working_directory.clone()),
virtual_name: mapping.map_prefix(working_directory).0.into_owned(),
};
assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
// Unmapped absolute path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/foo/src/main.rs")),
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/foo/src/main.rs")),
virtual_name: path("FOO/src/main.rs")
}
);
// Unmapped absolute path with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/bar/src/main.rs")),
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs")
}
);
// Already remapped absolute path, with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs"),
},
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs")
}
);
// Already remapped relative path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") },
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }
);
}
#[test]
fn path_prefix_remapping_reverse() {
// Ignores options without alphanumeric chars.
@@ -455,6 +525,7 @@ fn path_prefix_remapping_reverse() {
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/")), (path("def"), path("."))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
@@ -466,6 +537,7 @@ fn path_prefix_remapping_reverse() {
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/redacted")), (path("def"), path("/redacted"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
@@ -476,6 +548,7 @@ fn path_prefix_remapping_reverse() {
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/redacted")), (path("def/ghi"), path("/fake/dir"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
);
assert_eq!(

View File

@@ -91,6 +91,59 @@ fn main() {
)),
dwarf_test: DwarfDump::ParentTest,
});
check_dwarf_deps("macro", DwarfDump::AvoidSrcPath);
check_dwarf_deps("diagnostics", DwarfDump::AvoidSrcPath);
check_dwarf_deps("macro,diagnostics", DwarfDump::AvoidSrcPath);
check_dwarf_deps("object", DwarfDump::ContainsSrcPath);
}
#[track_caller]
fn check_dwarf_deps(scope: &str, dwarf_test: DwarfDump) {
// build some_value.rs
let mut rustc_sm = rustc();
rustc_sm.input(cwd().join("src/some_value.rs"));
rustc_sm.arg("-Cdebuginfo=2");
rustc_sm.arg(format!("-Zremap-path-scope={}", scope));
rustc_sm.arg("--remap-path-prefix");
rustc_sm.arg(format!("{}=/REMAPPED", cwd().display()));
rustc_sm.arg("-Csplit-debuginfo=off");
rustc_sm.run();
// build print_value.rs
let print_value_rlib = rust_lib_name(&format!("print_value.{scope}"));
let mut rustc_pv = rustc();
rustc_pv.input(cwd().join("src/print_value.rs"));
rustc_pv.output(&print_value_rlib);
rustc_pv.arg("-Cdebuginfo=2");
rustc_pv.arg(format!("-Zremap-path-scope={}", scope));
rustc_pv.arg("--remap-path-prefix");
rustc_pv.arg(format!("{}=/REMAPPED", cwd().display()));
rustc_pv.arg("-Csplit-debuginfo=off");
rustc_pv.run();
match dwarf_test {
DwarfDump::AvoidSrcPath => {
llvm_dwarfdump()
.input(print_value_rlib)
.run()
.assert_stdout_not_contains("REMAPPED/src/some_value.rs")
.assert_stdout_not_contains("REMAPPED/src/print_value.rs")
.assert_stdout_not_contains("REMAPPED/REMAPPED")
.assert_stdout_contains(cwd().join("src/some_value.rs").display().to_string())
.assert_stdout_contains(cwd().join("src/print_value.rs").display().to_string());
}
DwarfDump::ContainsSrcPath => {
llvm_dwarfdump()
.input(print_value_rlib)
.run()
.assert_stdout_contains("REMAPPED/src/some_value.rs")
.assert_stdout_contains("REMAPPED/src/print_value.rs")
.assert_stdout_not_contains(cwd().join("src/some_value.rs").display().to_string())
.assert_stdout_not_contains(cwd().join("src/print_value.rs").display().to_string());
}
_ => unreachable!(),
}
}
#[track_caller]

View File

@@ -0,0 +1,7 @@
#![crate_type = "rlib"]
extern crate some_value;
pub fn print_value() {
println!("{}", some_value::get_some_value());
}

View File

@@ -0,0 +1,6 @@
#![crate_type = "rlib"]
#[inline]
pub fn get_some_value() -> i32 {
42
}

View File

@@ -47,10 +47,10 @@ fn main() {
out_object.run();
rmeta_contains("/the/aux/lib.rs");
rmeta_not_contains("auxiliary");
rmeta_contains("auxiliary");
out_macro.run();
rmeta_contains("/the/aux/lib.rs");
rmeta_not_contains("auxiliary");
rmeta_contains("auxiliary");
out_diagobj.run();
rmeta_contains("/the/aux/lib.rs");
rmeta_not_contains("auxiliary");
@@ -58,6 +58,7 @@ fn main() {
//FIXME(Oneirical): These could be generalized into run_make_support
// helper functions.
#[track_caller]
fn rmeta_contains(expected: &str) {
// Normalize to account for path differences in Windows.
if !bstr::BString::from(rfs::read("liblib.rmeta")).replace(b"\\", b"/").contains_str(expected) {
@@ -69,6 +70,7 @@ fn rmeta_contains(expected: &str) {
}
}
#[track_caller]
fn rmeta_not_contains(expected: &str) {
// Normalize to account for path differences in Windows.
if bstr::BString::from(rfs::read("liblib.rmeta")).replace(b"\\", b"/").contains_str(expected) {