use super::compiler::RustcInvocation;
use super::compiler_resolve::{find_workspace_root, resolve_clippy_driver, resolve_rustc};
use super::types::BuildMode;
use anyhow::Result;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
pub fn build_compiler_command(inv: &RustcInvocation) -> Result<(Command, String, bool)> {
if inv
.extra_compiler_args
.iter()
.any(|a| a == "--config" || a.starts_with("--config="))
{
anyhow::bail!("Unsupported option: --config is not yet supported in mitts");
}
if inv
.extra_compiler_args
.iter()
.any(|a| a == "--profile" || a.starts_with("--profile="))
{
anyhow::bail!("Unsupported option: --profile is not yet supported in mitts");
}
let clean_crate = if inv.unit.kind == "test" && inv.unit.target_name.ends_with("_unit_test") {
inv.unit.package_name.replace('-', "_")
} else {
inv.unit.target_name.replace('-', "_")
};
let edition = inv.unit.edition.as_deref().unwrap_or("2015");
let short_key = if inv.action_key.len() > 16 {
&inv.action_key[..16]
} else {
inv.action_key
};
let compiler_bin = if inv.mode == BuildMode::Clippy && inv.unit.is_workspace {
resolve_clippy_driver()
} else {
resolve_rustc()
};
let mut cmd = Command::new(&compiler_bin);
cmd.arg("--crate-name").arg(&clean_crate);
let actual_src = if inv.unit.src_path.is_file() {
inv.unit.src_path.clone()
} else {
let cache_root = std::env::var_os("MITTS_CACHE_DIR")
.map(PathBuf::from)
.or_else(|| {
inv.out_dir
.ancestors()
.find(|p| p.ends_with(".cache/mitts") || p.ends_with(".cache/mitts/"))
.map(|p| p.to_path_buf())
})
.unwrap_or_else(|| PathBuf::from("."));
let store_src = cache_root
.join("sources")
.join(format!("{}-{}", inv.unit.package_name, inv.unit.version))
.join("src")
.join(if inv.unit.kind == "bin" {
"main.rs"
} else {
"lib.rs"
});
if store_src.is_file() {
store_src
} else {
inv.unit.src_path.clone()
}
};
cmd.arg(&actual_src);
cmd.arg("--edition").arg(edition);
let is_bin = inv.unit.kind == "bin";
let is_test = inv.unit.kind == "test" || inv.unit.kind == "custom-test";
let is_proc_macro = inv.unit.kind == "proc-macro";
if is_test {
if inv.unit.kind == "custom-test" {
cmd.arg("--crate-type").arg("bin");
cmd.arg("--cfg").arg("test");
} else {
cmd.arg("--test");
}
cmd.arg("--emit=dep-info,link");
cmd.arg("--extern").arg("proc_macro");
cmd.arg("--out-dir").arg(inv.out_dir);
} else if is_proc_macro {
cmd.arg("--crate-type").arg("proc-macro");
cmd.arg("--extern").arg("proc_macro");
cmd.arg("-C").arg("prefer-dynamic");
cmd.arg("--emit=dep-info,link");
cmd.arg("-C").arg(format!("metadata={}", short_key));
cmd.arg("-C").arg(format!("extra-filename=-{}", short_key));
cmd.arg("--out-dir").arg(inv.out_dir);
} else if (inv.mode == BuildMode::Check || inv.mode == BuildMode::Clippy)
&& inv.unit.is_workspace
{
if is_bin {
cmd.arg("--crate-type").arg("bin");
cmd.arg("--emit=dep-info,metadata");
} else {
cmd.arg("--crate-type").arg("rlib");
cmd.arg("--emit=dep-info,metadata,link");
}
cmd.arg("-C").arg(format!("metadata={}", short_key));
cmd.arg("-C").arg(format!("extra-filename=-{}", short_key));
cmd.arg("--out-dir").arg(inv.out_dir);
} else if is_bin {
cmd.arg("--crate-type").arg("bin");
cmd.arg("--emit=dep-info,link");
cmd.arg("--out-dir").arg(inv.out_dir);
} else {
cmd.arg("--crate-type").arg("rlib");
cmd.arg("--emit=dep-info,metadata,link");
cmd.arg("-C").arg(format!("metadata={}", short_key));
cmd.arg("-C").arg(format!("extra-filename=-{}", short_key));
cmd.arg("--out-dir").arg(inv.out_dir);
}
let debuginfo = crate::adapter::rust::key_flags::resolve_unit_debuginfo(
&inv.unit.manifest_path,
&inv.unit.package_name,
inv.unit.is_workspace,
inv.unit.for_host,
inv.release,
inv.unit.workspace_root.as_deref(),
)?;
if inv.release {
cmd.arg("-C").arg("opt-level=3");
}
cmd.arg("-C").arg(format!("debuginfo={}", debuginfo));
cmd.arg("-C").arg("embed-bitcode=no");
if !inv.unit.is_workspace {
cmd.arg("--cap-lints").arg("allow");
} else {
let pkg_dir = inv.unit.manifest_path.parent().unwrap_or(Path::new("."));
let ws_root = find_workspace_root(&inv.unit.manifest_path);
if pkg_dir.starts_with(&ws_root) {
let ws_str = ws_root.to_string_lossy();
let ws_clean = ws_str.trim_end_matches('/');
cmd.arg(format!("--remap-path-prefix={}/=", ws_clean));
cmd.arg(format!("--remap-path-prefix={}=", ws_clean));
}
}
let stable_out = format!("/mitts/out/{}", clean_crate);
let stable_out_slash = format!("/mitts/out/{}/", clean_crate);
let out_str = inv.out_dir.to_string_lossy();
let out_clean = out_str.trim_end_matches('/');
cmd.arg(format!(
"--remap-path-prefix={}/={}",
out_clean, stable_out_slash
));
cmd.arg(format!("--remap-path-prefix={}={}", out_clean, stable_out));
if let Some(dir) = inv.directives {
if let Some(ref d_out) = dir.out_dir {
let d_str = d_out.to_string_lossy();
let d_clean = d_str.trim_end_matches('/');
cmd.arg(format!(
"--remap-path-prefix={}/={}",
d_clean, stable_out_slash
));
cmd.arg(format!("--remap-path-prefix={}={}", d_clean, stable_out));
}
}
cmd.arg("--check-cfg").arg("cfg(docsrs,test)");
if let Ok(toml) = super::manifest::CargoToml::from_file(&inv.unit.manifest_path) {
if let Some(ref lints_val) = toml.lints {
for cc in super::manifest_inherit::extract_check_cfg_from_lints(lints_val) {
cmd.arg("--check-cfg").arg(cc);
}
for lf in super::manifest_inherit::extract_lint_flags_from_lints(lints_val) {
cmd.arg(lf);
}
}
let mut all_features: Vec<String> = toml.features.keys().cloned().collect();
for (dep_name, dep_val) in &toml.dependencies {
if let super::manifest::TomlDependency::Detailed(ref d) = dep_val {
if d.optional == Some(true) {
all_features.push(dep_name.clone());
}
}
}
for target_spec in toml.target.values() {
for (dep_name, dep_val) in &target_spec.dependencies {
if let super::manifest::TomlDependency::Detailed(ref d) = dep_val {
if d.optional == Some(true) {
all_features.push(dep_name.clone());
}
}
}
}
all_features.sort();
all_features.dedup();
let quoted: Vec<String> = all_features.iter().map(|f| format!("\"{}\"", f)).collect();
cmd.arg("--check-cfg")
.arg(format!("cfg(feature, values({}))", quoted.join(", ")));
} else {
cmd.arg("--check-cfg").arg("cfg(feature, values(any()))");
}
for feat in inv.features {
cmd.arg("--cfg").arg(format!("feature=\"{}\"", feat));
}
if let Some(dir) = inv.directives {
for c in &dir.cfgs {
cmd.arg("--cfg").arg(c);
}
for cc in &dir.check_cfgs {
cmd.arg("--check-cfg").arg(cc);
}
for f in &dir.flags {
for part in f.split_whitespace() {
cmd.arg(part);
}
}
for l in &dir.link_libs {
cmd.arg("-l").arg(l);
}
for s in &dir.link_searches {
cmd.arg("-L").arg(s);
}
for a in &dir.link_args {
cmd.arg("-C").arg(format!("link-arg={}", a));
}
}
for (dep_crate, dep_artifact_path) in inv.extern_crates {
let clean_dep = dep_crate.replace('-', "_");
cmd.arg("--extern")
.arg(format!("{}={}", clean_dep, dep_artifact_path.display()));
}
if let Some(deps) = inv.deps_dir {
cmd.arg("-L").arg(format!("dependency={}", deps.display()));
}
fs::create_dir_all(inv.out_dir)?;
let cwd = if inv.unit.is_workspace {
find_workspace_root(&inv.unit.manifest_path)
} else {
inv.unit
.manifest_path
.parent()
.filter(|p| p.is_dir())
.unwrap_or(inv.out_dir)
.to_path_buf()
};
cmd.current_dir(&cwd);
let unit_env =
super::unit_env::compute_unit_rustc_env(inv.unit, inv.directives, inv.jobserver_auth);
for (k, v) in &unit_env {
cmd.env(k, v);
}
if is_test {
super::unit_env::apply_test_binary_env(&mut cmd, inv.unit, &cwd, inv.target_dir, inv.release);
}
cmd.env("RUSTC", &compiler_bin);
if let Some(toolchain_name) = compiler_bin
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.file_name())
{
if compiler_bin
.to_string_lossy()
.contains(".rustup/toolchains")
{
cmd.env("RUSTUP_TOOLCHAIN", toolchain_name);
}
}
let base_dir = if let Some(ws) = &inv.unit.workspace_root {
ws.clone()
} else if inv.unit.is_workspace {
find_workspace_root(&inv.unit.manifest_path)
} else {
inv.unit
.manifest_path
.parent()
.unwrap_or(Path::new("."))
.to_path_buf()
};
let config_start_dir = super::cargo_config::resolve_config_start_dir(&base_dir);
let explicit_target = super::key_flags::resolve_explicit_target_from_args_and_dir(
inv.extra_compiler_args,
&config_start_dir,
);
if super::key_flags::receives_target_rustflags(inv.unit, explicit_target.as_deref()) {
if let Ok(encoded) = std::env::var("CARGO_ENCODED_RUSTFLAGS") {
for flag in encoded.split('\x1f').filter(|f| !f.is_empty()) {
cmd.arg(flag);
}
} else if let Ok(flags) = std::env::var("RUSTFLAGS") {
for flag in flags.split_whitespace() {
cmd.arg(flag);
}
} else {
let config_flags = super::cargo_config::load_cargo_config_rustflags(
&config_start_dir,
explicit_target.as_deref(),
)?;
for flag in config_flags {
cmd.arg(flag);
}
}
}
let is_host_unit = inv.unit.for_host
|| inv.unit.kind == "proc-macro"
|| inv.unit.kind == "custom-build"
|| inv.unit.kind == "build-script";
let has_cli_target = inv
.extra_compiler_args
.iter()
.any(|a| a == "--target" || a.starts_with("--target="));
if !is_host_unit && !has_cli_target {
if let Some(target) = &explicit_target {
cmd.arg("--target").arg(target);
}
}
let mut skip_next = false;
for arg in inv.extra_compiler_args {
if skip_next {
skip_next = false;
continue;
}
if arg == "--workspace" || arg == "--all" {
continue;
}
if is_host_unit {
if arg == "--target" {
skip_next = true;
continue;
}
if arg.starts_with("--target=") {
continue;
}
}
cmd.arg(arg);
}
if !inv.unit.is_workspace {
cmd.arg("--cap-lints").arg("allow");
}
let is_pipelined_lib =
inv.unit.kind == "lib" && inv.mode != BuildMode::Check && inv.mode != BuildMode::Clippy;
if is_pipelined_lib {
cmd.arg("--error-format=json").arg("--json=artifacts");
} else {
cmd.arg("--color=never");
}
Ok((cmd, clean_crate, is_pipelined_lib))
}