Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Commit

Permalink
Merge pull request #848 from vados-cosmonic/refactor/embed-component-…
Browse files Browse the repository at this point in the history
…metadata
  • Loading branch information
connorsmith256 authored Oct 4, 2023
2 parents 7b12ac0 + 001ab1d commit d14ea8e
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 58 deletions.
56 changes: 42 additions & 14 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,13 @@ wadm = "0.5.0"
walkdir = "2.3"
wascap = "0.11.0"
wash-lib = { version = "0.9", path = "./crates/wash-lib" }
wasm-encoder = "0.33.2"
wasmbus-rpc = "0.14.0"
wasmcloud-component-adapters = { version = "0.2.0" }
wasmcloud-control-interface = "0.27"
wasmcloud-test-util = "0.10.0"
weld-codegen = "0.7.0"
which = "4.4.0"
wit-component = "0.13.2"
wit-component = "0.14.4"
wit-parser = "0.11.3"
wat = "1.0.74"
9 changes: 6 additions & 3 deletions crates/wash-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ reqwest = { workspace = true, features = ["json", "rustls-tls", "stream"] }
rmp-serde = "1"
semver = { workspace = true, features = ["serde"], optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde-transcode = "1"
serde_cbor = "0.11"
serde_json = { workspace = true, optional = true }
serde-transcode = "1"
serde_with = { workspace = true }
tempfile = { workspace = true }
term-table = { workspace = true, optional = true }
Expand All @@ -65,11 +65,14 @@ toml = { workspace = true }
wadm = { workspace = true, optional = true}
walkdir = { workspace = true }
wascap = { workspace = true }
wasm-encoder = { workspace = true }
wasmbus-rpc = { workspace = true }
wasmcloud-component-adapters = { workspace = true }
wasmcloud-control-interface = { workspace = true }
wat = { workspace = true }
weld-codegen = { workspace = true }
wasmbus-rpc = { workspace = true }
wit-component = { workspace = true }
wasmcloud-component-adapters = { workspace = true }
wit-parser = { workspace = true }

[dev-dependencies]
claims = { workspace = true }
Expand Down
97 changes: 86 additions & 11 deletions crates/wash-lib/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Build (and sign) a wasmCloud actor, provider, or interface. Depends on the "cli" feature
use std::{
borrow::Cow,
fs,
io::ErrorKind,
path::{Path, PathBuf},
Expand All @@ -9,7 +10,8 @@ use std::{
};

use anyhow::{anyhow, bail, Context, Result};
use wit_component::ComponentEncoder;
use wasm_encoder::{Encode, Section};
use wit_component::{ComponentEncoder, StringEncoding};

use crate::{
cli::{
Expand Down Expand Up @@ -95,14 +97,21 @@ pub fn build_actor(
}
}?;

if actor_config.wasm_target == WasmTarget::TinyGoWasiPreview1 {
// TODO: run equivalent of `wasm-tools component embed --world [world] ./wit [actor_wasm_path] -o [actor_wasm_embed_path]
}
// Perform embedding, if necessary
if let WasmTarget::WasiPreview1 | WasmTarget::WasiPreview2 = &actor_config.wasm_target {
embed_wasm_component_metadata(
&common_config.path,
actor_config
.wit_world
.as_ref()
.context("`wit_world` must be specified in wasmcloud.toml for creating preview1 or preview2 components")?,
&actor_wasm_path,
&actor_wasm_path,
)?;
};

// If the actor has been configured as WASI Preview2, adapt it
if actor_config.wasm_target == WasmTarget::WasiPreview2
|| actor_config.wasm_target == WasmTarget::TinyGoWasiPreview1
{
// If the actor has been configured as WASI Preview2, adapt it from preview1
if actor_config.wasm_target == WasmTarget::WasiPreview2 {
let adapter_wasm_bytes = get_wasi_preview2_adapter_bytes(actor_config)?;
// Adapt the component, using the adapter that is available locally
let wasm_bytes = adapt_wasi_preview1_component(&actor_wasm_path, adapter_wasm_bytes)
Expand Down Expand Up @@ -170,7 +179,7 @@ fn build_rust_actor(

let metadata = cargo_metadata::MetadataCommand::new().exec()?;
let target_path = metadata.target_directory.as_path();
let build_target = actor_config.build_target(&actor_config.wasm_target);
let build_target = rust_config.build_target(&actor_config.wasm_target);

let result = command
.args(["build", "--release", "--target", build_target])
Expand Down Expand Up @@ -249,7 +258,7 @@ fn build_tinygo_actor(
"-o",
filename.as_str(),
"-target",
actor_config.build_target(&actor_config.wasm_target),
tinygo_config.build_target(&actor_config.wasm_target),
"-scheduler",
"none",
"-no-debug",
Expand Down Expand Up @@ -319,7 +328,7 @@ fn adapt_wasi_preview1_component(
/// if required by project configuration
pub(crate) fn get_wasi_preview2_adapter_bytes(config: &ActorConfig) -> Result<Vec<u8>> {
if let ActorConfig {
wasm_target: WasmTarget::WasiPreview2 | WasmTarget::TinyGoWasiPreview1,
wasm_target: WasmTarget::WasiPreview2,
wasi_preview2_adapter_path: Some(path),
..
} = config
Expand All @@ -330,6 +339,72 @@ pub(crate) fn get_wasi_preview2_adapter_bytes(config: &ActorConfig) -> Result<Ve
Ok(wasmcloud_component_adapters::WASI_PREVIEW1_REACTOR_COMPONENT_ADAPTER.into())
}

/// Embed required component metadata to a given WebAssembly binary
fn embed_wasm_component_metadata(
project_path: impl AsRef<Path>,
wit_world: impl AsRef<str>,
input_wasm: impl AsRef<Path>,
output_wasm: impl AsRef<Path>,
) -> Result<()> {
// Find the the WIT directory for the project
let wit_dir = project_path.as_ref().join("wit");
if !wit_dir.is_dir() {
bail!(
"expected 'wit' directory under project path at [{}] is missing",
wit_dir.display()
);
};

// Resolve the WIT directory packages & worlds
let mut resolve = wit_parser::Resolve::default();
let (package_id, _paths) = resolve
.push_dir(&wit_dir)
.context("failed to add WIT deps directory")?;
log::info!("successfully loaded WIT @ [{}]", wit_dir.display());

// Select the target world that was specified by the user
let world = resolve
.select_world(package_id, wit_world.as_ref().into())
.context("failed to select world from built resolver")?;

// Encode the metadata
let encoded_metadata =
wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8, None)
.context("failed to encode WIT metadata for component")?;

// Load the wasm binary
let mut wasm_bytes = wat::parse_file(input_wasm.as_ref()).with_context(|| {
format!(
"failed to read wasm bytes from [{}]",
input_wasm.as_ref().display()
)
})?;

// Build & encode a new custom section at the end of the wasm
let section = wasm_encoder::CustomSection {
name: "component-type".into(),
data: Cow::Borrowed(&encoded_metadata),
};
wasm_bytes.push(section.id());
section.encode(&mut wasm_bytes);
log::info!("successfully embedded component metadata in WASM");

// Output the WASM to disk (possibly overwriting the original path)
std::fs::write(output_wasm.as_ref(), wasm_bytes).with_context(|| {
format!(
"failed to write updated wasm to disk at [{}]",
output_wasm.as_ref().display()
)
})?;

log::info!(
"successfully wrote component w/ metadata to [{}]",
output_wasm.as_ref().display()
);

Ok(())
}

/// Placeholder for future functionality for building providers
#[allow(unused)]
fn build_provider(
Expand Down
Loading

0 comments on commit d14ea8e

Please sign in to comment.