Skip to content

feat: inherit path remap rules from cargo trim-paths - #1794

Merged
NobodyXu merged 3 commits into
rust-lang:mainfrom
weihanglo:trim-paths
Jul 18, 2026
Merged

feat: inherit path remap rules from cargo trim-paths#1794
NobodyXu merged 3 commits into
rust-lang:mainfrom
weihanglo:trim-paths

Conversation

@weihanglo

@weihanglo weihanglo commented Jul 17, 2026

Copy link
Copy Markdown
Member

What this is for

Fixes #593

Cargo with -Ztrim-paths feature would set
CARGO_TRIM_PATHS_SCOPE and CARGO_TRIM_PATHS_REMAP for build scripts,
so they can forward path remaps to C/C++ compilers.

  • macro scope -> -fmacro-prefix-map (__FILE__ and friends)
  • object scope -> -fdebug-prefix-map (debug info)
  • all scope -> both
  • diagnostics and none scopes have no C equivalent

MSVC is skipped as it has no equivalent flag family.
It seems to have an undocumented /pathmap though,
see bazelbuild/bazel#9466

This is inherited by default. Rationale:

  • Mirroring inherit_rustflags
  • The env vars only exist when the user opted into Cargo profile trim-paths.
  • It is easy to opt-out from user. Cargo only looks at your local
    package's profile, and you can also do a per-dependency profile
    override.

Do note again this is nightly only feature from Cargo's point of view,
so I put a warning in the doc comment.

See also https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option

How to review

Commit by commit.
The first one capture the current behavior.
Subsequent commits show the behavior change through the diff.

I have a cargo -Zscript repro you can run against master or this PR:

repro script

#!/usr/bin/env -S cargo +nightly -Zscript
---
[package]
edition = "2024"
---

//! End-to-end repro: cargo `-Ztrim-paths` -> cc path remap inheritance.
//!
//! Put this under the cc-rs repo root and run it.
//!
//! - `__FILE__` carrier: absolute `.file()` + `debug = false`
//!   -> trimmed by `-fmacro-prefix-map` (scopes `macro`, `object`, `all`)
//! - DWARF carrier: relative `.file()` + `debug = true`
//!   (`DW_AT_comp_dir` embeds the build-script cwd)
//!   -> trimmed by `-fdebug-prefix-map` (scopes `object`, `all`);
//!   `macro` must leave it EMBEDDED, mirroring rustc scope semantics.

use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};

fn main() {
    let cc_root = env::current_dir().unwrap();
    assert!(
        fs::read_to_string("Cargo.toml")
            .unwrap_or_default()
            .contains("name = \"cc\""),
        "run this from the cc-rs repo root"
    );

    let work = env::temp_dir().join(format!("cc-trim-e2e-{}", std::process::id()));
    let _ = fs::remove_dir_all(&work);
    fs::create_dir_all(&work).unwrap();
    // cargo's remap pairs use canonical paths,
    // and prefix remapping is plain string matching.
    let work = work.canonicalize().unwrap();
    let abs = work.display().to_string();

    let mut ok = true;
    for dwarf in [false, true] {
        eprintln!("== {} carrier ==", if dwarf { "DWARF" } else { "__FILE__" });
        for scope in ["all", "object", "macro", "none"] {
            // `none` trims nothing; `macro` does not touch debug info.
            let expect = scope == "none" || (dwarf && scope == "macro");
            let archive = build(&cc_root, &work, dwarf, scope);
            let found = contains(&archive, abs.as_bytes());
            let emb = |b| if b { "EMBEDDED" } else { "absent" };
            eprintln!(
                "trim-paths = {scope:8}: absolute path {:8} (expected {})",
                emb(found),
                emb(expect)
            );
            if found != expect {
                ok = false;
            }
        }
    }

    let _ = fs::remove_dir_all(&work);
    if !ok {
        eprintln!(
            "\nFAIL (does this cc checkout have trim-paths inheritance,\n\
             and does this cargo set CARGO_TRIM_PATHS_* for build scripts?)"
        );
        std::process::exit(1);
    }
    eprintln!("\nOK: cc inherited cargo's trim-paths remap end to end");
}

