From 2c969164de0bfa434978eb790df67ba583fd4b1d Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 10:29:04 +0800 Subject: [PATCH 1/6] Ported gen_c_header over to the compat layer --- lib/cli/src/commands/gen_c_header.rs | 74 +++++++++++++++------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/lib/cli/src/commands/gen_c_header.rs b/lib/cli/src/commands/gen_c_header.rs index 2cb07d2e53f..7c2f26503c9 100644 --- a/lib/cli/src/commands/gen_c_header.rs +++ b/lib/cli/src/commands/gen_c_header.rs @@ -1,11 +1,12 @@ use crate::store::CompilerOptions; -use anyhow::Context; +use anyhow::{Context, Error}; +use bytes::Bytes; use clap::Parser; use std::path::PathBuf; use wasmer_compiler::Artifact; use wasmer_types::compilation::symbols::ModuleMetadataSymbolRegistry; use wasmer_types::{CpuFeature, MetadataHeader, Triple}; -use webc::v1::WebC; +use webc::{compat::SharedBytes, Container, DetectError}; #[derive(Debug, Parser)] /// The options for the `wasmer gen-c-header` subcommand @@ -47,44 +48,25 @@ pub struct GenCHeader { impl GenCHeader { /// Runs logic for the `gen-c-header` subcommand - pub fn execute(&self) -> Result<(), anyhow::Error> { - let path = crate::common::normalize_path(&format!("{}", self.path.display())); - let mut file = std::fs::read(&path) - .map_err(|e| anyhow::anyhow!("{e}")) - .with_context(|| anyhow::anyhow!("{path}"))?; + pub fn execute(&self) -> Result<(), Error> { + let file: Bytes = std::fs::read(&self.path) + .with_context(|| format!("Unable to read \"{}\"", self.path.display()))? + .into(); let prefix = match self.prefix.as_deref() { Some(s) => s.to_string(), None => crate::commands::PrefixMapCompilation::hash_for_bytes(&file), }; - if let Ok(pirita) = WebC::parse(&file, &webc::v1::ParseOptions::default()) { - let atoms = pirita - .manifest - .atoms - .iter() - .map(|a| a.0.clone()) - .collect::>(); - if atoms.len() == 1 { - file = pirita - .get_atom(&pirita.get_package_name(), &atoms[0]) - .unwrap() - .to_vec(); - } else if self.atom.is_none() { - return Err(anyhow::anyhow!("-> note: available atoms are: {}", atoms.join(", "))) - .context(anyhow::anyhow!("file has multiple atoms, please specify which atom to generate the header file for"))?; - } else { - file = pirita - .get_atom(&pirita.get_package_name(), &atoms[0]) - .map_err(|_| { - anyhow::anyhow!("-> note: available atoms are: {}", atoms.join(", ")) - }) - .context(anyhow::anyhow!( - "could not get atom {} from file (invalid atom name)", - &atoms[0] - ))? - .to_vec(); + let atom = match Container::from_bytes(file.clone()) { + Ok(webc) => self.get_atom(&webc)?, + Err(webc::compat::ContainerError::Detect(DetectError::InvalidMagic { .. })) => { + // we've probably got a WebAssembly file + file.into() } - } + Err(other) => { + return Err(Error::new(other).context("Unable to parse the webc file")); + } + }; let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host); let target = crate::commands::create_exe::utils::target_triple_to_target( @@ -131,4 +113,28 @@ impl GenCHeader { Ok(()) } + + fn get_atom(&self, pirita: &Container) -> Result { + let atoms = pirita.atoms(); + let atom_names: Vec<_> = atoms.keys().map(|s| s.as_str()).collect(); + + match *atom_names.as_slice() { + [] => Err(Error::msg("The file doesn't contain any atoms")), + [name] => Ok(atoms[name].clone()), + [..] => match &self.atom { + Some(name) => atoms + .get(name) + .cloned() + .with_context(|| format!("The file doesn't contain a \"{name}\" atom")) + .with_context(|| { + format!("-> note: available atoms are: {}", atom_names.join(", ")) + }), + None => { + let err = Error::msg("file has multiple atoms, please specify which atom to generate the header file for") + .context(format!("-> note: available atoms are: {}", atom_names.join(", "))); + Err(err) + } + }, + } + } } From 6390be14209b815ecaa4faba91c4b83f8444031e Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 14:15:00 +0800 Subject: [PATCH 2/6] Remove the "wasmer list" sub-command --- lib/cli/src/cli.rs | 11 ++++----- lib/cli/src/commands.rs | 3 +-- lib/cli/src/commands/list.rs | 45 ------------------------------------ 3 files changed, 5 insertions(+), 54 deletions(-) delete mode 100644 lib/cli/src/commands/list.rs diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index b18de7ba816..dbc15309022 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -9,7 +9,7 @@ use crate::commands::CreateExe; #[cfg(feature = "wast")] use crate::commands::Wast; use crate::commands::{ - Add, Cache, Config, Init, Inspect, List, Login, Publish, Run, SelfUpdate, Validate, Whoami, + Add, Cache, Config, Init, Inspect, Login, Publish, Run, SelfUpdate, Validate, Whoami, }; #[cfg(feature = "static-artifact-create")] use crate::commands::{CreateObj, GenCHeader}; @@ -37,9 +37,6 @@ use clap::{error::ErrorKind, CommandFactory, Parser}; )] /// The options for the wasmer Command Line Interface enum WasmerCLIOptions { - /// List all locally installed packages - List(List), - /// Login into a wapm.io-like registry Login(Login), @@ -188,7 +185,6 @@ impl WasmerCLIOptions { Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), Self::Init(init) => init.execute(), - Self::List(list) => list.execute(), Self::Login(login) => login.execute(), Self::Publish(publish) => publish.execute(), #[cfg(feature = "static-artifact-create")] @@ -257,8 +253,9 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { match command.unwrap_or(&String::new()).as_ref() { "add" | "cache" | "compile" | "config" | "create-obj" | "create-exe" | "help" | "gen-c-header" | "inspect" | "init" | "run" | "run-unstable" | "self-update" - | "validate" | "wast" | "binfmt" | "list" | "login" | "publish" | "app" - | "namespace" | "" => WasmerCLIOptions::parse(), + | "validate" | "wast" | "binfmt" | "login" | "publish" | "app" | "namespace" | "" => { + WasmerCLIOptions::parse() + } _ => { WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| { match e.kind() { diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index a86d8aa56bf..9f2365837cd 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -14,7 +14,6 @@ mod create_obj; mod gen_c_header; mod init; mod inspect; -mod list; mod login; mod publish; mod run; @@ -33,7 +32,7 @@ pub use create_exe::*; #[cfg(feature = "wast")] pub use wast::*; pub use { - add::*, cache::*, config::*, init::*, inspect::*, list::*, login::*, publish::*, run::Run, + add::*, cache::*, config::*, init::*, inspect::*, login::*, publish::*, run::Run, self_update::*, validate::*, whoami::*, }; #[cfg(feature = "static-artifact-create")] diff --git a/lib/cli/src/commands/list.rs b/lib/cli/src/commands/list.rs deleted file mode 100644 index ae0ec3462f6..00000000000 --- a/lib/cli/src/commands/list.rs +++ /dev/null @@ -1,45 +0,0 @@ -use clap::Parser; -use wasmer_registry::WasmerConfig; - -/// Subcommand for listing packages -#[derive(Debug, Copy, Clone, Parser)] -pub struct List {} - -impl List { - /// execute [List] - pub fn execute(&self) -> Result<(), anyhow::Error> { - use prettytable::{format, row, Table}; - let wasmer_dir = - WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - let rows = wasmer_registry::get_all_local_packages(&wasmer_dir) - .into_iter() - .filter_map(|pkg| { - let package_root_path = pkg.path; - let (manifest, _) = - wasmer_registry::get_executable_file_from_path(&package_root_path, None) - .ok()?; - let commands = manifest - .command - .unwrap_or_default() - .iter() - .map(|c| c.get_name()) - .collect::>() - .join(" \r\n"); - - Some(row![pkg.registry, pkg.name, pkg.version, commands]) - }) - .collect::>(); - - let empty_table = rows.is_empty(); - let mut table = Table::init(rows); - table.set_titles(row!["Registry", "Package", "Version", "Commands"]); - table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE); - table.set_format(*format::consts::FORMAT_NO_COLSEP); - if empty_table { - table.add_empty_row(); - } - table.printstd(); - - Ok(()) - } -} From 4da1090032fea55e2f47df03849fd09320ec8e05 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 14:30:26 +0800 Subject: [PATCH 3/6] Fixed a compilation error in gen_c_header --- lib/cli/src/commands/gen_c_header.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/gen_c_header.rs b/lib/cli/src/commands/gen_c_header.rs index 7c2f26503c9..21a472aace2 100644 --- a/lib/cli/src/commands/gen_c_header.rs +++ b/lib/cli/src/commands/gen_c_header.rs @@ -80,7 +80,7 @@ impl GenCHeader { let tunables = engine.tunables(); let (metadata, _, _) = Artifact::metadata( compiler, - &file, + &atom, Some(prefix.as_str()), &target, tunables, From c8370ef57610eb99a045b3f3de7fb62322eac97e Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 14:36:56 +0800 Subject: [PATCH 4/6] Stripped a bunch of dead code out of the wasmer-registry crate --- Cargo.lock | 3 - lib/cli/src/package_source.rs | 80 ---- lib/registry/Cargo.toml | 5 - lib/registry/src/graphql/proxy.rs | 23 +- lib/registry/src/lib.rs | 616 +----------------------------- 5 files changed, 11 insertions(+), 716 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 669862c1632..1788c3cb27f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5958,7 +5958,6 @@ dependencies = [ "dirs", "filetime", "flate2", - "fs_extra", "futures-util", "graphql_client", "hex", @@ -5968,7 +5967,6 @@ dependencies = [ "log", "lzma-rs", "minisign", - "rand", "regex", "reqwest", "rpassword", @@ -5987,7 +5985,6 @@ dependencies = [ "wasmer-toml", "wasmer-wasm-interface 4.0.0-beta.1", "wasmparser 0.51.4", - "webc", "whoami", ] diff --git a/lib/cli/src/package_source.rs b/lib/cli/src/package_source.rs index e50258e9c51..975071409f5 100644 --- a/lib/cli/src/package_source.rs +++ b/lib/cli/src/package_source.rs @@ -1,10 +1,7 @@ //! Module for parsing and installing packages -use anyhow::Context; -use std::path::{Path, PathBuf}; use std::str::FromStr; use url::Url; -use wasmer_registry::WasmerConfig; /// Source of a package #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -46,83 +43,6 @@ impl PackageSource { Err(_) => Self::File(s.to_string()), }) } - - /// Downloads the package (if any) to the installation directory, returns the path - /// of the package directory (containing the wasmer.toml) - pub fn download_and_get_filepath(&self) -> Result { - let url = match self { - Self::File(f) => { - let path = Path::new(&f).to_path_buf(); - return if path.exists() { - Ok(path) - } else { - Err(anyhow::anyhow!("Could not find local file {f}")) - }; - } - Self::Url(u) => { - let wasmer_dir = WasmerConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - if let Some(path) = - wasmer_registry::Package::is_url_already_installed(u, &wasmer_dir) - { - return Ok(path); - } else { - u.clone() - } - } - Self::Package(p) => { - let wasmer_dir = WasmerConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - let package_path = Path::new(&p.file()).to_path_buf(); - if package_path.exists() { - return Ok(package_path); - } else if let Some(path) = p.already_installed(&wasmer_dir) { - return Ok(path); - } else { - let config = WasmerConfig::from_file(&wasmer_dir) - .map_err(|e| anyhow::anyhow!("error loading wasmer config file: {e}"))?; - p.url(&config.registry.get_current_registry())? - } - } - }; - - let extra = if let Self::Package(p) = self { - format!(", local file {} does not exist either", p.file()) - } else { - String::new() - }; - - let wasmer_dir = - WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - let mut sp = start_spinner(format!("Installing package {url} ...")); - let opt_path = wasmer_registry::install_package(&wasmer_dir, &url); - if let Some(sp) = sp.take() { - use std::io::Write; - sp.clear(); - let _ = std::io::stdout().flush(); - } - - let path = opt_path - .with_context(|| anyhow::anyhow!("Could not fetch package from URL {url}{extra}"))?; - - Ok(path) - } -} - -fn start_spinner(msg: String) -> Option { - if !isatty::stdout_isatty() { - return None; - } - #[cfg(target_os = "windows")] - { - use colored::control; - let _ = control::set_virtual_terminal(true); - } - Some(spinoff::Spinner::new( - spinoff::Spinners::Dots, - msg, - spinoff::Color::White, - )) } #[test] diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 3ce4b10829a..747cc3b90bf 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -12,9 +12,6 @@ rust-version.workspace = true [features] build-package = ["rusqlite", "indexmap", "wasmer-wasm-interface", "wasmparser", "rpassword", "minisign", "time"] -[dev-dependencies] -rand = "0.8.5" - [dependencies] dirs = "4.0.0" graphql_client = "0.11.0" @@ -31,12 +28,10 @@ tar = "0.4.38" flate2 = "1.0.24" semver = "1.0.14" lzma-rs = "0.2.0" -webc = { version = "5.0", features = ["mmap"] } hex = "0.4.3" tokio = "1.24.0" log = "0.4.17" regex = "1.7.0" -fs_extra = "1.2.0" filetime = "0.2.19" tldextract = "0.6.0" console = "0.15.2" diff --git a/lib/registry/src/graphql/proxy.rs b/lib/registry/src/graphql/proxy.rs index 92b8f304a3e..b89d17e37a1 100644 --- a/lib/registry/src/graphql/proxy.rs +++ b/lib/registry/src/graphql/proxy.rs @@ -1,5 +1,8 @@ //! Code for dealing with setting things up to proxy network requests +use std::env; + +use anyhow::Context; use thiserror::Error; #[derive(Debug, Error)] @@ -17,23 +20,8 @@ pub enum ProxyError { pub fn maybe_set_up_proxy_blocking( builder: reqwest::blocking::ClientBuilder, ) -> anyhow::Result { - use anyhow::Context; - if let Some(proxy) = maybe_set_up_proxy_inner() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("install_webc_package: failed to setup proxy for reqwest Client")? - { - return Ok(builder.proxy(proxy)); - } - Ok(builder) -} - -pub fn maybe_set_up_proxy( - builder: reqwest::ClientBuilder, -) -> anyhow::Result { - use anyhow::Context; - if let Some(proxy) = maybe_set_up_proxy_inner() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("install_webc_package: failed to setup proxy for reqwest Client")? + if let Some(proxy) = + maybe_set_up_proxy_inner().context("failed to setup proxy for reqwest Client")? { return Ok(builder.proxy(proxy)); } @@ -53,7 +41,6 @@ pub fn maybe_set_up_proxy( /// `Ok(Some(proxy))` means that the proxy was set up successfully, and `Err(e)` that /// there was a failure while attempting to set up the proxy. fn maybe_set_up_proxy_inner() -> anyhow::Result> { - use std::env; let proxy = if let Ok(proxy_url) = env::var("ALL_PROXY").or_else(|_| env::var("all_proxy")) { reqwest::Proxy::all(&proxy_url).map(|proxy| (proxy_url, proxy, "ALL_PROXY")) } else if let Ok(https_proxy_url) = env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy")) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 652049abb17..3f49f4f2350 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -21,16 +21,15 @@ pub mod utils; pub use client::RegistryClient; +use std::{ + fmt, + path::{Path, PathBuf}, + time::Duration, +}; + use anyhow::Context; -use core::ops::Range; use graphql_client::GraphQLQuery; -use reqwest::header::{ACCEPT, RANGE}; -use std::fmt; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use std::time::Duration; use tar::EntryType; -use url::Url; use crate::utils::normalize_path; pub use crate::{ @@ -55,220 +54,6 @@ pub struct PackageDownloadInfo { pub pirita_url: Option, } -pub fn get_package_local_dir(wasmer_dir: &Path, url: &str, version: &str) -> Option { - let checkouts_dir = get_checkouts_dir(wasmer_dir); - let url_hash = Package::hash_url(url); - let dir = checkouts_dir.join(format!("{url_hash}@{version}")); - Some(dir) -} - -pub fn try_finding_local_command(wasmer_dir: &Path, cmd: &str) -> Option { - let local_packages = get_all_local_packages(wasmer_dir); - for p in local_packages { - let commands = p.get_commands(); - - if commands.unwrap_or_default().iter().any(|c| c == cmd) { - return Some(p); - } - } - None -} - -#[derive(Debug, Clone)] -pub struct LocalPackage { - pub registry: String, - pub name: String, - pub version: String, - pub path: PathBuf, -} - -impl fmt::Display for LocalPackage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}@{} (registry {}, located at {})", - self.name, - self.version, - self.registry, - self.path.display() - ) - } -} - -impl LocalPackage { - pub fn get_path(&self) -> Result { - Ok(self.path.clone()) - } - - /// Returns the wasmer.toml path if it exists - pub fn get_wasmer_toml_path(base_path: &Path) -> Result { - let path = base_path.join(PACKAGE_TOML_FILE_NAME); - let fallback_path = base_path.join(PACKAGE_TOML_FALLBACK_NAME); - if path.exists() { - Ok(path) - } else if fallback_path.exists() { - Ok(fallback_path) - } else { - Err(anyhow::anyhow!( - "neither {} nor {} exists", - path.display(), - fallback_path.display() - )) - } - } - - /// Reads the wasmer.toml fron $PATH with wapm.toml as a fallback - pub fn read_toml(base_path: &Path) -> Result { - let wasmer_toml = std::fs::read_to_string(base_path.join(PACKAGE_TOML_FILE_NAME)) - .or_else(|_| std::fs::read_to_string(base_path.join(PACKAGE_TOML_FALLBACK_NAME))) - .map_err(|_| { - format!( - "Path {} has no {PACKAGE_TOML_FILE_NAME} or {PACKAGE_TOML_FALLBACK_NAME}", - base_path.display() - ) - })?; - let wasmer_toml = toml::from_str::(&wasmer_toml) - .map_err(|e| format!("Could not parse toml for {:?}: {e}", base_path.display()))?; - Ok(wasmer_toml) - } - - pub fn get_commands(&self) -> Result, String> { - let path = self.get_path()?; - let toml_parsed = Self::read_toml(&path)?; - Ok(toml_parsed - .command - .unwrap_or_default() - .iter() - .map(|c| c.get_name()) - .collect()) - } -} - -/// Returns the (manifest, .wasm file name), given a package dir -pub fn get_executable_file_from_path( - package_dir: &Path, - command: Option<&str>, -) -> Result<(wasmer_toml::Manifest, PathBuf), anyhow::Error> { - let wasmer_toml = LocalPackage::read_toml(package_dir).map_err(|e| anyhow::anyhow!("{e}"))?; - - let name = wasmer_toml.package.name.clone(); - let version = wasmer_toml.package.version.clone(); - - let commands = wasmer_toml.command.clone().unwrap_or_default(); - let entrypoint_module = match command { - Some(s) => commands.iter().find(|c| c.get_name() == s).ok_or_else(|| { - anyhow::anyhow!("Cannot run {name}@{version}: package has no command {s:?}") - })?, - None => { - if commands.is_empty() { - Err(anyhow::anyhow!( - "Cannot run {name}@{version}: package has no commands" - )) - } else if commands.len() == 1 { - Ok(&commands[0]) - } else { - Err(anyhow::anyhow!(" -> wasmer run {name}@{version} --command-name={0}", commands.first().map(|f| f.get_name()).unwrap())) - .context(anyhow::anyhow!("{}", commands.iter().map(|c| format!("`{}`", c.get_name())).collect::>().join(", "))) - .context(anyhow::anyhow!("You can run any of those by using the --command-name=COMMAND flag")) - .context(anyhow::anyhow!("The `{name}@{version}` package doesn't have a default entrypoint, but has multiple available commands:")) - }? - } - }; - - let module_name = entrypoint_module.get_module(); - let modules = wasmer_toml.module.clone().unwrap_or_default(); - let entrypoint_module = modules - .iter() - .find(|m| m.name == module_name) - .ok_or_else(|| { - anyhow::anyhow!( - "Cannot run {name}@{version}: module {module_name} not found in {GLOBAL_CONFIG_FILE_NAME}" - ) - })?; - - let entrypoint_source = package_dir.join(&entrypoint_module.source); - - Ok((wasmer_toml, entrypoint_source)) -} - -fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { - if !dir.is_dir() { - return Vec::new(); - } - - let read_dir = match std::fs::read_dir(dir) { - Ok(o) => o, - Err(_) => return Vec::new(), - }; - - let entries = read_dir - .map(|res| res.map(|e| e.path())) - .collect::, std::io::Error>>(); - - let registry_entries = match entries { - Ok(o) => o, - Err(_) => return Vec::new(), - }; - - registry_entries - .into_iter() - .filter_map(|re| Some((re.clone(), re.file_name()?.to_str()?.to_string()))) - .collect() -} - -/// Returns a list of all locally installed packages -pub fn get_all_local_packages(wasmer_dir: &Path) -> Vec { - let mut packages = Vec::new(); - - let checkouts_dir = get_checkouts_dir(wasmer_dir); - - for (path, url_hash_with_version) in get_all_names_in_dir(&checkouts_dir) { - let manifest = match LocalPackage::read_toml(&path) { - Ok(o) => o, - Err(_) => continue, - }; - let url_hash = match url_hash_with_version.split('@').next() { - Some(s) => s, - None => continue, - }; - let package = - Url::parse(&Package::unhash_url(url_hash)).map(|s| s.origin().ascii_serialization()); - let host = match package { - Ok(s) => s, - Err(_) => continue, - }; - packages.push(LocalPackage { - registry: host, - name: manifest.package.name, - version: manifest.package.version.to_string(), - path, - }); - } - - packages -} - -pub fn get_local_package( - wasmer_dir: &Path, - name: &str, - version: Option<&str>, -) -> Option { - get_all_local_packages(wasmer_dir) - .iter() - .find(|p| { - if p.name != name { - return false; - } - if let Some(v) = version { - if p.version != v { - return false; - } - } - true - }) - .cloned() -} - pub fn query_command_from_registry( registry_url: &str, command_name: &str, @@ -569,61 +354,6 @@ where Ok(()) } -/// Installs the .tar.gz if it doesn't yet exist, returns the -/// (package dir, entrypoint .wasm file path) -pub fn install_package(wasmer_dir: &Path, url: &Url) -> Result { - use fs_extra::dir::copy; - - let tempdir = tempfile::TempDir::new() - .map_err(|e| anyhow::anyhow!("could not create download temp dir: {e}"))?; - - let target_targz_path = tempdir.path().join("package.tar.gz"); - let unpacked_targz_path = tempdir.path().join("package"); - std::fs::create_dir_all(&unpacked_targz_path).map_err(|e| { - anyhow::anyhow!( - "could not create dir {}: {e}", - unpacked_targz_path.display() - ) - })?; - - get_targz_bytes(url, None, Some(target_targz_path.clone())) - .map_err(|e| anyhow::anyhow!("failed to download {url}: {e}"))?; - - try_unpack_targz( - target_targz_path.as_path(), - unpacked_targz_path.as_path(), - false, - ) - .map_err(|e| anyhow::anyhow!("Could not unpack file downloaded from {url}: {e}"))?; - - // read {unpacked}/wasmer.toml to get the name + version number - let toml_parsed = LocalPackage::read_toml(&unpacked_targz_path) - .map_err(|e| anyhow::anyhow!("error reading package name / version number: {e}"))?; - - let version = toml_parsed.package.version.to_string(); - - let checkouts_dir = crate::get_checkouts_dir(wasmer_dir); - - let installation_path = - checkouts_dir.join(format!("{}@{version}", Package::hash_url(url.as_ref()))); - - std::fs::create_dir_all(&installation_path) - .map_err(|e| anyhow::anyhow!("could not create installation path for {url}: {e}"))?; - - let mut options = fs_extra::dir::CopyOptions::new(); - options.content_only = true; - options.overwrite = true; - copy(&unpacked_targz_path, &installation_path, &options)?; - - #[cfg(not(target_os = "wasi"))] - let _ = filetime::set_file_mtime( - LocalPackage::get_wasmer_toml_path(&installation_path)?, - filetime::FileTime::now(), - ); - - Ok(installation_path) -} - pub fn whoami( wasmer_dir: &Path, registry: Option<&str>, @@ -687,340 +417,6 @@ pub fn get_all_available_registries(wasmer_dir: &Path) -> Result, St Ok(registries) } -#[derive(Debug, PartialEq, Clone)] -pub struct RemoteWebcInfo { - pub checksum: String, - pub manifest: webc::metadata::Manifest, -} - -pub fn install_webc_package( - wasmer_dir: &Path, - url: &Url, - checksum: &str, -) -> Result<(), anyhow::Error> { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(async { install_webc_package_inner(wasmer_dir, url, checksum).await }) -} - -async fn install_webc_package_inner( - wasmer_dir: &Path, - url: &Url, - checksum: &str, -) -> Result<(), anyhow::Error> { - use futures_util::StreamExt; - - let path = get_webc_dir(wasmer_dir); - let _ = std::fs::create_dir_all(&path); - let webc_path = path.join(checksum); - - let mut file = std::fs::File::create(&webc_path) - .map_err(|e| anyhow::anyhow!("{e}")) - .context(anyhow::anyhow!("{}", webc_path.display()))?; - - let client = { - let builder = reqwest::Client::builder(); - let builder = crate::graphql::proxy::maybe_set_up_proxy(builder)?; - builder - .redirect(reqwest::redirect::Policy::limited(10)) - .build() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("install_webc_package: failed to build reqwest Client")? - }; - - let res = client - .get(url.clone()) - .header(ACCEPT, "application/webc") - .send() - .await - .and_then(|response| response.error_for_status()) - .map_err(|e| anyhow::anyhow!("{e}")) - .context(anyhow::anyhow!("install_webc_package: failed to GET {url}"))?; - - let mut stream = res.bytes_stream(); - - while let Some(item) = stream.next().await { - let item = item - .map_err(|e| anyhow::anyhow!("{e}")) - .context(anyhow::anyhow!("install_webc_package: failed to GET {url}"))?; - file.write_all(&item) - .map_err(|e| anyhow::anyhow!("{e}")) - .context(anyhow::anyhow!( - "install_webc_package: failed to write chunk to {}", - webc_path.display() - ))?; - } - - Ok(()) -} - -/// Returns a list of all installed webc packages -pub fn get_all_installed_webc_packages(wasmer_dir: &Path) -> Vec { - get_all_installed_webc_packages_inner(wasmer_dir) -} - -fn get_all_installed_webc_packages_inner(wasmer_dir: &Path) -> Vec { - let dir = get_webc_dir(wasmer_dir); - - let read_dir = match std::fs::read_dir(dir) { - Ok(s) => s, - Err(_) => return Vec::new(), - }; - - read_dir - .filter_map(|r| Some(r.ok()?.path())) - .filter_map(|path| { - webc::v1::WebCMmap::parse( - path, - &webc::v1::ParseOptions { - parse_atoms: false, - parse_volumes: false, - ..Default::default() - }, - ) - .ok() - }) - .filter_map(|webc| { - let checksum = webc.checksum.as_ref().map(|s| &s.data)?.to_vec(); - let hex_string = get_checksum_hash(&checksum); - Some(RemoteWebcInfo { - checksum: hex_string, - manifest: webc.manifest.clone(), - }) - }) - .collect() -} - -/// The checksum of the webc file has a bunch of zeros at the end -/// (it's currently encoded that way in the webc format). This function -/// strips the zeros because otherwise the filename would become too long. -/// -/// So: -/// -/// `3ea47cb0000000000000` -> `3ea47cb` -/// -pub fn get_checksum_hash(bytes: &[u8]) -> String { - let mut checksum = bytes.to_vec(); - while checksum.last().copied() == Some(0) { - checksum.pop(); - } - hex::encode(&checksum).chars().take(64).collect() -} - -/// Returns the checksum of the .webc file, so that we can check whether the -/// file is already installed before downloading it -pub fn get_remote_webc_checksum(url: &Url) -> Result { - let request_max_bytes = webc::v1::WebC::get_signature_offset_start() + 4 + 1024 + 8 + 8; - let data = get_webc_bytes(url, Some(0..request_max_bytes), None) - .with_context(|| anyhow::anyhow!("note: use --registry to change the registry URL"))? - .unwrap(); - let checksum = webc::v1::WebC::get_checksum_bytes(&data) - .map_err(|e| anyhow::anyhow!("{e}"))? - .to_vec(); - Ok(get_checksum_hash(&checksum)) -} - -/// Before fetching the entire file from a remote URL, just fetch the manifest -/// so we can see if the package has already been installed -pub fn get_remote_webc_manifest(url: &Url) -> Result { - // Request up unti manifest size / manifest len - let request_max_bytes = webc::v1::WebC::get_signature_offset_start() + 4 + 1024 + 8 + 8; - let data = get_webc_bytes(url, Some(0..request_max_bytes), None)?.unwrap(); - let checksum = webc::v1::WebC::get_checksum_bytes(&data) - .map_err(|e| anyhow::anyhow!("{e}")) - .context("WebC::get_checksum_bytes failed")? - .to_vec(); - let hex_string = get_checksum_hash(&checksum); - - let (manifest_start, manifest_len) = webc::v1::WebC::get_manifest_offset_size(&data) - .map_err(|e| anyhow::anyhow!("{e}")) - .context("WebC::get_manifest_offset_size failed")?; - let data_with_manifest = - get_webc_bytes(url, Some(0..manifest_start + manifest_len), None)?.unwrap(); - let manifest = webc::v1::WebC::get_manifest(&data_with_manifest) - .map_err(|e| anyhow::anyhow!("{e}")) - .context("WebC::get_manifest failed")?; - Ok(RemoteWebcInfo { - checksum: hex_string, - manifest, - }) -} - -fn setup_client( - url: &Url, - application_type: &'static str, -) -> Result { - let client = { - let builder = reqwest::blocking::Client::builder(); - let builder = crate::graphql::proxy::maybe_set_up_proxy_blocking(builder) - .context("setup_webc_client")?; - builder - .redirect(reqwest::redirect::Policy::limited(10)) - .build() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("setup_webc_client: builder.build() failed")? - }; - - Ok(client.get(url.clone()).header(ACCEPT, application_type)) -} - -fn get_webc_bytes( - url: &Url, - range: Option>, - stream_response_into: Option, -) -> Result>, anyhow::Error> { - get_bytes(url, range, "application/webc", stream_response_into) -} - -fn get_targz_bytes( - url: &Url, - range: Option>, - stream_response_into: Option, -) -> Result>, anyhow::Error> { - get_bytes(url, range, "application/tar+gzip", stream_response_into) -} - -fn get_bytes( - url: &Url, - range: Option>, - application_type: &'static str, - stream_response_into: Option, -) -> Result>, anyhow::Error> { - // curl -r 0-500 -L https://wapm.dev/syrusakbary/python -H "Accept: application/webc" --output python.webc - - let mut res = setup_client(url, application_type)?; - - if let Some(range) = range.as_ref() { - res = res.header(RANGE, format!("bytes={}-{}", range.start, range.end)); - } - - let mut res = res - .send() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("send() failed")?; - - if res.status().is_redirection() { - return Err(anyhow::anyhow!("redirect: {:?}", res.status())); - } - - if res.status().is_server_error() { - return Err(anyhow::anyhow!("server error: {:?}", res.status())); - } - - if res.status().is_client_error() { - return Err(anyhow::anyhow!("client error: {:?}", res.status())); - } - - if let Some(path) = stream_response_into.as_ref() { - let mut file = std::fs::File::create(path).map_err(|e| { - anyhow::anyhow!("failed to download {url} into {}: {e}", path.display()) - })?; - - res.copy_to(&mut file) - .map_err(|e| anyhow::anyhow!("{e}")) - .map_err(|e| { - anyhow::anyhow!("failed to download {url} into {}: {e}", path.display()) - })?; - - if application_type == "application/webc" { - let mut buf = vec![0; 100]; - file.read_exact(&mut buf) - .map_err(|e| anyhow::anyhow!("invalid webc downloaded from {url}: {e}"))?; - if buf[0..webc::MAGIC.len()] != webc::MAGIC[..] { - let first_100_bytes = String::from_utf8_lossy(&buf); - return Err(anyhow::anyhow!("invalid webc bytes: {first_100_bytes:?}")); - } - } - - Ok(None) - } else { - let bytes = res - .bytes() - .map_err(|e| anyhow::anyhow!("{e}")) - .context("bytes() failed")?; - - if application_type == "application/webc" - && (range.is_none() || range.unwrap().start == 0) - && bytes[0..webc::MAGIC.len()] != webc::MAGIC[..] - { - let bytes = bytes.iter().copied().take(100).collect::>(); - let first_100_bytes = String::from_utf8_lossy(&bytes); - return Err(anyhow::anyhow!("invalid webc bytes: {first_100_bytes:?}")); - } - - // else if "application/tar+gzip" - we would need to uncompress the response here - // since failure responses are very small, this will fail during unpacking instead - - Ok(Some(bytes.to_vec())) - } -} - -// TODO: this test is segfaulting only on linux-musl, no other OS -// See https://github.com/wasmerio/wasmer/pull/3215 -#[cfg(not(target_env = "musl"))] -#[test] -fn test_install_package() { - println!("test install package..."); - let registry = "https://registry.wapm.io/graphql"; - - match test_if_registry_present(registry) { - Ok(true) => {} - Ok(false) => panic!("registry.wapm.io not reachable, test will fail"), - Err(e) => { - panic!("registry.wapm.io not reachable: {e}"); - } - } - - println!("registry present"); - - let wabt = query_package_from_registry(registry, "wasmer/wabt", Some("1.0.29")).unwrap(); - - println!("wabt queried: {wabt:#?}"); - - assert_eq!(wabt.registry, registry); - assert_eq!(wabt.package, "wasmer/wabt"); - assert_eq!(wabt.version, "1.0.29"); - assert_eq!( - wabt.commands, - "wat2wasm, wast2json, wasm2wat, wasm-interp, wasm-validate, wasm-strip" - ); - assert_eq!( - wabt.url, - "https://storage.googleapis.com/wapm-registry-prod/packages/wasmer/wabt/wabt-1.0.29.tar.gz" - .to_string() - ); - - let fake_wasmer_dir = tempfile::TempDir::new().unwrap(); - let wasmer_dir = fake_wasmer_dir.path(); - let path = install_package(wasmer_dir, &url::Url::parse(&wabt.url).unwrap()).unwrap(); - - println!("package installed: {path:?}"); - - assert_eq!( - path, - get_checkouts_dir(wasmer_dir).join(format!("{}@1.0.29", Package::hash_url(&wabt.url))) - ); - - let all_installed_packages = get_all_local_packages(wasmer_dir); - - let is_installed = all_installed_packages - .iter() - .any(|p| p.name == "wasmer/wabt" && p.version == "1.0.29"); - - if !is_installed { - let panic_str = get_all_local_packages(wasmer_dir) - .iter() - .map(|p| format!("{} {} {}", p.registry, p.name, p.version)) - .collect::>() - .join("\r\n"); - panic!("get all local packages: failed to install:\r\n{panic_str}"); - } - - println!("ok, done"); -} - /// A library that exposes bindings to a WAPM package. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Bindings { From 2cded952f6c80b2668c061ae756518d8a1c916c6 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 15:46:41 +0800 Subject: [PATCH 5/6] Migrated create-exe's webc v1 code over to the compatibility layer --- lib/cli/src/commands/create_exe.rs | 192 ++++++++++++++++++++--------- lib/cli/src/commands/create_obj.rs | 6 +- 2 files changed, 138 insertions(+), 60 deletions(-) diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 6dac3e068c0..d21a57d874a 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -16,7 +16,10 @@ use tar::Archive; use wasmer::*; use wasmer_object::{emit_serialized, get_object_for_target}; use wasmer_types::{compilation::symbols::ModuleMetadataSymbolRegistry, ModuleInfo}; -use webc::v1::{ParseOptions, WebCMmap}; +use webc::{ + compat::{Container, Volume as WebcVolume}, + PathSegments, +}; const LINK_SYSTEM_LIBRARIES_WINDOWS: &[&str] = &["userenv", "Ws2_32", "advapi32", "bcrypt"]; @@ -229,31 +232,30 @@ impl CreateExe { }; std::fs::create_dir_all(&tempdir)?; - let atoms = - if let Ok(pirita) = WebCMmap::parse(input_path.clone(), &ParseOptions::default()) { - // pirita file - compile_pirita_into_directory( - &pirita, - &tempdir, - &self.compiler, - &self.cpu_features, - &cross_compilation.target, - &self.precompiled_atom, - AllowMultiWasm::Allow, - self.debug_dir.is_some(), - ) - } else { - // wasm file - prepare_directory_from_single_wasm_file( - &input_path, - &tempdir, - &self.compiler, - &cross_compilation.target, - &self.cpu_features, - &self.precompiled_atom, - self.debug_dir.is_some(), - ) - }?; + let atoms = if let Ok(pirita) = Container::from_disk(&input_path) { + // pirita file + compile_pirita_into_directory( + &pirita, + &tempdir, + &self.compiler, + &self.cpu_features, + &cross_compilation.target, + &self.precompiled_atom, + AllowMultiWasm::Allow, + self.debug_dir.is_some(), + ) + } else { + // wasm file + prepare_directory_from_single_wasm_file( + &input_path, + &tempdir, + &self.compiler, + &cross_compilation.target, + &self.cpu_features, + &self.precompiled_atom, + self.debug_dir.is_some(), + ) + }?; get_module_infos(&store, &tempdir, &atoms)?; let mut entrypoint = get_entrypoint(&tempdir)?; @@ -334,7 +336,7 @@ pub enum AllowMultiWasm { /// Given a pirita file, compiles the .wasm files into the target directory #[allow(clippy::too_many_arguments)] pub(super) fn compile_pirita_into_directory( - pirita: &WebCMmap, + pirita: &Container, target_dir: &Path, compiler: &CompilerOptions, cpu_features: &[CpuFeature], @@ -345,39 +347,17 @@ pub(super) fn compile_pirita_into_directory( ) -> anyhow::Result)>> { let all_atoms = match &allow_multi_wasm { AllowMultiWasm::Allow | AllowMultiWasm::Reject(None) => { - pirita.get_all_atoms().into_iter().collect::>() + pirita.atoms().into_iter().collect::>() } AllowMultiWasm::Reject(Some(s)) => { - vec![( - s.to_string(), - pirita - .get_atom(&pirita.get_package_name(), s) - .with_context(|| { - anyhow::anyhow!( - "could not find atom {s} in package {}", - pirita.get_package_name() - ) - })?, - )] + let atom = pirita + .get_atom(s) + .with_context(|| format!("could not find atom \"{s}\"",))?; + vec![(s.to_string(), atom)] } }; - if allow_multi_wasm == AllowMultiWasm::Reject(None) && all_atoms.len() > 1 { - let keys = all_atoms - .iter() - .map(|(name, _)| name.clone()) - .collect::>(); - return Err(anyhow::anyhow!( - "where is one of: {}", - keys.join(", ") - )) - .context(anyhow::anyhow!( - "note: use --atom to specify which atom to compile" - )) - .context(anyhow::anyhow!( - "cannot compile more than one atom at a time" - )); - } + allow_multi_wasm.validate(&all_atoms)?; std::fs::create_dir_all(target_dir) .map_err(|e| anyhow::anyhow!("cannot create / dir in {}: {e}", target_dir.display()))?; @@ -392,7 +372,8 @@ pub(super) fn compile_pirita_into_directory( ) })?; - let volume_bytes = pirita.get_volumes_as_fileblock(); + let volumes = pirita.volumes(); + let volume_bytes = volume_file_block(&volumes); let volume_name = "VOLUMES"; let volume_path = target_dir.join("volumes").join("volume.o"); write_volume_obj(&volume_bytes, volume_name, &volume_path, target)?; @@ -479,6 +460,105 @@ pub(super) fn compile_pirita_into_directory( Ok(atoms_from_file) } +/// Serialize a set of volumes so they can be read by the C API. +/// +/// This is really backwards, but the only way to create a v1 volume "fileblock" +/// is by first reading every file in a [`webc::compat::Volume`], serializing it +/// to webc v1's binary form, parsing those bytes into a [`webc::v1::Volume`], +/// then constructing a dummy [`webc::v1::WebC`] object just so we can make a +/// copy of its file block. +fn volume_file_block(volumes: &BTreeMap) -> Vec { + let serialized_volumes: Vec<(&String, Vec)> = volumes + .iter() + .map(|(name, volume)| (name, serialize_volume_to_webc_v1(volume))) + .collect(); + + let parsed_volumes: indexmap::IndexMap> = serialized_volumes + .iter() + .filter_map(|(name, serialized_volume)| { + let volume = webc::v1::Volume::parse(serialized_volume).ok()?; + Some((name.to_string(), volume)) + }) + .collect(); + + let webc = webc::v1::WebC { + version: 0, + checksum: None, + signature: None, + manifest: webc::metadata::Manifest::default(), + atoms: webc::v1::Volume::default(), + volumes: parsed_volumes, + }; + + webc.get_volumes_as_fileblock() +} + +fn serialize_volume_to_webc_v1(volume: &WebcVolume) -> Vec { + fn read_dir( + volume: &WebcVolume, + path: &mut PathSegments, + files: &mut BTreeMap>, + ) { + for (segment, meta) in volume.read_dir(&*path).unwrap_or_default() { + path.push(segment); + + match meta { + webc::compat::Metadata::Dir => { + files.insert( + webc::v1::DirOrFile::Dir(path.to_string().into()), + Vec::new(), + ); + read_dir(volume, path, files); + } + webc::compat::Metadata::File { .. } => { + if let Some(contents) = volume.read_file(&*path) { + files.insert( + webc::v1::DirOrFile::File(path.to_string().into()), + contents.into(), + ); + } + } + } + + path.pop(); + } + } + + let mut path = PathSegments::ROOT; + let mut files = BTreeMap::new(); + + read_dir(volume, &mut path, &mut files); + + webc::v1::Volume::serialize_files(files) +} + +impl AllowMultiWasm { + fn validate( + &self, + all_atoms: &Vec<(String, webc::compat::SharedBytes)>, + ) -> Result<(), anyhow::Error> { + if matches!(self, AllowMultiWasm::Reject(None)) && all_atoms.len() > 1 { + let keys = all_atoms + .iter() + .map(|(name, _)| name.clone()) + .collect::>(); + + return Err(anyhow::anyhow!( + "where is one of: {}", + keys.join(", ") + )) + .context(anyhow::anyhow!( + "note: use --atom to specify which atom to compile" + )) + .context(anyhow::anyhow!( + "cannot compile more than one atom at a time" + )); + } + + Ok(()) + } +} + /// Prefix map used during compilation of object files #[derive(Debug, Default, PartialEq)] pub(crate) struct PrefixMapCompilation { diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index 0ba8ecde8be..13261d6f586 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -84,11 +84,9 @@ impl CreateObj { println!("Compiler: {}", compiler_type.to_string()); println!("Target: {}", target.triple()); - let atoms = if let Ok(pirita) = - webc::v1::WebCMmap::parse(input_path.clone(), &webc::v1::ParseOptions::default()) - { + let atoms = if let Ok(webc) = webc::compat::Container::from_disk(&input_path) { crate::commands::create_exe::compile_pirita_into_directory( - &pirita, + &webc, &output_directory_path, &self.compiler, &self.cpu_features, From c4a92c48dede9a003dc74022612c3c068b1ccb2d Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 2 Jun 2023 15:46:49 +0800 Subject: [PATCH 6/6] Lints --- lib/c-api/src/wasm_c_api/wasi/mod.rs | 8 ++++---- lib/wasi/src/runners/mod.rs | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index 0d0400959ae..a79e6546e11 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -226,12 +226,12 @@ unsafe fn wasi_env_with_filesystem_inner( config, &mut store.store_mut(), module, - std::mem::transmute(fs.ptr), // cast wasi_filesystem_t.ptr as &'static [u8] + &*(fs.ptr as *const u8), // cast wasi_filesystem_t.ptr as &'static [u8] fs.size, package, )?; - imports_set_buffer(&store, module, import_object, imports)?; + imports_set_buffer(store, module, import_object, imports)?; Some(Box::new(wasi_env_t { inner: wasi_env, @@ -268,7 +268,7 @@ fn prepare_webc_env( }) .collect::>(); - let filesystem = Box::new(StaticFileSystem::init(slice, &package_name)?); + let filesystem = Box::new(StaticFileSystem::init(slice, package_name)?); let mut builder = config.builder; if !config.inherit_stdout { @@ -288,7 +288,7 @@ fn prepare_webc_env( } let env = builder.finalize(store).ok()?; - let import_object = env.import_object(store, &module).ok()?; + let import_object = env.import_object(store, module).ok()?; Some((env, import_object)) } diff --git a/lib/wasi/src/runners/mod.rs b/lib/wasi/src/runners/mod.rs index ef603f80412..0bc5260a2b2 100644 --- a/lib/wasi/src/runners/mod.rs +++ b/lib/wasi/src/runners/mod.rs @@ -28,6 +28,11 @@ pub struct MappedDirectory { } /// Compile a module, trying to use a pre-compiled version if possible. +#[cfg(any( + feature = "webc_runner_rt_wasi", + feature = "webc_runner_rt_wcgi", + feature = "webc_runner_rt_emscripten", +))] pub(crate) fn compile_module(wasm: &[u8], runtime: &dyn Runtime) -> Result { // TODO(Michael-F-Bryan,theduke): This should be abstracted out into some // sort of ModuleResolver component that is attached to the runtime and