1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
* Mitts Build — Rust-first build and package tooling with shared artifact cache
* Copyright (c) 2026 Alexander Panasenko
*
* Contact: alex@prod.codes
* Author: https://prod.codes/about/
* Project: https://github.com/alex09x/mitts
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
use super::compiler::RustcInvocation;
use super::compiler_resolve::find_workspace_root;
use std::path::{Path, PathBuf};
use std::process::Command;
// Version the normalized semantics, never the machine-specific alias spellings.
pub(crate) const KEY_FLAG: &str = "REMAP_ALIASES=canonical-v2";
/// rustc and the Darwin linker can canonicalize paths independently of the
/// manifest spelling (notably /var -> /private/var). Cover both spellings so
/// debug metadata and macro spans cannot retain the checkout's absolute root.
fn append_aliases(cmd: &mut Command, path: &Path, replacement: &str) {
append_prefix(cmd, path, replacement);
if let Ok(canonical) = path.canonicalize() {
if canonical != path {
append_prefix(cmd, &canonical, replacement);
}
}
}
fn append_prefix(cmd: &mut Command, path: &Path, replacement: &str) {
let path = path.to_string_lossy();
let prefix = path.trim_end_matches('/');
let with_separator = if replacement.is_empty() {
String::new()
} else {
format!("{replacement}/")
};
cmd.arg(format!("--remap-path-prefix={prefix}/={with_separator}"));
if !prefix.is_empty() {
cmd.arg(format!("--remap-path-prefix={prefix}={replacement}"));
}
}
/// The Darwin linker records every object file's absolute path in the output's N_OSO
/// stabs (that is how lldb finds unpacked debug info); `--remap-path-prefix` does not
/// reach them, so every binary linked in a checkout embedded the checkout's path and
/// could never be shared with another one. `-oso_prefix` makes the linker store those
/// paths relative to the workspace root: the same string in every checkout, and still
/// resolvable by a debugger started there.
fn append_darwin_oso_prefix(inv: &RustcInvocation<'_>, cmd: &mut Command) {
if !cfg!(target_os = "macos") {
return;
}
// The paths the linker records are the ones rustc received: rlibs under
// `<checkout>/target/…/deps` (the `-L dependency` directory) and archives build scripts
// leave under `<checkout>/target/…/build`. The checkout spelling is whatever precedes
// that `target` component, whichever package (workspace member, registry proc macro)
// is being linked.
let checkout_of = |p: &Path| -> Option<PathBuf> {
p.ancestors()
.find(|a| a.file_name().is_some_and(|n| n == "target"))
.and_then(|t| t.parent())
.map(Path::to_path_buf)
};
let root = inv
.deps_dir
.and_then(checkout_of)
.or_else(|| inv.target_dir.and_then(checkout_of));
let Some(root) = root else {
return;
};
let prefix = format!("{}/", root.to_string_lossy().trim_end_matches('/'));
cmd.args(["-C", &format!("link-arg=-Wl,-oso_prefix,{prefix}")]);
}
pub(crate) fn append_path_remaps(
inv: &RustcInvocation<'_>,
clean_crate: &str,
cmd: &mut Command,
pkg_dir: &Path,
) {
if !super::inputs::is_immutable_package_dir(pkg_dir, inv.unit.is_workspace) {
let ws_root = find_workspace_root(&inv.unit.manifest_path);
if pkg_dir.starts_with(&ws_root) {
append_aliases(cmd, &ws_root, "");
}
}
append_darwin_oso_prefix(inv, cmd);
// More specific output mappings follow the workspace mapping: rustc gives
// the last matching rule precedence when the cache is inside the workspace.
let stable_out = format!("/mitts/out/{clean_crate}");
for out in std::iter::once(inv.out_dir).chain(
inv.directives
.and_then(|directives| directives.out_dir.as_deref()),
) {
append_aliases(cmd, out, &stable_out);
}
}