use crate::cargo_toolchain::Toolchain;
use anyhow::{Context, Result, anyhow, ensure};
use std::{env, path::PathBuf, process::Command};
#[derive(Clone)]
pub struct CargoCommand {
path: String,
manifest_path: PathBuf,
profile: String,
rustc_flags: Vec<&'static str>,
target_dir: PathBuf,
features: Vec<String>,
toolchain: Toolchain,
check_recommended_toolchain: bool,
force_recommended_toolchain: bool,
}
impl CargoCommand {
pub fn new() -> CargoCommand {
let toolchain = Toolchain::try_from_rustup().expect("Failed to resolve toolchain version");
let rustc_version = rustc_version::version().expect("Failed to get rustc version");
let linker_plugin_lto = rustc_version.major == 1 && rustc_version.minor >= 91;
let mut rustc_flags = vec!["-C", "link-arg=--import-memory"];
if linker_plugin_lto {
rustc_flags.extend_from_slice(&[
"-C",
"link-arg=--mllvm=-mcpu=mvp",
"-C",
"link-arg=--mllvm=-mattr=+mutable-globals",
"-C",
"linker-plugin-lto",
]);
}
CargoCommand {
path: "rustup".to_string(),
manifest_path: "Cargo.toml".into(),
profile: "dev".to_string(),
rustc_flags,
target_dir: "target".into(),
features: vec![],
toolchain,
check_recommended_toolchain: false,
force_recommended_toolchain: false,
}
}
}
impl Default for CargoCommand {
fn default() -> Self {
Self::new()
}
}
impl CargoCommand {
pub fn set_manifest_path(&mut self, path: PathBuf) {
self.manifest_path = path;
}
pub fn set_target_dir(&mut self, path: PathBuf) {
self.target_dir = path;
}
pub fn set_profile(&mut self, profile: String) {
self.profile = profile;
}
pub fn set_features(&mut self, features: &[String]) {
self.features = features.into();
}
pub fn set_check_recommended_toolchain(&mut self, check_recommended_toolchain: bool) {
self.check_recommended_toolchain = check_recommended_toolchain;
}
pub fn set_force_recommended_toolchain(&mut self, force_recommended_toolchain: bool) {
self.force_recommended_toolchain = force_recommended_toolchain;
}
pub fn run(&self) -> Result<()> {
if self.check_recommended_toolchain {
self.toolchain.check_recommended_toolchain()?;
}
let toolchain = if self.force_recommended_toolchain {
Toolchain::recommended_nightly()
} else {
self.toolchain.clone()
};
let mut cargo = Command::new(&self.path);
cargo
.arg("run")
.arg(toolchain.raw_toolchain_str().as_ref())
.arg("cargo");
if self.force_recommended_toolchain {
self.clean_up_environment(&mut cargo);
}
cargo
.arg("rustc")
.arg("--target=wasm32v1-none")
.arg("--color=always")
.arg(format!("--manifest-path={}", self.manifest_path.display()))
.arg("--profile")
.arg(&self.profile);
if !self.features.is_empty() {
cargo.arg("--features");
cargo.arg(self.features.join(","));
}
cargo
.arg("--")
.args(&self.rustc_flags)
.env("CARGO_TARGET_DIR", &self.target_dir)
.env("__GEAR_WASM_BUILDER_NO_BUILD", "1");
self.remove_cargo_encoded_rustflags(&mut cargo);
let status = cargo.status().context("unable to execute cargo command")?;
ensure!(
status.success(),
anyhow!("cargo command run failed: {status}")
);
Ok(())
}
fn clean_up_environment(&self, command: &mut Command) {
const INHERITED_ENV_VARS: &[&str] = &[
"CARGO",
"CARGO_MANIFEST_DIR",
"CARGO_MANIFEST_LINKS",
"CARGO_MAKEFLAGS",
"OUT_DIR",
"TARGET",
"HOST",
"NUM_JOBS",
"OPT_LEVEL",
"PROFILE",
"RUSTC",
"RUSTDOC",
"RUSTC_LINKER",
"CARGO_ENCODED_RUSTFLAGS",
];
for env_var in INHERITED_ENV_VARS {
command.env_remove(env_var);
}
const INHERITED_ENV_VARS_WITH_PREFIX: &[&str] =
&["CARGO_FEATURE_", "CARGO_CFG_", "DEP_", "CARGO_PKG_"];
for (env_var, _) in env::vars() {
for prefix in INHERITED_ENV_VARS_WITH_PREFIX {
if env_var.starts_with(prefix) {
command.env_remove(&env_var);
}
}
}
}
fn remove_cargo_encoded_rustflags(&self, command: &mut Command) {
command.env_remove("CARGO_ENCODED_RUSTFLAGS");
command.env_remove("RUSTFLAGS");
}
}