wasm-utils/cli/build/main.rs

243 lines
7.4 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;
2018-03-13 15:58:18 +03:00
extern crate pwasm_utils as utils;
2017-08-09 13:45:35 +03:00
extern crate clap;
2017-08-09 14:30:37 +03:00
extern crate parity_wasm;
extern crate pwasm_utils_cli as logger;
2017-08-08 16:13:15 +03:00
2017-12-26 13:38:07 +03:00
mod source;
2017-08-09 13:51:47 +03:00
use std::{fs, io};
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;
2018-09-30 18:24:36 +01:00
use utils::{build, BuildError, SourceTarget, TargetRuntime};
2017-10-26 15:49:55 +03:00
2017-08-08 16:13:15 +03:00
#[derive(Debug)]
pub enum Error {
Io(io::Error),
2018-03-27 15:23:19 +03:00
FailedToCopy(String),
2018-03-27 16:47:48 +03:00
Decoding(elements::Error, String),
Encoding(elements::Error),
Build(BuildError),
2018-03-27 17:11:00 +03:00
}
2018-03-27 15:23:19 +03:00
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use self::Error::*;
2018-03-27 15:23:19 +03:00
match *self {
Io(ref io) => write!(f, "Generic i/o error: {}", io),
2018-03-27 17:15:31 +03:00
FailedToCopy(ref msg) => write!(f, "{}. Have you tried to run \"cargo build\"?", msg),
2018-03-27 16:47:48 +03:00
Decoding(ref err, ref file) => write!(f, "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", err, file),
Encoding(ref err) => write!(f, "Encoding error ({}). Almost impossible to happen, no free disk space?", err),
Build(ref err) => write!(f, "Build error: {}", err)
2018-03-27 15:23:19 +03:00
}
}
}
2017-12-26 13:38:07 +03:00
pub fn wasm_path(input: &source::SourceInput) -> String {
let mut path = PathBuf::from(input.target_dir());
2017-12-27 12:12:09 +03:00
path.push(format!("{}.wasm", input.final_name()));
2017-08-09 14:30:37 +03:00
path.to_string_lossy().to_string()
}
2017-12-26 13:38:07 +03:00
pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
let mut cargo_path = PathBuf::from(input.target_dir());
let wasm_name = input.bin_name().to_string().replace("-", "_");
2017-12-27 12:12:09 +03:00
cargo_path.push(
match input.target() {
SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
2017-12-27 12:12:09 +03:00
}
);
2017-11-03 13:52:51 +03:00
cargo_path.push("release");
cargo_path.push(format!("{}.wasm", wasm_name));
2017-12-26 13:38:07 +03:00
let mut target_path = PathBuf::from(input.target_dir());
2017-12-27 12:12:09 +03:00
target_path.push(format!("{}.wasm", input.final_name()));
2018-03-27 15:23:19 +03:00
fs::copy(cargo_path.as_path(), target_path.as_path())
.map_err(|io| Error::FailedToCopy(
format!("Failed to copy '{}' to '{}': {}", cargo_path.display(), target_path.display(), io)
))?;
2017-08-08 16:13:15 +03:00
Ok(())
2017-08-09 13:45:35 +03:00
}
2018-03-27 17:33:10 +03:00
fn do_main() -> Result<(), Error> {
logger::init_log();
2017-08-09 13:45:35 +03:00
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"))
2018-09-30 18:24:36 +01:00
.arg(Arg::with_name("target-runtime")
.help("What runtime we are compiling to")
.long("target-runtime")
.takes_value(true)
.default_value("pwasm")
.possible_values(&["substrate", "pwasm"]))
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"))
.arg(Arg::with_name("enforce_stack_adjustment")
.help("Enforce stack size adjustment (used for old wasm32-unknown-unknown)")
.long("enforce-stack-adjustment"))
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-12-26 14:03:08 +03:00
.arg(Arg::with_name("source_target")
2017-12-27 17:03:51 +03:00
.help("Cargo target type kind ('wasm32-unknown-unknown' or 'wasm32-unknown-emscripten'")
2017-12-26 14:03:08 +03:00
.takes_value(true)
.long("target"))
2017-12-27 12:12:09 +03:00
.arg(Arg::with_name("final_name")
.help("Final wasm binary name")
.takes_value(true)
.long("final"))
2017-12-27 12:35:31 +03:00
.arg(Arg::with_name("save_raw")
.help("Save intermediate raw bytecode to path")
.takes_value(true)
.long("save-raw"))
2018-02-05 18:20:34 +03:00
.arg(Arg::with_name("shrink_stack")
.help("Shrinks the new stack size for wasm32-unknown-unknown")
.takes_value(true)
.long("shrink-stack"))
2018-05-18 17:51:37 +04:00
.arg(Arg::with_name("public_api")
.help("Preserves specific imports in the library")
.takes_value(true)
.long("public-api"))
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-12-26 14:03:08 +03:00
let mut source_input = source::SourceInput::new(target_dir, wasm_binary);
2017-12-27 17:50:31 +03:00
let source_target_val = matches.value_of("source_target").unwrap_or_else(|| source::EMSCRIPTEN_TRIPLET);
if source_target_val == source::UNKNOWN_TRIPLET {
2017-12-26 14:03:08 +03:00
source_input = source_input.unknown()
2017-12-27 17:50:31 +03:00
} else if source_target_val == source::EMSCRIPTEN_TRIPLET {
2017-12-26 14:03:08 +03:00
source_input = source_input.emscripten()
} else {
2018-03-27 17:33:10 +03:00
eprintln!("--target can be: '{}' or '{}'", source::EMSCRIPTEN_TRIPLET, source::UNKNOWN_TRIPLET);
2017-12-26 14:03:08 +03:00
::std::process::exit(1);
}
2017-08-09 13:45:35 +03:00
2017-12-27 12:12:09 +03:00
if let Some(final_name) = matches.value_of("final_name") {
source_input = source_input.with_final(final_name);
}
2018-03-27 17:33:10 +03:00
process_output(&source_input)?;
2017-08-09 14:30:37 +03:00
2017-12-26 13:38:07 +03:00
let path = wasm_path(&source_input);
2017-08-09 18:38:46 +03:00
let module = parity_wasm::deserialize_file(&path)
2018-03-27 17:33:10 +03:00
.map_err(|e| Error::Decoding(e, path.to_string()))?;
2017-08-09 14:30:37 +03:00
let runtime_type_version = if let (Some(runtime_type), Some(runtime_version))
= (matches.value_of("runtime_type"), matches.value_of("runtime_version")) {
let mut ty: [u8; 4] = Default::default();
let runtime_bytes = runtime_type.as_bytes();
if runtime_bytes.len() != 4 {
2017-09-25 20:14:46 +03:00
panic!("--runtime-type should be equal to 4 bytes");
}
ty.copy_from_slice(runtime_bytes);
let version: u32 = runtime_version.parse()
2017-09-25 20:14:46 +03:00
.expect("--runtime-version should be a positive integer");
Some((ty, version))
} else {
None
};
2017-10-25 20:36:05 +03:00
let public_api_entries = matches.value_of("public_api")
2018-05-18 17:51:37 +04:00
.map(|val| val.split(",").collect())
.unwrap_or(Vec::new());
2018-09-30 18:24:36 +01:00
let target_runtime = match matches.value_of("target-runtime").expect("target-runtime has a default value; qed") {
"pwasm" => TargetRuntime::pwasm(),
"substrate" => TargetRuntime::substrate(),
_ => unreachable!("all possible values are enumerated in clap config; qed"),
};
let (module, ctor_module) = build(
module,
source_input.target(),
runtime_type_version,
&public_api_entries,
matches.is_present("enforce_stack_adjustment"),
matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse()
.expect("New stack size is not valid u32"),
matches.is_present("skip_optimization"),
2018-09-30 18:24:36 +01:00
&target_runtime,
).map_err(Error::Build)?;
2017-10-25 20:36:05 +03:00
2017-12-27 12:35:31 +03:00
if let Some(save_raw_path) = matches.value_of("save_raw") {
2018-03-27 17:33:10 +03:00
parity_wasm::serialize_to_file(save_raw_path, module.clone()).map_err(Error::Encoding)?;
2017-12-27 12:35:31 +03:00
}
2018-08-06 15:16:54 +03:00
if let Some(ctor_module) = ctor_module {
parity_wasm::serialize_to_file(
&path,
ctor_module,
).map_err(Error::Encoding)?;
} else {
parity_wasm::serialize_to_file(&path, module).map_err(Error::Encoding)?;
2018-08-06 15:16:54 +03:00
}
2018-03-27 17:33:10 +03:00
Ok(())
}
fn main() {
if let Err(e) = do_main() {
eprintln!("{}", e);
std::process::exit(1)
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;
2017-12-26 13:38:07 +03:00
use super::source::SourceInput;
2017-11-03 13:52:51 +03:00
#[test]
fn processes_cargo_output() {
let tmp_dir = TempDir::new("target").expect("temp dir failed");
2017-11-03 13:52:51 +03:00
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");
}
2017-12-26 13:38:07 +03:00
let path = tmp_dir.path().to_string_lossy();
let input = SourceInput::new(&path, "example-wasm");
process_output(&input).expect("process output failed");
2017-11-03 13:52:51 +03:00
assert!(
fs::metadata(tmp_dir.path().join("example-wasm.wasm")).expect("metadata failed").is_file()
)
}
}