wasm-utils/build/src/main.rs

169 lines
4.8 KiB
Rust
Raw Normal View History

2017-08-09 13:45:35 +03:00
//! Experimental build tool for cargo
2017-08-08 16:13:15 +03:00
extern crate glob;
2017-08-09 13:45:35 +03:00
extern crate wasm_utils;
extern crate clap;
2017-08-09 14:30:37 +03:00
extern crate parity_wasm;
2017-08-08 16:13:15 +03:00
2017-08-09 13:51:47 +03:00
use std::{fs, io};
2017-10-25 20:36:05 +03:00
use std::io::Write;
2017-08-08 16:13:15 +03:00
use std::path::PathBuf;
2017-08-09 13:45:35 +03:00
use clap::{App, Arg};
2017-10-25 20:36:05 +03:00
use parity_wasm::elements;
2017-08-09 13:45:35 +03:00
use wasm_utils::{CREATE_SYMBOL, CALL_SYMBOL};
2017-10-26 15:49:55 +03:00
2017-08-08 16:13:15 +03:00
#[derive(Debug)]
pub enum Error {
Io(io::Error),
NoSuitableFile(String),
TooManyFiles(String),
NoEnvVar,
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
2017-08-09 14:30:37 +03:00
pub fn wasm_path(target_dir: &str, bin_name: &str) -> String {
let mut path = PathBuf::from(target_dir);
path.push(format!("{}.wasm", bin_name));
path.to_string_lossy().to_string()
}
2017-08-09 13:51:47 +03:00
pub fn process_output(target_dir: &str, bin_name: &str) -> Result<(), Error> {
2017-11-03 13:52:51 +03:00
let mut cargo_path = PathBuf::from(target_dir);
2017-08-09 13:45:35 +03:00
let wasm_name = bin_name.to_string().replace("-", "_");
2017-11-03 13:52:51 +03:00
cargo_path.push("wasm32-unknown-emscripten");
cargo_path.push("release");
cargo_path.push(format!("{}.wasm", wasm_name));
let mut target_path = PathBuf::from(target_dir);
target_path.push(format!("{}.wasm", bin_name));
fs::copy(cargo_path, target_path)?;
2017-08-08 16:13:15 +03:00
Ok(())
2017-08-09 13:45:35 +03:00
}
2017-10-25 20:36:05 +03:00
fn has_ctor(module: &elements::Module) -> bool {
if let Some(ref section) = module.export_section() {
2017-10-27 18:32:33 +03:00
section.entries().iter().any(|e| CREATE_SYMBOL == e.field())
2017-10-25 20:36:05 +03:00
} else {
false
}
}
2017-08-09 13:45:35 +03:00
fn main() {
wasm_utils::init_log();
2017-11-03 13:52:51 +03:00
let matches = App::new("wasm-build")
2017-08-09 13:51:47 +03:00
.arg(Arg::with_name("target")
2017-08-09 13:45:35 +03:00
.index(1)
.required(true)
2017-08-09 13:51:47 +03:00
.help("Cargo target directory"))
.arg(Arg::with_name("wasm")
2017-08-09 13:45:35 +03:00
.index(2)
.required(true)
2017-08-09 13:51:47 +03:00
.help("Wasm binary name"))
2017-08-15 16:44:25 +03:00
.arg(Arg::with_name("skip_optimization")
.help("Skip symbol optimization step producing final wasm")
.long("skip-optimization"))
2017-11-06 19:06:26 +03:00
.arg(Arg::with_name("skip_externalize")
.help("Skip externalizer step producing final wasm")
2017-08-15 16:44:25 +03:00
.long("skip-externalize"))
2017-09-25 20:14:46 +03:00
.arg(Arg::with_name("runtime_type")
.help("Injects RUNTIME_TYPE global export")
.takes_value(true)
.long("runtime-type"))
.arg(Arg::with_name("runtime_version")
.help("Injects RUNTIME_VERSION global export")
.takes_value(true)
.long("runtime-version"))
2017-08-09 13:45:35 +03:00
.get_matches();
2017-08-09 13:51:47 +03:00
let target_dir = matches.value_of("target").expect("is required; qed");
let wasm_binary = matches.value_of("wasm").expect("is required; qed");
2017-08-09 13:45:35 +03:00
2017-08-09 13:51:47 +03:00
process_output(target_dir, wasm_binary).expect("Failed to process cargo target directory");
2017-08-09 14:30:37 +03:00
let path = wasm_path(target_dir, wasm_binary);
2017-08-09 18:38:46 +03:00
2017-08-15 16:44:25 +03:00
let mut module = parity_wasm::deserialize_file(&path).unwrap();
2017-08-09 14:30:37 +03:00
2017-11-06 19:06:26 +03:00
if !matches.is_present("skip_externalize") {
2017-08-15 16:44:25 +03:00
module = wasm_utils::externalize(
module,
2017-11-06 19:06:26 +03:00
vec!["_free", "_malloc", "_memcpy", "_memset", "_memmove"],
2017-08-15 16:44:25 +03:00
);
}
2017-09-25 20:14:46 +03:00
if let Some(runtime_type) = matches.value_of("runtime_type") {
let runtime_type: &[u8] = runtime_type.as_bytes();
if runtime_type.len() != 4 {
panic!("--runtime-type should be equal to 4 bytes");
}
let runtime_version: u32 = matches.value_of("runtime_version").unwrap_or("1").parse()
.expect("--runtime-version should be a positive integer");
module = wasm_utils::inject_runtime_type(module, &runtime_type, runtime_version);
}
2017-10-25 20:36:05 +03:00
let mut ctor_module = module.clone();
if !matches.is_present("skip_optimization") {
wasm_utils::optimize(&mut module, vec![CALL_SYMBOL]).expect("Optimizer to finish without errors");
2017-10-25 20:36:05 +03:00
}
let raw_module = parity_wasm::serialize(module).expect("Failed to serialize module");
2017-10-26 15:49:55 +03:00
// If module has an exported function with name=CREATE_SYMBOL
// build will pack the module (raw_module) into this funciton and export as CALL_SYMBOL.
// Otherwise it will just save an optimised raw_module
2017-10-26 19:24:40 +03:00
if has_ctor(&ctor_module) {
2017-10-25 20:36:05 +03:00
if !matches.is_present("skip_optimization") {
wasm_utils::optimize(&mut ctor_module, vec![CREATE_SYMBOL]).expect("Optimizer to finish without errors");
2017-10-25 20:36:05 +03:00
}
wasm_utils::pack_instance(raw_module, &mut ctor_module);
2017-10-26 19:24:40 +03:00
parity_wasm::serialize_to_file(&path, ctor_module).expect("Failed to serialize to file");
2017-10-26 15:49:55 +03:00
} else {
let mut file = fs::File::create(&path).expect("Failed to create file");
file.write_all(&raw_module).expect("Failed to write module to file");
2017-10-25 20:36:05 +03:00
}
2017-09-25 20:14:46 +03:00
}
2017-11-03 13:52:51 +03:00
#[cfg(test)]
mod tests {
extern crate tempdir;
use self::tempdir::TempDir;
use std::fs;
use super::process_output;
#[test]
fn processes_cargo_output() {
let tmp_dir = TempDir::new("target").expect("temp dir failed");
let target_path = tmp_dir.path().join("wasm32-unknown-emscripten").join("release");
fs::create_dir_all(target_path.clone()).expect("create dir failed");
{
use std::io::Write;
let wasm_path = target_path.join("example_wasm.wasm");
let mut f = fs::File::create(wasm_path).expect("create fail failed");
f.write(b"\0asm").expect("write file failed");
}
process_output(&tmp_dir.path().to_string_lossy(), "example-wasm").expect("process output failed");
assert!(
fs::metadata(tmp_dir.path().join("example-wasm.wasm")).expect("metadata failed").is_file()
)
}
}