use crate::metadata::Metadata;
use anyhow::{Result, anyhow};
use cargo_toml::Manifest;
use colored::Colorize;
use gear_wasm_optimizer::{self as optimize, CargoCommand, Optimizer};
use std::{
env, fs,
path::{Path, PathBuf},
};
pub struct Artifacts {
kargo: CargoCommand,
pub source: PathBuf,
pub root: PathBuf,
pub artifacts: Vec<Artifact>,
}
impl Artifacts {
pub fn new(
root: PathBuf,
source: PathBuf,
metadata: Metadata,
kargo: CargoCommand,
) -> Result<Self> {
fs::create_dir_all(&root)
.map_err(|e| anyhow!("Failed to create the artifact directory, {e}"))?;
let cwd = env::current_dir()?;
env::set_current_dir(&metadata.workspace_root);
let mut artifacts: Vec<Artifact> = collect_crates(&cwd, &metadata.gbuild.programs)?
.into_iter()
.collect();
if !metadata.workspace {
let current: Vec<Artifact> = artifacts
.iter()
.filter(|a| a.manifest.eq(&metadata.manifest))
.cloned()
.collect();
if current.is_empty() {
let manifest = Manifest::from_path(&metadata.manifest)?;
if manifest.package.is_some() {
artifacts = vec![Artifact {
manifest: metadata.manifest,
name: manifest.package().name.clone(),
}];
}
} else {
artifacts = current;
}
}
env::set_current_dir(cwd)?;
Ok(Artifacts {
source,
root,
kargo,
artifacts,
})
}
pub fn process(&self) -> Result<()> {
let all = self.artifacts.len();
for (idx, artifact) in self.artifacts.iter().enumerate() {
tracing::info!(
"[{}/{all}] Compiling package {} ...",
idx + 1,
artifact.name.bold()
);
let mut kargo = self.kargo.clone();
kargo.set_manifest_path(artifact.manifest.clone());
kargo.run()?;
artifact.optimize(&self.source, &self.root)?;
}
tracing::info!("Finished ({})", self.root.to_string_lossy());
Ok(())
}
pub fn list(&self) -> Vec<PathBuf> {
self.artifacts
.iter()
.map(|a| self.root.join(a.names().1))
.collect()
}
}
#[derive(Clone, Debug)]
pub struct Artifact {
pub manifest: PathBuf,
pub name: String,
}
impl Artifact {
fn names(&self) -> (String, String) {
let name = self.name.replace('-', "_");
let input = name.clone() + ".wasm";
let output = input.clone();
(input, output)
}
pub fn optimize(&self, src: &Path, root: &Path) -> Result<()> {
let (input, output) = self.names();
let output = root.join(output);
let mut optimizer = Optimizer::new(&src.join(input))?;
optimizer
.insert_stack_end_export()
.map_err(|e| anyhow!("{e}"));
optimizer.strip_custom_sections();
optimizer.strip_exports();
optimizer.flush_to_file(&output);
optimize::optimize_wasm(&output, &output, "4", true)?;
Ok(())
}
}
fn collect_crates(cwd: &Path, patterns: &[String]) -> Result<Vec<Artifact>> {
let cwd = env::current_dir()?;
let mut crates: Vec<PathBuf> = Default::default();
for p in patterns {
crates.append(
&mut glob::glob(p)?
.filter_map(|p| {
p.ok().and_then(|p| {
tracing::trace!("checking {p:?}");
let manifest = cwd.join(p.join("Cargo.toml"));
if manifest.exists() {
Some(manifest)
} else {
tracing::warn!("Invalid manifest: {manifest:?}");
None
}
})
})
.collect(),
);
}
crates
.into_iter()
.map(|manifest| -> Result<Artifact> {
let mut toml = Manifest::from_slice(&fs::read(&manifest)?)?;
let mut cdylib = false;
if let Some(lib) = &toml.lib {
cdylib = lib.crate_type.contains(&"cdylib".to_string());
}
if !cdylib {
eprint!(
"{}: could not find `cdylib` in [lib.crate-type] from {}",
"error".bold().red(),
manifest.display()
);
std::process::exit(1);
}
Ok(Artifact {
name: toml.package().name().into(),
manifest,
})
})
.collect()
}