Skip to content

Commit

Permalink
Add the adapt command for adapting core wasm to a component
Browse files Browse the repository at this point in the history
  • Loading branch information
elliottt committed May 23, 2024
1 parent b2d05c5 commit 52e5690
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 11 deletions.
14 changes: 4 additions & 10 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ wasmtime-wasi = "21.0.0"
wasmtime-wasi-nn = "21.0.0"
wiggle = "21.0.0"
wasmparser = "0.208.0"
wasm-encoder = { version = "0.208.0", features = ["wasmparser"] }
wit-component = "0.208.0"

# Adapter dependencies
byte-array-literals = { path = "crates/adapter/byte-array-literals" }
bitflags = { version = "2.5.0", default-features = false }
object = { version = "0.33", default-features = false, features = ["archive"] }
wasi = { version = "0.11.0", default-features = false }
wasm-encoder = "0.205.0"
wit-bindgen-rust-macro = { version = "0.25.0", default-features = false }

[profile.release.package.viceroy-component-adapter]
Expand Down
28 changes: 28 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,34 @@ pub async fn main() -> ExitCode {
Err(_) => ExitCode::FAILURE,
}
}
Commands::Adapt(adapt_args) => {
let input = adapt_args.input();
let output = adapt_args.output();
let bytes = match std::fs::read(&input) {
Ok(bytes) => bytes,
Err(_) => {
eprintln!("Failed to read module from: {}", input.display());
return ExitCode::FAILURE;
}
};

let module = match viceroy_lib::adapt::adapt_bytes(&bytes) {
Ok(module) => module,
Err(e) => {
eprintln!("Failed to adapt module: {e}");
return ExitCode::FAILURE;
}
};

println!("Writing component to: {}", output.display());
match std::fs::write(output, module) {
Ok(_) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("Failed to write component: {e}");
return ExitCode::FAILURE;
}
}
}
}
}

Expand Down
34 changes: 34 additions & 0 deletions cli/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub enum Commands {

/// Run the input wasm once and then exit.
Run(RunArgs),

/// Adapt core wasm to a component.
Adapt(AdaptArgs),
}

#[derive(Debug, Args, Clone)]
Expand Down Expand Up @@ -212,6 +215,37 @@ impl SharedArgs {
}
}

#[derive(Args, Debug, Clone)]
pub struct AdaptArgs {
/// The path to the service's Wasm module.
#[arg(value_parser = check_module, required=true)]
input: Option<String>,

/// The output name
#[arg(short = 'o', long = "output")]
output: Option<PathBuf>,
}

impl AdaptArgs {
pub(crate) fn input(&self) -> PathBuf {
PathBuf::from(self.input.as_ref().expect("input wasm name"))
}

pub(crate) fn output(&self) -> PathBuf {
if let Some(output) = self.output.as_ref() {
return output.clone();
}

let mut output = PathBuf::from(
PathBuf::from(self.input.as_ref().expect("input wasm name"))
.file_name()
.expect("input filename"),
);
output.set_extension("component.wasm");
output
}
}

/// Enum of available (experimental) wasi modules
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Hash)]
pub enum ExperimentalModuleArg {
Expand Down
2 changes: 2 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ tracing = { workspace = true }
tracing-futures = { workspace = true }
url = { workspace = true }
wasmparser = { workspace = true }
wasm-encoder = { workspace = true }
wit-component = { workspace = true }
wasmtime = { workspace = true }
wasmtime-wasi = { workspace = true }
wasmtime-wasi-nn = { workspace = true }
Expand Down
66 changes: 66 additions & 0 deletions lib/src/adapt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
fn mangle_imports(bytes: &[u8]) -> anyhow::Result<wasm_encoder::Module> {
let mut module = wasm_encoder::Module::new();

for payload in wasmparser::Parser::new(0).parse_all(&bytes) {
let payload = payload?;
match payload {
wasmparser::Payload::Version {
encoding: wasmparser::Encoding::Component,
..
} => {
anyhow::bail!("Mangling only supports core-wasm modules, not components");
}

wasmparser::Payload::ImportSection(section) => {
let mut imports = wasm_encoder::ImportSection::new();

for import in section {
let import = import?;
let entity = wasm_encoder::EntityType::try_from(import.ty).map_err(|_| {
anyhow::anyhow!(
"Failed to translate type for import {}:{}",
import.module,
import.name
)
})?;

// Leave the existing preview1 imports alone
if import.module == "wasi_snapshot_preview1" {
imports.import(import.module, import.name, entity);
} else {
let module = "wasi_snapshot_preview1";
let name = format!("{}#{}", import.module, import.name);
imports.import(module, &name, entity);
}
}

module.section(&imports);
}

payload => {
if let Some((id, range)) = payload.as_section() {
module.section(&wasm_encoder::RawSection {
id,
data: &bytes[range],
});
}
}
}
}

Ok(module)
}

/// Given bytes that represent a core wasm module, adapt it to a component using the viceroy
/// adapter.
pub fn adapt_bytes(bytes: &[u8]) -> anyhow::Result<Vec<u8>> {
let module = mangle_imports(bytes)?;

let component = wit_component::ComponentEncoder::default()
.module(module.as_slice())?
.adapter("wasi_snapshot_preview1", viceroy_artifacts::ADAPTER_BYTES)?
.validate(true)
.encode()?;

Ok(component)
}
1 change: 1 addition & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#![cfg_attr(not(debug_assertions), doc(test(attr(allow(dead_code)))))]
#![cfg_attr(not(debug_assertions), doc(test(attr(allow(unused_variables)))))]

pub mod adapt;
pub mod body;
pub mod config;
pub mod error;
Expand Down

0 comments on commit 52e5690

Please sign in to comment.