/// Scaffold a package
///
/// * `build.rs` compiles `hello.c` through this cc checkout
/// * `cargo build`
/// * return the built archive bytes
fn build(cc_root: &Path, work: &Path, dwarf: bool, scope: &str) -> Vec<u8> {
    let pkg = work.join(format!("pkg-{}-{scope}", u8::from(dwarf)));
    fs::create_dir_all(pkg.join("src")).unwrap();
    // Absolute `.file()` makes `__FILE__` carry the absolute path; a
    // relative one leaves only DWARF (`DW_AT_comp_dir` = cwd) carrying it.
    let src = if dwarf {
        PathBuf::from("src/hello.c")
    } else {
        pkg.join("src/hello.c")
    };
    fs::write(
        pkg.join("Cargo.toml"),
        format!(
            r#"cargo-features = ["trim-paths"]

[package]
name = "trim-repro"
edition = "2021"

[build-dependencies]
cc = {{ path = "{}" }}

[profile.dev]
trim-paths = "{scope}"
debug = {dwarf}
"#,
            cc_root.display()
        ),
    )
    .unwrap();
    fs::write(
        pkg.join("build.rs"),
        format!(
            "fn main() {{ cc::Build::new().file(r\"{}\").compile(\"hello\"); }}\n",
            src.display()
        ),
    )
    .unwrap();
    fs::write(pkg.join("src/lib.rs"), "").unwrap();
    fs::write(
        pkg.join("src/hello.c"),
        "const char *embedded = __FILE__;\nconst char *get(void) { return embedded; }\n",
    )
    .unwrap();

    let out = Command::new("cargo")
        .args(["+nightly", "build", "-Ztrim-paths"])
        .current_dir(&pkg)
        .output()
        .unwrap();
    assert!(
        out.status.success(),
        "cargo build failed:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );

    fs::read_dir(pkg.join("target/debug/build"))
        .unwrap()
        .find_map(|e| {
            let a = e.ok()?.path().join("out/libhello.a");
            a.exists().then(|| fs::read(a).unwrap())
        })
        .expect("no libhello.a produced")
}

fn contains(hay: &[u8], needle: &[u8]) -> bool {
    hay.windows(needle.len()).any(|w| w == needle)
}

Note

I don't know if this is a known issue,
but the flag-support probe (is_flag_supported_inner) doesn't inherit env vars from parent or Build::env.
This would result in a different invocation from the real compile.
For example you may set CC="zig cc",
and zig cc may support less flags than latest gcc/clang.

See #1794 (comment)

cargo with `-Ztrim-paths` nightly feature would set
`CARGO_TRIM_PATHS_{SCOPE,REMAP}` for build scripts,
so they can forward path remaps to C/C++ compilers.

https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option
@NobodyXu

Copy link
Copy Markdown
Contributor

(is_flag_supported_inner) doesn't inherit env vars from parent or Build::env.
This would result in a different invocation from the real compile.
For example you may set CC="zig cc",
and zig cc may support less flags than latest gcc/clang.

is_flag_supported_inner does explicitly accept the compiler/CC, so it can't invoke the wrong compiler to test.

For other env vars, unfortunately we don't have any pass through, if there's any affecting support if flags I'll add it

@NobodyXu NobodyXu left a comment

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.

Thank you!

I have some feedbacks on code, but overall looks good to me!

Comment thread src/lib.rs
Comment thread src/lib.rs
Comment thread src/lib.rs Outdated
Comment thread src/lib.rs Outdated
@weihanglo
weihanglo force-pushed the trim-paths branch 2 times, most recently from 61520b6 to cb96bd5 Compare July 18, 2026 04:21
@weihanglo
weihanglo requested a review from NobodyXu July 18, 2026 04:38

@NobodyXu NobodyXu left a comment

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.

Thanks, let's just add a probe and I'll cut a release if the code looks good

Comment thread src/lib.rs Outdated
//
// So we may assume `-fdebug-prefix-map` is always available,
// and only probe `-fmacro-prefix-map`.
if macro_scope {

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.

Well I think we should still have s probe for debug, and I think it can be extracted as a function and only some minor differences in the string used

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.

Done the probe enhancement and extraction. Thanks for the suggestion. Looks better now.

BTW I am thinking about the support of clang-cl.exe though I guess we can leave this as a follow-up?

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.

Yeah for clang-cl.exe another PR makes sense, we have a is_like_clang_cl fn

pub fn is_like_clang_cl(&self) -> bool {

Cargo with `-Ztrim-paths` feature would set
`CARGO_TRIM_PATHS_SCOPE` and `CARGO_TRIM_PATHS_REMAP` for build scripts,
so they can forward path remaps to C/C++ compilers.

* `macro` scope -> `-fmacro-prefix-map` (`__FILE__` and friends)
* `object` scope -> `-fdebug-prefix-map` (debug info)
* `all` scope -> both
* `diagnostics` and `none` scopes have no C equivalent

MSVC is skipped as it has no equivalent flag family.
It seems to have an undocumented `/pathmap` though,
see bazelbuild/bazel 9466

This is inherited by default. Rationale:

* Mirroring `inherit_rustflags`
* The env vars only exist when the user opted into Cargo profile trim-paths.
* It is easy to opt-out from user. Cargo only looks at your local
  package's profile, and you can also do a per-dependency profile
  override.

Do note this is nightly only feature from Cargo's point of view.
@weihanglo
weihanglo force-pushed the trim-paths branch 2 times, most recently from a58148f to c69f330 Compare July 18, 2026 12:09
@weihanglo
weihanglo requested a review from NobodyXu July 18, 2026 12:11
We probe these flags and skip with a warning when the compiler rejects it.

* `-fdebug-prefix-map`: supported since GCC 4.3 (2008-03), Clang 3.8 (2016-03):
  * <https://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Debugging-Options.html>
  * <llvm/llvm-project@436256a>
* `-fmacro-prefix-map`: supported since GCC 8.1 (2018-05), Clang 10.0 (2020-03)
  * <https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Option-Summary.html>
  * <https://releases.llvm.org/10.0.0/tools/clang/docs/ReleaseNotes.html>

@NobodyXu NobodyXu left a comment

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.

Thank you LGTM! I will merge and cut a release

@NobodyXu
NobodyXu merged commit e805bf3 into rust-lang:main Jul 18, 2026
79 checks passed
pull Bot pushed a commit to Mattlk13/cargo that referenced this pull request Aug 15, 2026
### What does this PR try to resolve?

With this we get remap for free when building in rustc bootstrap:

See

* rust-lang/rust#161049
* rust-lang#17309
* rust-lang/cc-rs#1794

### How to test and review this PR?
pull Bot pushed a commit to xtqqczze/rust-lang-miri that referenced this pull request Aug 17, 2026
With this,
we get C dep remap for free when building in rustc bootstrap:

See

* rust-lang/rust#161049
* rust-lang/cargo#17309
* rust-lang/cc-rs#1794
RalfJung pushed a commit to RalfJung/rust that referenced this pull request Aug 23, 2026
With this,
we get C dep remap for free when building in rustc bootstrap:

See

* rust-lang#161049
* rust-lang/cargo#17309
* rust-lang/cc-rs#1794
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pass --remap-path-prefix to C compilers

2 participants