workspace hot-patching, dynamic tls fixups (#5291)

* Refactor cross-tls-crate-dylib configuration and update test output

- Commented out the crate-type configuration in Cargo.toml for cross-tls-crate-dylib.
- Modified the initial value of the thread-local variable FOO from 2.0 to 1.0 in the main.rs test file.
- Updated the output message in main.rs to be more descriptive.
- Added a new field `tls_fixups` to the JumpTable struct in subsecond-types, which will facilitate future runtime TLS sharing.

* ws hotpatch!

* remove old file

* undo core change

* svh

* Enhance rlib path resolution by incorporating -C extra-filename handling

* remove obj diffing

* small cleanups

* obj cache as files

* simplify

* simplify a bit

* dont bother round tripping cache

* extract to helper

* cleanup `build` function

* dont round-trip modified_crates

* make sure to wipe between full

* bit of a cleanup

* use explicit naming

* compact extra method

* fix temp cleanup

* fix warnign

* cleanup

* fmt, clippy
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2026-02-10 22:37:16 -08:00
1 parent 9b0d380765
commit 33159f366a
12 files changed
+1229 -348

No files matched your search

+40 -20
View File
@@ -5,9 +5,10 @@ use std::{
process::ExitCode,
};
/// The environment variable indicating where the args file is located.
/// The environment variable indicating where the args directory is located.
///
/// When `dx-rustc` runs, it writes its arguments to this file.
/// When `dx-rustc` runs, it writes each workspace crate's arguments to a
/// separate file in this directory: `{dir}/{crate_name}.json`.
pub const DX_RUSTC_WRAPPER_ENV_VAR: &str = "DX_RUSTC";
/// Is `dx` being used as a rustc wrapper?
@@ -64,7 +65,7 @@ fn has_linking_args() -> bool {
false
}
/// Run rustc directly, but output the result to a file.
/// Run rustc directly, but output the result to a per-crate file in the args directory.
///
/// <https://doc.rust-lang.org/cargo/reference/config.html#buildrustc>
pub fn run_rustc() -> ExitCode {
@@ -75,11 +76,11 @@ pub fn run_rustc() -> ExitCode {
.run_link();
}
let var_file: PathBuf = std::env::var(DX_RUSTC_WRAPPER_ENV_VAR)
let args_dir: PathBuf = std::env::var(DX_RUSTC_WRAPPER_ENV_VAR)
.expect("DX_RUSTC env var must be set")
.into();
// Cargo invokes a wrapper like: `wrapper-name rustc [args...]`
// Cargo invokes a workspace wrapper like: `wrapper-name rustc [args...]`
// We skip our own executable name (`wrapper-name`) to get the args passed to us.
let captured_args = args().skip(1).collect::<Vec<_>>();
@@ -89,32 +90,51 @@ pub fn run_rustc() -> ExitCode {
link_args: Default::default(),
};
// Another terrible hack to avoid caching non-sensical args when
// a build is completely fresh (rustc is invoked with --crate-name ___)
if rustc_args
// Extract the crate name from the args to use as the filename.
// Skip non-sensical args when a build is completely fresh (rustc is invoked with --crate-name ___)
let crate_name = rustc_args
.args
.iter()
.skip_while(|arg| *arg != "--crate-name")
.nth(1)
.is_some_and(|name| name != "___")
{
let parent_dir = var_file
.parent()
.expect("Args file path has no parent directory");
std::fs::create_dir_all(parent_dir)
.expect("Failed to create parent directory for args file");
.nth(1);
let serialized_args =
serde_json::to_string(&rustc_args).expect("Failed to serialize rustc args");
if let Some(crate_name) = crate_name {
if crate_name != "___" {
std::fs::create_dir_all(&args_dir)
.expect("Failed to create args directory for rustc wrapper");
std::fs::write(&var_file, serialized_args).expect("Failed to write rustc args to file");
let crate_type = rustc_args
.args
.iter()
.skip_while(|arg| *arg != "--crate-type")
.nth(1)
.map(|s| s.as_str());
let serialized_args =
serde_json::to_string(&rustc_args).expect("Failed to serialize rustc args");
// Write args with an explicit target suffix: {crate_name}.lib.json or
// {crate_name}.bin.json. This avoids the ambiguity of a bare {crate_name}.json
// and ensures lib+bin crates don't overwrite each other.
let suffix = match crate_type {
Some("lib" | "rlib") => "lib",
Some("bin") => "bin",
_ => "bin", // proc-macro, cdylib, etc. — treat as bin
};
std::fs::write(
args_dir.join(format!("{crate_name}.{suffix}.json")),
&serialized_args,
)
.expect("Failed to write rustc args to file");
}
}
// Run the actual rustc command.
// We want all stdout/stderr to be inherited, so the user sees the compiler output.
let mut cmd = std::process::Command::new("rustc");
// The first argument in `captured_args` is "rustc", which we need to skip
// The first argument in `captured_args` is the rustc path, which we need to skip
// when passing arguments to the `rustc` command we are spawning.
cmd.args(captured_args.iter().skip(1));
cmd.envs(rustc_args.envs);