Skip to content

Incorrect .d depfile escaping on Windows in edge case #16096

@seritools

Description

@seritools

Problem

Cargo works around dependencies passed from rustc's depfile with a trailing slash so that Windows-style paths work as expected:

while file.ends_with('\\') {
file.pop();
file.push(' ');
file.push_str(deps.next().ok_or_else(|| {
crate::util::internal("malformed dep-info format, trailing \\")
})?);
}

The dependency format being what it is requires that paths don't end with a trailing backslash.

When writing its own depfile, however, there is an edge case where cargo accidentally does emit a path with a trailing backslash.

What seems to happen is that rust_cwd seems to be a path with a trailing backslash on Windows.
Joining any path with an empty path is effectively a no-op. Imporantly, this means that the trailing backslash is preserved.

In here

let abs_file = rustc_cwd.join(file);
// If canonicalization fails, just use the abs path. There is currently
// a bug where --remap-path-prefix is affecting .d files, causing them
// to point to non-existent paths.
let canon_file =
crate::util::try_canonicalize(&abs_file).unwrap_or_else(|_| abs_file.clone());

cargo joins rust_cwd with the potentially-empty path, then passes it to canonicalize/absolute, both of which will also keep the trailing backslash. Because the check mentioned above is not repeaded here, an invalid .d dependency line is emitted.

Steps

There are two ways I know of to trigger the bug:

  1. via cargo build script passing an empty string:
fn main() {
    println!("cargo::rerun-if-changed=");
}

This can easily happen with a computed path that ends up being empty, for example:

fn main() {
    let some_calculated_path = calculate_path(); // might become an empty path!
    println!("cargo::rerun-if-changed={}", some_calculated_path.display());
}

We end up with this in the target depfile target\debug\a.d:

X:\rustprojs\buggy-test\target\debug\a.exe: X:\rustprojs\buggy-test\ X:\rustprojs\buggy-test\build.rs X:\rustprojs\buggy-test\src\main.rs

  1. (nightly-only) via proc macro that passes "." (but not ""!) as tracked_path:
#![feature(track_path)]
use proc_macro::TokenStream;

#[proc_macro]
pub fn testing(stream: TokenStream) -> TokenStream {
    proc_macro::tracked_path::path(".");
    stream
}

This can similarly easily happen with a calculated path that is .-relative at some point.

We end up with:

X:\rustprojs\buggy-test\target\debug\a.exe: X:\rustprojs\buggy-test\ X:\rustprojs\buggy-test\build.rs X:\rustprojs\buggy-test\procmacro\src\lib.rs X:\rustprojs\buggy-test\src\main.rs

Because of the trailing backslash, the first dependency becomes 'X:\rustprojs\buggy-test\ X:\rustprojs\buggy-test\build.rs', which of course is an invalid path.


Relatedly, adding a backslash to the path in the proc macro causes us to fail building:

#![feature(track_path)]
use proc_macro::TokenStream;

#[proc_macro]
pub fn testing(stream: TokenStream) -> TokenStream {
    proc_macro::tracked_path::path(".\\");

    stream
}

Results in this, triggering the check/workaround mentioned at the top:

error: could not parse/generate dep info at: X:\rustprojs\buggy-test\target\debug\deps\a.d

Caused by:
  malformed dep-info format, trailing \
note: this is an unexpected cargo internal error
note: we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/

Possible Solution(s)

A few ways would come to mind:

  1. Ensure rust_cwd does not have a trailing slash
  2. Check that abs_file is different from rust_cwd
  3. Apply the same \ check mentioned at the top when writing out cargo's depfile
  4. (likely not valid across platforms) Remove trailing backslashes in general as they are never valid for depfiles

I'd be happy to provide a patch if I could get some guidance on what the preferred way to fix this is.

Notes

No response

Version

Verified on stable and on the current nightly:

cargo 1.92.0-nightly (81c3f77a4 2025-10-10)
release: 1.92.0-nightly
commit-hash: 81c3f77a467359c8be6bc747dc93ec66a6e4ce11
commit-date: 2025-10-10
host: x86_64-pc-windows-msvc
libgit2: 1.9.1 (sys:0.20.2 vendored)
libcurl: 8.15.0-DEV (sys:0.4.83+curl-8.15.0 vendored ssl:Schannel)
os: Windows 10.0.26200 (Windows 11 Professional) [64-bit]

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dep-infoArea: dep-info, .d filesC-bugCategory: bugO-windowsOS: WindowsS-acceptedStatus: Issue or feature is accepted, and has a team member available to help mentor or review

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions