Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions doc/book/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,44 @@ Paths to all other source files will not be affected.

This will not affect any hard-coded paths in the source code, such as in strings.

##### Unremap files

When the `object` scope is active and debuginfo is enabled,
Cargo writes an unremap file beside each final artifact.
The file is aimed at helping debuggers substitute sanitized paths back to local ones,
e.g., via GDB's `set substitute-path` or LLDB's `target.source-map`.

The unremap file name ends with `.trim-paths.jsonl`.
For example,
your `my-app` executable would come with an unremap file named
`my-app.trim-paths.jsonl` beside it.

The unremap file is in JSONL format:

* The first record carries the format version.
* The second record is file-level metadata,
such as the toolchain version and the workspace root.
* Each following record maps a sanitized path prefix in the artifact
(`from`) back to the local path it replaced (`to`),
ordered by the `from` prefix.
Note that this follows the debugger substitution direction,
which is the inverse of `--remap-path-prefix`.

An example of the unremap file:

```json
{"v":1}
{"rust_version":"1.96.0-nightly","workspace_root":"/home/me/app"}
{"from":"/cargo/build-dir","to":"/home/me/app/target"}
{"from":"/cargo/registry/6f17d22d3f0a95d1","to":"/home/me/.cargo/registry/src/index.crates.io-6f17d22d3f0a95d1"}
{"from":"/rustc/abc123","to":"/home/me/.rustup/toolchains/nightly/lib/rustlib/src/rust"}
```

Since it is meant to be a debugging aid,
it includes absolute paths of your system,
so there is no artifact privacy guarantee.
You might want to exclude `*.trim-paths.jsonl` files when distributing artifacts.

#### Environment variable

*as a new entry of ["Environment variables Cargo sets for build scripts"](./environment-variables.md#environment-variables-cargo-sets-for-crates)*
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub enum FileFlavor {
DebugInfo,
/// SBOM (Software Bill of Materials pre-cursor) file (e.g. cargo-sbon.json).
Sbom,
/// Unremap file for `-Ztrim-paths` (e.g. `foo.trim-paths.jsonl`).
Unremap,
/// Cross-crate info JSON files generated by rustdoc.
DocParts,
}
Expand Down
22 changes: 22 additions & 0 deletions src/compiler/build_runner/compilation_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::sync::Arc;
use tracing::debug;

use super::{BuildContext, BuildRunner, CompileKind, FileFlavor, Layout};
use crate::compiler::trim_paths;
use crate::compiler::{CompileMode, CompileTarget, CrateType, FileType, Unit};
use crate::util::{self, CargoResult, OnceExt, StableHasher};
use crate::workspace::{Target, TargetKind, Workspace};
Expand Down Expand Up @@ -612,6 +613,27 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
.collect();
outputs.extend(sbom_files.into_iter());
}

// Only generates unremap files for root units.

@epage epage Aug 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking back to my debugger days, I feel like a user would just want a general remap file for all of their artifacts. However, that gets weird from a rebuild perspective unless we have remaps as intermediate artifacts and have a merge step.

On the other hand, these aren't files they can use directly anyways, so they can manually merge the results.

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have the point. What they commonly get in a unremap file would be

  • One build-dir remap
  • One sysroot remap
  • One remap per registry
  • One remap per (Git repo, rev)
  • One remap per path dep outside workspace

People rarely use non-workspace path deps. Custom registry is not common but also not that common. People tend to be on the same rev for each git dep, and usually tend to have git dep as possible as they can.

I feel like for most projects they'll have build-dir + sysroot + crates.io registry unremap files, which is identical for all binaries. If we can merge, that we can write less files.

However, that gets weird from a rebuild perspective unless we have remaps as intermediate artifacts and have a merge step.

Yeah that make things weird, and also we already work on root units so it would be way less except for projects like bevy that has tons of examples. I am not sure which direction we'd like to go.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, one counterargument not inclining with a unified unremap file: Different binaries with the same PackageId path-dep-outside-workspace may collide in the unremap file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not possible in the same lockfile. I think I was wrong

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this as-is and put this on the tracking issue.

if bcx.roots.contains(unit) && trim_paths::should_emit_unremap_file(unit) {
let unremap_files: Vec<_> = outputs
.iter()
.filter(|o| matches!(o.flavor, FileFlavor::Normal | FileFlavor::Linkable))
.map(|output| OutputFile {
path: trim_paths::append_unremap_suffix(&output.path),
hardlink: output
.hardlink
.as_ref()
.map(trim_paths::append_unremap_suffix),
export_path: output
.export_path
.as_ref()
.map(trim_paths::append_unremap_suffix),
flavor: FileFlavor::Unremap,
})
.collect();
outputs.extend(unremap_files.into_iter());
}
outputs
}
};
Expand Down
15 changes: 14 additions & 1 deletion src/compiler/build_runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,10 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
for output in self.outputs(unit)?.iter() {
if matches!(
output.flavor,
FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
FileFlavor::DebugInfo
| FileFlavor::Auxiliary
| FileFlavor::Sbom
| FileFlavor::Unremap
) {
continue;
}
Expand Down Expand Up @@ -573,6 +576,16 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
.collect())
}

/// Returns the list of unremap file paths for a given [`Unit`].
pub fn unremap_output_files(&self, unit: &Unit) -> CargoResult<Vec<PathBuf>> {
Ok(self
.outputs(unit)?
.iter()
.filter(|o| o.flavor == FileFlavor::Unremap)
.map(|o| o.path.clone())
.collect())
}

pub fn is_primary_package(&self, unit: &Unit) -> bool {
self.primary_packages.contains(&unit.pkg.package_id())
}
Expand Down
18 changes: 17 additions & 1 deletion src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mod output_sbom;
pub mod rustdoc;
pub mod standard_lib;
pub mod timings;
mod trim_paths;
pub(crate) mod trim_paths;
mod unit;
pub mod unit_dependencies;
pub mod unit_graph;
Expand Down Expand Up @@ -342,6 +342,15 @@ fn rustc(
let sbom_files = build_runner.sbom_output_files(unit)?;
let sbom = build_sbom(build_runner, unit)?;

let unremap_files = build_runner.unremap_output_files(unit)?;
let unremap_content = if unremap_files.is_empty() {
None
} else {
let mut buf = Vec::new();
trim_paths::write_unremap_file(&mut buf, build_runner, unit)?;
Some(buf)
};

let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
&& !matches!(
build_runner.bcx.gctx.shell().verbosity(),
Expand Down Expand Up @@ -430,6 +439,13 @@ fn rustc(
serde_json::to_writer(outfile, &sbom)?;
}

if let Some(content) = &unremap_content {
for file in &unremap_files {
tracing::debug!("writing unremap file to {}", file.display());
paths::write_atomic(file, content)?;
}
}

let result = exec
.exec(
&rustc,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/output_depinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> Ca
for output in build_runner.outputs(unit)?.iter().filter(|o| {
!matches!(
o.flavor,
FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom | FileFlavor::Unremap
)
}) {
if let Some(ref link_dst) = output.hardlink {
Expand Down
Loading