mirror of
https://github.com/mii443/wasmer.git
synced 2025-08-31 12:49:28 +00:00
Created stand-alone wasmer-compiler, builded only as a .wasm target for now
Removed leftover trace of compiler feature in compiler-cli Excluded the new wasmer-compiler-cli from lint test, like wasmer-cli it's based on
This commit is contained in:
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -2906,6 +2906,28 @@ dependencies = [
|
||||
"wasmparser 0.83.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-compiler-cli"
|
||||
version = "2.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"bytesize",
|
||||
"cfg-if 1.0.0",
|
||||
"colored 2.0.0",
|
||||
"distance",
|
||||
"fern",
|
||||
"log",
|
||||
"structopt",
|
||||
"tempfile",
|
||||
"unix_mode",
|
||||
"wasmer-compiler",
|
||||
"wasmer-compiler-singlepass",
|
||||
"wasmer-engine-universal-artifact",
|
||||
"wasmer-types",
|
||||
"wasmer-vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-compiler-cranelift"
|
||||
version = "2.2.1"
|
||||
|
@ -34,6 +34,7 @@ members = [
|
||||
"lib/cache",
|
||||
"lib/c-api",
|
||||
"lib/cli",
|
||||
"lib/cli-compiler",
|
||||
"lib/compiler",
|
||||
"lib/compiler-cranelift",
|
||||
"lib/compiler-singlepass",
|
||||
|
5
Makefile
5
Makefile
@ -151,7 +151,7 @@ ifneq ($(ENABLE_LLVM), 0)
|
||||
endif
|
||||
endif
|
||||
|
||||
exclude_tests := --exclude wasmer-c-api --exclude wasmer-cli
|
||||
exclude_tests := --exclude wasmer-c-api --exclude wasmer-cli --exclude wasmer-compiler-cli
|
||||
# Is failing to compile in Linux for some reason
|
||||
exclude_tests += --exclude wasmer-wasi-experimental-io-devices
|
||||
# We run integration tests separately (it requires building the c-api)
|
||||
@ -379,6 +379,9 @@ build-wasmer-debug:
|
||||
bench:
|
||||
cargo bench $(compiler_features)
|
||||
|
||||
build-wasmer-wasm:
|
||||
cargo build --release --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasi --features singlepass,universal --bin wasmer-compiler
|
||||
|
||||
# For best results ensure the release profile looks like the following
|
||||
# in Cargo.toml:
|
||||
# [profile.release]
|
||||
|
65
lib/cli-compiler/Cargo.toml
Normal file
65
lib/cli-compiler/Cargo.toml
Normal file
@ -0,0 +1,65 @@
|
||||
[package]
|
||||
name = "wasmer-compiler-cli"
|
||||
version = "2.2.1"
|
||||
description = "Wasmer Compiler CLI"
|
||||
categories = ["wasm", "command-line-interface"]
|
||||
keywords = ["wasm", "webassembly", "cli"]
|
||||
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
default-run = "wasmer-compiler"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasmer-compiler"
|
||||
path = "src/bin/wasmer_compiler.rs"
|
||||
doc = false
|
||||
|
||||
[dependencies]
|
||||
wasmer-engine-universal-artifact = { version = "=2.2.1", path = "../universal-artifact", features = ["compiler"] }
|
||||
wasmer-compiler = { version = "=2.2.1", path = "../compiler" }
|
||||
wasmer-types = { version = "=2.2.1", path = "../types" }
|
||||
wasmer-vfs = { version = "=2.2.1", path = "../vfs", default-features = false, features = ["host-fs"] }
|
||||
atty = "0.2"
|
||||
colored = "2.0"
|
||||
anyhow = "1.0"
|
||||
structopt = { version = "0.3", features = ["suggestions"] }
|
||||
# For the function names autosuggestion
|
||||
distance = "0.4"
|
||||
# For the inspect subcommand
|
||||
bytesize = "1.0"
|
||||
cfg-if = "1.0"
|
||||
# For debug feature
|
||||
fern = { version = "0.6", features = ["colored"], optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
tempfile = "3"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true, default-features = false, features = ["wasm"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
unix_mode = "0.1.3"
|
||||
|
||||
[features]
|
||||
# Don't add the compiler features in default, please add them on the Makefile
|
||||
# since we might want to autoconfigure them depending on the availability on the host.
|
||||
default = [
|
||||
"universal",
|
||||
]
|
||||
engine = []
|
||||
universal = []
|
||||
compiler = [
|
||||
"wasmer-compiler/translator",
|
||||
]
|
||||
singlepass = [
|
||||
"wasmer-compiler-singlepass",
|
||||
"compiler",
|
||||
]
|
||||
debug = ["fern", "log"]
|
||||
disable-all-logging = []
|
||||
jit = ["universal"]
|
34
lib/cli-compiler/README.md
Normal file
34
lib/cli-compiler/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# `wasmer-cli-compiler` [](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [](https://slack.wasmer.io) [](https://github.com/wasmerio/wasmer/blob/master/LICENSE)
|
||||
|
||||
This crate is the Wasmer Compiler only CLI.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
The Compiler only Wasmer supports the following features:
|
||||
* `universal` (default): support for the [Universal engine].
|
||||
* `wasi` (default): support for [WASI].
|
||||
* `experimental-io-devices`: support for experimental IO devices in WASI.
|
||||
* `emscripten` (default): support for [Emscripten].
|
||||
* `singlepass`: support for the [Singlepass compiler].
|
||||
|
||||
[Universal engine]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal/
|
||||
[WASI]: https://github.com/wasmerio/wasmer/tree/master/lib/wasi/
|
||||
[Emscripten]: https://github.com/wasmerio/wasmer/tree/master/lib/emscripten/
|
||||
[Singlepass compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass/
|
||||
|
||||
## CLI commands
|
||||
|
||||
Once you have Wasmer installed, you can start executing WebAssembly files easily:
|
||||
|
||||
Get the current Wasmer version:
|
||||
|
||||
```bash
|
||||
wasmer-compiler -V
|
||||
```
|
||||
|
||||
Compile a WebAssembly file:
|
||||
|
||||
```bash
|
||||
wasmer-compiler compile myfile.wasm -o myfile.wasmu --singlepass --universal
|
||||
```
|
4
lib/cli-compiler/build.rs
Normal file
4
lib/cli-compiler/build.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX");
|
||||
}
|
13
lib/cli-compiler/src/bin/wasmer_compiler.rs
Normal file
13
lib/cli-compiler/src/bin/wasmer_compiler.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use wasmer_compiler_cli::cli::wasmer_main;
|
||||
|
||||
#[cfg(not(any(feature = "cranelift", feature = "singlepass", feature = "llvm")))]
|
||||
compile_error!(
|
||||
"Either enable at least one compiler, or compile the wasmer-headless binary instead"
|
||||
);
|
||||
|
||||
#[cfg(featue = "run")]
|
||||
compile_error!("Cannot enable run with the compile-only build");
|
||||
|
||||
fn main() {
|
||||
wasmer_main();
|
||||
}
|
75
lib/cli-compiler/src/cli.rs
Normal file
75
lib/cli-compiler/src/cli.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! The logic for the Wasmer CLI tool.
|
||||
|
||||
use crate::commands::Compile;
|
||||
use crate::commands::{Config, Validate};
|
||||
use crate::error::PrettyError;
|
||||
use anyhow::Result;
|
||||
|
||||
use structopt::{clap::ErrorKind, StructOpt};
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "wasmer-compiler",
|
||||
about = "WebAssembly standalone Compiler.",
|
||||
author
|
||||
)]
|
||||
/// The options for the wasmer Command Line Interface
|
||||
enum WasmerCLIOptions {
|
||||
/// Validate a WebAssembly binary
|
||||
#[structopt(name = "validate")]
|
||||
Validate(Validate),
|
||||
|
||||
/// Compile a WebAssembly binary
|
||||
#[structopt(name = "compile")]
|
||||
Compile(Compile),
|
||||
|
||||
/// Get various configuration information needed
|
||||
/// to compile programs which use Wasmer
|
||||
#[structopt(name = "config")]
|
||||
Config(Config),
|
||||
}
|
||||
|
||||
impl WasmerCLIOptions {
|
||||
fn execute(&self) -> Result<()> {
|
||||
match self {
|
||||
Self::Validate(validate) => validate.execute(),
|
||||
Self::Compile(compile) => compile.execute(),
|
||||
Self::Config(config) => config.execute(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The main function for the Wasmer CLI tool.
|
||||
pub fn wasmer_main() {
|
||||
// We allow windows to print properly colors
|
||||
#[cfg(windows)]
|
||||
colored::control::set_virtual_terminal(true).unwrap();
|
||||
|
||||
// We try to run wasmer with the normal arguments.
|
||||
// Eg. `wasmer <SUBCOMMAND>`
|
||||
// In case that fails, we fallback trying the Run subcommand directly.
|
||||
// Eg. `wasmer myfile.wasm --dir=.`
|
||||
//
|
||||
// In case we've been run as wasmer-binfmt-interpreter myfile.wasm args,
|
||||
// we assume that we're registered via binfmt_misc
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let command = args.get(1);
|
||||
let options = {
|
||||
match command.unwrap_or(&"".to_string()).as_ref() {
|
||||
"compile" | "config" | "help" | "inspect" | "validate" => WasmerCLIOptions::from_args(),
|
||||
_ => {
|
||||
WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| {
|
||||
match e.kind {
|
||||
// This fixes a issue that:
|
||||
// 1. Shows the version twice when doing `wasmer -V`
|
||||
// 2. Shows the run help (instead of normal help) when doing `wasmer --help`
|
||||
ErrorKind::VersionDisplayed | ErrorKind::HelpDisplayed => e.exit(),
|
||||
_ => WasmerCLIOptions::Compile(Compile::from_args()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PrettyError::report(options.execute());
|
||||
}
|
7
lib/cli-compiler/src/commands.rs
Normal file
7
lib/cli-compiler/src/commands.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! The commands available in the Wasmer binary.
|
||||
mod compile;
|
||||
mod config;
|
||||
mod validate;
|
||||
|
||||
pub use compile::*;
|
||||
pub use {config::*, validate::*};
|
124
lib/cli-compiler/src/commands/compile.rs
Normal file
124
lib/cli-compiler/src/commands/compile.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use crate::store::{EngineType, StoreOptions};
|
||||
use crate::warning;
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::{Path, PathBuf};
|
||||
use structopt::StructOpt;
|
||||
use wasmer_compiler::{CompileError, CpuFeature, ModuleEnvironment, Target, Triple};
|
||||
use wasmer_engine_universal_artifact::{ArtifactCreate, UniversalArtifactBuild};
|
||||
use wasmer_types::entity::PrimaryMap;
|
||||
use wasmer_types::{MemoryIndex, MemoryStyle, TableIndex, TableStyle};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer compile` subcommand
|
||||
pub struct Compile {
|
||||
/// Input file
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
/// Output file
|
||||
#[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
|
||||
/// Compilation Target triple
|
||||
#[structopt(long = "target")]
|
||||
target_triple: Option<Triple>,
|
||||
|
||||
#[structopt(flatten)]
|
||||
store: StoreOptions,
|
||||
|
||||
#[structopt(short = "m", multiple = true, number_of_values = 1)]
|
||||
cpu_features: Vec<CpuFeature>,
|
||||
}
|
||||
|
||||
impl Compile {
|
||||
/// Runs logic for the `compile` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
self.inner_execute()
|
||||
.context(format!("failed to compile `{}`", self.path.display()))
|
||||
}
|
||||
|
||||
pub(crate) fn get_recommend_extension(
|
||||
engine_type: &EngineType,
|
||||
target_triple: &Triple,
|
||||
) -> Result<&'static str> {
|
||||
Ok(match engine_type {
|
||||
EngineType::Universal => {
|
||||
wasmer_engine_universal_artifact::UniversalArtifactBuild::get_default_extension(
|
||||
target_triple,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn inner_execute(&self) -> Result<()> {
|
||||
let target = self
|
||||
.target_triple
|
||||
.as_ref()
|
||||
.map(|target_triple| {
|
||||
let mut features = self
|
||||
.cpu_features
|
||||
.clone()
|
||||
.into_iter()
|
||||
.fold(CpuFeature::set(), |a, b| a | b);
|
||||
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
|
||||
// usage
|
||||
features |= CpuFeature::SSE2;
|
||||
Target::new(target_triple.clone(), features)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let (mut engine, engine_type, compiler_type) =
|
||||
self.store.get_engine_for_target(target.clone())?;
|
||||
let output_filename = self
|
||||
.output
|
||||
.file_stem()
|
||||
.map(|osstr| osstr.to_string_lossy().to_string())
|
||||
.unwrap_or_default();
|
||||
let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple())?;
|
||||
match self.output.extension() {
|
||||
Some(ext) => {
|
||||
if ext != recommended_extension {
|
||||
warning!("the output file has a wrong extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
warning!("the output file has no extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension)
|
||||
}
|
||||
}
|
||||
let tunables = self.store.get_tunables_for_target(&target)?;
|
||||
|
||||
println!("Engine: {}", engine_type.to_string());
|
||||
println!("Compiler: {}", compiler_type.to_string());
|
||||
println!("Target: {}", target.triple());
|
||||
|
||||
// compile and save the artifact (without using module from api)
|
||||
let path: &Path = self.path.as_ref();
|
||||
let wasm_bytes = std::fs::read(path)?;
|
||||
let environ = ModuleEnvironment::new();
|
||||
let translation = environ.translate(&wasm_bytes).map_err(CompileError::Wasm)?;
|
||||
let module = translation.module;
|
||||
let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = module
|
||||
.memories
|
||||
.values()
|
||||
.map(|memory_type| tunables.memory_style(memory_type))
|
||||
.collect();
|
||||
let table_styles: PrimaryMap<TableIndex, TableStyle> = module
|
||||
.tables
|
||||
.values()
|
||||
.map(|table_type| tunables.table_style(table_type))
|
||||
.collect();
|
||||
let artifact = UniversalArtifactBuild::new(
|
||||
&mut engine,
|
||||
&wasm_bytes,
|
||||
&target,
|
||||
memory_styles,
|
||||
table_styles,
|
||||
)?;
|
||||
artifact.serialize_to_file(self.output.as_ref())?;
|
||||
eprintln!(
|
||||
"✔ File compiled successfully to `{}`.",
|
||||
self.output.display(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
102
lib/cli-compiler/src/commands/config.rs
Normal file
102
lib/cli-compiler/src/commands/config.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use crate::VERSION;
|
||||
use anyhow::{Context, Result};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer config` subcommand
|
||||
pub struct Config {
|
||||
/// Print the installation prefix.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
prefix: bool,
|
||||
|
||||
/// Directory containing Wasmer executables.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
bindir: bool,
|
||||
|
||||
/// Directory containing Wasmer headers.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
includedir: bool,
|
||||
|
||||
/// Directory containing Wasmer libraries.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
libdir: bool,
|
||||
|
||||
/// Libraries needed to link against Wasmer components.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
libs: bool,
|
||||
|
||||
/// C compiler flags for files that include Wasmer headers.
|
||||
#[structopt(long, conflicts_with = "pkg-config")]
|
||||
cflags: bool,
|
||||
|
||||
/// It outputs the necessary details for compiling
|
||||
/// and linking a program to Wasmer, using the `pkg-config` format.
|
||||
#[structopt(long)]
|
||||
pkg_config: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Runs logic for the `config` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
self.inner_execute()
|
||||
.context("failed to retrieve the wasmer config".to_string())
|
||||
}
|
||||
fn inner_execute(&self) -> Result<()> {
|
||||
let key = "WASMER_DIR";
|
||||
let wasmer_dir = env::var(key)
|
||||
.or_else(|e| {
|
||||
option_env!("WASMER_INSTALL_PREFIX")
|
||||
.map(str::to_string)
|
||||
.ok_or(e)
|
||||
})
|
||||
.context(format!(
|
||||
"failed to retrieve the {} environment variables",
|
||||
key
|
||||
))?;
|
||||
|
||||
let prefix = PathBuf::from(wasmer_dir);
|
||||
|
||||
let prefixdir = prefix.display().to_string();
|
||||
let bindir = prefix.join("bin").display().to_string();
|
||||
let includedir = prefix.join("include").display().to_string();
|
||||
let libdir = prefix.join("lib").display().to_string();
|
||||
let cflags = format!("-I{}", includedir);
|
||||
let libs = format!("-L{} -lwasmer", libdir);
|
||||
|
||||
if self.pkg_config {
|
||||
println!("prefix={}", prefixdir);
|
||||
println!("exec_prefix={}", bindir);
|
||||
println!("includedir={}", includedir);
|
||||
println!("libdir={}", libdir);
|
||||
println!();
|
||||
println!("Name: wasmer");
|
||||
println!("Description: The Wasmer library for running WebAssembly");
|
||||
println!("Version: {}", VERSION);
|
||||
println!("Cflags: {}", cflags);
|
||||
println!("Libs: {}", libs);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.prefix {
|
||||
println!("{}", prefixdir);
|
||||
}
|
||||
if self.bindir {
|
||||
println!("{}", bindir);
|
||||
}
|
||||
if self.includedir {
|
||||
println!("{}", includedir);
|
||||
}
|
||||
if self.libdir {
|
||||
println!("{}", libdir);
|
||||
}
|
||||
if self.libs {
|
||||
println!("{}", libs);
|
||||
}
|
||||
if self.cflags {
|
||||
println!("{}", cflags);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
40
lib/cli-compiler/src/commands/validate.rs
Normal file
40
lib/cli-compiler/src/commands/validate.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::store::StoreOptions;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use structopt::StructOpt;
|
||||
use wasmer_compiler::{CpuFeature, Target, Triple};
|
||||
use wasmer_types::is_wasm;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer validate` subcommand
|
||||
pub struct Validate {
|
||||
/// File to validate as WebAssembly
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
#[structopt(flatten)]
|
||||
store: StoreOptions,
|
||||
}
|
||||
|
||||
impl Validate {
|
||||
/// Runs logic for the `validate` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
self.inner_execute()
|
||||
.context(format!("failed to validate `{}`", self.path.display()))
|
||||
}
|
||||
fn inner_execute(&self) -> Result<()> {
|
||||
let target = Target::new(
|
||||
Triple::from_str("x86_64-linux-gnu").unwrap(),
|
||||
CpuFeature::SSE2 | CpuFeature::AVX,
|
||||
);
|
||||
let (engine, _engine_type, _compiler_type) = self.store.get_engine_for_target(target)?;
|
||||
let module_contents = std::fs::read(&self.path)?;
|
||||
if !is_wasm(&module_contents) {
|
||||
bail!("`wasmer validate` only validates WebAssembly files");
|
||||
}
|
||||
engine.validate(&module_contents)?;
|
||||
eprintln!("Validation passed for `{}`.", self.path.display());
|
||||
Ok(())
|
||||
}
|
||||
}
|
53
lib/cli-compiler/src/common.rs
Normal file
53
lib/cli-compiler/src/common.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! Common module with common used structures across different
|
||||
//! commands.
|
||||
use crate::VERSION;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt, Clone, Default)]
|
||||
/// The WebAssembly features that can be passed through the
|
||||
/// Command Line args.
|
||||
pub struct WasmFeatures {
|
||||
/// Enable support for the SIMD proposal.
|
||||
#[structopt(long = "enable-simd")]
|
||||
pub simd: bool,
|
||||
|
||||
/// Enable support for the threads proposal.
|
||||
#[structopt(long = "enable-threads")]
|
||||
pub threads: bool,
|
||||
|
||||
/// Enable support for the reference types proposal.
|
||||
#[structopt(long = "enable-reference-types")]
|
||||
pub reference_types: bool,
|
||||
|
||||
/// Enable support for the multi value proposal.
|
||||
#[structopt(long = "enable-multi-value")]
|
||||
pub multi_value: bool,
|
||||
|
||||
/// Enable support for the bulk memory proposal.
|
||||
#[structopt(long = "enable-bulk-memory")]
|
||||
pub bulk_memory: bool,
|
||||
|
||||
/// Enable support for all pre-standard proposals.
|
||||
#[structopt(long = "enable-all")]
|
||||
pub all: bool,
|
||||
}
|
||||
|
||||
/// Get the cache dir
|
||||
pub fn get_cache_dir() -> PathBuf {
|
||||
match env::var("WASMER_CACHE_DIR") {
|
||||
Ok(dir) => {
|
||||
let mut path = PathBuf::from(dir);
|
||||
path.push(VERSION);
|
||||
path
|
||||
}
|
||||
Err(_) => {
|
||||
// We use a temporal directory for saving cache files
|
||||
let mut temp_dir = env::temp_dir();
|
||||
temp_dir.push("wasmer");
|
||||
temp_dir.push(VERSION);
|
||||
temp_dir
|
||||
}
|
||||
}
|
||||
}
|
114
lib/cli-compiler/src/error.rs
Normal file
114
lib/cli-compiler/src/error.rs
Normal file
@ -0,0 +1,114 @@
|
||||
//! Implements `PretyError` to print pretty errors in the CLI (when they happen)
|
||||
|
||||
use anyhow::{Chain, Error};
|
||||
use colored::*;
|
||||
use std::fmt::{self, Debug, Write};
|
||||
|
||||
/// A `PrettyError` for printing `anyhow::Error` nicely.
|
||||
pub struct PrettyError {
|
||||
error: Error,
|
||||
}
|
||||
|
||||
/// A macro that prints a warning with nice colors
|
||||
#[macro_export]
|
||||
macro_rules! warning {
|
||||
($($arg:tt)*) => ({
|
||||
use colored::*;
|
||||
eprintln!("{}: {}", "warning".yellow().bold(), format!($($arg)*));
|
||||
})
|
||||
}
|
||||
|
||||
impl PrettyError {
|
||||
/// Process a `Result` printing any errors and exiting
|
||||
/// the process after
|
||||
pub fn report<T>(result: Result<T, Error>) -> ! {
|
||||
std::process::exit(match result {
|
||||
Ok(_t) => 0,
|
||||
Err(error) => {
|
||||
eprintln!("{:?}", PrettyError { error });
|
||||
1
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PrettyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let error = &self.error;
|
||||
|
||||
if f.alternate() {
|
||||
return Debug::fmt(&error, f);
|
||||
}
|
||||
|
||||
write!(f, "{}", format!("{}: {}", "error".red(), error).bold())?;
|
||||
// write!(f, "{}", error)?;
|
||||
|
||||
if let Some(cause) = error.source() {
|
||||
// write!(f, "\n{}:", "caused by".bold().blue())?;
|
||||
let chain = Chain::new(cause);
|
||||
let (total_errors, _) = chain.size_hint();
|
||||
for (n, error) in chain.enumerate() {
|
||||
writeln!(f)?;
|
||||
let mut indented = Indented {
|
||||
inner: f,
|
||||
number: Some(n + 1),
|
||||
is_last: n == total_errors - 1,
|
||||
started: false,
|
||||
};
|
||||
write!(indented, "{}", error)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Indented<'a, D> {
|
||||
inner: &'a mut D,
|
||||
number: Option<usize>,
|
||||
started: bool,
|
||||
is_last: bool,
|
||||
}
|
||||
|
||||
impl<T> Write for Indented<'_, T>
|
||||
where
|
||||
T: Write,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for (i, line) in s.split('\n').enumerate() {
|
||||
if !self.started {
|
||||
self.started = true;
|
||||
match self.number {
|
||||
Some(number) => {
|
||||
if !self.is_last {
|
||||
write!(
|
||||
self.inner,
|
||||
"{} {: >4} ",
|
||||
"│".bold().blue(),
|
||||
format!("{}:", number).dimmed()
|
||||
)?
|
||||
} else {
|
||||
write!(
|
||||
self.inner,
|
||||
"{}{: >2}: ",
|
||||
"╰─▶".bold().blue(),
|
||||
format!("{}", number).bold().blue()
|
||||
)?
|
||||
}
|
||||
}
|
||||
None => self.inner.write_str(" ")?,
|
||||
}
|
||||
} else if i > 0 {
|
||||
self.inner.write_char('\n')?;
|
||||
if self.number.is_some() {
|
||||
self.inner.write_str(" ")?;
|
||||
} else {
|
||||
self.inner.write_str(" ")?;
|
||||
}
|
||||
}
|
||||
|
||||
self.inner.write_str(line)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
30
lib/cli-compiler/src/lib.rs
Normal file
30
lib/cli-compiler/src/lib.rs
Normal file
@ -0,0 +1,30 @@
|
||||
//! The Wasmer binary lib
|
||||
|
||||
#![deny(
|
||||
missing_docs,
|
||||
dead_code,
|
||||
nonstandard_style,
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns,
|
||||
unstable_features
|
||||
)]
|
||||
#![doc(html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
|
||||
pub mod commands;
|
||||
pub mod common;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
pub mod cli;
|
||||
#[cfg(feature = "debug")]
|
||||
pub mod logging;
|
||||
pub mod store;
|
||||
pub mod utils;
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
68
lib/cli-compiler/src/logging.rs
Normal file
68
lib/cli-compiler/src/logging.rs
Normal file
@ -0,0 +1,68 @@
|
||||
//! Logging functions for the debug feature.
|
||||
use crate::utils::wasmer_should_print_color;
|
||||
use anyhow::Result;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use std::time;
|
||||
|
||||
/// The debug level
|
||||
pub type DebugLevel = log::LevelFilter;
|
||||
|
||||
/// Subroutine to instantiate the loggers
|
||||
pub fn set_up_logging(verbose: u8) -> Result<(), String> {
|
||||
let colors_line = ColoredLevelConfig::new()
|
||||
.error(Color::Red)
|
||||
.warn(Color::Yellow)
|
||||
.trace(Color::BrightBlack);
|
||||
let should_color = wasmer_should_print_color();
|
||||
|
||||
let colors_level = colors_line.info(Color::Green);
|
||||
let level = match verbose {
|
||||
1 => DebugLevel::Debug,
|
||||
_ => DebugLevel::Trace,
|
||||
};
|
||||
let dispatch = fern::Dispatch::new()
|
||||
.level(level)
|
||||
.chain({
|
||||
let base = if should_color {
|
||||
fern::Dispatch::new().format(move |out, message, record| {
|
||||
let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time");
|
||||
out.finish(format_args!(
|
||||
"{color_line}[{seconds}.{millis} {level} {target}{color_line}]{ansi_close} {message}",
|
||||
color_line = format_args!(
|
||||
"\x1B[{}m",
|
||||
colors_line.get_color(&record.level()).to_fg_str()
|
||||
),
|
||||
seconds = time.as_secs(),
|
||||
millis = time.subsec_millis(),
|
||||
level = colors_level.color(record.level()),
|
||||
target = record.target(),
|
||||
ansi_close = "\x1B[0m",
|
||||
message = message,
|
||||
));
|
||||
})
|
||||
} else {
|
||||
// default formatter without color
|
||||
fern::Dispatch::new().format(move |out, message, record| {
|
||||
let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time");
|
||||
out.finish(format_args!(
|
||||
"[{seconds}.{millis} {level} {target}] {message}",
|
||||
seconds = time.as_secs(),
|
||||
millis = time.subsec_millis(),
|
||||
level = record.level(),
|
||||
target = record.target(),
|
||||
message = message,
|
||||
));
|
||||
})
|
||||
};
|
||||
|
||||
base
|
||||
.filter(|metadata| {
|
||||
metadata.target().starts_with("wasmer")
|
||||
})
|
||||
.chain(std::io::stdout())
|
||||
});
|
||||
|
||||
dispatch.apply().map_err(|e| format!("{}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
410
lib/cli-compiler/src/store.rs
Normal file
410
lib/cli-compiler/src/store.rs
Normal file
@ -0,0 +1,410 @@
|
||||
//! Common module with common used structures across different
|
||||
//! commands.
|
||||
|
||||
use crate::common::WasmFeatures;
|
||||
use anyhow::Result;
|
||||
use std::string::ToString;
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::Arc;
|
||||
use structopt::StructOpt;
|
||||
use wasmer_compiler::{CompilerConfig, Features, PointerWidth, Target};
|
||||
use wasmer_engine_universal_artifact::UniversalEngineBuilder;
|
||||
use wasmer_types::{MemoryStyle, MemoryType, Pages, TableStyle, TableType};
|
||||
|
||||
/// Minimul Subset of Tunable parameters for WebAssembly compilation.
|
||||
#[derive(Clone)]
|
||||
pub struct SubsetTunables {
|
||||
/// For static heaps, the size in wasm pages of the heap protected by bounds checking.
|
||||
pub static_memory_bound: Pages,
|
||||
|
||||
/// The size in bytes of the offset guard for static heaps.
|
||||
pub static_memory_offset_guard_size: u64,
|
||||
|
||||
/// The size in bytes of the offset guard for dynamic heaps.
|
||||
pub dynamic_memory_offset_guard_size: u64,
|
||||
}
|
||||
|
||||
impl SubsetTunables {
|
||||
/// Get the `BaseTunables` for a specific Target
|
||||
pub fn for_target(target: &Target) -> Self {
|
||||
let triple = target.triple();
|
||||
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
|
||||
let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) =
|
||||
match pointer_width {
|
||||
PointerWidth::U16 => (0x400.into(), 0x1000),
|
||||
PointerWidth::U32 => (0x4000.into(), 0x1_0000),
|
||||
// Static Memory Bound:
|
||||
// Allocating 4 GiB of address space let us avoid the
|
||||
// need for explicit bounds checks.
|
||||
// Static Memory Guard size:
|
||||
// Allocating 2 GiB of address space lets us translate wasm
|
||||
// offsets into x86 offsets as aggressively as we can.
|
||||
PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
|
||||
};
|
||||
|
||||
// Allocate a small guard to optimize common cases but without
|
||||
// wasting too much memory.
|
||||
// The Windows memory manager seems more laxed than the other ones
|
||||
// And a guard of just 1 page may not be enough is some borderline cases
|
||||
// So using 2 pages for guard on this platform
|
||||
#[cfg(target_os = "windows")]
|
||||
let dynamic_memory_offset_guard_size: u64 = 0x2_0000;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
|
||||
|
||||
Self {
|
||||
static_memory_bound,
|
||||
static_memory_offset_guard_size,
|
||||
dynamic_memory_offset_guard_size,
|
||||
}
|
||||
}
|
||||
/// Get a `MemoryStyle` for the provided `MemoryType`
|
||||
pub fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
|
||||
// A heap with a maximum that doesn't exceed the static memory bound specified by the
|
||||
// tunables make it static.
|
||||
//
|
||||
// If the module doesn't declare an explicit maximum treat it as 4GiB.
|
||||
let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
|
||||
if maximum <= self.static_memory_bound {
|
||||
MemoryStyle::Static {
|
||||
// Bound can be larger than the maximum for performance reasons
|
||||
bound: self.static_memory_bound,
|
||||
offset_guard_size: self.static_memory_offset_guard_size,
|
||||
}
|
||||
} else {
|
||||
MemoryStyle::Dynamic {
|
||||
offset_guard_size: self.dynamic_memory_offset_guard_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`TableStyle`] for the provided [`TableType`].
|
||||
pub fn table_style(&self, _table: &TableType) -> TableStyle {
|
||||
TableStyle::CallerChecksSignature
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructOpt, Default)]
|
||||
/// The compiler and engine options
|
||||
pub struct StoreOptions {
|
||||
#[structopt(flatten)]
|
||||
compiler: CompilerOptions,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructOpt, Default)]
|
||||
/// The compiler options
|
||||
pub struct CompilerOptions {
|
||||
/// Use Singlepass compiler.
|
||||
#[structopt(long, conflicts_with_all = &["cranelift", "llvm"])]
|
||||
singlepass: bool,
|
||||
|
||||
/// Use Cranelift compiler.
|
||||
#[structopt(long, conflicts_with_all = &["singlepass", "llvm"])]
|
||||
cranelift: bool,
|
||||
|
||||
/// Use LLVM compiler.
|
||||
#[structopt(long, conflicts_with_all = &["singlepass", "cranelift"])]
|
||||
llvm: bool,
|
||||
|
||||
/// Enable compiler internal verification.
|
||||
#[structopt(long)]
|
||||
enable_verifier: bool,
|
||||
|
||||
/// LLVM debug directory, where IR and object files will be written to.
|
||||
#[cfg(feature = "llvm")]
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
llvm_debug_dir: Option<PathBuf>,
|
||||
|
||||
#[structopt(flatten)]
|
||||
features: WasmFeatures,
|
||||
}
|
||||
|
||||
impl CompilerOptions {
|
||||
fn get_compiler(&self) -> Result<CompilerType> {
|
||||
if self.cranelift {
|
||||
Ok(CompilerType::Cranelift)
|
||||
} else if self.llvm {
|
||||
Ok(CompilerType::LLVM)
|
||||
} else if self.singlepass {
|
||||
Ok(CompilerType::Singlepass)
|
||||
} else {
|
||||
// Auto mode, we choose the best compiler for that platform
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] {
|
||||
Ok(CompilerType::Cranelift)
|
||||
}
|
||||
else if #[cfg(all(feature = "singlepass", target_arch = "x86_64"))] {
|
||||
Ok(CompilerType::Singlepass)
|
||||
}
|
||||
else if #[cfg(feature = "llvm")] {
|
||||
Ok(CompilerType::LLVM)
|
||||
} else {
|
||||
bail!("There are no available compilers for your architecture");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the enaled Wasm features.
|
||||
pub fn get_features(&self, mut features: Features) -> Result<Features> {
|
||||
if self.features.threads || self.features.all {
|
||||
features.threads(true);
|
||||
}
|
||||
if self.features.multi_value || self.features.all {
|
||||
features.multi_value(true);
|
||||
}
|
||||
if self.features.simd || self.features.all {
|
||||
features.simd(true);
|
||||
}
|
||||
if self.features.bulk_memory || self.features.all {
|
||||
features.bulk_memory(true);
|
||||
}
|
||||
if self.features.reference_types || self.features.all {
|
||||
features.reference_types(true);
|
||||
}
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
fn get_engine_by_type(
|
||||
&self,
|
||||
target: Target,
|
||||
compiler_config: Box<dyn CompilerConfig>,
|
||||
engine_type: EngineType,
|
||||
) -> Result<UniversalEngineBuilder> {
|
||||
let features = self.get_features(compiler_config.default_features_for_target(&target))?;
|
||||
let engine: UniversalEngineBuilder = match engine_type {
|
||||
EngineType::Universal => {
|
||||
UniversalEngineBuilder::new(Some(compiler_config.compiler()), features)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
/// Get the Compiler Config for the current options
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, CompilerType)> {
|
||||
let compiler = self.get_compiler()?;
|
||||
let compiler_config: Box<dyn CompilerConfig> = match compiler {
|
||||
CompilerType::Headless => bail!("The headless engine can't be chosen"),
|
||||
#[cfg(feature = "singlepass")]
|
||||
CompilerType::Singlepass => {
|
||||
let mut config = wasmer_compiler_singlepass::Singlepass::new();
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "cranelift")]
|
||||
CompilerType::Cranelift => {
|
||||
let mut config = wasmer_compiler_cranelift::Cranelift::new();
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(feature = "llvm")]
|
||||
CompilerType::LLVM => {
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use wasmer_compiler_llvm::{
|
||||
CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM,
|
||||
};
|
||||
use wasmer_types::entity::EntityRef;
|
||||
let mut config = LLVM::new();
|
||||
struct Callbacks {
|
||||
debug_dir: PathBuf,
|
||||
}
|
||||
impl Callbacks {
|
||||
fn new(debug_dir: PathBuf) -> Result<Self> {
|
||||
// Create the debug dir in case it doesn't exist
|
||||
std::fs::create_dir_all(&debug_dir)?;
|
||||
Ok(Self { debug_dir })
|
||||
}
|
||||
}
|
||||
// Converts a kind into a filename, that we will use to dump
|
||||
// the contents of the IR object file to.
|
||||
fn types_to_signature(types: &[Type]) -> String {
|
||||
types
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
Type::I32 => "i".to_string(),
|
||||
Type::I64 => "I".to_string(),
|
||||
Type::F32 => "f".to_string(),
|
||||
Type::F64 => "F".to_string(),
|
||||
Type::V128 => "v".to_string(),
|
||||
Type::ExternRef => "e".to_string(),
|
||||
Type::FuncRef => "r".to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("")
|
||||
}
|
||||
// Converts a kind into a filename, that we will use to dump
|
||||
// the contents of the IR object file to.
|
||||
fn function_kind_to_filename(kind: &CompiledKind) -> String {
|
||||
match kind {
|
||||
CompiledKind::Local(local_index) => {
|
||||
format!("function_{}", local_index.index())
|
||||
}
|
||||
CompiledKind::FunctionCallTrampoline(func_type) => format!(
|
||||
"trampoline_call_{}_{}",
|
||||
types_to_signature(&func_type.params()),
|
||||
types_to_signature(&func_type.results())
|
||||
),
|
||||
CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
|
||||
"trampoline_dynamic_{}_{}",
|
||||
types_to_signature(&func_type.params()),
|
||||
types_to_signature(&func_type.results())
|
||||
),
|
||||
CompiledKind::Module => "module".into(),
|
||||
}
|
||||
}
|
||||
impl LLVMCallbacks for Callbacks {
|
||||
fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
|
||||
let mut path = self.debug_dir.clone();
|
||||
path.push(format!("{}.preopt.ll", function_kind_to_filename(kind)));
|
||||
module
|
||||
.print_to_file(&path)
|
||||
.expect("Error while dumping pre optimized LLVM IR");
|
||||
}
|
||||
fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
|
||||
let mut path = self.debug_dir.clone();
|
||||
path.push(format!("{}.postopt.ll", function_kind_to_filename(kind)));
|
||||
module
|
||||
.print_to_file(&path)
|
||||
.expect("Error while dumping post optimized LLVM IR");
|
||||
}
|
||||
fn obj_memory_buffer(
|
||||
&self,
|
||||
kind: &CompiledKind,
|
||||
memory_buffer: &InkwellMemoryBuffer,
|
||||
) {
|
||||
let mut path = self.debug_dir.clone();
|
||||
path.push(format!("{}.o", function_kind_to_filename(kind)));
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
let mut file = File::create(path)
|
||||
.expect("Error while creating debug object file from LLVM IR");
|
||||
let mut pos = 0;
|
||||
while pos < mem_buf_slice.len() {
|
||||
pos += file.write(&mem_buf_slice[pos..]).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Callbacks {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "LLVMCallbacks")
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
|
||||
config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
|
||||
}
|
||||
if self.enable_verifier {
|
||||
config.enable_verifier();
|
||||
}
|
||||
Box::new(config)
|
||||
}
|
||||
#[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm",)))]
|
||||
compiler => {
|
||||
bail!(
|
||||
"The `{}` compiler is not included in this binary.",
|
||||
compiler.to_string()
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Ok((compiler_config, compiler))
|
||||
}
|
||||
}
|
||||
|
||||
/// The compiler used for the store
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum CompilerType {
|
||||
/// Singlepass compiler
|
||||
Singlepass,
|
||||
/// Cranelift compiler
|
||||
Cranelift,
|
||||
/// LLVM compiler
|
||||
LLVM,
|
||||
/// Headless compiler
|
||||
Headless,
|
||||
}
|
||||
|
||||
impl CompilerType {
|
||||
/// Return all enabled compilers
|
||||
pub fn enabled() -> Vec<CompilerType> {
|
||||
vec![
|
||||
#[cfg(feature = "singlepass")]
|
||||
Self::Singlepass,
|
||||
#[cfg(feature = "cranelift")]
|
||||
Self::Cranelift,
|
||||
#[cfg(feature = "llvm")]
|
||||
Self::LLVM,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CompilerType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Singlepass => "singlepass".to_string(),
|
||||
Self::Cranelift => "cranelift".to_string(),
|
||||
Self::LLVM => "llvm".to_string(),
|
||||
Self::Headless => "headless".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The engine used for the store
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum EngineType {
|
||||
/// Universal Engine
|
||||
Universal,
|
||||
}
|
||||
|
||||
impl ToString for EngineType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Universal => "universal".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreOptions {
|
||||
/// Get a UniversalEngineBulder for the Target
|
||||
pub fn get_engine_for_target(
|
||||
&self,
|
||||
target: Target,
|
||||
) -> Result<(UniversalEngineBuilder, EngineType, CompilerType)> {
|
||||
let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?;
|
||||
let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?;
|
||||
Ok((engine, engine_type, compiler_type))
|
||||
}
|
||||
|
||||
/// Get default EngineType
|
||||
pub fn get_engine(&self) -> Result<EngineType> {
|
||||
Ok(EngineType::Universal)
|
||||
}
|
||||
|
||||
fn get_engine_with_compiler(
|
||||
&self,
|
||||
target: Target,
|
||||
compiler_config: Box<dyn CompilerConfig>,
|
||||
) -> Result<(UniversalEngineBuilder, EngineType)> {
|
||||
let engine_type = self.get_engine()?;
|
||||
let engine = self
|
||||
.compiler
|
||||
.get_engine_by_type(target, compiler_config, engine_type)?;
|
||||
|
||||
Ok((engine, engine_type))
|
||||
}
|
||||
|
||||
/// Get (Subset)Tunables for the Target
|
||||
pub fn get_tunables_for_target(&self, target: &Target) -> Result<SubsetTunables> {
|
||||
let tunables = SubsetTunables::for_target(target);
|
||||
Ok(tunables)
|
||||
}
|
||||
}
|
92
lib/cli-compiler/src/utils.rs
Normal file
92
lib/cli-compiler/src/utils.rs
Normal file
@ -0,0 +1,92 @@
|
||||
//! Utility functions for the WebAssembly module
|
||||
use anyhow::{bail, Result};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Whether or not Wasmer should print with color
|
||||
pub fn wasmer_should_print_color() -> bool {
|
||||
env::var("WASMER_COLOR")
|
||||
.ok()
|
||||
.and_then(|inner| inner.parse::<bool>().ok())
|
||||
.unwrap_or_else(|| atty::is(atty::Stream::Stdout))
|
||||
}
|
||||
|
||||
fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result<(String, PathBuf)> {
|
||||
let pb = PathBuf::from(&real_dir);
|
||||
if let Ok(pb_metadata) = pb.metadata() {
|
||||
if !pb_metadata.is_dir() {
|
||||
bail!("\"{}\" exists, but it is not a directory", &real_dir);
|
||||
}
|
||||
} else {
|
||||
bail!("Directory \"{}\" does not exist", &real_dir);
|
||||
}
|
||||
Ok((alias.to_string(), pb))
|
||||
}
|
||||
|
||||
/// Parses a mapdir from a string
|
||||
pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
|
||||
// We try first splitting by `::`
|
||||
if let [alias, real_dir] = entry.split("::").collect::<Vec<&str>>()[..] {
|
||||
retrieve_alias_pathbuf(alias, real_dir)
|
||||
}
|
||||
// And then we try splitting by `:` (for compatibility with previous API)
|
||||
else if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
|
||||
retrieve_alias_pathbuf(alias, real_dir)
|
||||
} else {
|
||||
bail!(
|
||||
"Directory mappings must consist of two paths separate by a `::` or `:`. Found {}",
|
||||
&entry
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an environment variable.
|
||||
pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
|
||||
let entry = entry.trim();
|
||||
|
||||
match entry.find('=') {
|
||||
None => bail!(
|
||||
"Environment variable must be of the form `<name>=<value>`; found `{}`",
|
||||
&entry
|
||||
),
|
||||
|
||||
Some(0) => bail!(
|
||||
"Environment variable is not well formed, the `name` is missing in `<name>=<value>`; got `{}`",
|
||||
&entry
|
||||
),
|
||||
|
||||
Some(position) if position == entry.len() - 1 => bail!(
|
||||
"Environment variable is not well formed, the `value` is missing in `<name>=<value>`; got `{}`",
|
||||
&entry
|
||||
),
|
||||
|
||||
Some(position) => Ok((entry[..position].into(), entry[position + 1..].into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::parse_envvar;
|
||||
|
||||
#[test]
|
||||
fn test_parse_envvar() {
|
||||
assert_eq!(
|
||||
parse_envvar("A").unwrap_err().to_string(),
|
||||
"Environment variable must be of the form `<name>=<value>`; found `A`"
|
||||
);
|
||||
assert_eq!(
|
||||
parse_envvar("=A").unwrap_err().to_string(),
|
||||
"Environment variable is not well formed, the `name` is missing in `<name>=<value>`; got `=A`"
|
||||
);
|
||||
assert_eq!(
|
||||
parse_envvar("A=").unwrap_err().to_string(),
|
||||
"Environment variable is not well formed, the `value` is missing in `<name>=<value>`; got `A=`"
|
||||
);
|
||||
assert_eq!(parse_envvar("A=B").unwrap(), ("A".into(), "B".into()));
|
||||
assert_eq!(parse_envvar(" A=B\t").unwrap(), ("A".into(), "B".into()));
|
||||
assert_eq!(
|
||||
parse_envvar("A=B=C=D").unwrap(),
|
||||
("A".into(), "B=C=D".into())
|
||||
);
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
wasmer-compiler = { path = "../compiler", version = "=2.2.1", features = ["translator"], default-features = false }
|
||||
wasmer-types = { path = "../types", version = "=2.2.1", default-features = false, features = ["std"] }
|
||||
rayon = { version = "1.5", optional = true }
|
||||
hashbrown = { version = "0.11", optional = true }
|
||||
gimli = { version = "0.26", optional = true }
|
||||
more-asserts = "0.2"
|
||||
@ -25,6 +24,9 @@ byteorder = "1.3"
|
||||
smallvec = "1.6"
|
||||
loupe = "0.1"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
rayon = { version = "1.5", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
target-lexicon = { version = "0.12.2", default-features = false }
|
||||
|
||||
@ -33,6 +35,7 @@ maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
default = ["std", "rayon", "unwind", "avx"]
|
||||
wasm = ["std", "avx"]
|
||||
std = ["wasmer-compiler/std", "wasmer-types/std"]
|
||||
core = ["hashbrown", "wasmer-types/core"]
|
||||
unwind = ["gimli"]
|
||||
|
Reference in New Issue
Block a user