use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
env::{args, vars},
path::PathBuf,
process::ExitCode,
};
#[derive(Clone, Debug, PartialEq)]
pub struct WorkspaceRustcArgs {
pub link_args: Vec<String>,
pub rustc_args: HashMap<String, RustcArgs>,
}
impl WorkspaceRustcArgs {
pub fn new(link_args: Vec<String>) -> Self {
Self {
link_args,
rustc_args: Default::default(),
}
}
}
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RustcArgs {
pub args: Vec<String>,
pub envs: Vec<(String, String)>,
}
pub const DX_RUSTC_WRAPPER_ENV_VAR: &str = "DX_RUSTC";
pub fn is_wrapping_rustc() -> bool {
std::env::var(DX_RUSTC_WRAPPER_ENV_VAR).is_ok()
}
pub fn run_rustc() -> ExitCode {
let args_dir: PathBuf = std::env::var(DX_RUSTC_WRAPPER_ENV_VAR)
.expect("DX_RUSTC env var must be set")
.into();
let captured_args = args().skip(1).collect::<Vec<_>>();
let rustc_args = RustcArgs {
args: captured_args.clone(),
envs: vars().collect::<_>(),
};
write_rustc_args(&args_dir, &rustc_args);
if has_linking_args() {
return crate::link::LinkAction::from_env()
.expect("Linker action not found")
.run_link();
}
let mut cmd = std::process::Command::new("rustc");
cmd.args(captured_args.iter().skip(1));
cmd.envs(rustc_args.envs);
cmd.stdout(std::process::Stdio::inherit());
cmd.stderr(std::process::Stdio::inherit());
cmd.current_dir(std::env::current_dir().expect("Failed to get current dir"));
let status = cmd.status().expect("Failed to execute rustc command");
std::process::exit(status.code().unwrap_or(1)); }
fn write_rustc_args(args_dir: &PathBuf, rustc_args: &RustcArgs) {
let crate_name = rustc_args
.args
.iter()
.skip_while(|arg| *arg != "--crate-name")
.nth(1);
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");
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");
let suffix = match crate_type {
Some("lib" | "rlib") => "lib",
Some("bin") => "bin",
_ => "bin", };
std::fs::write(
args_dir.join(format!("{crate_name}.{suffix}.json")),
&serialized_args,
)
.expect("Failed to write rustc args to file");
}
}
}
fn has_linking_args() -> bool {
for arg in std::env::args() {
if arg.ends_with(".o") || arg == "-flavor" {
return true;
}
if let Some(path_str) = arg.strip_prefix('@') {
if let Ok(file_binary) = std::fs::read(path_str) {
let content = String::from_utf8(file_binary.clone()).unwrap_or_else(|_| {
let binary_u16le: Vec<u16> = file_binary
.chunks_exact(2)
.map(|a| u16::from_le_bytes([a[0], a[1]]))
.collect();
String::from_utf16_lossy(&binary_u16le)
});
if content.lines().any(|line| {
let trimmed_line = line.trim().trim_matches('"');
trimmed_line.ends_with(".o") || trimmed_line == "-flavor"
}) {
return true;
}
}
}
}
false
}