Skip to content

Commit

Permalink
Created stand-alone wasmer-compiler, builded only as a .wasm target f…
Browse files Browse the repository at this point in the history
…or 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
  • Loading branch information
ptitSeb committed May 3, 2022
1 parent d8a7d9f commit 8010cb8
Show file tree
Hide file tree
Showing 19 changed files with 1,262 additions and 2 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ members = [
"lib/cache",
"lib/c-api",
"lib/cli",
"lib/cli-compiler",
"lib/compiler",
"lib/compiler-cranelift",
"lib/compiler-singlepass",
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand Down
65 changes: 65 additions & 0 deletions lib/cli-compiler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
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 changes: 34 additions & 0 deletions lib/cli-compiler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# `wasmer-cli-compiler` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](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 changes: 4 additions & 0 deletions lib/cli-compiler/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX");
}
13 changes: 13 additions & 0 deletions lib/cli-compiler/src/bin/wasmer_compiler.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 75 additions & 0 deletions lib/cli-compiler/src/cli.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions lib/cli-compiler/src/commands.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 124 additions & 0 deletions lib/cli-compiler/src/commands/compile.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
Loading

0 comments on commit 8010cb8

Please sign in to comment.