From 6cd80f4937c8ac47c460d1ac6a059329b45ea723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 10:32:57 +0200 Subject: [PATCH 01/83] Update clap, rewrite main CLI logic --- Cargo.lock | 43 +++- lib/cli-compiler/src/commands/compile.rs | 4 +- lib/cli-compiler/src/commands/validate.rs | 2 +- lib/cli-compiler/src/store.rs | 2 +- lib/cli/Cargo.toml | 2 +- lib/cli/src/cli.rs | 240 ++++++---------------- lib/cli/src/commands/compile.rs | 4 +- lib/cli/src/commands/create_exe.rs | 4 +- lib/cli/src/commands/create_obj.rs | 8 +- lib/cli/src/commands/inspect.rs | 2 +- lib/cli/src/commands/run.rs | 2 +- lib/cli/src/commands/run/wasi.rs | 8 +- lib/cli/src/commands/validate.rs | 2 +- lib/cli/src/commands/wast.rs | 2 +- lib/cli/src/compilers/llvm.rs | 6 +- lib/cli/src/store.rs | 2 +- lib/wasi-types-generated/wit-bindgen | 1 - lib/wasi-types/wit-bindgen | 1 + 18 files changed, 125 insertions(+), 210 deletions(-) delete mode 160000 lib/wasi-types-generated/wit-bindgen create mode 160000 lib/wasi-types/wit-bindgen diff --git a/Cargo.lock b/Cargo.lock index 9a2da8f47cd..4aa3296e797 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -370,8 +370,8 @@ checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" dependencies = [ "atty", "bitflags", - "clap_derive", - "clap_lex", + "clap_derive 3.2.18", + "clap_lex 0.2.4", "indexmap", "once_cell", "strsim 0.10.0", @@ -379,6 +379,21 @@ dependencies = [ "textwrap 0.15.0", ] +[[package]] +name = "clap" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e0d0fdd7dc774d433c78c9de5695ca04724beaec6a7d4d13ac6014608f3eaf" +dependencies = [ + "atty", + "bitflags", + "clap_derive 4.0.1", + "clap_lex 0.3.0", + "once_cell", + "strsim 0.10.0", + "termcolor", +] + [[package]] name = "clap_derive" version = "3.2.18" @@ -392,6 +407,19 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_derive" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -401,6 +429,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmake" version = "0.1.48" @@ -3079,7 +3116,7 @@ dependencies = [ "atty", "bytesize", "cfg-if 1.0.0", - "clap 3.2.21", + "clap 4.0.5", "colored 2.0.0", "dirs", "distance", diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index bcffabff54f..a98c472a070 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -14,11 +14,11 @@ use wasmer_types::{ /// The options for the `wasmer compile` subcommand pub struct Compile { /// Input file - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] + #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString)))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli-compiler/src/commands/validate.rs b/lib/cli-compiler/src/commands/validate.rs index e6d832d10c6..3dd464582d6 100644 --- a/lib/cli-compiler/src/commands/validate.rs +++ b/lib/cli-compiler/src/commands/validate.rs @@ -9,7 +9,7 @@ use wasmer_types::{is_wasm, CpuFeature, Target, Triple}; /// The options for the `wasmer validate` subcommand pub struct Validate { /// File to validate as WebAssembly - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli-compiler/src/store.rs b/lib/cli-compiler/src/store.rs index 0411798f572..e7bd2b3cb63 100644 --- a/lib/cli-compiler/src/store.rs +++ b/lib/cli-compiler/src/store.rs @@ -115,7 +115,7 @@ pub struct CompilerOptions { /// LLVM debug directory, where IR and object files will be written to. #[allow(unused)] #[cfg(feature = "llvm")] - #[cfg_attr(feature = "llvm", clap(long, parse(from_os_str)))] + #[cfg_attr(feature = "llvm", clap(long, value_parser = clap::value_parser!(std::ffi::OsString)))] llvm_debug_dir: Option, #[clap(flatten)] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index f26d91ef2bb..45ce77760a1 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -42,7 +42,7 @@ wasmer-vfs = { version = "=3.0.0-beta.2", path = "../vfs", default-features = f atty = "0.2" colored = "2.0" anyhow = "1.0" -clap = { version = "3.1", features = ["derive"] } +clap = { version = "4.0.5", features = ["derive"] } # For the function names autosuggestion distance = "0.4" # For the inspect subcommand diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 8a5972fd084..9e56d8a0a87 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -14,195 +14,73 @@ use crate::commands::{Cache, Config, Inspect, Run, SelfUpdate, Validate}; use crate::error::PrettyError; use anyhow::Result; -use clap::{ErrorKind, Parser}; - -#[derive(Parser)] -#[cfg_attr( - not(feature = "headless"), - clap( - name = "wasmer", - about = "WebAssembly standalone runtime.", - version, - author - ) -)] -#[cfg_attr( - feature = "headless", - clap( - name = "wasmer-headless", - about = "WebAssembly standalone runtime (headless).", - version, - author - ) -)] -/// The options for the wasmer Command Line Interface -enum WasmerCLIOptions { - /// Run a WebAssembly file. Formats accepted: wasm, wat - #[clap(name = "run")] - Run(Run), - - /// Wasmer cache - #[clap(subcommand, name = "cache")] - Cache(Cache), - - /// Validate a WebAssembly binary - #[clap(name = "validate")] - Validate(Validate), - - /// Compile a WebAssembly binary - #[cfg(feature = "compiler")] - #[clap(name = "compile")] - Compile(Compile), - - /// Compile a WebAssembly binary into a native executable - /// - /// To use, you need to set the `WASMER_DIR` environment variable - /// to the location of your Wasmer installation. This will probably be `~/.wasmer`. It - /// should include a `lib`, `include` and `bin` subdirectories. To create an executable - /// you will need `libwasmer`, so by setting `WASMER_DIR` the CLI knows where to look for - /// header files and libraries. - /// - /// Example usage: - /// - /// ```text - /// $ # in two lines: - /// $ export WASMER_DIR=/home/user/.wasmer/ - /// $ wasmer create-exe qjs.wasm -o qjs.exe # or in one line: - /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm -o qjs.exe - /// $ file qjs.exe - /// qjs.exe: ELF 64-bit LSB pie executable, x86-64 ... - /// ``` - /// - /// ## Cross-compilation - /// - /// Accepted target triple values must follow the - /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. - /// - /// The recommended targets we try to support are: - /// - /// - "x86_64-linux-gnu" - /// - "aarch64-linux-gnu" - /// - "x86_64-apple-darwin" - /// - "arm64-apple-darwin" - #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))] - #[clap(name = "create-exe", verbatim_doc_comment)] - CreateExe(CreateExe), - - /// Compile a WebAssembly binary into an object file - /// - /// To use, you need to set the `WASMER_DIR` environment variable to the location of your - /// Wasmer installation. This will probably be `~/.wasmer`. It should include a `lib`, - /// `include` and `bin` subdirectories. To create an object you will need `libwasmer`, so by - /// setting `WASMER_DIR` the CLI knows where to look for header files and libraries. - /// - /// Example usage: - /// - /// ```text - /// $ # in two lines: - /// $ export WASMER_DIR=/home/user/.wasmer/ - /// $ wasmer create-obj qjs.wasm --object-format symbols -o qjs.obj # or in one line: - /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm --object-format symbols -o qjs.obj - /// $ file qjs.obj - /// qjs.obj: ELF 64-bit LSB relocatable, x86-64 ... - /// ``` - /// - /// ## Cross-compilation - /// - /// Accepted target triple values must follow the - /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. - /// - /// The recommended targets we try to support are: - /// - /// - "x86_64-linux-gnu" - /// - "aarch64-linux-gnu" - /// - "x86_64-apple-darwin" - /// - "arm64-apple-darwin" - #[cfg(feature = "static-artifact-create")] - #[structopt(name = "create-obj", verbatim_doc_comment)] - CreateObj(CreateObj), - - /// Get various configuration information needed - /// to compile programs which use Wasmer - #[clap(name = "config")] - Config(Config), - - /// Update wasmer to the latest version - #[clap(name = "self-update")] - SelfUpdate(SelfUpdate), - - /// Inspect a WebAssembly file - #[clap(name = "inspect")] - Inspect(Inspect), - - /// Run spec testsuite - #[cfg(feature = "wast")] - #[clap(name = "wast")] - Wast(Wast), - - /// Unregister and/or register wasmer as binfmt interpreter - #[cfg(target_os = "linux")] - #[clap(name = "binfmt")] - Binfmt(Binfmt), -} - -impl WasmerCLIOptions { - fn execute(&self) -> Result<()> { - match self { - Self::Run(options) => options.execute(), - Self::SelfUpdate(options) => options.execute(), - Self::Cache(cache) => cache.execute(), - Self::Validate(validate) => validate.execute(), - #[cfg(feature = "compiler")] - Self::Compile(compile) => compile.execute(), - #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))] - Self::CreateExe(create_exe) => create_exe.execute(), - #[cfg(feature = "static-artifact-create")] - Self::CreateObj(create_obj) => create_obj.execute(), - Self::Config(config) => config.execute(), - Self::Inspect(inspect) => inspect.execute(), - #[cfg(feature = "wast")] - Self::Wast(wast) => wast.execute(), - #[cfg(target_os = "linux")] - Self::Binfmt(binfmt) => binfmt.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 ` - // 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 cmd_output = parse_cli_args(); + + PrettyError::report(cmd_output); +} + +fn parse_cli_args() -> Result<(), anyhow::Error> { + let args = std::env::args().collect::>(); let binpath = args.get(0).map(|s| s.as_ref()).unwrap_or(""); - let command = args.get(1); - let options = if cfg!(target_os = "linux") && binpath.ends_with("wasmer-binfmt-interpreter") { - WasmerCLIOptions::Run(Run::from_binfmt_args()) - } else { - match command.unwrap_or(&"".to_string()).as_ref() { - "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" - | "self-update" | "validate" | "wast" | "binfmt" => WasmerCLIOptions::parse(), - _ => { - WasmerCLIOptions::try_parse_from(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::DisplayVersion | ErrorKind::DisplayHelp => e.exit(), - _ => WasmerCLIOptions::Run(Run::parse()), - } - }) + + // In case we've been run as wasmer-binfmt-interpreter myfile.wasm args, + // we assume that we're registered via binfmt_misc + if cfg!(target_os = "linux") && binpath.ends_with("wasmer-binfmt-interpreter") { + return Run::from_binfmt_args().execute(); + } + + match (args.get(1).map(|s| s.as_str()), args.get(2).map(|s| s.as_str())) { + (None, _) | + (Some("help"), _) | + (Some("--help"), _) => return print_help(), + + (Some("-vV"), _) | + (Some("version"), Some("--verbose")) | + (Some("--version"), Some("--verbose")) => return print_version(false), + + (Some("-v"), _) | + (Some("version"), _) | + (Some("--version"), _) => return print_version(false), + + (Some("cache"), Some(_)) | + (Some("compile"), Some(_)) | + (Some("config"), Some(_)) | + (Some("create-exe"), Some(_)) | + (Some("inspect"), Some(_)) | + (Some("self-update"), _) | + (Some("validate"), Some(_)) | + (Some("wast"), Some(_)) | + (Some("binfmt"), Some(_)) => { + println!("running {:?}", args.get(1)); + return Ok(()) + }, + (Some("run"), Some(_)) | + (Some(_), _) => { + use clap::Parser; + // wasmer run file + // wasmer run [package] + if let Ok(run) = Run::try_parse() { + return run.execute(); + } else { + return print_help(); } - } - }; + }, + } +} - PrettyError::report(options.execute()); +fn print_help() -> Result<(), anyhow::Error>{ + println!("help"); + Ok(()) } + +fn print_version(_: bool) -> Result<(), anyhow::Error> { + println!("version"); + Ok(()) +} \ No newline at end of file diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index d6be43411c9..dd21e539850 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -9,11 +9,11 @@ use wasmer::*; /// The options for the `wasmer compile` subcommand pub struct Compile { /// Input file - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] + #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index a3b51f0836a..0f1dff2c573 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -46,11 +46,11 @@ struct CrossCompileSetup { /// The options for the `wasmer create-exe` subcommand pub struct CreateExe { /// Input file - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] + #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index 79995fd6f15..f86db8418d9 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -19,18 +19,18 @@ const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_deserialize_modul /// The options for the `wasmer create-exe` subcommand pub struct CreateObj { /// Input file - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT_PATH", short = 'o', parse(from_os_str))] + #[clap(name = "OUTPUT_PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] output: PathBuf, /// Header output file #[clap( name = "OUTPUT_HEADER_PATH", long = "output-header-path", - parse(from_os_str) + value_parser = clap::value_parser!(std::ffi::OsString) )] header_output: Option, @@ -56,7 +56,7 @@ pub struct CreateObj { #[clap(name = "OBJECT_FORMAT", long = "object-format", verbatim_doc_comment)] object_format: Option, - #[clap(short = 'm', multiple = true, number_of_values = 1)] + #[clap(short = 'm', number_of_values = 1)] cpu_features: Vec, #[clap(flatten)] diff --git a/lib/cli/src/commands/inspect.rs b/lib/cli/src/commands/inspect.rs index 4d56faa6136..461e20e271f 100644 --- a/lib/cli/src/commands/inspect.rs +++ b/lib/cli/src/commands/inspect.rs @@ -9,7 +9,7 @@ use wasmer::*; /// The options for the `wasmer validate` subcommand pub struct Inspect { /// File to validate as WebAssembly - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index c6e0fde6ee8..965a3598559 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -30,7 +30,7 @@ pub struct Run { disable_cache: bool, /// File to run - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, /// Invoke a specified function diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 2fd31666bed..37c64473a1c 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -18,18 +18,18 @@ pub struct Wasi { pre_opened_directories: Vec, /// Map a host directory to a different location for the Wasm module - #[clap( + #[arg( long = "mapdir", name = "GUEST_DIR:HOST_DIR", - parse(try_from_str = parse_mapdir), + value_parser = parse_mapdir, )] mapped_dirs: Vec<(String, PathBuf)>, /// Pass custom environment variables - #[clap( + #[arg( long = "env", name = "KEY=VALUE", - parse(try_from_str = parse_envvar), + value_parser = parse_envvar, )] env_vars: Vec<(String, String)>, diff --git a/lib/cli/src/commands/validate.rs b/lib/cli/src/commands/validate.rs index 4f620ecc3d2..a1baf49ba56 100644 --- a/lib/cli/src/commands/validate.rs +++ b/lib/cli/src/commands/validate.rs @@ -8,7 +8,7 @@ use wasmer::*; /// The options for the `wasmer validate` subcommand pub struct Validate { /// File to validate as WebAssembly - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/commands/wast.rs b/lib/cli/src/commands/wast.rs index 5064a94c8e8..c8d62d5184d 100644 --- a/lib/cli/src/commands/wast.rs +++ b/lib/cli/src/commands/wast.rs @@ -9,7 +9,7 @@ use wasmer_wast::Wast as WastSpectest; /// The options for the `wasmer wast` subcommand pub struct Wast { /// Wast file to run - #[clap(name = "FILE", parse(from_os_str))] + #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/compilers/llvm.rs b/lib/cli/src/compilers/llvm.rs index b7a7a95e42d..fd312a1fdda 100644 --- a/lib/cli/src/compilers/llvm.rs +++ b/lib/cli/src/compilers/llvm.rs @@ -2,15 +2,15 @@ /// LLVM backend flags. pub struct LLVMCLIOptions { /// Emit LLVM IR before optimization pipeline. - #[clap(long = "llvm-pre-opt-ir", parse(from_os_str))] + #[clap(long = "llvm-pre-opt-ir", value_parser = clap::value_parser!(std::ffi::OsString))] pre_opt_ir: Option, /// Emit LLVM IR after optimization pipeline. - #[clap(long = "llvm-post-opt-ir", parse(from_os_str))] + #[clap(long = "llvm-post-opt-ir", value_parser = clap::value_parser!(std::ffi::OsString))] post_opt_ir: Option, /// Emit LLVM generated native code object file. - #[clap(long = "llvm-object-file", parse(from_os_str))] + #[clap(long = "llvm-object-file", value_parser = clap::value_parser!(std::ffi::OsString))] obj_file: Option, } diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 49b897c7bc3..76ee227e22e 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -47,7 +47,7 @@ pub struct CompilerOptions { /// LLVM debug directory, where IR and object files will be written to. #[cfg(feature = "llvm")] - #[clap(long, parse(from_os_str))] + #[clap(long, value_parser = clap::value_parser!(std::ffi::OsString))] llvm_debug_dir: Option, #[clap(flatten)] diff --git a/lib/wasi-types-generated/wit-bindgen b/lib/wasi-types-generated/wit-bindgen deleted file mode 160000 index 095d295be63..00000000000 --- a/lib/wasi-types-generated/wit-bindgen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 095d295be6392259924e48488af188d3ed3e4102 diff --git a/lib/wasi-types/wit-bindgen b/lib/wasi-types/wit-bindgen new file mode 160000 index 00000000000..44a2bf81489 --- /dev/null +++ b/lib/wasi-types/wit-bindgen @@ -0,0 +1 @@ +Subproject commit 44a2bf8148932590479bd64b9f90502b14c3b0d3 From 771e1e33a1b58f4b294f649038496ee923ee5c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 12:10:31 +0200 Subject: [PATCH 02/83] Added `RunWithoutFile` to prepare downloading + GraphQL schema --- Cargo.lock | 8 + Cargo.toml | 1 + lib/cli-compiler/src/commands/compile.rs | 2 +- lib/cli/Cargo.toml | 1 + lib/cli/src/cli.rs | 98 +- lib/cli/src/commands/run.rs | 77 + lib/registry/Cargo.toml | 7 + .../graphql/queries/get_package.graphql | 13 + .../queries/get_package_version.graphql | 12 + lib/registry/graphql/queries/login.graphql | 5 + .../queries/test_if_registry_present.graphql | 3 + lib/registry/graphql/schema.graphql | 1401 +++++++++++++++++ lib/registry/src/commands/install.rs | 0 lib/registry/src/commands/search.rs | 0 lib/registry/src/lib.rs | 53 + 15 files changed, 1646 insertions(+), 35 deletions(-) create mode 100644 lib/registry/Cargo.toml create mode 100644 lib/registry/graphql/queries/get_package.graphql create mode 100644 lib/registry/graphql/queries/get_package_version.graphql create mode 100644 lib/registry/graphql/queries/login.graphql create mode 100644 lib/registry/graphql/queries/test_if_registry_present.graphql create mode 100644 lib/registry/graphql/schema.graphql create mode 100644 lib/registry/src/commands/install.rs create mode 100644 lib/registry/src/commands/search.rs create mode 100644 lib/registry/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4aa3296e797..4d88ae0fc64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3135,6 +3135,7 @@ dependencies = [ "wasmer-compiler-singlepass", "wasmer-emscripten", "wasmer-object", + "wasmer-registry", "wasmer-types", "wasmer-vfs", "wasmer-vm", @@ -3335,6 +3336,13 @@ dependencies = [ "wasmer-types", ] +[[package]] +name = "wasmer-registry" +version = "3.0.0-beta.2" +dependencies = [ + "dirs", +] + [[package]] name = "wasmer-types" version = "3.0.0-beta.2" diff --git a/Cargo.toml b/Cargo.toml index 6ed449f5f8b..f342779f4fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ members = [ "lib/c-api/tests/wasmer-c-api-test-runner", "lib/c-api/examples/wasmer-capi-examples-runner", "lib/types", + "lib/registry", "tests/wasi-wast", "tests/lib/wast", "tests/lib/compiler-test-derive", diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index a98c472a070..04e95168d5d 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -18,7 +18,7 @@ pub struct Compile { path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString)))] + #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 45ce77760a1..c6b3bd96c36 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -37,6 +37,7 @@ wasmer-wasi-experimental-io-devices = { version = "=3.0.0-beta.2", path = "../wa wasmer-wast = { version = "=3.0.0-beta.2", path = "../../tests/lib/wast", optional = true } wasmer-cache = { version = "=3.0.0-beta.2", path = "../cache", optional = true } wasmer-types = { version = "=3.0.0-beta.2", path = "../types" } +wasmer-registry = { version = "=3.0.0-beta.2", path = "../registry" } wasmer-object = { version = "=3.0.0-beta.2", path = "../object", optional = true } wasmer-vfs = { version = "=3.0.0-beta.2", path = "../vfs", default-features = false, features = ["host-fs"] } atty = "0.2" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 9e56d8a0a87..567d2631cc2 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -10,9 +10,10 @@ use crate::commands::CreateExe; use crate::commands::CreateObj; #[cfg(feature = "wast")] use crate::commands::Wast; -use crate::commands::{Cache, Config, Inspect, Run, SelfUpdate, Validate}; +use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; use anyhow::Result; +use clap::Parser; /// The main function for the Wasmer CLI tool. pub fn wasmer_main() { @@ -26,7 +27,6 @@ pub fn wasmer_main() { } fn parse_cli_args() -> Result<(), anyhow::Error> { - let args = std::env::args().collect::>(); let binpath = args.get(0).map(|s| s.as_ref()).unwrap_or(""); @@ -36,46 +36,76 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { return Run::from_binfmt_args().execute(); } - match (args.get(1).map(|s| s.as_str()), args.get(2).map(|s| s.as_str())) { - (None, _) | - (Some("help"), _) | - (Some("--help"), _) => return print_help(), - - (Some("-vV"), _) | - (Some("version"), Some("--verbose")) | - (Some("--version"), Some("--verbose")) => return print_version(false), + match ( + args.get(1).map(|s| s.as_str()), + args.get(2).map(|s| s.as_str()), + ) { + (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(), + + (Some("-vV"), _) + | (Some("version"), Some("--verbose")) + | (Some("--version"), Some("--verbose")) => return print_version(false), - (Some("-v"), _) | - (Some("version"), _) | - (Some("--version"), _) => return print_version(false), - - (Some("cache"), Some(_)) | - (Some("compile"), Some(_)) | - (Some("config"), Some(_)) | - (Some("create-exe"), Some(_)) | - (Some("inspect"), Some(_)) | - (Some("self-update"), _) | - (Some("validate"), Some(_)) | - (Some("wast"), Some(_)) | - (Some("binfmt"), Some(_)) => { - println!("running {:?}", args.get(1)); - return Ok(()) - }, - (Some("run"), Some(_)) | - (Some(_), _) => { - use clap::Parser; - // wasmer run file - // wasmer run [package] + (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { + return print_version(false) + } + + (Some("cache"), _) => Cache::try_parse_from(args.iter())?.execute(), + (Some("compile"), _) => Compile::try_parse_from(args.iter())?.execute(), + (Some("config"), _) => Config::try_parse_from(args.iter())?.execute(), + (Some("create-exe"), _) => CreateExe::try_parse_from(args.iter())?.execute(), + (Some("inspect"), _) => Inspect::try_parse_from(args.iter())?.execute(), + (Some("self-update"), _) => SelfUpdate::try_parse_from(args.iter())?.execute(), + (Some("validate"), _) => Validate::try_parse_from(args.iter())?.execute(), + (Some("wast"), _) => Wast::try_parse_from(args.iter())?.execute(), + #[cfg(feature = "binfmt")] + (Some("binfmt"), _) => Binfmt::try_parse_from(args.iter())?.execute(), + (Some("run"), Some(package)) | (Some(package), _) => { if let Ok(run) = Run::try_parse() { return run.execute(); + } else if let Ok((package, version)) = split_version(package) { + if let Ok(o) = wasmer_registry::get_package_local( + &package, + version.as_ref().map(|s| s.as_str()), + ) { + // Try finding the local package + let mut args_without_package = args.clone(); + args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(o) + .execute(); + } else if let Ok(o) = + wasmer_registry::install_package(&package, version.as_ref().map(|s| s.as_str())) + { + // Try auto-installing the remote package + let mut args_without_package = args.clone(); + args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(o) + .execute(); + } else { + // Failed to find the remote package + // + // TODO: print list of similar packages + return print_help(); + } } else { return print_help(); } - }, + } } } -fn print_help() -> Result<(), anyhow::Error>{ +fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { + let package_version = s.split("@").collect::>(); + match package_version.as_slice() { + &[p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), + &[p] => Ok((p.trim().to_string(), None)), + _ => Err(anyhow!("Invalid package / version: {s:?}")), + } +} + +fn print_help() -> Result<(), anyhow::Error> { println!("help"); Ok(()) } @@ -83,4 +113,4 @@ fn print_help() -> Result<(), anyhow::Error>{ fn print_version(_: bool) -> Result<(), anyhow::Error> { println!("version"); Ok(()) -} \ No newline at end of file +} diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 965a3598559..5ffabe9dce5 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -21,6 +21,83 @@ mod wasi; #[cfg(feature = "wasi")] use wasi::Wasi; +/// Same as `wasmer run`, but without the required `path` argument (injected previously) +#[derive(Debug, Parser, Clone, Default)] +pub struct RunWithoutFile { + /// Disable the cache + #[cfg(feature = "cache")] + #[clap(long = "disable-cache")] + disable_cache: bool, + + /// Invoke a specified function + #[clap(long = "invoke", short = 'i')] + invoke: Option, + + /// The command name is a string that will override the first argument passed + /// to the wasm program. This is used in wapm to provide nicer output in + /// help commands and error messages of the running wasm program + #[clap(long = "command-name", hide = true)] + command_name: Option, + + /// A prehashed string, used to speed up start times by avoiding hashing the + /// wasm module. If the specified hash is not found, Wasmer will hash the module + /// as if no `cache-key` argument was passed. + #[cfg(feature = "cache")] + #[clap(long = "cache-key", hide = true)] + cache_key: Option, + + #[clap(flatten)] + store: StoreOptions, + + // TODO: refactor WASI structure to allow shared options with Emscripten + #[cfg(feature = "wasi")] + #[clap(flatten)] + wasi: Wasi, + + /// Enable non-standard experimental IO devices + #[cfg(feature = "io-devices")] + #[clap(long = "enable-io-devices")] + enable_experimental_io_devices: bool, + + /// Enable debug output + #[cfg(feature = "debug")] + #[clap(long = "debug", short = 'd')] + debug: bool, + + #[cfg(feature = "debug")] + #[clap(short, long, parse(from_occurrences))] + verbose: u8, + + /// Application arguments + #[clap(value_name = "ARGS")] + args: Vec, +} + +impl RunWithoutFile { + /// Given a local path, returns the `Run` command (overriding the `--path` argument). + pub fn into_run_args(self, pathbuf: PathBuf) -> Run { + Run { + #[cfg(feature = "cache")] + disable_cache: self.disable_cache, + path: pathbuf, + invoke: self.invoke, + command_name: self.command_name, + #[cfg(feature = "cache")] + cache_key: self.cache_key, + store: self.store, + #[cfg(feature = "wasi")] + wasi: self.wasi, + #[cfg(feature = "io-devices")] + enable_experimental_io_devices: self.enable_experimental_io_devices, + #[cfg(feature = "debug")] + debug: self.debug, + #[cfg(feature = "debug")] + verbose: self.verbose, + args: self.args, + } + } +} + #[derive(Debug, Parser, Clone, Default)] /// The options for the `wasmer run` subcommand pub struct Run { diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml new file mode 100644 index 00000000000..7c027d5f3ec --- /dev/null +++ b/lib/registry/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wasmer-registry" +version = "3.0.0-beta.2" +edition = "2021" + +[dependencies] +dirs = "4.0.0" \ No newline at end of file diff --git a/lib/registry/graphql/queries/get_package.graphql b/lib/registry/graphql/queries/get_package.graphql new file mode 100644 index 00000000000..43a953d2682 --- /dev/null +++ b/lib/registry/graphql/queries/get_package.graphql @@ -0,0 +1,13 @@ +query GetPackageQuery ($name: String!) { + package: getPackage(name:$name) { + name + private + lastVersion { + version + distribution { + downloadUrl + } + manifest + } + } +} diff --git a/lib/registry/graphql/queries/get_package_version.graphql b/lib/registry/graphql/queries/get_package_version.graphql new file mode 100644 index 00000000000..e70b86ba76e --- /dev/null +++ b/lib/registry/graphql/queries/get_package_version.graphql @@ -0,0 +1,12 @@ +query GetPackageVersionQuery ($name: String!, $version: String) { + packageVersion: getPackageVersion(name:$name, version:$version) { + package { + name + } + version + distribution { + downloadUrl + } + manifest + } +} \ No newline at end of file diff --git a/lib/registry/graphql/queries/login.graphql b/lib/registry/graphql/queries/login.graphql new file mode 100644 index 00000000000..438ebb44272 --- /dev/null +++ b/lib/registry/graphql/queries/login.graphql @@ -0,0 +1,5 @@ +mutation LoginMutation($username: String!, $password: String!) { + tokenAuth(input: {username: $username, password: $password}) { + refreshToken + } +} diff --git a/lib/registry/graphql/queries/test_if_registry_present.graphql b/lib/registry/graphql/queries/test_if_registry_present.graphql new file mode 100644 index 00000000000..7b1e4d33a56 --- /dev/null +++ b/lib/registry/graphql/queries/test_if_registry_present.graphql @@ -0,0 +1,3 @@ +query TestIfRegistryPresent { + __typename +} \ No newline at end of file diff --git a/lib/registry/graphql/schema.graphql b/lib/registry/graphql/schema.graphql new file mode 100644 index 00000000000..07687311d1c --- /dev/null +++ b/lib/registry/graphql/schema.graphql @@ -0,0 +1,1401 @@ +type APIToken { + createdAt: DateTime! + id: ID! + identifier: String + lastUsedAt: DateTime + revokedAt: DateTime + user: User! +} + +type APITokenConnection { + # Contains the nodes in this connection. + edges: [APITokenEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `APIToken` and its cursor. +type APITokenEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: APIToken +} + +input AcceptNamespaceCollaboratorInviteInput { + clientMutationId: String + inviteId: ID! +} + +type AcceptNamespaceCollaboratorInvitePayload { + clientMutationId: String + namespaceCollaboratorInvite: NamespaceCollaboratorInvite! +} + +input AcceptPackageCollaboratorInviteInput { + clientMutationId: String + inviteId: ID! +} + +type AcceptPackageCollaboratorInvitePayload { + clientMutationId: String + packageCollaboratorInvite: PackageCollaboratorInvite! +} + +input AcceptPackageTransferRequestInput { + clientMutationId: String + packageTransferRequestId: ID! +} + +type AcceptPackageTransferRequestPayload { + clientMutationId: String + package: Package! + packageTransferRequest: PackageTransferRequest! +} + +type ActivityEvent implements Node { + actorIcon: String! + body: ActivityEventBody! + createdAt: DateTime! + + # The ID of the object + id: ID! +} + +type ActivityEventBody { + ranges: [NodeBodyRange!]! + text: String! +} + +type ActivityEventConnection { + # Contains the nodes in this connection. + edges: [ActivityEventEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `ActivityEvent` and its cursor. +type ActivityEventEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: ActivityEvent +} + +input ArchivePackageInput { + clientMutationId: String + packageId: ID! +} + +type ArchivePackagePayload { + clientMutationId: String + package: Package! +} + +input ChangePackageVersionArchivedStatusInput { + clientMutationId: String + isArchived: Boolean + packageVersionId: ID! +} + +type ChangePackageVersionArchivedStatusPayload { + clientMutationId: String + packageVersion: PackageVersion! +} + +input ChangeUserEmailInput { + clientMutationId: String + newEmail: String! +} + +type ChangeUserEmailPayload { + clientMutationId: String + user: User! +} + +input ChangeUserPasswordInput { + clientMutationId: String + password: String! + + # The token associated to change the password. If not existing it will use the request user by default + token: String +} + +type ChangeUserPasswordPayload { + clientMutationId: String + token: String +} + +input ChangeUserUsernameInput { + clientMutationId: String + + # The new user username + username: String! +} + +type ChangeUserUsernamePayload { + clientMutationId: String + token: String + user: User +} + +input CheckUserExistsInput { + clientMutationId: String + + # The user + user: String! +} + +type CheckUserExistsPayload { + clientMutationId: String + exists: Boolean! + + # The user is only returned if the user input was the username + user: User +} + +type Command { + command: String! + module: PackageVersionModule! + packageVersion: PackageVersion! +} + +input CreateNamespaceInput { + # The namespace avatar + avatar: String + clientMutationId: String + + # The namespace description + description: String + + # The namespace display name + displayName: String + name: String! +} + +type CreateNamespacePayload { + clientMutationId: String + namespace: Namespace! + user: User! +} + +# The `DateTime` scalar type represents a DateTime +# value as specified by +# [iso8601](https://en.wikipedia.org/wiki/ISO_8601). +scalar DateTime + +input DeleteNamespaceInput { + clientMutationId: String + namespaceId: ID! +} + +type DeleteNamespacePayload { + clientMutationId: String + success: Boolean! +} + +type ErrorType { + field: String! + messages: [String!]! +} + +input GenerateAPITokenInput { + clientMutationId: String + identifier: String +} + +type GenerateAPITokenPayload { + clientMutationId: String + token: APIToken + tokenRaw: String + user: User +} + +# The `GenericScalar` scalar type represents a generic +# GraphQL scalar value that could be: +# String, Boolean, Int, Float, List or Object. +scalar GenericScalar + +type GetPasswordResetToken { + user: User + valid: Boolean! +} + +union GlobalObject = Namespace | User + +input InputSignature { + data: String! + publicKeyKeyId: String! +} + +type Interface implements Node { + createdAt: DateTime! + description: String! + displayName: String! + homepage: String + icon: String + + # The ID of the object + id: ID! + lastVersion: InterfaceVersion + name: String! + updatedAt: DateTime! + versions(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): InterfaceVersionConnection! +} + +type InterfaceVersion implements Node { + content: String! + createdAt: DateTime! + + # The ID of the object + id: ID! + interface: Interface! + packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): PackageVersionConnection! + publishedBy: User! + updatedAt: DateTime! + version: String! +} + +type InterfaceVersionConnection { + # Contains the nodes in this connection. + edges: [InterfaceVersionEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `InterfaceVersion` and its cursor. +type InterfaceVersionEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: InterfaceVersion +} + +input InviteNamespaceCollaboratorInput { + clientMutationId: String + email: String + namespaceId: ID! + role: Role! + username: String +} + +type InviteNamespaceCollaboratorPayload { + clientMutationId: String + invite: NamespaceCollaboratorInvite! + namespace: Namespace! +} + +input InvitePackageCollaboratorInput { + clientMutationId: String + email: String + packageName: String! + role: Role! + username: String +} + +type InvitePackageCollaboratorPayload { + clientMutationId: String + invite: PackageCollaboratorInvite! + package: Package! +} + +input LikePackageInput { + clientMutationId: String + packageId: ID! +} + +type LikePackagePayload { + clientMutationId: String + package: Package! +} + +interface Likeable { + id: ID! + likersCount: Int! + viewerHasLiked: Boolean! +} + +type Mutation { + acceptNamespaceCollaboratorInvite(input: AcceptNamespaceCollaboratorInviteInput!): AcceptNamespaceCollaboratorInvitePayload + acceptPackageCollaboratorInvite(input: AcceptPackageCollaboratorInviteInput!): AcceptPackageCollaboratorInvitePayload + acceptPackageTransferRequest(input: AcceptPackageTransferRequestInput!): AcceptPackageTransferRequestPayload + archivePackage(input: ArchivePackageInput!): ArchivePackagePayload + changePackageVersionArchivedStatus(input: ChangePackageVersionArchivedStatusInput!): ChangePackageVersionArchivedStatusPayload + changeUserEmail(input: ChangeUserEmailInput!): ChangeUserEmailPayload + changeUserPassword(input: ChangeUserPasswordInput!): ChangeUserPasswordPayload + changeUserUsername(input: ChangeUserUsernameInput!): ChangeUserUsernamePayload + checkUserExists(input: CheckUserExistsInput!): CheckUserExistsPayload + createNamespace(input: CreateNamespaceInput!): CreateNamespacePayload + deleteNamespace(input: DeleteNamespaceInput!): DeleteNamespacePayload + generateApiToken(input: GenerateAPITokenInput!): GenerateAPITokenPayload + inviteNamespaceCollaborator(input: InviteNamespaceCollaboratorInput!): InviteNamespaceCollaboratorPayload + invitePackageCollaborator(input: InvitePackageCollaboratorInput!): InvitePackageCollaboratorPayload + likePackage(input: LikePackageInput!): LikePackagePayload + publishPackage(input: PublishPackageInput!): PublishPackagePayload + publishPublicKey(input: PublishPublicKeyInput!): PublishPublicKeyPayload + readNotification(input: ReadNotificationInput!): ReadNotificationPayload + refreshToken(input: RefreshInput!): RefreshPayload + registerUser(input: RegisterUserInput!): RegisterUserPayload + removeNamespaceCollaborator(input: RemoveNamespaceCollaboratorInput!): RemoveNamespaceCollaboratorPayload + removeNamespaceCollaboratorInvite(input: RemoveNamespaceCollaboratorInviteInput!): RemoveNamespaceCollaboratorInvitePayload + removePackageCollaborator(input: RemovePackageCollaboratorInput!): RemovePackageCollaboratorPayload + removePackageCollaboratorInvite(input: RemovePackageCollaboratorInviteInput!): RemovePackageCollaboratorInvitePayload + removePackageTransferRequest(input: RemovePackageTransferRequestInput!): RemovePackageTransferRequestPayload + requestPackageTransfer(input: RequestPackageTransferInput!): RequestPackageTransferPayload + requestPasswordReset(input: RequestPasswordResetInput!): RequestPasswordResetPayload + requestValidationEmail(input: RequestValidationEmailInput!): RequestValidationEmailPayload + revokeApiToken(input: RevokeAPITokenInput!): RevokeAPITokenPayload + seePendingNotifications(input: SeePendingNotificationsInput!): SeePendingNotificationsPayload + + # Social Auth for JSON Web Token (JWT) + socialAuth(input: SocialAuthJWTInput!): SocialAuthJWTPayload + + # Obtain JSON Web Token mutation + tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload + unlikePackage(input: UnlikePackageInput!): UnlikePackagePayload + unwatchPackage(input: UnwatchPackageInput!): UnwatchPackagePayload + updateNamespace(input: UpdateNamespaceInput!): UpdateNamespacePayload + updateNamespaceCollaboratorRole(input: UpdateNamespaceCollaboratorRoleInput!): UpdateNamespaceCollaboratorRolePayload + updatePackage(input: UpdatePackageInput!): UpdatePackagePayload + updatePackageCollaboratorRole(input: UpdatePackageCollaboratorRoleInput!): UpdatePackageCollaboratorRolePayload + updateUserInfo(input: UpdateUserInfoInput!): UpdateUserInfoPayload + validateUserEmail(input: ValidateUserEmailInput!): ValidateUserEmailPayload + validateUserPassword(input: ValidateUserPasswordInput!): ValidateUserPasswordPayload + verifyToken(input: VerifyInput!): VerifyPayload + watchPackage(input: WatchPackageInput!): WatchPackagePayload +} + +type Namespace implements Node & PackageOwner { + avatar: String! + avatarUpdatedAt: DateTime + collaborators(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorConnection + createdAt: DateTime! + description: String! + displayName: String + globalName: String! + + # The ID of the object + id: ID! + maintainerInvites: [NamespaceCollaboratorInvite!]! + maintainersWithRoles(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): NamespaceMaintainerConnection! + name: String! + packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageVersionConnection + packages(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageConnection + pendingInvites(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorInviteConnection + publicActivity(after: String = null, before: String = null, first: Int = null, last: Int = null): ActivityEventConnection! + updatedAt: DateTime! + userSet(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): UserConnection! + viewerHasRole(role: Role!): Boolean! +} + +type NamespaceCollaborator { + createdAt: DateTime! + id: ID! + invite: NamespaceCollaboratorInvite + namespace: Namespace! + role: RegistryNamespaceMaintainerRoleChoices! + updatedAt: DateTime! + user: User! +} + +type NamespaceCollaboratorConnection { + # Contains the nodes in this connection. + edges: [NamespaceCollaboratorEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `NamespaceCollaborator` and its cursor. +type NamespaceCollaboratorEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: NamespaceCollaborator +} + +type NamespaceCollaboratorInvite { + accepted: NamespaceMaintainer + approvedBy: User + closedAt: DateTime + createdAt: DateTime! + declinedBy: User + expiresAt: DateTime! + id: ID! + inviteEmail: String + namespace: Namespace! + requestedBy: User! + role: RegistryNamespaceMaintainerInviteRoleChoices! + user: User +} + +type NamespaceCollaboratorInviteConnection { + # Contains the nodes in this connection. + edges: [NamespaceCollaboratorInviteEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `NamespaceCollaboratorInvite` and its cursor. +type NamespaceCollaboratorInviteEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: NamespaceCollaboratorInvite +} + +type NamespaceConnection { + # Contains the nodes in this connection. + edges: [NamespaceEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `Namespace` and its cursor. +type NamespaceEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: Namespace +} + +type NamespaceMaintainer implements Node { + createdAt: DateTime! + + # The ID of the object + id: ID! + invite: NamespaceCollaboratorInvite + namespace: Namespace! + role: RegistryNamespaceMaintainerRoleChoices! + updatedAt: DateTime! + user: User! +} + +type NamespaceMaintainerConnection { + # Contains the nodes in this connection. + edges: [NamespaceMaintainerEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `NamespaceMaintainer` and its cursor. +type NamespaceMaintainerEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: NamespaceMaintainer +} + +# An object with an ID +interface Node { + # The ID of the object + id: ID! +} + +type NodeBodyRange { + entity: Node! + length: Int! + offset: Int! +} + +input ObtainJSONWebTokenInput { + clientMutationId: String + password: String! + username: String! +} + +# Obtain JSON Web Token mutation +type ObtainJSONWebTokenPayload { + clientMutationId: String + payload: GenericScalar! + refreshExpiresIn: Int! + refreshToken: String! + token: String! +} + +type Package implements Likeable & Node & PackageOwner { + alias: String + + # The app icon. It should be formatted in the same way as Apple icons + appIcon: String! @deprecated(reason: "Please use icon instead") + collaborators(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorConnection + createdAt: DateTime! + curated: Boolean! + displayName: String! + + # The total number of downloads of the package + downloadsCount: Int + globalName: String! + + # The app icon. It should be formatted in the same way as Apple icons + icon: String! + iconUpdatedAt: DateTime + + # The ID of the object + id: ID! + isTransferring: Boolean! + lastVersion: PackageVersion + likeCount: Int! + likersCount: Int! + maintainers: [User]! @deprecated(reason: "Please use collaborators instead") + name: String! + namespace: String + owner: PackageOwner + ownerObjectId: Int! + + # The name of the package without the owner + packageName: String! + pendingInvites(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorInviteConnection + private: Boolean! + + # The public keys for all the published versions + publicKeys: [PublicKey!]! + updatedAt: DateTime! + versions: [PackageVersion] + viewerHasLiked: Boolean! + viewerHasRole(role: Role!): Boolean! + viewerIsWatching: Boolean! + watchCount: Int! +} + +type PackageCollaborator implements Node { + createdAt: DateTime! + + # The ID of the object + id: ID! + invite: PackageCollaboratorInvite + package: Package! + role: RegistryPackageMaintainerRoleChoices! + updatedAt: DateTime! + user: User! +} + +type PackageCollaboratorConnection { + # Contains the nodes in this connection. + edges: [PackageCollaboratorEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `PackageCollaborator` and its cursor. +type PackageCollaboratorEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: PackageCollaborator +} + +type PackageCollaboratorInvite implements Node { + accepted: PackageCollaborator + approvedBy: User + closedAt: DateTime + createdAt: DateTime! + declinedBy: User + expiresAt: DateTime! + + # The ID of the object + id: ID! + inviteEmail: String + package: Package! + requestedBy: User! + role: RegistryPackageMaintainerInviteRoleChoices! + user: User +} + +type PackageCollaboratorInviteConnection { + # Contains the nodes in this connection. + edges: [PackageCollaboratorInviteEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `PackageCollaboratorInvite` and its cursor. +type PackageCollaboratorInviteEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: PackageCollaboratorInvite +} + +type PackageConnection { + # Contains the nodes in this connection. + edges: [PackageEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +type PackageDistribution { + downloadUrl: String! + size: Int! +} + +# A Relay edge containing a `Package` and its cursor. +type PackageEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: Package +} + +interface PackageOwner { + globalName: String! +} + +type PackageTransferRequest implements Node { + approvedBy: User + closedAt: DateTime + createdAt: DateTime! + declinedBy: User + expiresAt: DateTime! + + # The ID of the object + id: ID! + newOwnerObjectId: Int! + package: Package! + previousOwnerObjectId: Int! + requestedBy: User! +} + +type PackageTransferRequestConnection { + # Contains the nodes in this connection. + edges: [PackageTransferRequestEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `PackageTransferRequest` and its cursor. +type PackageTransferRequestEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: PackageTransferRequest +} + +type PackageVersion implements Node { + bindings: [PackageVersionBinding]! + commands: [Command!]! + createdAt: DateTime! + description: String! + distribution: PackageDistribution! + file: String! + fileSize: Int! + filesystem: [PackageVersionFilesystem]! + homepage: String + + # The ID of the object + id: ID! + isArchived: Boolean! + isLastVersion: Boolean! + isSigned: Boolean! + license: String + licenseFile: String + manifest: String! + moduleInterfaces: [InterfaceVersion!]! + modules: [PackageVersionModule!]! + package: Package! + publishedBy: User! + readme: String + repository: String + signature: Signature + updatedAt: DateTime! + version: String! +} + +interface PackageVersionBinding { + # The module these bindings are associated with. + module: String! +} + +type PackageVersionNPMBinding implements PackageVersionBinding { + npmDefaultInstallPackageName: String! +} + +type PackageVersionPythonBinding implements PackageVersionBinding { + pythonDefaultInstallPackageName: String! +} + +type PackageVersionConnection { + # Contains the nodes in this connection. + edges: [PackageVersionEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `PackageVersion` and its cursor. +type PackageVersionEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: PackageVersion +} + +type PackageVersionFilesystem { + host: String! + wasm: String! +} + +type PackageVersionModule { + abi: String + name: String! + publicUrl: String! + source: String! +} + +# The Relay compliant `PageInfo` type, containing data necessary to paginate this connection. +type PageInfo { + # When paginating forwards, the cursor to continue. + endCursor: String + + # When paginating forwards, are there more items? + hasNextPage: Boolean! + + # When paginating backwards, are there more items? + hasPreviousPage: Boolean! + + # When paginating backwards, the cursor to continue. + startCursor: String +} + +type PublicKey implements Node { + # The ID of the object + id: ID! + key: String! + keyId: String! + owner: User! + revoked: Boolean! + revokedAt: DateTime + uploadedAt: DateTime! + verifyingSignature: Signature +} + +input PublishPackageInput { + clientMutationId: String + description: String! + file: String + homepage: String + + # The package icon + icon: String + license: String + licenseFile: String + manifest: String! + name: String! + readme: String + repository: String + signature: InputSignature + version: String! +} + +type PublishPackagePayload { + clientMutationId: String + packageVersion: PackageVersion! + success: Boolean! +} + +input PublishPublicKeyInput { + clientMutationId: String + key: String! + keyId: String! + verifyingSignatureId: String +} + +type PublishPublicKeyPayload { + clientMutationId: String + publicKey: PublicKey! + success: Boolean! +} + +type SignedUrl { + url: String! +} + +type Query { + getCommand(name: String!): Command + getCommands(names: [String!]!): [Command] + getContract(name: String!): Interface @deprecated(reason: "Please use getInterface instead") + getContractVersion(name: String!, version: String = null): InterfaceVersion @deprecated(reason: "Please use getInterfaceVersion instead") + getContracts(names: [String!]!): [Interface]! @deprecated(reason: "Please use getInterfaces instead") + getGlobalObject(slug: String!): GlobalObject + getInterface(name: String!): Interface + getInterfaceVersion(name: String!, version: String = "latest"): InterfaceVersion + getInterfaces(names: [String!]!): [Interface]! + getNamespace(name: String!): Namespace + getPackage(name: String!): Package + getPackageVersion(name: String!, version: String = "latest"): PackageVersion + getPackageVersions(names: [String!]!): [PackageVersion] + getPackages(names: [String!]!): [Package]! + getPasswordResetToken(token: String!): GetPasswordResetToken + getSignedUrlForPackageUpload(name:String!,version:String!): SignedUrl + getUser(username: String!): User + node( + # The ID of the object + id: ID! + ): Node + packages(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageConnection + recentPackageVersions(after: String = null, before: String = null, curated: Boolean = null, first: Int = null, last: Int = null, offset: Int = null): PackageVersionConnection + search(after: String = null, before: String = null, curated: Boolean = null, first: Int = null, hasBindings: Boolean = null, isStandalone: Boolean = null, kind: [SearchKind!] = null, last: Int = null, orderBy: SearchOrderBy = null, publishDate: SearchPublishDate = null, query: String!, sort: SearchOrderSort = null, withInterfaces: [String!] = null): SearchConnection! + searchAutocomplete(after: String = null, before: String = null, first: Int = null, kind: [SearchKind!] = null, last: Int = null, query: String!): SearchConnection! + viewer: User +} + +input ReadNotificationInput { + clientMutationId: String + notificationId: ID! +} + +type ReadNotificationPayload { + clientMutationId: String + notification: UserNotification +} + +input RefreshInput { + clientMutationId: String + refreshToken: String +} + +type RefreshPayload { + clientMutationId: String + payload: GenericScalar! + refreshExpiresIn: Int! + refreshToken: String! + token: String! +} + +input RegisterUserInput { + clientMutationId: String + email: String! + fullName: String! + password: String! + username: String! +} + +type RegisterUserPayload { + clientMutationId: String + token: String +} + +# An enumeration. +enum RegistryNamespaceMaintainerInviteRoleChoices { + # Admin + ADMIN + + # Editor + EDITOR + + # Viewer + VIEWER +} + +# An enumeration. +enum RegistryNamespaceMaintainerRoleChoices { + # Admin + ADMIN + + # Editor + EDITOR + + # Viewer + VIEWER +} + +# An enumeration. +enum RegistryPackageMaintainerInviteRoleChoices { + # Admin + ADMIN + + # Editor + EDITOR + + # Viewer + VIEWER +} + +# An enumeration. +enum RegistryPackageMaintainerRoleChoices { + # Admin + ADMIN + + # Editor + EDITOR + + # Viewer + VIEWER +} + +input RemoveNamespaceCollaboratorInput { + clientMutationId: String + namespaceCollaboratorId: ID! +} + +input RemoveNamespaceCollaboratorInviteInput { + clientMutationId: String + inviteId: ID! +} + +type RemoveNamespaceCollaboratorInvitePayload { + clientMutationId: String + namespace: Namespace! +} + +type RemoveNamespaceCollaboratorPayload { + clientMutationId: String + namespace: Namespace! +} + +input RemovePackageCollaboratorInput { + clientMutationId: String + packageCollaboratorId: ID! +} + +input RemovePackageCollaboratorInviteInput { + clientMutationId: String + inviteId: ID! +} + +type RemovePackageCollaboratorInvitePayload { + clientMutationId: String + package: Package! +} + +type RemovePackageCollaboratorPayload { + clientMutationId: String + package: Package! +} + +input RemovePackageTransferRequestInput { + clientMutationId: String + packageTransferRequestId: ID! +} + +type RemovePackageTransferRequestPayload { + clientMutationId: String + package: Package! +} + +input RequestPackageTransferInput { + clientMutationId: String + newOwnerId: ID! + packageId: ID! +} + +type RequestPackageTransferPayload { + clientMutationId: String + package: Package! +} + +input RequestPasswordResetInput { + clientMutationId: String + email: String! +} + +type RequestPasswordResetPayload { + clientMutationId: String + email: String! + errors: [ErrorType] +} + +input RequestValidationEmailInput { + clientMutationId: String + + # The user id + userId: ID +} + +type RequestValidationEmailPayload { + clientMutationId: String + success: Boolean! + user: User +} + +input RevokeAPITokenInput { + clientMutationId: String + + # The API token ID + tokenId: ID! +} + +type RevokeAPITokenPayload { + clientMutationId: String + success: Boolean + token: APIToken +} + +enum Role { + ADMIN + EDITOR + VIEWER +} + +type SearchConnection { + # Contains the nodes in this connection. + edges: [SearchEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `Search` and its cursor. +type SearchEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: SearchResult +} + +enum SearchKind { + NAMESPACE + PACKAGE + USER +} + +enum SearchOrderBy { + ALPHABETICALLY + PUBLISHED_DATE + SIZE + TOTAL_DOWNLOADS +} + +enum SearchOrderSort { + ASC + DESC +} + +enum SearchPublishDate { + LAST_DAY + LAST_MONTH + LAST_WEEK + LAST_YEAR +} + +union SearchResult = Namespace | PackageVersion | User + +input SeePendingNotificationsInput { + clientMutationId: String +} + +type SeePendingNotificationsPayload { + clientMutationId: String + success: Boolean +} + +type Signature { + createdAt: DateTime! + data: String! + id: ID! + publicKey: PublicKey! +} + +input SocialAuthJWTInput { + accessToken: String! + clientMutationId: String + provider: String! +} + +# Social Auth for JSON Web Token (JWT) +type SocialAuthJWTPayload { + clientMutationId: String + social: SocialNode + token: String +} + +scalar SocialCamelJSON + +type SocialNode implements Node { + created: DateTime! + extraData: SocialCamelJSON + + # The ID of the object + id: ID! + modified: DateTime! + provider: String! + uid: String! + user: User! +} + +type Subscription { + packageVersionCreated(ownerId: ID = null, publishedBy: ID = null): PackageVersion! + userNotificationCreated(userId: ID!): UserNotificationCreated! +} + +input UnlikePackageInput { + clientMutationId: String + packageId: ID! +} + +type UnlikePackagePayload { + clientMutationId: String + package: Package! +} + +input UnwatchPackageInput { + clientMutationId: String + packageId: ID! +} + +type UnwatchPackagePayload { + clientMutationId: String + package: Package! +} + +input UpdateNamespaceCollaboratorRoleInput { + clientMutationId: String + namespaceCollaboratorId: ID! + role: Role! +} + +type UpdateNamespaceCollaboratorRolePayload { + clientMutationId: String + collaborator: NamespaceCollaborator! +} + +input UpdateNamespaceInput { + # The namespace avatar + avatar: String + clientMutationId: String + + # The namespace description + description: String + + # The namespace display name + displayName: String + + # The namespace slug name + name: String + namespaceId: ID! +} + +type UpdateNamespacePayload { + clientMutationId: String + namespace: Namespace! +} + +input UpdatePackageCollaboratorRoleInput { + clientMutationId: String + packageCollaboratorId: ID! + role: Role! +} + +type UpdatePackageCollaboratorRolePayload { + clientMutationId: String + collaborator: PackageCollaborator! +} + +input UpdatePackageInput { + clientMutationId: String + + # The package icon + icon: String + packageId: ID! +} + +type UpdatePackagePayload { + clientMutationId: String + package: Package! +} + +input UpdateUserInfoInput { + # The user avatar + avatar: String + + # The user bio + bio: String + clientMutationId: String + + # The user full name + fullName: String + + # The user Github (it can be the url, or the handle with or without the @) + github: String + + # The user location + location: String + + # The user Twitter (it can be the url, or the handle with or without the @) + twitter: String + + # The user id + userId: ID + + # The user website (it must be a valid url) + websiteUrl: String +} + +type UpdateUserInfoPayload { + clientMutationId: String + user: User +} + +type User implements Node & PackageOwner { + apiTokens(after: String = null, before: String = null, first: Int = null, last: Int = null): APITokenConnection + avatar(size: Int = 80): String! + bio: String + dateJoined: DateTime! + email: String! + firstName: String! + fullName: String! + githubUrl: String + globalName: String! + + # The ID of the object + id: ID! + isEmailValidated: Boolean! + isViewer: Boolean! + lastName: String! + location: String + namespaceInvitesIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorInviteConnection + namespaces(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceConnection + notifications(after: String = null, before: String = null, first: Int = null, last: Int = null): UserNotificationConnection + packageInvitesIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorInviteConnection + packageTransfersIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageTransferRequestConnection + packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageVersionConnection + packages(after: String = null, before: String = null, collaborating: Boolean = null, first: Int = null, last: Int = null): PackageConnection + publicActivity(after: String = null, before: String = null, first: Int = null, last: Int = null): ActivityEventConnection! + twitterUrl: String + + # Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. + username: String! + websiteUrl: String +} + +type UserConnection { + # Contains the nodes in this connection. + edges: [UserEdge]! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +# A Relay edge containing a `User` and its cursor. +type UserEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: User +} + +type UserNotification implements Node { + body: UserNotificationBody! + createdAt: DateTime! + icon: String + + # The ID of the object + id: ID! + kind: UserNotificationKind + seenState: UserNotificationSeenState! +} + +type UserNotificationBody { + ranges: [NodeBodyRange]! + text: String! +} + +type UserNotificationConnection { + # Contains the nodes in this connection. + edges: [UserNotificationEdge]! + hasPendingNotifications: Boolean! + + # Pagination data for this connection. + pageInfo: PageInfo! +} + +type UserNotificationCreated { + notification: UserNotification + notificationDeletedId: ID +} + +# A Relay edge containing a `UserNotification` and its cursor. +type UserNotificationEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: UserNotification +} + +union UserNotificationKind = UserNotificationKindIncomingPackageInvite | UserNotificationKindIncomingPackageTransfer | UserNotificationKindPublishedPackageVersion + +type UserNotificationKindIncomingPackageInvite { + packageInvite: PackageCollaboratorInvite! +} + +type UserNotificationKindIncomingPackageTransfer { + packageTransferRequest: PackageTransferRequest! +} + +type UserNotificationKindPublishedPackageVersion { + packageVersion: PackageVersion! +} + +enum UserNotificationSeenState { + SEEN + SEEN_AND_READ + UNSEEN +} + +input ValidateUserEmailInput { + challenge: String! + clientMutationId: String + + # The user id + userId: ID +} + +type ValidateUserEmailPayload { + clientMutationId: String + user: User +} + +input ValidateUserPasswordInput { + clientMutationId: String + password: String! +} + +type ValidateUserPasswordPayload { + clientMutationId: String + success: Boolean +} + +input VerifyInput { + clientMutationId: String + token: String +} + +type VerifyPayload { + clientMutationId: String + payload: GenericScalar! +} + +input WatchPackageInput { + clientMutationId: String + packageId: ID! +} + +type WatchPackagePayload { + clientMutationId: String + package: Package! +} diff --git a/lib/registry/src/commands/install.rs b/lib/registry/src/commands/install.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/registry/src/commands/search.rs b/lib/registry/src/commands/search.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs new file mode 100644 index 00000000000..b3da1b15c0f --- /dev/null +++ b/lib/registry/src/lib.rs @@ -0,0 +1,53 @@ +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub struct PackageDownloadInfo { + pub package: String, + pub command: String, + pub url: String, +} + +pub fn get_command_local(name: &str) -> Result { + Err(format!("unimplemented")) +} + +pub fn get_package_local(name: &str, version: Option<&str>) -> Result { + Err(format!("unimplemented")) +} + +pub fn query_command_from_registry(name: &str) -> Result { + Err(format!("unimplemented")) +} + +pub fn query_package_from_registry( + name: &str, + version: Option<&str>, +) -> Result { + Err(format!("unimplemented")) +} + +/// Returs the path to the directory where all packages on this computer are being stored +pub fn get_global_install_dir(registry_host: &str) -> Option { + Some( + dirs::home_dir()? + .join(".wasmer") + .join("checkouts") + .join(registry_host), + ) +} + +pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result { + Err(format!("unimplemented")) +} + +pub fn install_package(name: &str, version: Option<&str>) -> Result { + Err(format!("unimplemented")) +} + +pub fn test_if_registry_present(url: &str) -> Result { + Ok(false) +} + +pub fn get_all_available_registries() -> Result, String> { + Ok(Vec::new()) +} From d74419dfb667c552f7b2d8067a62d0791d1dd634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 12:55:03 +0200 Subject: [PATCH 03/83] Don't panic when trying to run wasmer run [package] --- lib/cli/src/cli.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 567d2631cc2..8625cf91e53 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -36,10 +36,12 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { return Run::from_binfmt_args().execute(); } - match ( - args.get(1).map(|s| s.as_str()), - args.get(2).map(|s| s.as_str()), - ) { + let firstarg = args.get(1).map(|s| s.as_str()); + let secondarg = args.get(2).map(|s| s.as_str()); + + println!("{:?}", (firstarg, secondarg)); + + match (firstarg, secondarg) { (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(), (Some("-vV"), _) @@ -61,7 +63,13 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { #[cfg(feature = "binfmt")] (Some("binfmt"), _) => Binfmt::try_parse_from(args.iter())?.execute(), (Some("run"), Some(package)) | (Some(package), _) => { - if let Ok(run) = Run::try_parse() { + // Disable printing backtraces in case `Run::try_parse_from` panics + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(|_| {})); + let result = std::panic::catch_unwind(|| Run::try_parse_from(args.iter())); + std::panic::set_hook(hook); + + if let Ok(Ok(run)) = result { return run.execute(); } else if let Ok((package, version)) = split_version(package) { if let Ok(o) = wasmer_registry::get_package_local( @@ -71,6 +79,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { // Try finding the local package let mut args_without_package = args.clone(); args_without_package.remove(1); + println!("args without package: {:#?}", args_without_package); return RunWithoutFile::try_parse_from(args_without_package.iter())? .into_run_args(o) .execute(); @@ -98,11 +107,13 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { let package_version = s.split("@").collect::>(); - match package_version.as_slice() { + let r = match package_version.as_slice() { &[p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), &[p] => Ok((p.trim().to_string(), None)), _ => Err(anyhow!("Invalid package / version: {s:?}")), - } + }; + println!("{:?}", r); + r } fn print_help() -> Result<(), anyhow::Error> { From ba8b13c3e8d654f261ff7316e6bb02e6bb92ca15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 13:17:40 +0200 Subject: [PATCH 04/83] Add spinner when installing package --- Cargo.lock | 45 +++++++++++++++++++++++++++++++++++++---- lib/cli/Cargo.toml | 1 + lib/cli/src/cli.rs | 44 ++++++++++++++++++++++------------------ lib/registry/src/lib.rs | 1 + 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d88ae0fc64..ae735875fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30275ad0ad84ec1c06dde3b3f7d23c6006b7d76d61a85e7060b426b747eff70d" + [[package]] name = "ansi_term" version = "0.12.1" @@ -353,7 +359,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term", + "ansi_term 0.12.1", "atty", "bitflags", "strsim 0.8.0", @@ -828,6 +834,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys", +] + [[package]] name = "dirs" version = "4.0.0" @@ -2405,6 +2421,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spinner" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3a7cd01625b7e43e62815677d692cb59b221c2fdc2853d1eb86a260ee0c272" +dependencies = [ + "ansi_term 0.7.5", + "term 0.6.1", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2524,6 +2550,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "term" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" +dependencies = [ + "dirs 2.0.2", + "winapi", +] + [[package]] name = "term" version = "0.7.0" @@ -2579,7 +2615,7 @@ dependencies = [ "getopts", "libc", "num_cpus", - "term", + "term 0.7.0", ] [[package]] @@ -3118,12 +3154,13 @@ dependencies = [ "cfg-if 1.0.0", "clap 4.0.5", "colored 2.0.0", - "dirs", + "dirs 4.0.0", "distance", "fern", "http_req", "log", "serde_json", + "spinner", "target-lexicon 0.12.4", "tempfile", "unix_mode", @@ -3340,7 +3377,7 @@ dependencies = [ name = "wasmer-registry" version = "3.0.0-beta.2" dependencies = [ - "dirs", + "dirs 4.0.0", ] [[package]] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index c6b3bd96c36..7795d97c28e 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -43,6 +43,7 @@ wasmer-vfs = { version = "=3.0.0-beta.2", path = "../vfs", default-features = f atty = "0.2" colored = "2.0" anyhow = "1.0" +spinner = "0.5.0" clap = { version = "4.0.5", features = ["derive"] } # For the function names autosuggestion distance = "0.4" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 8625cf91e53..19946bdc42e 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -39,8 +39,6 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let firstarg = args.get(1).map(|s| s.as_str()); let secondarg = args.get(2).map(|s| s.as_str()); - println!("{:?}", (firstarg, secondarg)); - match (firstarg, secondarg) { (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(), @@ -79,24 +77,32 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { // Try finding the local package let mut args_without_package = args.clone(); args_without_package.remove(1); - println!("args without package: {:#?}", args_without_package); - return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(o) - .execute(); - } else if let Ok(o) = - wasmer_registry::install_package(&package, version.as_ref().map(|s| s.as_str())) - { - // Try auto-installing the remote package - let mut args_without_package = args.clone(); - args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? .into_run_args(o) .execute(); } else { - // Failed to find the remote package - // - // TODO: print list of similar packages - return print_help(); + let sp = + spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", + "⢀", "⠠", "⠐", "⠈", + ]) + .start(); + + let v = version.as_ref().map(|s| s.as_str()); + let result = wasmer_registry::install_package(&package, v); + sp.close(); + print!("\r\n"); + if let Ok(o) = result { + // Try auto-installing the remote package + let mut args_without_package = args.clone(); + args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(o) + .execute(); + } else { + return print_help(); + } } } else { return print_help(); @@ -107,13 +113,11 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { let package_version = s.split("@").collect::>(); - let r = match package_version.as_slice() { + match package_version.as_slice() { &[p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), &[p] => Ok((p.trim().to_string(), None)), _ => Err(anyhow!("Invalid package / version: {s:?}")), - }; - println!("{:?}", r); - r + } } fn print_help() -> Result<(), anyhow::Error> { diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index b3da1b15c0f..216b8d5bb21 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -41,6 +41,7 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result) -> Result { + std::thread::sleep(std::time::Duration::from_secs(4)); Err(format!("unimplemented")) } From 3b15e75d663308a42acb5684fb5447446f82c109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 13:22:14 +0200 Subject: [PATCH 05/83] Add GraphQL querying --- Cargo.lock | 102 ++++++++++++++++++++- lib/registry/Cargo.toml | 4 +- lib/registry/graphql/queries/login.graphql | 5 - lib/registry/src/lib.rs | 29 ++++++ 4 files changed, 130 insertions(+), 10 deletions(-) delete mode 100644 lib/registry/graphql/queries/login.graphql diff --git a/Cargo.lock b/Cargo.lock index ae735875fa1..f5a82eaa6bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + [[package]] name = "assert_cmd" version = "1.0.8" @@ -475,6 +481,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + [[package]] name = "compiler-test-derive" version = "0.0.1" @@ -1145,6 +1164,64 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "graphql-introspection-query" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2a4732cf5140bd6c082434494f785a19cfb566ab07d1382c3671f5812fed6d" +dependencies = [ + "serde", +] + +[[package]] +name = "graphql-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" +dependencies = [ + "combine", + "thiserror", +] + +[[package]] +name = "graphql_client" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc16d75d169fddb720d8f1c7aed6413e329e1584079b9734ff07266a193f5bc" +dependencies = [ + "graphql_query_derive", + "serde", + "serde_json", +] + +[[package]] +name = "graphql_client_codegen" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f290ecfa3bea3e8a157899dc8a1d96ee7dd6405c18c8ddd213fc58939d18a0e9" +dependencies = [ + "graphql-introspection-query", + "graphql-parser", + "heck", + "lazy_static", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "graphql_query_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a755cc59cda2641ea3037b4f9f7ef40471c329f55c1fa2db6fa0bb7ae6c1f7ce" +dependencies = [ + "graphql_client_codegen", + "proc-macro2", + "syn", +] + [[package]] name = "gumdrop" version = "0.8.1" @@ -2294,9 +2371,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] @@ -2322,9 +2399,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -2856,6 +2933,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35abed4630bb800f02451a7428205d1f37b8e125001471bfab259beee6a587ed" +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -2880,6 +2966,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -3378,6 +3470,8 @@ name = "wasmer-registry" version = "3.0.0-beta.2" dependencies = [ "dirs 4.0.0", + "graphql_client", + "serde", ] [[package]] diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 7c027d5f3ec..bb501af813a 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -4,4 +4,6 @@ version = "3.0.0-beta.2" edition = "2021" [dependencies] -dirs = "4.0.0" \ No newline at end of file +dirs = "4.0.0" +graphql_client = "0.11.0" +serde = "1.0.145" \ No newline at end of file diff --git a/lib/registry/graphql/queries/login.graphql b/lib/registry/graphql/queries/login.graphql deleted file mode 100644 index 438ebb44272..00000000000 --- a/lib/registry/graphql/queries/login.graphql +++ /dev/null @@ -1,5 +0,0 @@ -mutation LoginMutation($username: String!, $password: String!) { - tokenAuth(input: {username: $username, password: $password}) { - refreshToken - } -} diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 216b8d5bb21..d73421339e1 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,5 +1,34 @@ use std::path::{Path, PathBuf}; +pub mod queries { + + use graphql_client::*; + + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/get_package_version.graphql", + response_derives = "Debug" + )] + pub(crate) struct GetPackageVersionQuery; + + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/get_package.graphql", + response_derives = "Debug" + )] + pub(crate) struct GetPackageQuery; + + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/test_if_registry_present.graphql", + response_derives = "Debug" + )] + pub(crate) struct TestIfRegistryPresent; +} + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] pub struct PackageDownloadInfo { pub package: String, From 83c50d349d4681d175dc5b651a9e174314e2c0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 13:56:50 +0200 Subject: [PATCH 06/83] Add code for querying GraphQL --- Cargo.lock | 537 +++++++++++++++++++++++++++++++++++++++- lib/registry/Cargo.toml | 8 +- lib/registry/src/lib.rs | 152 +++++++++++- 3 files changed, 690 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5a82eaa6bd..ed74bd15a93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,6 +549,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -960,6 +970,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "enum-iterator" version = "0.7.0" @@ -1085,6 +1104,78 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -1242,6 +1333,25 @@ dependencies = [ "syn", ] +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" @@ -1287,6 +1397,28 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.3", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "http_req" version = "0.8.1" @@ -1299,12 +1431,61 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.3", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.48" @@ -1325,6 +1506,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -1409,6 +1600,12 @@ dependencies = [ "ghost", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itertools" version = "0.10.4" @@ -1577,6 +1774,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minifb" version = "0.19.3" @@ -1611,6 +1824,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.36.1", +] + [[package]] name = "miow" version = "0.3.7" @@ -1626,6 +1851,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.20.2" @@ -1727,6 +1970,51 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "orbclient" version = "0.3.32" @@ -1794,6 +2082,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pest" version = "2.3.1" @@ -1810,6 +2104,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.25" @@ -2151,6 +2451,44 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -2277,6 +2615,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys 0.36.1", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -2330,6 +2678,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -2419,6 +2790,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.3", + "ryu", + "serde", +] + [[package]] name = "serial_test" version = "0.5.1" @@ -2492,6 +2875,16 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.5.2" @@ -2712,18 +3105,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -2787,6 +3180,62 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.9" @@ -2796,6 +3245,12 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.36" @@ -2855,6 +3310,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "trybuild" version = "1.0.64" @@ -2915,12 +3376,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.10" @@ -2948,6 +3424,23 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -2992,6 +3485,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3469,9 +3972,15 @@ dependencies = [ name = "wasmer-registry" version = "3.0.0-beta.2" dependencies = [ + "anyhow", "dirs 4.0.0", "graphql_client", + "reqwest", "serde", + "serde_json", + "thiserror", + "url", + "whoami", ] [[package]] @@ -3822,6 +4331,17 @@ dependencies = [ "libc", ] +[[package]] +name = "whoami" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +dependencies = [ + "bumpalo", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3939,6 +4459,15 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "x11-dl" version = "2.20.0" diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index bb501af813a..362dce3e6e2 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -6,4 +6,10 @@ edition = "2021" [dependencies] dirs = "4.0.0" graphql_client = "0.11.0" -serde = "1.0.145" \ No newline at end of file +serde = "1.0.145" +anyhow = "1.0.65" +reqwest = { version = "0.11.12", features = ["blocking", "multipart", "json"] } +whoami = "1.2.3" +serde_json = "1.0.85" +url = "2.3.1" +thiserror = "1.0.37" \ No newline at end of file diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index d73421339e1..292ed56027d 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,8 +1,80 @@ use std::path::{Path, PathBuf}; -pub mod queries { - +pub mod graphql { + use graphql_client::*; + #[cfg(not(target_os = "wasi"))] + use reqwest::{ + blocking::{multipart::Form, Client}, + header::USER_AGENT, + }; + use std::env; + #[cfg(target_os = "wasi")] + use {wasm_bus_reqwest::prelude::header::*, wasm_bus_reqwest::prelude::*}; + + mod proxy { + //! Code for dealing with setting things up to proxy network requests + use thiserror::Error; + + #[derive(Debug, Error)] + pub enum ProxyError { + #[error("Failed to parse URL from {}: {}", url_location, error_message)] + UrlParseError { + url_location: String, + error_message: String, + }, + + #[error("Could not connect to proxy: {0}")] + ConnectionError(String), + } + + /// Tries to set up a proxy + /// + /// This function reads from wapm config's `proxy.url` first, then checks + /// `ALL_PROXY`, `HTTPS_PROXY`, and `HTTP_PROXY` environment variables, in both + /// upper case and lower case, in that order. + /// + /// If a proxy is specified in wapm config's `proxy.url`, it is assumed + /// to be a general proxy + /// + /// A return value of `Ok(None)` means that there was no attempt to set up a 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. + pub fn maybe_set_up_proxy() -> anyhow::Result> { + use std::env; + let proxy = if let Ok(proxy_url) = env::var("ALL_PROXY").or(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(env::var("https_proxy")) + { + reqwest::Proxy::https(&https_proxy_url) + .map(|proxy| (https_proxy_url, proxy, "HTTPS_PROXY")) + } else if let Ok(http_proxy_url) = env::var("HTTP_PROXY").or(env::var("http_proxy")) { + reqwest::Proxy::http(&http_proxy_url) + .map(|proxy| (http_proxy_url, proxy, "http_proxy")) + } else { + return Ok(None); + } + .map_err(|e| ProxyError::ConnectionError(e.to_string())) + .and_then( + |(proxy_url_str, proxy, url_location): (String, _, &'static str)| { + url::Url::parse(&proxy_url_str) + .map_err(|e| ProxyError::UrlParseError { + url_location: url_location.to_string(), + error_message: e.to_string(), + }) + .map(|url| { + if !(url.username().is_empty()) && url.password().is_some() { + proxy.basic_auth(url.username(), url.password().unwrap_or_default()) + } else { + proxy + } + }) + }, + )?; + + Ok(Some(proxy)) + } + } #[derive(GraphQLQuery)] #[graphql( @@ -27,6 +99,82 @@ pub mod queries { response_derives = "Debug" )] pub(crate) struct TestIfRegistryPresent; + + #[cfg(target_os = "wasi")] + pub fn whoami_distro() -> String { + whoami::os().to_lowercase() + } + + #[cfg(not(target_os = "wasi"))] + pub fn whoami_distro() -> String { + whoami::distro().to_lowercase() + } + + pub fn execute_query_modifier_inner( + registry_url: &str, + login_token: &str, + query: &QueryBody, + form_modifier: F, + ) -> anyhow::Result + where + for<'de> R: serde::Deserialize<'de>, + V: serde::Serialize, + F: FnOnce(Form) -> Form, + { + let client = { + let builder = Client::builder(); + + #[cfg(not(target_os = "wasi"))] + let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? { + builder.proxy(proxy) + } else { + builder + }; + builder.build()? + }; + + let vars = serde_json::to_string(&query.variables).unwrap(); + + let form = Form::new() + .text("query", query.query.to_string()) + .text("operationName", query.operation_name.to_string()) + .text("variables", vars); + + let form = form_modifier(form); + + let user_agent = format!( + "wapm/{} {} {}", + env!("CARGO_PKG_VERSION"), + whoami::platform(), + whoami_distro(), + ); + + let res = client + .post(registry_url) + .multipart(form) + .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) + .header(USER_AGENT, user_agent) + .send()?; + + let response_body: Response = res.json()?; + if let Some(errors) = response_body.errors { + let error_messages: Vec = errors.into_iter().map(|err| err.message).collect(); + return Err(anyhow::anyhow!("{}", error_messages.join(", ")).into()); + } + Ok(response_body.data.expect("missing response data")) + } + + pub fn execute_query( + registry_url: &str, + login_token: &str, + query: &QueryBody, + ) -> anyhow::Result + where + for<'de> R: serde::Deserialize<'de>, + V: serde::Serialize, + { + execute_query_modifier_inner(registry_url, login_token, query, |f| f) + } } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] From af01356eb14febe99595f214723eecb541111a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 15:10:31 +0200 Subject: [PATCH 07/83] Implement wasmer -vV --- Cargo.lock | 27 +++++++++++++++++++++++---- lib/cli/Cargo.toml | 3 +++ lib/cli/build.rs | 21 +++++++++++++++++++++ lib/cli/src/cli.rs | 28 +++++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed74bd15a93..2e4a91eebc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,6 +344,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", + "time 0.1.44", "wasm-bindgen", "winapi", ] @@ -1223,7 +1224,7 @@ dependencies = [ "cfg-if 1.0.0", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1832,7 +1833,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.36.1", ] @@ -3132,6 +3133,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.2.27" @@ -3495,6 +3507,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3747,6 +3765,7 @@ dependencies = [ "atty", "bytesize", "cfg-if 1.0.0", + "chrono", "clap 4.0.5", "colored 2.0.0", "dirs 4.0.0", @@ -3904,7 +3923,7 @@ dependencies = [ "lazy_static", "libc", "log", - "time", + "time 0.2.27", "wasmer", "wasmer-types", ] @@ -4108,7 +4127,7 @@ version = "3.0.0-beta.2" dependencies = [ "byteorder", "serde", - "time", + "time 0.2.27", "wasmer-derive", "wasmer-types", ] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 7795d97c28e..6947510eb48 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -59,6 +59,9 @@ dirs = { version = "4.0", optional = true } serde_json = { version = "1.0", optional = true } target-lexicon = { version = "0.12", features = ["std"] } +[build-dependencies] +chrono = "0.4.22" + [target.'cfg(target_os = "linux")'.dependencies] unix_mode = "0.1.3" diff --git a/lib/cli/build.rs b/lib/cli/build.rs index 36fe64763ed..8d58b47ac09 100644 --- a/lib/cli/build.rs +++ b/lib/cli/build.rs @@ -1,4 +1,25 @@ +use std::process::Command; +use chrono::prelude::*; + pub fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX"); + + // Set WASMER_GIT_HASH + let git_hash = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .output().ok() + .and_then(|output| { + String::from_utf8(output.stdout).ok() + }).unwrap_or_default(); + println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH={}", git_hash); + + if git_hash.len() > 5 { + println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT={}", &git_hash[..5]); + } + + let utc: DateTime = Utc::now(); + let date = utc.format("%Y-%m-%d").to_string(); + println!("cargo:rustc-env=WASMER_BUILD_DATE={}", date); + } diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 19946bdc42e..ad9f3629729 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -44,7 +44,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { (Some("-vV"), _) | (Some("version"), Some("--verbose")) - | (Some("--version"), Some("--verbose")) => return print_version(false), + | (Some("--version"), Some("--verbose")) => return print_version(true), (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { return print_version(false) @@ -125,7 +125,29 @@ fn print_help() -> Result<(), anyhow::Error> { Ok(()) } -fn print_version(_: bool) -> Result<(), anyhow::Error> { - println!("version"); +fn print_version(verbose: bool) -> Result<(), anyhow::Error> { + if !verbose { + println!("{}", env!("CARGO_PKG_VERSION")); + } else { + println!("wasmer {} ({} {})", env!("CARGO_PKG_VERSION"), env!("WASMER_BUILD_GIT_HASH_SHORT"), env!("WASMER_BUILD_DATE")); + println!("binary: {}", env!("CARGO_PKG_NAME")); + println!("commit-hash: {}", env!("WASMER_BUILD_GIT_HASH")); + println!("commit-date: {}", env!("WASMER_BUILD_DATE")); + println!("host: {}", target_lexicon::HOST); + println!("compiler: {}", { + + #[allow(unused_mut)] + let mut s = Vec::<&'static str>::new(); + + #[cfg(feature = "singlepass")] + s.push("singlepass"); + #[cfg(feature = "cranelift")] + s.push("cranelift"); + #[cfg(feature = "llvm")] + s.push("llvm"); + + s.join(",") + }); + } Ok(()) } From 6c57e1e30f7eff66eff622f4956c6e303e1d0701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 4 Oct 2022 15:32:10 +0200 Subject: [PATCH 08/83] Finished adding wasmer -vV and --help messages --- lib/cli/build.rs | 20 ++--- lib/cli/src/cli.rs | 183 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 178 insertions(+), 25 deletions(-) diff --git a/lib/cli/build.rs b/lib/cli/build.rs index 8d58b47ac09..ae2f7924d73 100644 --- a/lib/cli/build.rs +++ b/lib/cli/build.rs @@ -1,5 +1,5 @@ -use std::process::Command; use chrono::prelude::*; +use std::process::Command; pub fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -7,19 +7,21 @@ pub fn main() { // Set WASMER_GIT_HASH let git_hash = Command::new("git") - .args(&["rev-parse", "HEAD"]) - .output().ok() - .and_then(|output| { - String::from_utf8(output.stdout).ok() - }).unwrap_or_default(); + .args(&["rev-parse", "HEAD"]) + .output() + .ok() + .and_then(|output| String::from_utf8(output.stdout).ok()) + .unwrap_or_default(); println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH={}", git_hash); if git_hash.len() > 5 { - println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT={}", &git_hash[..5]); + println!( + "cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT={}", + &git_hash[..5] + ); } - let utc: DateTime = Utc::now(); + let utc: DateTime = Utc::now(); let date = utc.format("%Y-%m-%d").to_string(); println!("cargo:rustc-env=WASMER_BUILD_DATE={}", date); - } diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index ad9f3629729..8487a5e4c0a 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -15,6 +15,135 @@ use crate::error::PrettyError; use anyhow::Result; use clap::Parser; +#[derive(Parser)] +#[cfg_attr( + not(feature = "headless"), + clap( + name = "wasmer", + about = "WebAssembly standalone runtime.", + version, + author + ) +)] +#[cfg_attr( + feature = "headless", + clap( + name = "wasmer-headless", + about = "WebAssembly standalone runtime (headless).", + version, + author + ) +)] +/// The options for the wasmer Command Line Interface +enum WasmerCLIOptions { + /// Run a WebAssembly file. Formats accepted: wasm, wat + #[clap(name = "run")] + Run(Run), + + /// Wasmer cache + #[clap(subcommand, name = "cache")] + Cache(Cache), + + /// Validate a WebAssembly binary + #[clap(name = "validate")] + Validate(Validate), + + /// Compile a WebAssembly binary + #[cfg(feature = "compiler")] + #[clap(name = "compile")] + Compile(Compile), + + /// Compile a WebAssembly binary into a native executable + /// + /// To use, you need to set the `WASMER_DIR` environment variable + /// to the location of your Wasmer installation. This will probably be `~/.wasmer`. It + /// should include a `lib`, `include` and `bin` subdirectories. To create an executable + /// you will need `libwasmer`, so by setting `WASMER_DIR` the CLI knows where to look for + /// header files and libraries. + /// + /// Example usage: + /// + /// ```text + /// $ # in two lines: + /// $ export WASMER_DIR=/home/user/.wasmer/ + /// $ wasmer create-exe qjs.wasm -o qjs.exe # or in one line: + /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm -o qjs.exe + /// $ file qjs.exe + /// qjs.exe: ELF 64-bit LSB pie executable, x86-64 ... + /// ``` + /// + /// ## Cross-compilation + /// + /// Accepted target triple values must follow the + /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. + /// + /// The recommended targets we try to support are: + /// + /// - "x86_64-linux-gnu" + /// - "aarch64-linux-gnu" + /// - "x86_64-apple-darwin" + /// - "arm64-apple-darwin" + #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))] + #[clap(name = "create-exe", verbatim_doc_comment)] + CreateExe(CreateExe), + + /// Compile a WebAssembly binary into an object file + /// + /// To use, you need to set the `WASMER_DIR` environment variable to the location of your + /// Wasmer installation. This will probably be `~/.wasmer`. It should include a `lib`, + /// `include` and `bin` subdirectories. To create an object you will need `libwasmer`, so by + /// setting `WASMER_DIR` the CLI knows where to look for header files and libraries. + /// + /// Example usage: + /// + /// ```text + /// $ # in two lines: + /// $ export WASMER_DIR=/home/user/.wasmer/ + /// $ wasmer create-obj qjs.wasm --object-format symbols -o qjs.obj # or in one line: + /// $ WASMER_DIR=/home/user/.wasmer/ wasmer create-exe qjs.wasm --object-format symbols -o qjs.obj + /// $ file qjs.obj + /// qjs.obj: ELF 64-bit LSB relocatable, x86-64 ... + /// ``` + /// + /// ## Cross-compilation + /// + /// Accepted target triple values must follow the + /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format. + /// + /// The recommended targets we try to support are: + /// + /// - "x86_64-linux-gnu" + /// - "aarch64-linux-gnu" + /// - "x86_64-apple-darwin" + /// - "arm64-apple-darwin" + #[cfg(feature = "static-artifact-create")] + #[structopt(name = "create-obj", verbatim_doc_comment)] + CreateObj(CreateObj), + + /// Get various configuration information needed + /// to compile programs which use Wasmer + #[clap(name = "config")] + Config(Config), + + /// Update wasmer to the latest version + #[clap(name = "self-update")] + SelfUpdate(SelfUpdate), + + /// Inspect a WebAssembly file + #[clap(name = "inspect")] + Inspect(Inspect), + + /// Run spec testsuite + #[cfg(feature = "wast")] + #[clap(name = "wast")] + Wast(Wast), + + /// Unregister and/or register wasmer as binfmt interpreter + #[cfg(target_os = "linux")] + #[clap(name = "binfmt")] + Binfmt(Binfmt), +} + /// The main function for the Wasmer CLI tool. pub fn wasmer_main() { // We allow windows to print properly colors @@ -39,8 +168,12 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let firstarg = args.get(1).map(|s| s.as_str()); let secondarg = args.get(2).map(|s| s.as_str()); + let mut args_without_first_arg = args.clone(); + args_without_first_arg.remove(0); + match (firstarg, secondarg) { - (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(), + (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(true), + (Some("-h"), _) => return print_help(false), (Some("-vV"), _) | (Some("version"), Some("--verbose")) @@ -50,17 +183,25 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { return print_version(false) } - (Some("cache"), _) => Cache::try_parse_from(args.iter())?.execute(), - (Some("compile"), _) => Compile::try_parse_from(args.iter())?.execute(), - (Some("config"), _) => Config::try_parse_from(args.iter())?.execute(), - (Some("create-exe"), _) => CreateExe::try_parse_from(args.iter())?.execute(), - (Some("inspect"), _) => Inspect::try_parse_from(args.iter())?.execute(), - (Some("self-update"), _) => SelfUpdate::try_parse_from(args.iter())?.execute(), - (Some("validate"), _) => Validate::try_parse_from(args.iter())?.execute(), - (Some("wast"), _) => Wast::try_parse_from(args.iter())?.execute(), + (Some("cache"), _) => Cache::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("compile"), _) => Compile::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("config"), _) => Config::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("create-exe"), _) => { + CreateExe::try_parse_from(args_without_first_arg.iter())?.execute() + } + (Some("inspect"), _) => Inspect::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("self-update"), _) => { + SelfUpdate::try_parse_from(args_without_first_arg.iter())?.execute() + } + (Some("validate"), _) => Validate::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("wast"), _) => Wast::try_parse_from(args_without_first_arg.iter())?.execute(), #[cfg(feature = "binfmt")] - (Some("binfmt"), _) => Binfmt::try_parse_from(args.iter())?.execute(), + (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), (Some("run"), Some(package)) | (Some(package), _) => { + if package.starts_with("-") { + return Err(anyhow!("Unknown CLI argument {package:?}")); + } + // Disable printing backtraces in case `Run::try_parse_from` panics let hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| {})); @@ -101,11 +242,11 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { .into_run_args(o) .execute(); } else { - return print_help(); + return print_help(true); } } } else { - return print_help(); + return print_help(true); } } } @@ -120,8 +261,14 @@ fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { } } -fn print_help() -> Result<(), anyhow::Error> { - println!("help"); +fn print_help(verbose: bool) -> Result<(), anyhow::Error> { + use clap::CommandFactory; + let mut cmd = WasmerCLIOptions::command(); + if verbose { + let _ = cmd.print_long_help(); + } else { + let _ = cmd.print_help(); + } Ok(()) } @@ -129,13 +276,17 @@ fn print_version(verbose: bool) -> Result<(), anyhow::Error> { if !verbose { println!("{}", env!("CARGO_PKG_VERSION")); } else { - println!("wasmer {} ({} {})", env!("CARGO_PKG_VERSION"), env!("WASMER_BUILD_GIT_HASH_SHORT"), env!("WASMER_BUILD_DATE")); + println!( + "wasmer {} ({} {})", + env!("CARGO_PKG_VERSION"), + env!("WASMER_BUILD_GIT_HASH_SHORT"), + env!("WASMER_BUILD_DATE") + ); println!("binary: {}", env!("CARGO_PKG_NAME")); println!("commit-hash: {}", env!("WASMER_BUILD_GIT_HASH")); println!("commit-date: {}", env!("WASMER_BUILD_DATE")); println!("host: {}", target_lexicon::HOST); println!("compiler: {}", { - #[allow(unused_mut)] let mut s = Vec::<&'static str>::new(); From 4bf7c43ca3613707d753b97994411e5b501cf7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 5 Oct 2022 15:18:54 +0200 Subject: [PATCH 09/83] Query packages correctly from wapm registry and debug error msg --- Cargo.lock | 49 +++ lib/registry/Cargo.toml | 6 +- ...t_package.graphql => get_packages.graphql} | 6 +- lib/registry/src/lib.rs | 413 +++++++++++++++++- 4 files changed, 454 insertions(+), 20 deletions(-) rename lib/registry/graphql/queries/{get_package.graphql => get_packages.graphql} (52%) diff --git a/Cargo.lock b/Cargo.lock index 2e4a91eebc4..e3d483b0b0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1691,6 +1691,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "llvm-sys" version = "120.2.4" @@ -2725,6 +2731,9 @@ name = "semver" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2803,6 +2812,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "serial_test" version = "0.5.1" @@ -3507,6 +3528,23 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wapm-toml" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c0efbde13765eefd992112ea28cf8b8fa484a73652c2e8998e0e0e778aa9f0" +dependencies = [ + "anyhow", + "semver 1.0.14", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "serde_yaml", + "thiserror", + "toml", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -3998,7 +4036,9 @@ dependencies = [ "serde", "serde_json", "thiserror", + "toml", "url", + "wapm-toml", "whoami", ] @@ -4535,6 +4575,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 362dce3e6e2..6062e1c8328 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -6,10 +6,12 @@ edition = "2021" [dependencies] dirs = "4.0.0" graphql_client = "0.11.0" -serde = "1.0.145" +serde = { version = "1.0.145", features = ["derive"] } anyhow = "1.0.65" reqwest = { version = "0.11.12", features = ["blocking", "multipart", "json"] } whoami = "1.2.3" serde_json = "1.0.85" url = "2.3.1" -thiserror = "1.0.37" \ No newline at end of file +thiserror = "1.0.37" +toml = "0.5.9" +wapm-toml = "0.1.1" \ No newline at end of file diff --git a/lib/registry/graphql/queries/get_package.graphql b/lib/registry/graphql/queries/get_packages.graphql similarity index 52% rename from lib/registry/graphql/queries/get_package.graphql rename to lib/registry/graphql/queries/get_packages.graphql index 43a953d2682..b6d03e990bc 100644 --- a/lib/registry/graphql/queries/get_package.graphql +++ b/lib/registry/graphql/queries/get_packages.graphql @@ -1,8 +1,8 @@ -query GetPackageQuery ($name: String!) { - package: getPackage(name:$name) { +query GetPackagesQuery ($names: [String!]!) { + package: getPackages(names:$names) { name private - lastVersion { + versions { version distribution { downloadUrl diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 292ed56027d..eb79106d66c 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,4 +1,9 @@ use std::path::{Path, PathBuf}; +use std::env; +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; pub mod graphql { @@ -8,7 +13,9 @@ pub mod graphql { blocking::{multipart::Form, Client}, header::USER_AGENT, }; + use core::time; use std::env; + use std::time::Duration; #[cfg(target_os = "wasi")] use {wasm_bus_reqwest::prelude::header::*, wasm_bus_reqwest::prelude::*}; @@ -87,10 +94,10 @@ pub mod graphql { #[derive(GraphQLQuery)] #[graphql( schema_path = "graphql/schema.graphql", - query_path = "graphql/queries/get_package.graphql", + query_path = "graphql/queries/get_packages.graphql", response_derives = "Debug" )] - pub(crate) struct GetPackageQuery; + pub(crate) struct GetPackagesQuery; #[derive(GraphQLQuery)] #[graphql( @@ -110,10 +117,66 @@ pub mod graphql { whoami::distro().to_lowercase() } + pub fn execute_query_modifier_inner_check_json( + registry_url: &str, + login_token: &str, + query: &QueryBody, + timeout: Option, + form_modifier: F, + ) -> anyhow::Result<()> + where + V: serde::Serialize, + F: FnOnce(Form) -> Form, + { + let client = { + let builder = Client::builder(); + + #[cfg(not(target_os = "wasi"))] + let builder = if let Some(proxy) = proxy::maybe_set_up_proxy()? { + builder.proxy(proxy) + } else { + builder + }; + builder.build()? + }; + + let vars = serde_json::to_string(&query.variables).unwrap(); + + let form = Form::new() + .text("query", query.query.to_string()) + .text("operationName", query.operation_name.to_string()) + .text("variables", vars); + + let form = form_modifier(form); + + let user_agent = format!( + "wapm/{} {} {}", + env!("CARGO_PKG_VERSION"), + whoami::platform(), + whoami_distro(), + ); + + let mut res = client + .post(registry_url) + .multipart(form) + .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) + .header(USER_AGENT, user_agent); + + if let Some(t) = timeout { + res = res.timeout(t); + } + + let res = res.send()?; + + let _: Response = res.json()?; + return Ok(()); + } + pub fn execute_query_modifier_inner( registry_url: &str, login_token: &str, query: &QueryBody, + timeout: Option, form_modifier: F, ) -> anyhow::Result where @@ -149,13 +212,17 @@ pub mod graphql { whoami_distro(), ); - let res = client + let mut res = client .post(registry_url) .multipart(form) .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) - .header(USER_AGENT, user_agent) - .send()?; + .header(USER_AGENT, user_agent); + + if let Some(t) = timeout { + res = res.timeout(t); + } + let res = res.send()?; let response_body: Response = res.json()?; if let Some(errors) = response_body.errors { let error_messages: Vec = errors.into_iter().map(|err| err.message).collect(); @@ -173,14 +240,186 @@ pub mod graphql { for<'de> R: serde::Deserialize<'de>, V: serde::Serialize, { - execute_query_modifier_inner(registry_url, login_token, query, |f| f) + execute_query_modifier_inner(registry_url, login_token, query, None, |f| f) + } + + pub fn execute_query_with_timeout( + registry_url: &str, + login_token: &str, + timeout: Duration, + only_check_json_response: bool, + query: &QueryBody, + ) -> anyhow::Result + where + for<'de> R: serde::Deserialize<'de>, + V: serde::Serialize, + { + execute_query_modifier_inner( + registry_url, + login_token, + query, + Some(timeout), + |f| f + ) + } +} + +pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") { + "/.private/wapm.toml" +} else { + "wapm.toml" +}; + +#[derive(Deserialize, Default, Serialize, Debug, PartialEq)] +pub struct PartialWapmConfig { + /// The number of seconds to wait before checking the registry for a new + /// version of the package. + #[serde(default = "wax_default_cooldown")] + pub wax_cooldown: i32, + + /// The registry that wapm will connect to. + pub registry: Registries, + + /// Whether or not telemetry is enabled. + #[cfg(feature = "telemetry")] + #[serde(default)] + pub telemetry: Telemetry, + + /// Whether or not updated notifications are enabled. + #[cfg(feature = "update-notifications")] + #[serde(default)] + pub update_notifications: UpdateNotifications, + + /// The proxy to use when connecting to the Internet. + #[serde(default)] + pub proxy: Proxy, +} + +pub const fn wax_default_cooldown() -> i32 { + 5 * 60 +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Default)] +pub struct Proxy { + pub url: Option, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Default)] +pub struct UpdateNotifications { + pub enabled: String, +} + +#[cfg(feature = "telemetry")] +#[derive(Deserialize, Serialize, Debug, PartialEq)] +pub struct Telemetry { + pub enabled: String, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] +#[serde(untagged)] +pub enum Registries { + Single(Registry), + Multi(MultiRegistry), +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] +pub struct MultiRegistry { + /// Currently active registry + pub current: String, + /// Map from "RegistryUrl" to "LoginToken", in order to + /// be able to be able to easily switch between registries + pub tokens: BTreeMap, +} + +impl Default for Registries { + fn default() -> Self { + Registries::Single(Registry { + url: format_graphql("https://registry.wapm.io"), + token: None, + }) + } +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] +pub struct Registry { + pub url: String, + pub token: Option, +} + +fn format_graphql(registry: &str) -> String { + if registry.ends_with("/graphql") { + registry.to_string() + } else if registry.ends_with("/") { + format!("{}graphql", registry) + } else { + format!("{}/graphql", registry) + } +} + +impl PartialWapmConfig { + + pub fn from_file() -> Result { + let path = Self::get_file_location()?; + + match std::fs::read_to_string(&path) { + Ok(config_toml) => { + toml::from_str(&config_toml) + .map_err(|e| format!("could not parse {path:?}: {e}")) + } + Err(_e) => Ok(Self::default()), + } + } + + pub fn get_current_dir() -> std::io::Result { + #[cfg(target_os = "wasi")] + if let Some(pwd) = std::env::var("PWD").ok() { + return Ok(PathBuf::from(pwd)); + } + Ok(std::env::current_dir()?) + } + + pub fn get_folder() -> Result { + Ok( + if let Some(folder_str) = env::var("WASMER_DIR") + .ok() + .filter(|s| !s.is_empty()) + { + let folder = PathBuf::from(folder_str); + std::fs::create_dir_all(folder.clone()) + .map_err(|e| format!("cannot create config directory: {e}"))?; + folder + } else { + #[allow(unused_variables)] + let default_dir = Self::get_current_dir() + .ok() + .unwrap_or_else(|| PathBuf::from("/".to_string())); + #[cfg(feature = "dirs")] + let home_dir = + dirs::home_dir().ok_or(GlobalConfigError::CannotFindHomeDirectory)?; + #[cfg(not(feature = "dirs"))] + let home_dir = std::env::var("HOME") + .ok() + .unwrap_or_else(|| default_dir.to_string_lossy().to_string()); + let mut folder = PathBuf::from(home_dir); + folder.push(".wasmer"); + std::fs::create_dir_all(folder.clone()) + .map_err(|e| format!("cannot create config directory: {e}"))?; + folder + }, + ) + } + + fn get_file_location() -> Result { + Ok(Self::get_folder()?.join(GLOBAL_CONFIG_FILE_NAME)) } } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] pub struct PackageDownloadInfo { + pub registry: String, pub package: String, - pub command: String, + pub version: String, + pub commands: String, pub url: String, } @@ -196,18 +435,95 @@ pub fn query_command_from_registry(name: &str) -> Result, -) -> Result { - Err(format!("unimplemented")) +) -> Result, String)> { + + use crate::graphql::{ + GetPackagesQuery, get_packages_query, + execute_query + }; + use graphql_client::GraphQLQuery; + + let q = if name.contains("/") { + let name = name.split("/").nth(1).unwrap(); + GetPackagesQuery::build_query(get_packages_query::Variables { + names: vec![name.to_string()], + }) + } else { + GetPackagesQuery::build_query(get_packages_query::Variables { + names: vec![name.to_string()], + }) + }; + + let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q) + .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; + + let available_packages = response.package + .iter() + .filter_map(|p| { + + let p = p.as_ref()?; + let mut versions = Vec::new(); + + for v in p.versions.iter() { + for v in v.iter() { + let v = match v.as_ref() { + Some(s) => s, + None => continue, + }; + + versions.push(PackageDownloadInfo { + registry: registry_url.to_string(), + package: p.name.clone(), + + version: v.version.clone(), + + commands: toml::from_str::( + &v.manifest + ).ok()? + .command.unwrap_or_default() + .iter().map(|s| s.get_name()).collect(), + + url: v.distribution.download_url.clone(), + }); + } + } + + Some(versions) + }) + .collect::>() + .into_iter() + .flat_map(|v| v.into_iter()) + .collect::>(); + + let mut queried_package = available_packages.iter() + .filter_map(|v| { + if name.contains("/") && v.package != name { + return None; + } + + if version.is_some() && v.version != version.clone().unwrap() { + return None; + } + + Some(v) + }).next().cloned(); + + match queried_package { + None => Err((available_packages, format!("No package found for {name}@{}", version.unwrap_or("latest")))), + Some(s) => Ok(s), + } } /// Returs the path to the directory where all packages on this computer are being stored pub fn get_global_install_dir(registry_host: &str) -> Option { Some( - dirs::home_dir()? - .join(".wasmer") + PartialWapmConfig::get_folder().ok()? .join("checkouts") .join(registry_host), ) @@ -218,14 +534,81 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result) -> Result { - std::thread::sleep(std::time::Duration::from_secs(4)); + let registries = get_all_available_registries()?; + let mut url_of_package = None; + let mut error_packages = Vec::new(); + for r in registries.iter() { + let registry_test = test_if_registry_present(r); + if !registry_test.clone().unwrap_or(false) { + println!(" warning: registry {r} not present: {:#?}", registry_test); + continue; + } + match query_package_from_registry(&r, name, version) { + Ok(o) => { + url_of_package = Some((r, o)); + break; + }, + Err(e) => { + error_packages.push(e); + }, + } + } + + let version_str = match version { + None => format!("{name}"), + Some(v) => format!("{name}@{v}"), + }; + + let registries_searched = registries + .iter() + .filter_map(|s| url::Url::parse(s).ok()) + .filter_map(|s| Some(format!("{}", s.host_str()?))) + .collect::>(); + + let did_you_mean = error_packages.iter() + .flat_map(|(packages, _)| { + + }).collect::>(); + + // println!("error packages: {:#?}", error_packages); + + let url_of_package = url_of_package + .ok_or(format!("Package {version_str} not found in registries: {registries_searched:#?}"))?; + + println!("url of package: {:#?} in registries: {registries_searched:#?}", url_of_package.1); Err(format!("unimplemented")) } -pub fn test_if_registry_present(url: &str) -> Result { - Ok(false) +pub fn test_if_registry_present(registry: &str) -> Result { + + use graphql_client::GraphQLQuery; + use std::time::Duration; + use crate::graphql::{TestIfRegistryPresent, test_if_registry_present}; + + let q = TestIfRegistryPresent::build_query(test_if_registry_present::Variables {}); + let _ = crate::graphql::execute_query_modifier_inner_check_json( + registry, + "", + &q, + Some(Duration::from_secs(1)), + |f| f + ).map_err(|e| format!("{e}"))?; + + Ok(true) } pub fn get_all_available_registries() -> Result, String> { - Ok(Vec::new()) + let config = PartialWapmConfig::from_file()?; + let mut registries = Vec::new(); + match config.registry { + Registries::Single(s) => { + registries.push(format_graphql(&s.url)); + }, + Registries::Multi(m) => { + for key in m.tokens.keys() { + registries.push(format_graphql(&key)); + } + } + } + Ok(registries) } From 2c1cf2e7c559b6be7685474c7ae87c09c484c5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 5 Oct 2022 15:43:14 +0200 Subject: [PATCH 10/83] Implemented "did you mean" functionality --- lib/cli/src/cli.rs | 22 +++++++++++++--------- lib/registry/src/lib.rs | 13 +++++++------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 8487a5e4c0a..39923e4248f 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -234,15 +234,19 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let result = wasmer_registry::install_package(&package, v); sp.close(); print!("\r\n"); - if let Ok(o) = result { - // Try auto-installing the remote package - let mut args_without_package = args.clone(); - args_without_package.remove(1); - return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(o) - .execute(); - } else { - return print_help(true); + match result { + Ok(o) => { + // Try auto-installing the remote package + let mut args_without_package = args.clone(); + args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(o) + .execute(); + }, + Err(e) => { + println!("{e}"); + return Ok(()); + } } } } else { diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index eb79106d66c..c1997689223 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -540,7 +540,6 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result) -> Result>(); + packages.iter().filter_map(|f| { + let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); + Some(format!(" {}@{} (from {from})", f.package, f.version)) + }) + }).collect::>() + .join("\r\n"); - // println!("error packages: {:#?}", error_packages); - let url_of_package = url_of_package - .ok_or(format!("Package {version_str} not found in registries: {registries_searched:#?}"))?; + .ok_or(format!("Package {version_str} not found in registries: {registries_searched:?}.\r\n\r\nDid you mean:\r\n{did_you_mean}\r\n"))?; println!("url of package: {:#?} in registries: {registries_searched:#?}", url_of_package.1); Err(format!("unimplemented")) From 374494ef3c186e419b76316d115f8be06d64e257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 6 Oct 2022 11:56:07 +0200 Subject: [PATCH 11/83] Install packages correctly --- Cargo.lock | 53 +++++++++++ lib/cli/Cargo.toml | 1 + lib/cli/src/cli.rs | 86 ++++++++++++------ lib/registry/Cargo.toml | 4 +- lib/registry/src/lib.rs | 195 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 301 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3d483b0b0c..e4e5e2983dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -971,6 +971,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -1090,6 +1096,16 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.9.0" @@ -2199,6 +2215,20 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettytable-rs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f375cb74c23b51d23937ffdeb48b1fbf5b6409d4b9979c1418c1de58bc8f801" +dependencies = [ + "atty", + "csv", + "encode_unicode", + "lazy_static", + "term 0.7.0", + "unicode-width", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -3016,6 +3046,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.11.2" @@ -3811,6 +3852,7 @@ dependencies = [ "fern", "http_req", "log", + "prettytable-rs", "serde_json", "spinner", "target-lexicon 0.12.4", @@ -4031,10 +4073,12 @@ version = "3.0.0-beta.2" dependencies = [ "anyhow", "dirs 4.0.0", + "flate2", "graphql_client", "reqwest", "serde", "serde_json", + "tar", "thiserror", "toml", "url", @@ -4538,6 +4582,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "xcursor" version = "0.3.4" diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 6947510eb48..0f8ffc76a6d 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -58,6 +58,7 @@ http_req = { version="^0.8", default-features = false, features = ["rust-tls"], dirs = { version = "4.0", optional = true } serde_json = { version = "1.0", optional = true } target-lexicon = { version = "0.12", features = ["std"] } +prettytable-rs = "0.9.0" [build-dependencies] chrono = "0.4.22" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 39923e4248f..7ffc193e6a4 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -14,6 +14,7 @@ use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, V use crate::error::PrettyError; use anyhow::Result; use clap::Parser; +use wasmer_registry::get_all_local_packages; #[derive(Parser)] #[cfg_attr( @@ -197,6 +198,32 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { (Some("wast"), _) => Wast::try_parse_from(args_without_first_arg.iter())?.execute(), #[cfg(feature = "binfmt")] (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), + (Some("list"), Some("--installed")) => { + use prettytable::{format, Table, row}; + + let rows = get_all_local_packages() + .into_iter() + .map(|pkg| { + + let commands = pkg.manifest.command + .unwrap_or_default() + .iter() + .map(|c| c.get_name()) + .collect::>() + .join(" \r\n"); + + row![pkg.registry.clone(), pkg.name.clone(), pkg.version.clone(), commands] + }).collect::>(); + + let mut table = Table::init(rows); + table.set_titles(row!["Registry", "Package", "Version", "Commands"]); + table.add_empty_row(); + table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE); + table.set_format(*format::consts::FORMAT_NO_COLSEP); + let _ = table.printstd(); + println!(""); + Ok(()) + } (Some("run"), Some(package)) | (Some(package), _) => { if package.starts_with("-") { return Err(anyhow!("Unknown CLI argument {package:?}")); @@ -210,8 +237,8 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { if let Ok(Ok(run)) = result { return run.execute(); - } else if let Ok((package, version)) = split_version(package) { - if let Ok(o) = wasmer_registry::get_package_local( + } else if let Ok((package, version)) = split_version(package) { + if let Some(package) = wasmer_registry::get_local_package( &package, version.as_ref().map(|s| s.as_str()), ) { @@ -219,34 +246,35 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(o) + .into_run_args(package.path) .execute(); - } else { - let sp = - spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", - "⢀", "⠠", "⠐", "⠈", - ]) - .start(); - - let v = version.as_ref().map(|s| s.as_str()); - let result = wasmer_registry::install_package(&package, v); - sp.close(); - print!("\r\n"); - match result { - Ok(o) => { - // Try auto-installing the remote package - let mut args_without_package = args.clone(); - args_without_package.remove(1); - return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(o) - .execute(); - }, - Err(e) => { - println!("{e}"); - return Ok(()); - } + } + + // else: local package not found + let sp = + spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", + "⢀", "⠠", "⠐", "⠈", + ]) + .start(); + + let v = version.as_ref().map(|s| s.as_str()); + let result = wasmer_registry::install_package(&package, v); + sp.close(); + print!("\r\n"); + match result { + Ok(o) => { + // Try auto-installing the remote package + let mut args_without_package = args.clone(); + args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(o) + .execute(); + }, + Err(e) => { + println!("{e}"); + return Ok(()); } } } else { diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 6062e1c8328..3a308ff3db3 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -14,4 +14,6 @@ serde_json = "1.0.85" url = "2.3.1" thiserror = "1.0.37" toml = "0.5.9" -wapm-toml = "0.1.1" \ No newline at end of file +wapm-toml = "0.1.1" +tar = "0.4.38" +flate2 = "1.0.24" \ No newline at end of file diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index c1997689223..0fec5ab533f 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -247,7 +247,6 @@ pub mod graphql { registry_url: &str, login_token: &str, timeout: Duration, - only_check_json_response: bool, query: &QueryBody, ) -> anyhow::Result where @@ -427,8 +426,120 @@ pub fn get_command_local(name: &str) -> Result { Err(format!("unimplemented")) } -pub fn get_package_local(name: &str, version: Option<&str>) -> Result { - Err(format!("unimplemented")) +pub fn get_package_local_dir(registry_host: &str, name: &str, version: &str) -> Result { + if !name.contains("/") { + return Err(format!("package name has to be in the format namespace/package: {name:?}")); + } + let namespace = name.split("/").nth(0).ok_or(format!("missing namespace for {name:?}"))?; + let name = name.split("/").nth(1).ok_or(format!("missing name for {name:?}"))?; + let install_dir = get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; + Ok(install_dir.join(namespace).join(name).join(version)) +} + +#[derive(Debug, Clone)] +pub struct LocalPackage { + pub registry: String, + pub name: String, + pub version: String, + pub manifest: wapm_toml::Manifest, + pub path: PathBuf, +} + +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() -> Vec { + + let mut packages = Vec::new(); + + 'outer: for registry in get_all_available_registries().unwrap_or_default() { + + let root_dir = match get_global_install_dir(®istry) { + Some(o) => o, + None => continue 'outer, + }; + + for (registry_path, registry_host) in get_all_names_in_dir(&root_dir) { + for (package_path, package_name) in get_all_names_in_dir(®istry_path) { + for (version_path, package_version) in get_all_names_in_dir(&package_path) { + let toml_str = match std::fs::read_to_string(version_path.join("wapm.toml")) { + Ok(o) => o, + Err(_) => continue, + }; + let manifest = match toml::from_str(&toml_str) { + Ok(o) => o, + Err(_) => continue, + }; + packages.push(LocalPackage { + registry: registry_host.clone(), + name: package_name.clone(), + version: package_version, + manifest: manifest, + path: version_path, + }); + } + } + } + } + + packages +} + +pub fn get_local_package(name: &str, version: Option<&str>) -> Option { + get_all_local_packages() + .iter() + .filter(|p| { + if p.name != name { return false; } + if let Some(v) = version { + if p.version != v { return false; } + } + true + }) + .cloned() + .next() +} + +pub fn get_package_local_wasm_file(registry_host: &str, name: &str, version: &str) -> Result { + + let dir = get_package_local_dir(registry_host, name, version)?; + let wapm_toml_path = dir.join("wapm.toml"); + let wapm_toml_str = std::fs::read_to_string(&wapm_toml_path) + .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; + let wapm = toml::from_str::(&wapm_toml_str) + .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; + + // TODO: this will just return the path for the first command, so this might not be correct + let command_name = wapm.command + .unwrap_or_default() + .first() + .map(|m| m.get_module()) + .ok_or(format!("cannot get entrypoint for {name}@{version}: package has no commands"))?; + + Ok(dir.join(command_name)) } pub fn query_command_from_registry(name: &str) -> Result { @@ -501,7 +612,7 @@ pub fn query_package_from_registry( .flat_map(|v| v.into_iter()) .collect::>(); - let mut queried_package = available_packages.iter() + let queried_package = available_packages.iter() .filter_map(|v| { if name.contains("/") && v.package != name { return None; @@ -530,13 +641,47 @@ pub fn get_global_install_dir(registry_host: &str) -> Option { } pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result { - Err(format!("unimplemented")) + + let target_targz_path = target_path.to_path_buf().join("package.tar.gz"); + + let mut resp = reqwest::blocking::get(url) + .map_err(|e| format!("failed to download {url}: {e}"))?; + + if !target_targz_path.exists() { + // create all the parent paths, only remove the created directory, not the parent dirs + let _ = std::fs::create_dir_all(&target_targz_path); + let _ = std::fs::remove_dir(&target_targz_path); + } + + { + let mut file = std::fs::File::create(&target_targz_path) + .map_err(|e| format!("failed to download {url} into {}: {e}", target_targz_path.display()))?; + + reqwest::blocking::get(url) + .map_err(|e| format!("{e}"))?; + + resp.copy_to(&mut file).map_err(|e| format!("{e}"))?; + } + + let file = std::fs::File::open(&target_targz_path) + .map_err(|e| format!("failed to download {url} into {}: {e}", target_targz_path.display()))?; + + let gz_decoded = flate2::read::GzDecoder::new(file); + let mut ar = tar::Archive::new(gz_decoded); + ar.unpack(target_path) + .map_err(|e| format!("failed to unpack {}: {e}", target_targz_path.display()))?; + + let _ = std::fs::remove_file(target_targz_path); + + Ok(target_path.to_path_buf()) } pub fn install_package(name: &str, version: Option<&str>) -> Result { + let registries = get_all_available_registries()?; let mut url_of_package = None; let mut error_packages = Vec::new(); + for r in registries.iter() { let registry_test = test_if_registry_present(r); if !registry_test.clone().unwrap_or(false) { @@ -573,11 +718,45 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result>() .join("\r\n"); - let url_of_package = url_of_package + let (_, package_info) = url_of_package .ok_or(format!("Package {version_str} not found in registries: {registries_searched:?}.\r\n\r\nDid you mean:\r\n{did_you_mean}\r\n"))?; - println!("url of package: {:#?} in registries: {registries_searched:#?}", url_of_package.1); - Err(format!("unimplemented")) + let host = url::Url::parse(&package_info.url) + .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? + .host_str() + .ok_or(format!("invalid url: {}", package_info.url))? + .to_string(); + + let dir = get_package_local_dir( + &host, + &package_info.package, + &package_info.version + )?; + + let version = package_info.version; + let name = package_info.package; + + let target_path = download_and_unpack_targz(&package_info.url, &dir)?; + + let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")) + .map_err(|_| format!("Package {name}@{version} has no wapm.toml (path: {})", target_path.display()))?; + + let wapm_toml = toml::from_str::(&wapm_toml) + .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; + + let commands = wapm_toml.command.unwrap_or_default(); + let entrypoint_module = commands.first() + .ok_or(format!("Cannot run {name}@{version}: package has no commands"))?; + + let module_name = entrypoint_module.get_module(); + let modules = wapm_toml.module.unwrap_or_default(); + let entrypoint_module = modules + .iter() + .filter(|m| m.name == module_name) + .next() + .ok_or(format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml"))?; + + Ok(target_path.join(&entrypoint_module.source)) } pub fn test_if_registry_present(registry: &str) -> Result { From efdb1f4e76dd500e9b551435eb7aa22fbd192a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 6 Oct 2022 12:00:21 +0200 Subject: [PATCH 12/83] cargo fmt --- lib/cli/src/cli.rs | 50 +++--- lib/registry/src/lib.rs | 359 +++++++++++++++++++++------------------- 2 files changed, 221 insertions(+), 188 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 7ffc193e6a4..014c69d58d0 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -199,21 +199,28 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { #[cfg(feature = "binfmt")] (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), (Some("list"), Some("--installed")) => { - use prettytable::{format, Table, row}; + use prettytable::{format, row, Table}; let rows = get_all_local_packages() - .into_iter() - .map(|pkg| { - - let commands = pkg.manifest.command - .unwrap_or_default() - .iter() - .map(|c| c.get_name()) - .collect::>() - .join(" \r\n"); - - row![pkg.registry.clone(), pkg.name.clone(), pkg.version.clone(), commands] - }).collect::>(); + .into_iter() + .map(|pkg| { + let commands = pkg + .manifest + .command + .unwrap_or_default() + .iter() + .map(|c| c.get_name()) + .collect::>() + .join(" \r\n"); + + row![ + pkg.registry.clone(), + pkg.name.clone(), + pkg.version.clone(), + commands + ] + }) + .collect::>(); let mut table = Table::init(rows); table.set_titles(row!["Registry", "Package", "Version", "Commands"]); @@ -237,7 +244,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { if let Ok(Ok(run)) = result { return run.execute(); - } else if let Ok((package, version)) = split_version(package) { + } else if let Ok((package, version)) = split_version(package) { if let Some(package) = wasmer_registry::get_local_package( &package, version.as_ref().map(|s| s.as_str()), @@ -251,13 +258,12 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { } // else: local package not found - let sp = - spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", - "⢀", "⠠", "⠐", "⠈", - ]) - .start(); + let sp = spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", + "⠐", "⠈", + ]) + .start(); let v = version.as_ref().map(|s| s.as_str()); let result = wasmer_registry::install_package(&package, v); @@ -271,7 +277,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { return RunWithoutFile::try_parse_from(args_without_package.iter())? .into_run_args(o) .execute(); - }, + } Err(e) => { println!("{e}"); return Ok(()); diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 0fec5ab533f..87c1a14654f 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,19 +1,19 @@ -use std::path::{Path, PathBuf}; -use std::env; use std::collections::BTreeMap; +use std::env; +use std::path::{Path, PathBuf}; use serde::Deserialize; use serde::Serialize; pub mod graphql { + use core::time; use graphql_client::*; #[cfg(not(target_os = "wasi"))] use reqwest::{ blocking::{multipart::Form, Client}, header::USER_AGENT, }; - use core::time; use std::env; use std::time::Duration; #[cfg(target_os = "wasi")] @@ -161,7 +161,7 @@ pub mod graphql { .multipart(form) .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) .header(USER_AGENT, user_agent); - + if let Some(t) = timeout { res = res.timeout(t); } @@ -217,7 +217,7 @@ pub mod graphql { .multipart(form) .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) .header(USER_AGENT, user_agent); - + if let Some(t) = timeout { res = res.timeout(t); } @@ -253,13 +253,7 @@ pub mod graphql { for<'de> R: serde::Deserialize<'de>, V: serde::Serialize, { - execute_query_modifier_inner( - registry_url, - login_token, - query, - Some(timeout), - |f| f - ) + execute_query_modifier_inner(registry_url, login_token, query, Some(timeout), |f| f) } } @@ -356,14 +350,12 @@ fn format_graphql(registry: &str) -> String { } impl PartialWapmConfig { - pub fn from_file() -> Result { let path = Self::get_file_location()?; match std::fs::read_to_string(&path) { Ok(config_toml) => { - toml::from_str(&config_toml) - .map_err(|e| format!("could not parse {path:?}: {e}")) + toml::from_str(&config_toml).map_err(|e| format!("could not parse {path:?}: {e}")) } Err(_e) => Ok(Self::default()), } @@ -379,10 +371,7 @@ impl PartialWapmConfig { pub fn get_folder() -> Result { Ok( - if let Some(folder_str) = env::var("WASMER_DIR") - .ok() - .filter(|s| !s.is_empty()) - { + if let Some(folder_str) = env::var("WASMER_DIR").ok().filter(|s| !s.is_empty()) { let folder = PathBuf::from(folder_str); std::fs::create_dir_all(folder.clone()) .map_err(|e| format!("cannot create config directory: {e}"))?; @@ -426,13 +415,26 @@ pub fn get_command_local(name: &str) -> Result { Err(format!("unimplemented")) } -pub fn get_package_local_dir(registry_host: &str, name: &str, version: &str) -> Result { - if !name.contains("/") { - return Err(format!("package name has to be in the format namespace/package: {name:?}")); +pub fn get_package_local_dir( + registry_host: &str, + name: &str, + version: &str, +) -> Result { + if !name.contains("/") { + return Err(format!( + "package name has to be in the format namespace/package: {name:?}" + )); } - let namespace = name.split("/").nth(0).ok_or(format!("missing namespace for {name:?}"))?; - let name = name.split("/").nth(1).ok_or(format!("missing name for {name:?}"))?; - let install_dir = get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; + let namespace = name + .split("/") + .nth(0) + .ok_or(format!("missing namespace for {name:?}"))?; + let name = name + .split("/") + .nth(1) + .ok_or(format!("missing name for {name:?}"))?; + let install_dir = + get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; Ok(install_dir.join(namespace).join(name).join(version)) } @@ -446,17 +448,18 @@ pub struct LocalPackage { } fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { + if !dir.is_dir() { + return Vec::new(); + } - 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>>(); + .map(|res| res.map(|e| e.path())) + .collect::, std::io::Error>>(); let registry_entries = match entries { Ok(o) => o, @@ -464,25 +467,21 @@ fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { }; registry_entries - .into_iter() - .filter_map(|re| { - Some((re.clone(), re.file_name()?.to_str()?.to_string())) - }) - .collect() + .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() -> Vec { - let mut packages = Vec::new(); 'outer: for registry in get_all_available_registries().unwrap_or_default() { - let root_dir = match get_global_install_dir(®istry) { Some(o) => o, None => continue 'outer, }; - + for (registry_path, registry_host) in get_all_names_in_dir(&root_dir) { for (package_path, package_name) in get_all_names_in_dir(®istry_path) { for (version_path, package_version) in get_all_names_in_dir(&package_path) { @@ -494,9 +493,9 @@ pub fn get_all_local_packages() -> Vec { Ok(o) => o, Err(_) => continue, }; - packages.push(LocalPackage { - registry: registry_host.clone(), - name: package_name.clone(), + packages.push(LocalPackage { + registry: registry_host.clone(), + name: package_name.clone(), version: package_version, manifest: manifest, path: version_path, @@ -511,34 +510,44 @@ pub fn get_all_local_packages() -> Vec { pub fn get_local_package(name: &str, version: Option<&str>) -> Option { get_all_local_packages() - .iter() - .filter(|p| { - if p.name != name { return false; } - if let Some(v) = version { - if p.version != v { return false; } - } - true - }) - .cloned() - .next() + .iter() + .filter(|p| { + if p.name != name { + return false; + } + if let Some(v) = version { + if p.version != v { + return false; + } + } + true + }) + .cloned() + .next() } -pub fn get_package_local_wasm_file(registry_host: &str, name: &str, version: &str) -> Result { - +pub fn get_package_local_wasm_file( + registry_host: &str, + name: &str, + version: &str, +) -> Result { let dir = get_package_local_dir(registry_host, name, version)?; let wapm_toml_path = dir.join("wapm.toml"); let wapm_toml_str = std::fs::read_to_string(&wapm_toml_path) .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; let wapm = toml::from_str::(&wapm_toml_str) .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; - + // TODO: this will just return the path for the first command, so this might not be correct - let command_name = wapm.command + let command_name = wapm + .command .unwrap_or_default() .first() .map(|m| m.get_module()) - .ok_or(format!("cannot get entrypoint for {name}@{version}: package has no commands"))?; - + .ok_or(format!( + "cannot get entrypoint for {name}@{version}: package has no commands" + ))?; + Ok(dir.join(command_name)) } @@ -553,11 +562,7 @@ pub fn query_package_from_registry( name: &str, version: Option<&str>, ) -> Result, String)> { - - use crate::graphql::{ - GetPackagesQuery, get_packages_query, - execute_query - }; + use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; let q = if name.contains("/") { @@ -572,61 +577,72 @@ pub fn query_package_from_registry( }; let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q) - .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; - - let available_packages = response.package - .iter() - .filter_map(|p| { - - let p = p.as_ref()?; - let mut versions = Vec::new(); - - for v in p.versions.iter() { - for v in v.iter() { - let v = match v.as_ref() { - Some(s) => s, - None => continue, - }; - - versions.push(PackageDownloadInfo { - registry: registry_url.to_string(), - package: p.name.clone(), - - version: v.version.clone(), - - commands: toml::from_str::( - &v.manifest - ).ok()? - .command.unwrap_or_default() - .iter().map(|s| s.get_name()).collect(), - - url: v.distribution.download_url.clone(), - }); + .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; + + let available_packages = response + .package + .iter() + .filter_map(|p| { + let p = p.as_ref()?; + let mut versions = Vec::new(); + + for v in p.versions.iter() { + for v in v.iter() { + let v = match v.as_ref() { + Some(s) => s, + None => continue, + }; + + versions.push(PackageDownloadInfo { + registry: registry_url.to_string(), + package: p.name.clone(), + + version: v.version.clone(), + + commands: toml::from_str::(&v.manifest) + .ok()? + .command + .unwrap_or_default() + .iter() + .map(|s| s.get_name()) + .collect(), + + url: v.distribution.download_url.clone(), + }); + } } - } - Some(versions) - }) - .collect::>() - .into_iter() - .flat_map(|v| v.into_iter()) - .collect::>(); - - let queried_package = available_packages.iter() - .filter_map(|v| { - if name.contains("/") && v.package != name { - return None; - } + Some(versions) + }) + .collect::>() + .into_iter() + .flat_map(|v| v.into_iter()) + .collect::>(); - if version.is_some() && v.version != version.clone().unwrap() { - return None; - } + let queried_package = available_packages + .iter() + .filter_map(|v| { + if name.contains("/") && v.package != name { + return None; + } + + if version.is_some() && v.version != version.clone().unwrap() { + return None; + } - Some(v) - }).next().cloned(); + Some(v) + }) + .next() + .cloned(); match queried_package { - None => Err((available_packages, format!("No package found for {name}@{}", version.unwrap_or("latest")))), + None => Err(( + available_packages, + format!( + "No package found for {name}@{}", + version.unwrap_or("latest") + ), + )), Some(s) => Ok(s), } } @@ -634,18 +650,18 @@ pub fn query_package_from_registry( /// Returs the path to the directory where all packages on this computer are being stored pub fn get_global_install_dir(registry_host: &str) -> Option { Some( - PartialWapmConfig::get_folder().ok()? + PartialWapmConfig::get_folder() + .ok()? .join("checkouts") .join(registry_host), ) } pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result { - let target_targz_path = target_path.to_path_buf().join("package.tar.gz"); - let mut resp = reqwest::blocking::get(url) - .map_err(|e| format!("failed to download {url}: {e}"))?; + let mut resp = + reqwest::blocking::get(url).map_err(|e| format!("failed to download {url}: {e}"))?; if !target_targz_path.exists() { // create all the parent paths, only remove the created directory, not the parent dirs @@ -654,22 +670,29 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result Result) -> Result { - let registries = get_all_available_registries()?; let mut url_of_package = None; let mut error_packages = Vec::new(); - + for r in registries.iter() { let registry_test = test_if_registry_present(r); if !registry_test.clone().unwrap_or(false) { @@ -691,88 +713,93 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result { url_of_package = Some((r, o)); break; - }, + } Err(e) => { error_packages.push(e); - }, + } } } - + let version_str = match version { None => format!("{name}"), Some(v) => format!("{name}@{v}"), }; - + let registries_searched = registries .iter() .filter_map(|s| url::Url::parse(s).ok()) .filter_map(|s| Some(format!("{}", s.host_str()?))) .collect::>(); - let did_you_mean = error_packages.iter() - .flat_map(|(packages, _)| { - packages.iter().filter_map(|f| { - let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); - Some(format!(" {}@{} (from {from})", f.package, f.version)) + let did_you_mean = error_packages + .iter() + .flat_map(|(packages, _)| { + packages.iter().filter_map(|f| { + let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); + Some(format!(" {}@{} (from {from})", f.package, f.version)) + }) }) - }).collect::>() - .join("\r\n"); - + .collect::>() + .join("\r\n"); + let (_, package_info) = url_of_package .ok_or(format!("Package {version_str} not found in registries: {registries_searched:?}.\r\n\r\nDid you mean:\r\n{did_you_mean}\r\n"))?; let host = url::Url::parse(&package_info.url) - .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? - .host_str() - .ok_or(format!("invalid url: {}", package_info.url))? - .to_string(); + .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? + .host_str() + .ok_or(format!("invalid url: {}", package_info.url))? + .to_string(); - let dir = get_package_local_dir( - &host, - &package_info.package, - &package_info.version - )?; + let dir = get_package_local_dir(&host, &package_info.package, &package_info.version)?; let version = package_info.version; let name = package_info.package; let target_path = download_and_unpack_targz(&package_info.url, &dir)?; - let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")) - .map_err(|_| format!("Package {name}@{version} has no wapm.toml (path: {})", target_path.display()))?; + let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")).map_err(|_| { + format!( + "Package {name}@{version} has no wapm.toml (path: {})", + target_path.display() + ) + })?; let wapm_toml = toml::from_str::(&wapm_toml) - .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; + .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; let commands = wapm_toml.command.unwrap_or_default(); - let entrypoint_module = commands.first() - .ok_or(format!("Cannot run {name}@{version}: package has no commands"))?; - + let entrypoint_module = commands.first().ok_or(format!( + "Cannot run {name}@{version}: package has no commands" + ))?; + let module_name = entrypoint_module.get_module(); let modules = wapm_toml.module.unwrap_or_default(); let entrypoint_module = modules - .iter() - .filter(|m| m.name == module_name) - .next() - .ok_or(format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml"))?; - + .iter() + .filter(|m| m.name == module_name) + .next() + .ok_or(format!( + "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" + ))?; + Ok(target_path.join(&entrypoint_module.source)) } pub fn test_if_registry_present(registry: &str) -> Result { - + use crate::graphql::{test_if_registry_present, TestIfRegistryPresent}; use graphql_client::GraphQLQuery; use std::time::Duration; - use crate::graphql::{TestIfRegistryPresent, test_if_registry_present}; let q = TestIfRegistryPresent::build_query(test_if_registry_present::Variables {}); let _ = crate::graphql::execute_query_modifier_inner_check_json( - registry, - "", - &q, - Some(Duration::from_secs(1)), - |f| f - ).map_err(|e| format!("{e}"))?; + registry, + "", + &q, + Some(Duration::from_secs(1)), + |f| f, + ) + .map_err(|e| format!("{e}"))?; Ok(true) } @@ -783,12 +810,12 @@ pub fn get_all_available_registries() -> Result, String> { match config.registry { Registries::Single(s) => { registries.push(format_graphql(&s.url)); - }, + } Registries::Multi(m) => { for key in m.tokens.keys() { registries.push(format_graphql(&key)); } - } + } } Ok(registries) } From 81786734c852f5fc651c45c4cd70a1e05ed449aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 6 Oct 2022 14:09:18 +0200 Subject: [PATCH 13/83] Fail on ambigouus package name --- lib/registry/src/lib.rs | 76 +++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 87c1a14654f..cd45d53871a 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -555,13 +555,37 @@ pub fn query_command_from_registry(name: &str) -> Result, + }, + ErrorSendingQuery(String), + NoPackageFound { + name: String, + version: Option, + packages: Vec, + } +} + +impl QueryPackageError { + pub fn get_packages(&self) -> Vec { + match self { + QueryPackageError::AmbigouusName { name: _, packages } | + QueryPackageError::NoPackageFound { name: _, version: _, packages } + => packages.clone(), + _ => Vec::new(), + } + } +} /// Returns the download info of the packages, on error returns all the available packages /// i.e. (("foo/python", "wapm.io"), ("bar/python" "wapm.io"))) pub fn query_package_from_registry( registry_url: &str, name: &str, version: Option<&str>, -) -> Result, String)> { +) -> Result { use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; @@ -577,7 +601,7 @@ pub fn query_package_from_registry( }; let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q) - .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; + .map_err(|e| QueryPackageError::ErrorSendingQuery(format!("Error sending GetPackagesQuery:  {e}")))?; let available_packages = response .package @@ -619,6 +643,13 @@ pub fn query_package_from_registry( .flat_map(|v| v.into_iter()) .collect::>(); + if !name.contains("/") { + return Err(QueryPackageError::AmbigouusName { + name: name.to_string(), + packages: available_packages, + }); + } + let queried_package = available_packages .iter() .filter_map(|v| { @@ -636,13 +667,13 @@ pub fn query_package_from_registry( .cloned(); match queried_package { - None => Err(( - available_packages, - format!( - "No package found for {name}@{}", - version.unwrap_or("latest") - ), - )), + None => { + return Err(QueryPackageError::NoPackageFound { + name: name.to_string(), + version: version.as_ref().map(|s| s.to_string()), + packages: available_packages, + }); + }, Some(s) => Ok(s), } } @@ -731,19 +762,33 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result>(); - let did_you_mean = error_packages + let mut error_str = format!("Package {version_str} not found in registries {registries_searched:?}."); + let mut did_you_mean = error_packages .iter() - .flat_map(|(packages, _)| { + .flat_map(|error| { + if let QueryPackageError::AmbigouusName { name, packages: _ } = error { + error_str = format!("Ambigouus package name {name:?}. Please specify the package in the namespace/name format."); + } + let packages = error.get_packages(); packages.iter().filter_map(|f| { let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); Some(format!(" {}@{} (from {from})", f.package, f.version)) }) + .collect::>() + .into_iter() }) - .collect::>() - .join("\r\n"); + .collect::>(); + + let did_you_mean = if did_you_mean.is_empty() { + String::new() + } else { + did_you_mean.sort(); + did_you_mean.dedup(); + format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) + }; let (_, package_info) = url_of_package - .ok_or(format!("Package {version_str} not found in registries: {registries_searched:?}.\r\n\r\nDid you mean:\r\n{did_you_mean}\r\n"))?; + .ok_or(format!("{error_str}{did_you_mean}"))?; let host = url::Url::parse(&package_info.url) .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? @@ -751,7 +796,8 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result Date: Thu, 6 Oct 2022 14:20:08 +0200 Subject: [PATCH 14/83] Do not re-download already-downloaded package --- lib/registry/src/commands/install.rs | 0 lib/registry/src/commands/search.rs | 0 lib/registry/src/lib.rs | 5 ++++- 3 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 lib/registry/src/commands/install.rs delete mode 100644 lib/registry/src/commands/search.rs diff --git a/lib/registry/src/commands/install.rs b/lib/registry/src/commands/install.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/lib/registry/src/commands/search.rs b/lib/registry/src/commands/search.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index cd45d53871a..a2dc546650c 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -802,7 +802,10 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result Date: Thu, 6 Oct 2022 15:47:26 +0200 Subject: [PATCH 15/83] Pass filesystem mappings from wapm.toml to --mapdir --- Cargo.lock | 1 + lib/cli/Cargo.toml | 1 + lib/cli/src/cli.rs | 6 +++--- lib/cli/src/commands/run.rs | 25 ++++++++++++++++++++++++- lib/cli/src/commands/run/wasi.rs | 10 ++++++++++ lib/registry/src/lib.rs | 14 ++++++++++---- 6 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4e5e2983dd..f3500ed5fe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3858,6 +3858,7 @@ dependencies = [ "target-lexicon 0.12.4", "tempfile", "unix_mode", + "wapm-toml", "wasmer", "wasmer-cache", "wasmer-compiler", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 0f8ffc76a6d..b9da5c2e21b 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -59,6 +59,7 @@ dirs = { version = "4.0", optional = true } serde_json = { version = "1.0", optional = true } target-lexicon = { version = "0.12", features = ["std"] } prettytable-rs = "0.9.0" +wapm-toml = "0.1.1" [build-dependencies] chrono = "0.4.22" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 014c69d58d0..255646e777a 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -253,7 +253,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(package.path) + .into_run_args(package.path, Some(package.manifest.clone())) .execute(); } @@ -270,12 +270,12 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { sp.close(); print!("\r\n"); match result { - Ok(o) => { + Ok((package, buf)) => { // Try auto-installing the remote package let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(o) + .into_run_args(buf, Some(package.manifest.clone())) .execute(); } Err(e) => { diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 5ffabe9dce5..8cb700dfc1b 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -75,7 +75,30 @@ pub struct RunWithoutFile { impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). - pub fn into_run_args(self, pathbuf: PathBuf) -> Run { + pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { + + #[cfg(feature = "wasi")] + if let Some(fs) = manifest.as_ref().and_then(|m| m.fs.as_ref()) { + for (alias, real_dir) in fs.iter() { + let real_dir = if let Some(parent) = pathbuf.parent() { + parent.join(real_dir) + } else { + pathbuf.join(real_dir) + }; + if !real_dir.exists() { + println!("warning: cannot map {alias:?} to {}: directory does not exist", real_dir.display()); + continue; + } + println!("mapping {alias:?} -> {}", real_dir.display()); + self.wasi.map_dir(alias, real_dir.clone()); + + #[cfg(feature = "wasi")] { + println!("setting PYTHONHOME to /{}", real_dir.display()); + self.wasi.set_env("PYTHONHOME", &format!("{}", real_dir.display())); + } + } + } + Run { #[cfg(feature = "cache")] disable_cache: self.disable_cache, diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 37c64473a1c..7fa3d0203da 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -52,6 +52,16 @@ pub struct Wasi { #[allow(dead_code)] impl Wasi { + + pub fn map_dir(&mut self, alias: &str, target_on_disk: PathBuf) { + self.mapped_dirs.push((alias.to_string(), target_on_disk)); + self.pre_opened_directories.push(std::path::Path::new(alias).to_path_buf()); + } + + pub fn set_env(&mut self, key: &str, value: &str) { + self.env_vars.push((key.to_string(), value.to_string())); + } + /// Gets the WASI version (if any) for the provided module pub fn get_versions(module: &Module) -> Option> { // Get the wasi version in strict mode, so no other imports are diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index a2dc546650c..d2c27d1633b 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -730,7 +730,7 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result) -> Result { +pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackage, PathBuf), String> { let registries = get_all_available_registries()?; let mut url_of_package = None; let mut error_packages = Vec::new(); @@ -817,13 +817,13 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result(&wapm_toml) .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; - let commands = wapm_toml.command.unwrap_or_default(); + let commands = wapm_toml.command.clone().unwrap_or_default(); let entrypoint_module = commands.first().ok_or(format!( "Cannot run {name}@{version}: package has no commands" ))?; let module_name = entrypoint_module.get_module(); - let modules = wapm_toml.module.unwrap_or_default(); + let modules = wapm_toml.module.clone().unwrap_or_default(); let entrypoint_module = modules .iter() .filter(|m| m.name == module_name) @@ -832,7 +832,13 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result Result { From 5238c8e56bed6de893aed4a3439f6de933d7d330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 6 Oct 2022 19:02:55 +0200 Subject: [PATCH 16/83] Fix errors in debug mode, implement getenv / setenv on emscripten This makes it possible to inject custom environment variables because the EmscriptenState is now sandboxed and decoupled from the host. --- lib/cli/src/commands/run.rs | 12 ++--- lib/emscripten/src/env/unix/mod.rs | 70 ++++++++++++++++++++---------- lib/emscripten/src/lib.rs | 62 ++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 8cb700dfc1b..fabcc5979e6 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -65,8 +65,8 @@ pub struct RunWithoutFile { debug: bool, #[cfg(feature = "debug")] - #[clap(short, long, parse(from_occurrences))] - verbose: u8, + #[clap(long = "verbose")] + verbose: Option, /// Application arguments #[clap(value_name = "ARGS")] @@ -93,8 +93,8 @@ impl RunWithoutFile { self.wasi.map_dir(alias, real_dir.clone()); #[cfg(feature = "wasi")] { - println!("setting PYTHONHOME to /{}", real_dir.display()); - self.wasi.set_env("PYTHONHOME", &format!("{}", real_dir.display())); + println!("setting PYTHONHOME to /lib/python3.7"); + self.wasi.set_env("PYTHONHOME", "/lib/python3.7"); } } } @@ -115,7 +115,7 @@ impl RunWithoutFile { #[cfg(feature = "debug")] debug: self.debug, #[cfg(feature = "debug")] - verbose: self.verbose, + verbose: self.verbose.unwrap_or(0), args: self.args, } } @@ -169,7 +169,7 @@ pub struct Run { debug: bool, #[cfg(feature = "debug")] - #[clap(short, long, parse(from_occurrences))] + #[clap(short, long, num_args(0..))] verbose: u8, /// Application arguments diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index f559468dc28..a1aff238004 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -1,9 +1,8 @@ /// NOTE: These syscalls only support wasm_32 for now because they take u32 offset use libc::{ - c_int, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv, sysconf, - unsetenv, + c_int, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, sysconf, }; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::mem; use std::os::raw::c_char; @@ -17,55 +16,82 @@ use wasmer::{FunctionEnvMut, WasmPtr}; pub fn _getenv(mut ctx: FunctionEnvMut, name: i32) -> u32 { debug!("emscripten::_getenv"); - let memory = ctx.data().memory(0); + let em_env = ctx.data(); + let memory = em_env.memory(0); let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); - let c_str = unsafe { getenv(name_addr) }; - if c_str.is_null() { - return 0; - } - - unsafe { copy_cstr_into_wasm(&mut ctx, c_str) } + let c_string = unsafe { CStr::from_ptr(name_addr) }; + let c_string = c_string.to_string_lossy(); + let env_var = em_env.get_env_var(c_string.as_ref()); + let env_var = match env_var { + Some(s) => s, + None => return 0, + }; + let new_env_var = match CString::new(env_var) { + Ok(s) => s, + Err(_) => return 0, + }; + unsafe { copy_cstr_into_wasm(&mut ctx, new_env_var.as_ptr()) } } /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); pub fn _setenv(ctx: FunctionEnvMut, name: c_int, value: c_int, overwrite: c_int) -> c_int { debug!("emscripten::_setenv"); - let memory = ctx.data().memory(0); + let em_env = ctx.data(); + let memory = em_env.memory(0); let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; let value_addr = emscripten_memory_pointer!(memory.view(&ctx), value) as *const c_char; - debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); - debug!("=> value({:?})", unsafe { CStr::from_ptr(value_addr) }); + let name = unsafe { CStr::from_ptr(name_addr) }.to_string_lossy(); + let value = unsafe { CStr::from_ptr(value_addr) }.to_string_lossy(); + + debug!("=> name({:?})", name); + debug!("=> value({:?})", value); + + let previous_entry = em_env.set_env_var(name.as_ref(), value.as_ref()); + + if let (0, Some(prev)) = (overwrite, previous_entry) { + let _ = em_env.set_env_var(name.as_ref(), prev.as_ref()); + } - unsafe { setenv(name_addr, value_addr, overwrite) } + 0 } /// emscripten: _putenv // (name: *const char); pub fn _putenv(ctx: FunctionEnvMut, name: c_int) -> c_int { debug!("emscripten::_putenv"); - let memory = ctx.data().memory(0); + let em_env = ctx.data(); + let memory = em_env.memory(0); let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; - debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + let name = unsafe { CStr::from_ptr(name_addr) }.to_string_lossy(); + debug!("=> name({:?})", name); - unsafe { putenv(name_addr as _) } + em_env.set_env_var(name.as_ref(), ""); + + 0 } /// emscripten: _unsetenv // (name: *const char); -pub fn _unsetenv(ctx: FunctionEnvMut, name: c_int) -> c_int { +pub fn _unsetenv(mut ctx: FunctionEnvMut, name: c_int) -> c_int { debug!("emscripten::_unsetenv"); - let memory = ctx.data().memory(0); - let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; + let name = { + let em_env = ctx.data(); + let memory = em_env.memory(0); + let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; + unsafe { CStr::from_ptr(name_addr) }.to_string_lossy().to_string() + }; - debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + debug!("=> name({:?})", name); + let em_env = ctx.data_mut(); + em_env.remove_env_var(name.as_ref()); - unsafe { unsetenv(name_addr) } + 0 } #[allow(clippy::cast_ptr_alignment)] diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 216171e3510..9d9fe54a163 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -74,12 +74,24 @@ pub use self::utils::{ get_emscripten_table_size, is_emscripten_module, }; +/// State of the emscripten environment (environment variables, CLI args) +#[derive(Debug, Clone, Default)] +pub struct EmscriptenState { + /// Environment variables in a [key -> value] mapping + pub env_vars: HashMap, + /// Command line arguments that this module received + pub cli_args: Vec, +} + #[derive(Clone)] /// The environment provided to the Emscripten imports. pub struct EmEnv { memory: Arc>>, data: Arc>>, funcs: Arc>, + // State that is passed to the wasm module (environment variables, CLI args, ...) + #[allow(dead_code)] + state: Arc>, } impl Default for EmEnv { @@ -95,6 +107,16 @@ impl EmEnv { memory: Arc::new(RwLock::new(None)), data: Arc::new(Mutex::new(None)), funcs: Arc::new(Mutex::new(EmscriptenFunctions::new())), + state: Arc::new(Mutex::new(EmscriptenState::default())), + } + } + + pub fn new_with_state(emstate: EmscriptenState) -> Self { + Self { + memory: Arc::new(RwLock::new(None)), + data: Arc::new(Mutex::new(None)), + funcs: Arc::new(Mutex::new(EmscriptenFunctions::new())), + state: Arc::new(Mutex::new(emstate)), } } @@ -120,6 +142,46 @@ impl EmEnv { let mut w = self.data.lock().unwrap(); *w = Some(EmscriptenData::new(data.clone(), mapped_dirs)); } + + pub fn get_env_var( + &self, + key: &str, + ) -> Option { + let w = self.state.lock().ok()?; + w.env_vars.get(key).cloned() + } + + pub fn get_env_vars_len(&self) -> usize { + let w = self.state.lock().unwrap(); + w.env_vars.len() + } + + pub fn set_env_var( + &self, + key: &str, + value: &str, + ) -> Option { + let mut w = self.state.lock().ok()?; + w.env_vars.insert(key.to_string(), value.to_string()) + } + + pub fn remove_env_var( + &mut self, + key: &str, + ) -> Option { + let mut w = self.state.lock().ok()?; + w.env_vars.remove(key) + } + + pub fn get_args_size(&self) -> usize { + let w = self.state.lock().unwrap(); + w.cli_args.len() + } + + pub fn get_args(&self) -> Vec { + let w = self.state.lock().unwrap(); + w.cli_args.clone() + } } #[derive(Debug, Clone)] From 3be8640a5ff4c1f5ddf487c38e5665e6bbf71dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 6 Oct 2022 20:04:42 +0200 Subject: [PATCH 17/83] Added --env flags for emscripten target, debugged PYTHONHOME --- lib/cli/src/commands/run.rs | 13 +++++++++---- lib/cli/src/commands/run/wasi.rs | 6 +++--- lib/emscripten/src/lib.rs | 19 ++++++++++++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index fabcc5979e6..0fd5d6bd58e 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -93,9 +93,10 @@ impl RunWithoutFile { self.wasi.map_dir(alias, real_dir.clone()); #[cfg(feature = "wasi")] { - println!("setting PYTHONHOME to /lib/python3.7"); - self.wasi.set_env("PYTHONHOME", "/lib/python3.7"); - } + let target = format!("{}", real_dir.parent().unwrap().display()); + println!("setting PYTHONHOME to {target}"); + self.wasi.set_env("PYTHONHOME", &target); + } } } @@ -238,8 +239,12 @@ impl Run { }; // TODO: refactor this if is_emscripten_module(&module) { + let em_env = EmEnv::new(); + for (k, v) in self.wasi.env_vars.iter() { + em_env.set_env_var(k, v); + } // create an EmEnv with default global - let env = FunctionEnv::new(&mut store, EmEnv::new()); + let env = FunctionEnv::new(&mut store, em_env); let mut emscripten_globals = EmscriptenGlobals::new(&mut store, &env, &module) .map_err(|e| anyhow!("{}", e))?; env.as_mut(&mut store) diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 7fa3d0203da..a28eb227018 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -15,7 +15,7 @@ use clap::Parser; pub struct Wasi { /// WASI pre-opened directory #[clap(long = "dir", name = "DIR", group = "wasi")] - pre_opened_directories: Vec, + pub(crate) pre_opened_directories: Vec, /// Map a host directory to a different location for the Wasm module #[arg( @@ -23,7 +23,7 @@ pub struct Wasi { name = "GUEST_DIR:HOST_DIR", value_parser = parse_mapdir, )] - mapped_dirs: Vec<(String, PathBuf)>, + pub(crate) mapped_dirs: Vec<(String, PathBuf)>, /// Pass custom environment variables #[arg( @@ -31,7 +31,7 @@ pub struct Wasi { name = "KEY=VALUE", value_parser = parse_envvar, )] - env_vars: Vec<(String, String)>, + pub(crate) env_vars: Vec<(String, String)>, /// Enable experimental IO devices #[cfg(feature = "experimental-io-devices")] diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 9d9fe54a163..d50518b3209 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -75,7 +75,7 @@ pub use self::utils::{ }; /// State of the emscripten environment (environment variables, CLI args) -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct EmscriptenState { /// Environment variables in a [key -> value] mapping pub env_vars: HashMap, @@ -83,6 +83,18 @@ pub struct EmscriptenState { pub cli_args: Vec, } +impl Default for EmscriptenState { + fn default() -> Self { + Self { + env_vars: std::env::vars_os() + .filter_map(|(k, v)| { + Some((k.to_str()?.to_string(), v.to_str()?.to_string())) + }).collect(), + cli_args: Vec::new(), + } + } +} + #[derive(Clone)] /// The environment provided to the Emscripten imports. pub struct EmEnv { @@ -148,7 +160,8 @@ impl EmEnv { key: &str, ) -> Option { let w = self.state.lock().ok()?; - w.env_vars.get(key).cloned() + let result = w.env_vars.get(key).cloned(); + result } pub fn get_env_vars_len(&self) -> usize { @@ -166,7 +179,7 @@ impl EmEnv { } pub fn remove_env_var( - &mut self, + &self, key: &str, ) -> Option { let mut w = self.state.lock().ok()?; From da1e29523f5779ffd78c1cf230d35713796bd8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 11:11:20 +0200 Subject: [PATCH 18/83] Fix error in listing local packages --- lib/registry/src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index d2c27d1633b..1608f0f06e0 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -477,7 +477,18 @@ pub fn get_all_local_packages() -> Vec { let mut packages = Vec::new(); 'outer: for registry in get_all_available_registries().unwrap_or_default() { - let root_dir = match get_global_install_dir(®istry) { + + let host = match url::Url::parse(®istry) { + Ok(o) => o.host_str().map(|s| s.to_string()), + Err(_) => continue 'outer, + }; + + let host = match host { + Some(s) => s, + None => continue 'outer, + }; + + let root_dir = match get_global_install_dir(&host) { Some(o) => o, None => continue 'outer, }; @@ -790,10 +801,10 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag let (_, package_info) = url_of_package .ok_or(format!("{error_str}{did_you_mean}"))?; - let host = url::Url::parse(&package_info.url) - .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? + let host = url::Url::parse(&package_info.registry) + .map_err(|e| format!("invalid url: {}: {e}", package_info.registry))? .host_str() - .ok_or(format!("invalid url: {}", package_info.url))? + .ok_or(format!("invalid url: {}", package_info.registry))? .to_string(); let dir = get_package_local_dir( From 50733a536b2f2332df881f51dc717a856d5e9a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 13:17:34 +0200 Subject: [PATCH 19/83] Try adding support for pkg_fs --- Cargo.lock | 4 ++-- lib/cli/Cargo.toml | 2 +- lib/cli/src/cli.rs | 31 +++++++++++++++++++--------- lib/cli/src/commands/run.rs | 40 ++++++++++++++++++++++++++++--------- lib/registry/src/lib.rs | 25 ++++++++++++++++------- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3500ed5fe7..a55252b07a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3571,9 +3571,9 @@ dependencies = [ [[package]] name = "wapm-toml" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c0efbde13765eefd992112ea28cf8b8fa484a73652c2e8998e0e0e778aa9f0" +checksum = "5de1aa82bb6aa2eb2f904cba634e93294436fa28746f3c72f1a24f827f9d4413" dependencies = [ "anyhow", "semver 1.0.14", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index b9da5c2e21b..d155d697ad2 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -59,7 +59,7 @@ dirs = { version = "4.0", optional = true } serde_json = { version = "1.0", optional = true } target-lexicon = { version = "0.12", features = ["std"] } prettytable-rs = "0.9.0" -wapm-toml = "0.1.1" +wapm-toml = "0.1.2" [build-dependencies] chrono = "0.4.22" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 255646e777a..d654585c7a2 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -198,7 +198,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { (Some("wast"), _) => Wast::try_parse_from(args_without_first_arg.iter())?.execute(), #[cfg(feature = "binfmt")] (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("list"), Some("--installed")) => { + (Some("list"), _) => { use prettytable::{format, row, Table}; let rows = get_all_local_packages() @@ -222,13 +222,20 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { }) .collect::>(); - let mut table = Table::init(rows); - table.set_titles(row!["Registry", "Package", "Version", "Commands"]); - table.add_empty_row(); - table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE); - table.set_format(*format::consts::FORMAT_NO_COLSEP); - let _ = table.printstd(); - println!(""); + let empty_table = rows.is_empty(); + if empty_table { + println!("--------------------------------------"); + println!("Registry Package Version Commands "); + println!("======================================"); + println!(""); + } else { + 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); + let _ = table.printstd(); + } + Ok(()) } (Some("run"), Some(package)) | (Some(package), _) => { @@ -249,11 +256,17 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { &package, version.as_ref().map(|s| s.as_str()), ) { + let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( + &package.registry, + &package.name, + &package.version, + ).map_err(|e| anyhow!("{e}"))?; + // Try finding the local package let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(package.path, Some(package.manifest.clone())) + .into_run_args(local_package_wasm_path, Some(package.manifest.clone())) .execute(); } diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 0fd5d6bd58e..fd81d416afe 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -77,9 +77,36 @@ impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { + let mut use_pkg_fs = false; + #[cfg(feature = "wasi")] { + let pkg_fs = match pathbuf.parent() { + Some(parent) => parent.join("pkg_fs"), + None => pathbuf.join("pkg_fs"), + }; + if let Some(mut m) = manifest.as_ref().and_then(|m| m.package.pkg_fs_mount_point.clone()) { + use_pkg_fs = true; + if m == "." { + self.wasi.map_dir("/", pkg_fs); + } else { + if m.starts_with(".") { + m = format!("{}{}", pkg_fs.display(), &m[1..]); + } + let path = std::path::Path::new(&m).to_path_buf(); + self.wasi.map_dir("/", path); + } + } + } + #[cfg(feature = "wasi")] if let Some(fs) = manifest.as_ref().and_then(|m| m.fs.as_ref()) { for (alias, real_dir) in fs.iter() { + let mut real_dir = format!("{}", real_dir.display()); + if real_dir.starts_with("/") { + real_dir = (&real_dir[1..]).to_string(); + } + if use_pkg_fs { + real_dir = format!("pkg_fs/{real_dir}"); + } let real_dir = if let Some(parent) = pathbuf.parent() { parent.join(real_dir) } else { @@ -89,14 +116,9 @@ impl RunWithoutFile { println!("warning: cannot map {alias:?} to {}: directory does not exist", real_dir.display()); continue; } - println!("mapping {alias:?} -> {}", real_dir.display()); - self.wasi.map_dir(alias, real_dir.clone()); - - #[cfg(feature = "wasi")] { - let target = format!("{}", real_dir.parent().unwrap().display()); - println!("setting PYTHONHOME to {target}"); - self.wasi.set_env("PYTHONHOME", &target); - } + let alias_pathbuf = std::path::Path::new(&real_dir).to_path_buf(); + self.wasi.map_dir(alias, alias_pathbuf); + } } @@ -248,7 +270,7 @@ impl Run { let mut emscripten_globals = EmscriptenGlobals::new(&mut store, &env, &module) .map_err(|e| anyhow!("{}", e))?; env.as_mut(&mut store) - .set_data(&emscripten_globals.data, Default::default()); + .set_data(&emscripten_globals.data, self.wasi.mapped_dirs.clone().into_iter().collect()); let import_object = generate_emscripten_env(&mut store, &env, &mut emscripten_globals); let mut instance = match Instance::new(&mut store, &module, &import_object) { diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 1608f0f06e0..e16457ab0d4 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -493,8 +493,8 @@ pub fn get_all_local_packages() -> Vec { None => continue 'outer, }; - for (registry_path, registry_host) in get_all_names_in_dir(&root_dir) { - for (package_path, package_name) in get_all_names_in_dir(®istry_path) { + for (username_path, user_name) in get_all_names_in_dir(&root_dir) { + for (package_path, package_name) in get_all_names_in_dir(&username_path) { for (version_path, package_version) in get_all_names_in_dir(&package_path) { let toml_str = match std::fs::read_to_string(version_path.join("wapm.toml")) { Ok(o) => o, @@ -505,8 +505,8 @@ pub fn get_all_local_packages() -> Vec { Err(_) => continue, }; packages.push(LocalPackage { - registry: registry_host.clone(), - name: package_name.clone(), + registry: host.clone(), + name: format!("{user_name}/{package_name}"), version: package_version, manifest: manifest, path: version_path, @@ -550,7 +550,7 @@ pub fn get_package_local_wasm_file( .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; // TODO: this will just return the path for the first command, so this might not be correct - let command_name = wapm + let module_name = wapm .command .unwrap_or_default() .first() @@ -558,8 +558,19 @@ pub fn get_package_local_wasm_file( .ok_or(format!( "cannot get entrypoint for {name}@{version}: package has no commands" ))?; - - Ok(dir.join(command_name)) + + let wasm_file_name = wapm + .module + .unwrap_or_default() + .iter() + .filter(|m| m.name == module_name) + .map(|m| m.source.clone()) + .next() + .ok_or(format!( + "cannot get entrypoint for {name}@{version}: package has no commands" + ))?; + + Ok(dir.join(&wasm_file_name)) } pub fn query_command_from_registry(name: &str) -> Result { From a6d9e3e63a46305c5484bf461221a823354814de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 13:35:32 +0200 Subject: [PATCH 20/83] Map every single directory on emscripten --- Cargo.lock | 1 + lib/cli/Cargo.toml | 1 + lib/cli/src/commands/run.rs | 24 +++++++++++++++++------- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a55252b07a0..f87a69198b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3858,6 +3858,7 @@ dependencies = [ "target-lexicon 0.12.4", "tempfile", "unix_mode", + "walkdir", "wapm-toml", "wasmer", "wasmer-cache", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index d155d697ad2..db9b42b078a 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -60,6 +60,7 @@ serde_json = { version = "1.0", optional = true } target-lexicon = { version = "0.12", features = ["std"] } prettytable-rs = "0.9.0" wapm-toml = "0.1.2" +walkdir = "2.3.2" [build-dependencies] chrono = "0.4.22" diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index fd81d416afe..001f688b922 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -77,14 +77,12 @@ impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { - let mut use_pkg_fs = false; #[cfg(feature = "wasi")] { let pkg_fs = match pathbuf.parent() { Some(parent) => parent.join("pkg_fs"), None => pathbuf.join("pkg_fs"), }; if let Some(mut m) = manifest.as_ref().and_then(|m| m.package.pkg_fs_mount_point.clone()) { - use_pkg_fs = true; if m == "." { self.wasi.map_dir("/", pkg_fs); } else { @@ -104,9 +102,6 @@ impl RunWithoutFile { if real_dir.starts_with("/") { real_dir = (&real_dir[1..]).to_string(); } - if use_pkg_fs { - real_dir = format!("pkg_fs/{real_dir}"); - } let real_dir = if let Some(parent) = pathbuf.parent() { parent.join(real_dir) } else { @@ -117,8 +112,23 @@ impl RunWithoutFile { continue; } let alias_pathbuf = std::path::Path::new(&real_dir).to_path_buf(); - self.wasi.map_dir(alias, alias_pathbuf); - + self.wasi.map_dir(alias, alias_pathbuf.clone()); + + fn is_dir(e: &walkdir::DirEntry) -> bool { + let meta = match e.metadata() { + Ok(o) => o, + Err(_) => return false, + }; + meta.is_dir() + } + + let root_display = format!("{}", alias_pathbuf.display()); + for entry in walkdir::WalkDir::new(&alias_pathbuf).into_iter().filter_entry(|e| is_dir(e)).filter_map(|e| e.ok()) { + let pathbuf = entry.path().canonicalize().unwrap(); + let path = format!("{}", pathbuf.display()); + let relativepath = path.replacen(&root_display, "", 1); + self.wasi.map_dir(&format!("/{alias}{relativepath}"), pathbuf); + } } } From 7edfd6875958c9a26e7e9f995422eb3dda1f38d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 13:44:08 +0200 Subject: [PATCH 21/83] cargo fmt --- lib/cli/src/cli.rs | 5 ++- lib/cli/src/commands/run.rs | 33 +++++++++++----- lib/cli/src/commands/run/wasi.rs | 4 +- lib/emscripten/src/env/unix/mod.rs | 12 +++--- lib/emscripten/src/lib.rs | 21 +++------- lib/registry/src/lib.rs | 63 +++++++++++++++++------------- 6 files changed, 75 insertions(+), 63 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index d654585c7a2..a792212883e 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -260,8 +260,9 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { &package.registry, &package.name, &package.version, - ).map_err(|e| anyhow!("{e}"))?; - + ) + .map_err(|e| anyhow!("{e}"))?; + // Try finding the local package let mut args_without_package = args.clone(); args_without_package.remove(1); diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 001f688b922..ff8c8eefee3 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -76,18 +76,21 @@ pub struct RunWithoutFile { impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { - - #[cfg(feature = "wasi")] { + #[cfg(feature = "wasi")] + { let pkg_fs = match pathbuf.parent() { Some(parent) => parent.join("pkg_fs"), None => pathbuf.join("pkg_fs"), }; - if let Some(mut m) = manifest.as_ref().and_then(|m| m.package.pkg_fs_mount_point.clone()) { + if let Some(mut m) = manifest + .as_ref() + .and_then(|m| m.package.pkg_fs_mount_point.clone()) + { if m == "." { self.wasi.map_dir("/", pkg_fs); } else { - if m.starts_with(".") { - m = format!("{}{}", pkg_fs.display(), &m[1..]); + if m.starts_with(".") { + m = format!("{}{}", pkg_fs.display(), &m[1..]); } let path = std::path::Path::new(&m).to_path_buf(); self.wasi.map_dir("/", path); @@ -108,7 +111,10 @@ impl RunWithoutFile { pathbuf.join(real_dir) }; if !real_dir.exists() { - println!("warning: cannot map {alias:?} to {}: directory does not exist", real_dir.display()); + println!( + "warning: cannot map {alias:?} to {}: directory does not exist", + real_dir.display() + ); continue; } let alias_pathbuf = std::path::Path::new(&real_dir).to_path_buf(); @@ -123,11 +129,16 @@ impl RunWithoutFile { } let root_display = format!("{}", alias_pathbuf.display()); - for entry in walkdir::WalkDir::new(&alias_pathbuf).into_iter().filter_entry(|e| is_dir(e)).filter_map(|e| e.ok()) { + for entry in walkdir::WalkDir::new(&alias_pathbuf) + .into_iter() + .filter_entry(|e| is_dir(e)) + .filter_map(|e| e.ok()) + { let pathbuf = entry.path().canonicalize().unwrap(); let path = format!("{}", pathbuf.display()); let relativepath = path.replacen(&root_display, "", 1); - self.wasi.map_dir(&format!("/{alias}{relativepath}"), pathbuf); + self.wasi + .map_dir(&format!("/{alias}{relativepath}"), pathbuf); } } } @@ -279,8 +290,10 @@ impl Run { let env = FunctionEnv::new(&mut store, em_env); let mut emscripten_globals = EmscriptenGlobals::new(&mut store, &env, &module) .map_err(|e| anyhow!("{}", e))?; - env.as_mut(&mut store) - .set_data(&emscripten_globals.data, self.wasi.mapped_dirs.clone().into_iter().collect()); + env.as_mut(&mut store).set_data( + &emscripten_globals.data, + self.wasi.mapped_dirs.clone().into_iter().collect(), + ); let import_object = generate_emscripten_env(&mut store, &env, &mut emscripten_globals); let mut instance = match Instance::new(&mut store, &module, &import_object) { diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index a28eb227018..0f84fc1e8e0 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -52,10 +52,10 @@ pub struct Wasi { #[allow(dead_code)] impl Wasi { - pub fn map_dir(&mut self, alias: &str, target_on_disk: PathBuf) { self.mapped_dirs.push((alias.to_string(), target_on_disk)); - self.pre_opened_directories.push(std::path::Path::new(alias).to_path_buf()); + self.pre_opened_directories + .push(std::path::Path::new(alias).to_path_buf()); } pub fn set_env(&mut self, key: &str, value: &str) { diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index a1aff238004..a638d31be21 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -1,7 +1,5 @@ /// NOTE: These syscalls only support wasm_32 for now because they take u32 offset -use libc::{ - c_int, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, sysconf, -}; +use libc::{c_int, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, sysconf}; use std::ffi::{CStr, CString}; use std::mem; use std::os::raw::c_char; @@ -29,7 +27,7 @@ pub fn _getenv(mut ctx: FunctionEnvMut, name: i32) -> u32 { Some(s) => s, None => return 0, }; - let new_env_var = match CString::new(env_var) { + let new_env_var = match CString::new(env_var) { Ok(s) => s, Err(_) => return 0, }; @@ -52,7 +50,7 @@ pub fn _setenv(ctx: FunctionEnvMut, name: c_int, value: c_int, overwrite: debug!("=> value({:?})", value); let previous_entry = em_env.set_env_var(name.as_ref(), value.as_ref()); - + if let (0, Some(prev)) = (overwrite, previous_entry) { let _ = em_env.set_env_var(name.as_ref(), prev.as_ref()); } @@ -84,7 +82,9 @@ pub fn _unsetenv(mut ctx: FunctionEnvMut, name: c_int) -> c_int { let em_env = ctx.data(); let memory = em_env.memory(0); let name_addr = emscripten_memory_pointer!(memory.view(&ctx), name) as *const c_char; - unsafe { CStr::from_ptr(name_addr) }.to_string_lossy().to_string() + unsafe { CStr::from_ptr(name_addr) } + .to_string_lossy() + .to_string() }; debug!("=> name({:?})", name); diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index d50518b3209..f60f0cc72dc 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -87,9 +87,8 @@ impl Default for EmscriptenState { fn default() -> Self { Self { env_vars: std::env::vars_os() - .filter_map(|(k, v)| { - Some((k.to_str()?.to_string(), v.to_str()?.to_string())) - }).collect(), + .filter_map(|(k, v)| Some((k.to_str()?.to_string(), v.to_str()?.to_string()))) + .collect(), cli_args: Vec::new(), } } @@ -155,10 +154,7 @@ impl EmEnv { *w = Some(EmscriptenData::new(data.clone(), mapped_dirs)); } - pub fn get_env_var( - &self, - key: &str, - ) -> Option { + pub fn get_env_var(&self, key: &str) -> Option { let w = self.state.lock().ok()?; let result = w.env_vars.get(key).cloned(); result @@ -169,19 +165,12 @@ impl EmEnv { w.env_vars.len() } - pub fn set_env_var( - &self, - key: &str, - value: &str, - ) -> Option { + pub fn set_env_var(&self, key: &str, value: &str) -> Option { let mut w = self.state.lock().ok()?; w.env_vars.insert(key.to_string(), value.to_string()) } - pub fn remove_env_var( - &self, - key: &str, - ) -> Option { + pub fn remove_env_var(&self, key: &str) -> Option { let mut w = self.state.lock().ok()?; w.env_vars.remove(key) } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index e16457ab0d4..137571b7574 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -477,7 +477,6 @@ pub fn get_all_local_packages() -> Vec { let mut packages = Vec::new(); 'outer: for registry in get_all_available_registries().unwrap_or_default() { - let host = match url::Url::parse(®istry) { Ok(o) => o.host_str().map(|s| s.to_string()), Err(_) => continue 'outer, @@ -487,7 +486,7 @@ pub fn get_all_local_packages() -> Vec { Some(s) => s, None => continue 'outer, }; - + let root_dir = match get_global_install_dir(&host) { Some(o) => o, None => continue 'outer, @@ -558,7 +557,7 @@ pub fn get_package_local_wasm_file( .ok_or(format!( "cannot get entrypoint for {name}@{version}: package has no commands" ))?; - + let wasm_file_name = wapm .module .unwrap_or_default() @@ -569,7 +568,7 @@ pub fn get_package_local_wasm_file( .ok_or(format!( "cannot get entrypoint for {name}@{version}: package has no commands" ))?; - + Ok(dir.join(&wasm_file_name)) } @@ -588,15 +587,18 @@ pub enum QueryPackageError { name: String, version: Option, packages: Vec, - } + }, } impl QueryPackageError { pub fn get_packages(&self) -> Vec { match self { - QueryPackageError::AmbigouusName { name: _, packages } | - QueryPackageError::NoPackageFound { name: _, version: _, packages } - => packages.clone(), + QueryPackageError::AmbigouusName { name: _, packages } + | QueryPackageError::NoPackageFound { + name: _, + version: _, + packages, + } => packages.clone(), _ => Vec::new(), } } @@ -622,8 +624,10 @@ pub fn query_package_from_registry( }) }; - let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q) - .map_err(|e| QueryPackageError::ErrorSendingQuery(format!("Error sending GetPackagesQuery:  {e}")))?; + let response: get_packages_query::ResponseData = + execute_query(registry_url, "", &q).map_err(|e| { + QueryPackageError::ErrorSendingQuery(format!("Error sending GetPackagesQuery:  {e}")) + })?; let available_packages = response .package @@ -690,12 +694,12 @@ pub fn query_package_from_registry( match queried_package { None => { - return Err(QueryPackageError::NoPackageFound { - name: name.to_string(), + return Err(QueryPackageError::NoPackageFound { + name: name.to_string(), version: version.as_ref().map(|s| s.to_string()), packages: available_packages, }); - }, + } Some(s) => Ok(s), } } @@ -752,7 +756,10 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result) -> Result<(LocalPackage, PathBuf), String> { +pub fn install_package( + name: &str, + version: Option<&str>, +) -> Result<(LocalPackage, PathBuf), String> { let registries = get_all_available_registries()?; let mut url_of_package = None; let mut error_packages = Vec::new(); @@ -784,7 +791,8 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag .filter_map(|s| Some(format!("{}", s.host_str()?))) .collect::>(); - let mut error_str = format!("Package {version_str} not found in registries {registries_searched:?}."); + let mut error_str = + format!("Package {version_str} not found in registries {registries_searched:?}."); let mut did_you_mean = error_packages .iter() .flat_map(|error| { @@ -800,7 +808,7 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag .into_iter() }) .collect::>(); - + let did_you_mean = if did_you_mean.is_empty() { String::new() } else { @@ -809,8 +817,7 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) }; - let (_, package_info) = url_of_package - .ok_or(format!("{error_str}{did_you_mean}"))?; + let (_, package_info) = url_of_package.ok_or(format!("{error_str}{did_you_mean}"))?; let host = url::Url::parse(&package_info.registry) .map_err(|e| format!("invalid url: {}: {e}", package_info.registry))? @@ -818,8 +825,7 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag .ok_or(format!("invalid url: {}", package_info.registry))? .to_string(); - let dir = get_package_local_dir( - &host, &package_info.package, &package_info.version)?; + let dir = get_package_local_dir(&host, &package_info.package, &package_info.version)?; let version = package_info.version; let name = package_info.package; @@ -854,13 +860,16 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result<(LocalPackag "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" ))?; - Ok((LocalPackage { - registry: package_info.registry.clone(), - name: wapm_toml.package.name.clone(), - version: wapm_toml.package.version.to_string(), - manifest: wapm_toml, - path: target_path.clone(), - }, target_path.join(&entrypoint_module.source))) + Ok(( + LocalPackage { + registry: package_info.registry.clone(), + name: wapm_toml.package.name.clone(), + version: wapm_toml.package.version.to_string(), + manifest: wapm_toml, + path: target_path.clone(), + }, + target_path.join(&entrypoint_module.source), + )) } pub fn test_if_registry_present(registry: &str) -> Result { From e696d78b70801fbe015136165c984696068a86e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 13:53:49 +0200 Subject: [PATCH 22/83] cargo update --- Cargo.lock | 183 ++++++++++++++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f87a69198b2..db16a7ee4c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arbitrary" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44124848854b941eafdb34f05b3bcf59472f643c7e151eba7c2b69daa469ed5" +checksum = "d86fd10d912cab78764cc44307d9cd5f164e09abbeb87fb19fb6d95937e8da5f" dependencies = [ "derive_arbitrary", ] @@ -291,7 +291,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" dependencies = [ - "clap 3.2.21", + "clap 3.2.22", "heck", "indexmap", "log", @@ -351,9 +351,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.21" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -389,18 +389,18 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.15.1", ] [[package]] name = "clap" -version = "4.0.5" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e0d0fdd7dc774d433c78c9de5695ca04724beaec6a7d4d13ac6014608f3eaf" +checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74" dependencies = [ "atty", "bitflags", - "clap_derive 4.0.1", + "clap_derive 4.0.10", "clap_lex 0.3.0", "once_cell", "strsim 0.10.0", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.1" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7" +checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" dependencies = [ "heck", "proc-macro-error", @@ -715,26 +715,24 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "once_cell", ] [[package]] @@ -787,9 +785,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "darling" -version = "0.13.4" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" dependencies = [ "darling_core", "darling_macro", @@ -797,9 +795,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.4" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" dependencies = [ "fnv", "ident_case", @@ -810,9 +808,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" dependencies = [ "darling_core", "quote", @@ -855,9 +853,9 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", @@ -1008,18 +1006,18 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4799cdb24d48f1f8a7a98d06b7fde65a85a2d1e42b25a889f5406aa1fbefe074" +checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea83a3fbdc1d999ccfbcbee717eab36f8edf2d71693a23ce0d7cca19e085304c" +checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" dependencies = [ "darling", "proc-macro2", @@ -1422,7 +1420,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.3", + "itoa 1.0.4", ] [[package]] @@ -1481,7 +1479,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.3", + "itoa 1.0.4", "pin-project-lite", "socket2", "tokio", @@ -1505,14 +1503,13 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.48" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0" +checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", - "once_cell", "wasm-bindgen", "winapi", ] @@ -1625,9 +1622,9 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -1640,15 +1637,15 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] @@ -1682,9 +1679,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libfuzzer-sys" @@ -1715,9 +1712,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "llvm-sys" -version = "120.2.4" +version = "120.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" +checksum = "a1c9655eec036faf512507746ce70765bda72ed98e52b4328f0d7b93e970c6d8" dependencies = [ "cc", "lazy_static", @@ -1728,9 +1725,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -1983,9 +1980,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "oorandom" @@ -2040,9 +2037,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3aa1482d3a9cb7547932f54a20910090073e81b3b7b236277c91698a10f83e" +checksum = "38f2f4f4738333cd181e9b52431ee415fa6d3646f0fe5c9572852218ec2cfe18" dependencies = [ "libc", "raw-window-handle 0.3.4", @@ -2113,9 +2110,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ "thiserror", "ucd-trie", @@ -2261,9 +2258,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -2344,9 +2341,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -2825,7 +2822,7 @@ version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ - "itoa 1.0.3", + "itoa 1.0.4", "ryu", "serde", ] @@ -2837,7 +2834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.3", + "itoa 1.0.4", "ryu", "serde", ] @@ -2923,9 +2920,9 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" @@ -3037,9 +3034,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -3162,9 +3159,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" @@ -3327,9 +3324,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", @@ -3340,9 +3337,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -3351,18 +3348,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ "matchers", "once_cell", @@ -3392,9 +3389,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "trybuild" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f408301c7480f9e6294eb779cfc907f54bd901a9660ef24d7f233ed5376485" +checksum = "9004c888f43705ebcb34f01d8c6f76616e3cf253cdb356e5de3bb0a7aa11aa5c" dependencies = [ "glob", "once_cell", @@ -3711,9 +3708,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d443c5a7daae71697d97ec12ad70b4fe8766d3a0f4db16158ac8b781365892f7" +checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" dependencies = [ "leb128", ] @@ -3845,7 +3842,7 @@ dependencies = [ "bytesize", "cfg-if 1.0.0", "chrono", - "clap 4.0.5", + "clap 4.0.10", "colored 2.0.0", "dirs 4.0.0", "distance", @@ -3911,7 +3908,7 @@ dependencies = [ "atty", "bytesize", "cfg-if 1.0.0", - "clap 3.2.21", + "clap 3.2.22", "colored 2.0.0", "distance", "fern", @@ -4012,9 +4009,9 @@ dependencies = [ [[package]] name = "wasmer-inline-c" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2405c99de49dc05338e5ed2eb397fe70b7128340d960507d0ba716f7d29a91a" +checksum = "7c4e7a2a3363ceeb2ee60371af9460748f2bf53569b58627f1f640284ab07778" dependencies = [ "assert_cmd", "cc", @@ -4272,21 +4269,21 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wasmparser" -version = "0.89.1" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +checksum = "7da34cec2a8c23db906cdf8b26e988d7a7f0d549eb5d51299129647af61a1b37" dependencies = [ "indexmap", ] [[package]] name = "wasmprinter" -version = "0.2.39" +version = "0.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9e5ee2f56cc8a5da489558114e8c118e5a8416d96aefe63dcf1b5b05b858c6" +checksum = "ca4374ec27194a12b85aa0e1681a42d5800e97f11c036fa85dea8087c8ccb10b" dependencies = [ "anyhow", - "wasmparser 0.89.1", + "wasmparser 0.92.0", ] [[package]] @@ -4309,23 +4306,23 @@ dependencies = [ [[package]] name = "wast" -version = "46.0.0" +version = "47.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0ab19660e3ea6891bba69167b9be40fad00fb1fe3dd39c5eebcee15607131b" +checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.16.0", + "wasm-encoder 0.18.0", ] [[package]] name = "wat" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f775282def4d5bffd94d60d6ecd57bfe6faa46171cdbf8d32bd5458842b1e3e" +checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" dependencies = [ - "wast 46.0.0", + "wast 47.0.1", ] [[package]] From 7147694443f52b5d18b98c4316e6c40471716c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 15:04:06 +0200 Subject: [PATCH 23/83] Fix "make lint" --- lib/registry/src/lib.rs | 75 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 137571b7574..595765a08ca 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -7,7 +7,6 @@ use serde::Serialize; pub mod graphql { - use core::time; use graphql_client::*; #[cfg(not(target_os = "wasi"))] use reqwest::{ @@ -49,13 +48,13 @@ pub mod graphql { /// there was a failure while attempting to set up the proxy. pub fn maybe_set_up_proxy() -> anyhow::Result> { use std::env; - let proxy = if let Ok(proxy_url) = env::var("ALL_PROXY").or(env::var("all_proxy")) { + 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(env::var("https_proxy")) + } else if let Ok(https_proxy_url) = env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy")) { reqwest::Proxy::https(&https_proxy_url) .map(|proxy| (https_proxy_url, proxy, "HTTPS_PROXY")) - } else if let Ok(http_proxy_url) = env::var("HTTP_PROXY").or(env::var("http_proxy")) { + } else if let Ok(http_proxy_url) = env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy")) { reqwest::Proxy::http(&http_proxy_url) .map(|proxy| (http_proxy_url, proxy, "http_proxy")) } else { @@ -159,7 +158,7 @@ pub mod graphql { let mut res = client .post(registry_url) .multipart(form) - .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) + .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string())) .header(USER_AGENT, user_agent); if let Some(t) = timeout { @@ -169,7 +168,8 @@ pub mod graphql { let res = res.send()?; let _: Response = res.json()?; - return Ok(()); + + Ok(()) } pub fn execute_query_modifier_inner( @@ -215,7 +215,7 @@ pub mod graphql { let mut res = client .post(registry_url) .multipart(form) - .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) + .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string())) .header(USER_AGENT, user_agent); if let Some(t) = timeout { @@ -226,7 +226,7 @@ pub mod graphql { let response_body: Response = res.json()?; if let Some(errors) = response_body.errors { let error_messages: Vec = errors.into_iter().map(|err| err.message).collect(); - return Err(anyhow::anyhow!("{}", error_messages.join(", ")).into()); + return Err(anyhow::anyhow!("{}", error_messages.join(", "))); } Ok(response_body.data.expect("missing response data")) } @@ -342,7 +342,7 @@ pub struct Registry { fn format_graphql(registry: &str) -> String { if registry.ends_with("/graphql") { registry.to_string() - } else if registry.ends_with("/") { + } else if registry.ends_with('/') { format!("{}graphql", registry) } else { format!("{}/graphql", registry) @@ -366,7 +366,7 @@ impl PartialWapmConfig { if let Some(pwd) = std::env::var("PWD").ok() { return Ok(PathBuf::from(pwd)); } - Ok(std::env::current_dir()?) + std::env::current_dir() } pub fn get_folder() -> Result { @@ -411,8 +411,8 @@ pub struct PackageDownloadInfo { pub url: String, } -pub fn get_command_local(name: &str) -> Result { - Err(format!("unimplemented")) +pub fn get_command_local(_name: &str) -> Result { + Err("unimplemented".to_string()) } pub fn get_package_local_dir( @@ -420,17 +420,17 @@ pub fn get_package_local_dir( name: &str, version: &str, ) -> Result { - if !name.contains("/") { + if !name.contains('/') { return Err(format!( "package name has to be in the format namespace/package: {name:?}" )); } let namespace = name - .split("/") - .nth(0) + .split('/') + .next() .ok_or(format!("missing namespace for {name:?}"))?; let name = name - .split("/") + .split('/') .nth(1) .ok_or(format!("missing name for {name:?}"))?; let install_dir = @@ -507,7 +507,7 @@ pub fn get_all_local_packages() -> Vec { registry: host.clone(), name: format!("{user_name}/{package_name}"), version: package_version, - manifest: manifest, + manifest, path: version_path, }); } @@ -532,8 +532,8 @@ pub fn get_local_package(name: &str, version: Option<&str>) -> Option(&wapm_toml_str) - .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; + .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}: {e}"))?; // TODO: this will just return the path for the first command, so this might not be correct let module_name = wapm @@ -572,8 +572,8 @@ pub fn get_package_local_wasm_file( Ok(dir.join(&wasm_file_name)) } -pub fn query_command_from_registry(name: &str) -> Result { - Err(format!("unimplemented")) +pub fn query_command_from_registry(_name: &str) -> Result { + Err("unimplemented".to_string()) } #[derive(Debug, Clone, PartialEq, PartialOrd)] @@ -613,8 +613,8 @@ pub fn query_package_from_registry( use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; - let q = if name.contains("/") { - let name = name.split("/").nth(1).unwrap(); + let q = if name.contains('/') { + let name = name.split('/').nth(1).unwrap(); GetPackagesQuery::build_query(get_packages_query::Variables { names: vec![name.to_string()], }) @@ -669,7 +669,7 @@ pub fn query_package_from_registry( .flat_map(|v| v.into_iter()) .collect::>(); - if !name.contains("/") { + if !name.contains('/') { return Err(QueryPackageError::AmbigouusName { name: name.to_string(), packages: available_packages, @@ -678,16 +678,16 @@ pub fn query_package_from_registry( let queried_package = available_packages .iter() - .filter_map(|v| { - if name.contains("/") && v.package != name { - return None; + .filter(|v| { + if name.contains('/') && v.package != name { + return false; } - if version.is_some() && v.version != version.clone().unwrap() { - return None; + if version.is_some() && v.version != version.unwrap() { + return false; } - Some(v) + true }) .next() .cloned(); @@ -769,7 +769,7 @@ pub fn install_package( if !registry_test.clone().unwrap_or(false) { continue; } - match query_package_from_registry(&r, name, version) { + match query_package_from_registry(r, name, version) { Ok(o) => { url_of_package = Some((r, o)); break; @@ -781,14 +781,14 @@ pub fn install_package( } let version_str = match version { - None => format!("{name}"), + None => name.to_string(), Some(v) => format!("{name}@{v}"), }; let registries_searched = registries .iter() .filter_map(|s| url::Url::parse(s).ok()) - .filter_map(|s| Some(format!("{}", s.host_str()?))) + .filter_map(|s| Some(s.host_str()?.to_string())) .collect::>(); let mut error_str = @@ -854,15 +854,14 @@ pub fn install_package( let modules = wapm_toml.module.clone().unwrap_or_default(); let entrypoint_module = modules .iter() - .filter(|m| m.name == module_name) - .next() + .find(|m| m.name == module_name) .ok_or(format!( "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" ))?; Ok(( LocalPackage { - registry: package_info.registry.clone(), + registry: package_info.registry, name: wapm_toml.package.name.clone(), version: wapm_toml.package.version.to_string(), manifest: wapm_toml, @@ -899,7 +898,7 @@ pub fn get_all_available_registries() -> Result, String> { } Registries::Multi(m) => { for key in m.tokens.keys() { - registries.push(format_graphql(&key)); + registries.push(format_graphql(key)); } } } From 0194f8e92ae1c3a2b47f756098303748e37db7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 15:05:44 +0200 Subject: [PATCH 24/83] cargo fmt --- lib/registry/src/lib.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 595765a08ca..a1cc3984dc7 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -48,13 +48,18 @@ pub mod graphql { /// there was a failure while attempting to set up the proxy. pub fn maybe_set_up_proxy() -> anyhow::Result> { use std::env; - let proxy = if let Ok(proxy_url) = env::var("ALL_PROXY").or_else(|_| env::var("all_proxy")) { + 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")) + } else if let Ok(https_proxy_url) = + env::var("HTTPS_PROXY").or_else(|_| env::var("https_proxy")) { reqwest::Proxy::https(&https_proxy_url) .map(|proxy| (https_proxy_url, proxy, "HTTPS_PROXY")) - } else if let Ok(http_proxy_url) = env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy")) { + } else if let Ok(http_proxy_url) = + env::var("HTTP_PROXY").or_else(|_| env::var("http_proxy")) + { reqwest::Proxy::http(&http_proxy_url) .map(|proxy| (http_proxy_url, proxy, "http_proxy")) } else { @@ -158,7 +163,9 @@ pub mod graphql { let mut res = client .post(registry_url) .multipart(form) - .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string())) + .bearer_auth( + env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()), + ) .header(USER_AGENT, user_agent); if let Some(t) = timeout { @@ -168,7 +175,7 @@ pub mod graphql { let res = res.send()?; let _: Response = res.json()?; - + Ok(()) } @@ -215,7 +222,9 @@ pub mod graphql { let mut res = client .post(registry_url) .multipart(form) - .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string())) + .bearer_auth( + env::var("WAPM_REGISTRY_TOKEN").unwrap_or_else(|_| login_token.to_string()), + ) .header(USER_AGENT, user_agent); if let Some(t) = timeout { From 862b155f82889da7b8e65b1e532e9a71e53fba77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 15:09:19 +0200 Subject: [PATCH 25/83] Always set WASMER_BUILD_GIT_HASH --- lib/cli/build.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/cli/build.rs b/lib/cli/build.rs index ae2f7924d73..9cd64961891 100644 --- a/lib/cli/build.rs +++ b/lib/cli/build.rs @@ -2,8 +2,6 @@ use chrono::prelude::*; use std::process::Command; pub fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX"); // Set WASMER_GIT_HASH let git_hash = Command::new("git") From b1c3f33c9d61deb80fbc490603fec0a9ed24ba65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 15:09:48 +0200 Subject: [PATCH 26/83] cargo fmt --- lib/cli/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cli/build.rs b/lib/cli/build.rs index 9cd64961891..c607120ec6b 100644 --- a/lib/cli/build.rs +++ b/lib/cli/build.rs @@ -2,7 +2,6 @@ use chrono::prelude::*; use std::process::Command; pub fn main() { - // Set WASMER_GIT_HASH let git_hash = Command::new("git") .args(&["rev-parse", "HEAD"]) From 5bc191487e2cd5230a3e440e03027dcdd41ec677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 7 Oct 2022 15:57:44 +0200 Subject: [PATCH 27/83] Try fixing errors when parsing CLI args: todo: undo clap migration --- lib/cli/src/cli.rs | 75 +++++++++++++++++++++++++++++++---------- lib/registry/src/lib.rs | 6 ++-- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index a792212883e..eaa56828c68 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -16,7 +16,7 @@ use anyhow::Result; use clap::Parser; use wasmer_registry::get_all_local_packages; -#[derive(Parser)] +#[derive(Parser, Debug)] #[cfg_attr( not(feature = "headless"), clap( @@ -145,6 +145,29 @@ enum WasmerCLIOptions { Binfmt(Binfmt), } +impl WasmerCLIOptions { + fn execute(&self) -> Result<()> { + match self { + Self::Run(options) => options.execute(), + Self::SelfUpdate(options) => options.execute(), + Self::Cache(cache) => cache.execute(), + Self::Validate(validate) => validate.execute(), + #[cfg(feature = "compiler")] + Self::Compile(compile) => compile.execute(), + #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))] + Self::CreateExe(create_exe) => create_exe.execute(), + #[cfg(feature = "static-artifact-create")] + Self::CreateObj(create_obj) => create_obj.execute(), + Self::Config(config) => config.execute(), + Self::Inspect(inspect) => inspect.execute(), + #[cfg(feature = "wast")] + Self::Wast(wast) => wast.execute(), + #[cfg(target_os = "linux")] + Self::Binfmt(binfmt) => binfmt.execute(), + } + } +} + /// The main function for the Wasmer CLI tool. pub fn wasmer_main() { // We allow windows to print properly colors @@ -172,6 +195,9 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let mut args_without_first_arg = args.clone(); args_without_first_arg.remove(0); + let mut args_without_first_and_second_arg = args_without_first_arg.clone(); + args_without_first_and_second_arg.remove(0); + match (firstarg, secondarg) { (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(true), (Some("-h"), _) => return print_help(false), @@ -183,21 +209,6 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { return print_version(false) } - - (Some("cache"), _) => Cache::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("compile"), _) => Compile::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("config"), _) => Config::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("create-exe"), _) => { - CreateExe::try_parse_from(args_without_first_arg.iter())?.execute() - } - (Some("inspect"), _) => Inspect::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("self-update"), _) => { - SelfUpdate::try_parse_from(args_without_first_arg.iter())?.execute() - } - (Some("validate"), _) => Validate::try_parse_from(args_without_first_arg.iter())?.execute(), - (Some("wast"), _) => Wast::try_parse_from(args_without_first_arg.iter())?.execute(), - #[cfg(feature = "binfmt")] - (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), (Some("list"), _) => { use prettytable::{format, row, Table}; @@ -298,13 +309,43 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { } } } else { - return print_help(true); + return WasmerCLIOptions::try_parse()?.execute(); + /* + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(|_| {})); + let cli_result = std::panic::catch_unwind(|| WasmerCLIOptions::try_parse_from(args.iter())); + std::panic::set_hook(hook); + + match cli_result { + Ok(Ok(opts)) => return opts.execute(), + Ok(Err(e)) => { + return Err(anyhow::anyhow!("Error: {e}")); + }, + Err(_) => { + return Err(anyhow::anyhow!("Error when trying to parse CLI arguments")); + } + }*/ } } } } fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { + let prohibited_package_names = [ + "run", + "cache", + "validate", + "compile", + "create-exe", + "create-obj", + "config", + "inspect", + "wast", + "help", + ]; + if prohibited_package_names.contains(&s.trim()) { + return Err(anyhow::anyhow!("Invalid package name {s:?}")); + } let package_version = s.split("@").collect::>(); match package_version.as_slice() { &[p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index a1cc3984dc7..6d6e3d87ace 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -530,7 +530,7 @@ pub fn get_all_local_packages() -> Vec { pub fn get_local_package(name: &str, version: Option<&str>) -> Option { get_all_local_packages() .iter() - .filter(|p| { + .find(|p| { if p.name != name { return false; } @@ -541,7 +541,6 @@ pub fn get_local_package(name: &str, version: Option<&str>) -> Option Date: Sun, 9 Oct 2022 19:45:03 +0200 Subject: [PATCH 28/83] Downgrade to clap v3 --- Cargo.lock | 43 ++--------------------- lib/cli-compiler/src/commands/compile.rs | 4 +-- lib/cli-compiler/src/commands/validate.rs | 2 +- lib/cli-compiler/src/store.rs | 2 +- lib/cli/Cargo.toml | 2 +- lib/cli/src/commands/compile.rs | 4 +-- lib/cli/src/commands/create_exe.rs | 4 +-- lib/cli/src/commands/create_obj.rs | 8 ++--- lib/cli/src/commands/inspect.rs | 2 +- lib/cli/src/commands/run.rs | 2 +- lib/cli/src/commands/run/wasi.rs | 8 ++--- lib/cli/src/commands/validate.rs | 2 +- lib/cli/src/commands/wast.rs | 2 +- lib/cli/src/compilers/llvm.rs | 6 ++-- lib/cli/src/store.rs | 2 +- 15 files changed, 28 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db16a7ee4c0..99ce836cf20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,8 +383,8 @@ checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", - "clap_derive 3.2.18", - "clap_lex 0.2.4", + "clap_derive", + "clap_lex", "indexmap", "once_cell", "strsim 0.10.0", @@ -392,21 +392,6 @@ dependencies = [ "textwrap 0.15.1", ] -[[package]] -name = "clap" -version = "4.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74" -dependencies = [ - "atty", - "bitflags", - "clap_derive 4.0.10", - "clap_lex 0.3.0", - "once_cell", - "strsim 0.10.0", - "termcolor", -] - [[package]] name = "clap_derive" version = "3.2.18" @@ -420,19 +405,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_derive" -version = "4.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -442,15 +414,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clap_lex" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "cmake" version = "0.1.48" @@ -3842,7 +3805,7 @@ dependencies = [ "bytesize", "cfg-if 1.0.0", "chrono", - "clap 4.0.10", + "clap 3.2.22", "colored 2.0.0", "dirs 4.0.0", "distance", diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs index 04e95168d5d..bcffabff54f 100644 --- a/lib/cli-compiler/src/commands/compile.rs +++ b/lib/cli-compiler/src/commands/compile.rs @@ -14,11 +14,11 @@ use wasmer_types::{ /// The options for the `wasmer compile` subcommand pub struct Compile { /// Input file - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli-compiler/src/commands/validate.rs b/lib/cli-compiler/src/commands/validate.rs index 3dd464582d6..e6d832d10c6 100644 --- a/lib/cli-compiler/src/commands/validate.rs +++ b/lib/cli-compiler/src/commands/validate.rs @@ -9,7 +9,7 @@ use wasmer_types::{is_wasm, CpuFeature, Target, Triple}; /// The options for the `wasmer validate` subcommand pub struct Validate { /// File to validate as WebAssembly - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli-compiler/src/store.rs b/lib/cli-compiler/src/store.rs index e7bd2b3cb63..0411798f572 100644 --- a/lib/cli-compiler/src/store.rs +++ b/lib/cli-compiler/src/store.rs @@ -115,7 +115,7 @@ pub struct CompilerOptions { /// LLVM debug directory, where IR and object files will be written to. #[allow(unused)] #[cfg(feature = "llvm")] - #[cfg_attr(feature = "llvm", clap(long, value_parser = clap::value_parser!(std::ffi::OsString)))] + #[cfg_attr(feature = "llvm", clap(long, parse(from_os_str)))] llvm_debug_dir: Option, #[clap(flatten)] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index db9b42b078a..9cd3ec1a84a 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -44,7 +44,7 @@ atty = "0.2" colored = "2.0" anyhow = "1.0" spinner = "0.5.0" -clap = { version = "4.0.5", features = ["derive"] } +clap = { version = "3.2.22", features = ["derive"] } # For the function names autosuggestion distance = "0.4" # For the inspect subcommand diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index dd21e539850..d6be43411c9 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -9,11 +9,11 @@ use wasmer::*; /// The options for the `wasmer compile` subcommand pub struct Compile { /// Input file - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 0f1dff2c573..a3b51f0836a 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -46,11 +46,11 @@ struct CrossCompileSetup { /// The options for the `wasmer create-exe` subcommand pub struct CreateExe { /// Input file - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "OUTPUT PATH", short = 'o', parse(from_os_str))] output: PathBuf, /// Compilation Target triple diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index f86db8418d9..79995fd6f15 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -19,18 +19,18 @@ const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_deserialize_modul /// The options for the `wasmer create-exe` subcommand pub struct CreateObj { /// Input file - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, /// Output file - #[clap(name = "OUTPUT_PATH", short = 'o', value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "OUTPUT_PATH", short = 'o', parse(from_os_str))] output: PathBuf, /// Header output file #[clap( name = "OUTPUT_HEADER_PATH", long = "output-header-path", - value_parser = clap::value_parser!(std::ffi::OsString) + parse(from_os_str) )] header_output: Option, @@ -56,7 +56,7 @@ pub struct CreateObj { #[clap(name = "OBJECT_FORMAT", long = "object-format", verbatim_doc_comment)] object_format: Option, - #[clap(short = 'm', number_of_values = 1)] + #[clap(short = 'm', multiple = true, number_of_values = 1)] cpu_features: Vec, #[clap(flatten)] diff --git a/lib/cli/src/commands/inspect.rs b/lib/cli/src/commands/inspect.rs index 461e20e271f..4d56faa6136 100644 --- a/lib/cli/src/commands/inspect.rs +++ b/lib/cli/src/commands/inspect.rs @@ -9,7 +9,7 @@ use wasmer::*; /// The options for the `wasmer validate` subcommand pub struct Inspect { /// File to validate as WebAssembly - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index ff8c8eefee3..8aa1cf11cc2 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -174,7 +174,7 @@ pub struct Run { disable_cache: bool, /// File to run - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, /// Invoke a specified function diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 0f84fc1e8e0..bbe9ec8c121 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -18,18 +18,18 @@ pub struct Wasi { pub(crate) pre_opened_directories: Vec, /// Map a host directory to a different location for the Wasm module - #[arg( + #[clap( long = "mapdir", name = "GUEST_DIR:HOST_DIR", - value_parser = parse_mapdir, + parse(try_from_str = parse_mapdir), )] pub(crate) mapped_dirs: Vec<(String, PathBuf)>, /// Pass custom environment variables - #[arg( + #[clap( long = "env", name = "KEY=VALUE", - value_parser = parse_envvar, + parse(try_from_str = parse_envvar), )] pub(crate) env_vars: Vec<(String, String)>, diff --git a/lib/cli/src/commands/validate.rs b/lib/cli/src/commands/validate.rs index a1baf49ba56..4f620ecc3d2 100644 --- a/lib/cli/src/commands/validate.rs +++ b/lib/cli/src/commands/validate.rs @@ -8,7 +8,7 @@ use wasmer::*; /// The options for the `wasmer validate` subcommand pub struct Validate { /// File to validate as WebAssembly - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/commands/wast.rs b/lib/cli/src/commands/wast.rs index c8d62d5184d..5064a94c8e8 100644 --- a/lib/cli/src/commands/wast.rs +++ b/lib/cli/src/commands/wast.rs @@ -9,7 +9,7 @@ use wasmer_wast::Wast as WastSpectest; /// The options for the `wasmer wast` subcommand pub struct Wast { /// Wast file to run - #[clap(name = "FILE", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(name = "FILE", parse(from_os_str))] path: PathBuf, #[clap(flatten)] diff --git a/lib/cli/src/compilers/llvm.rs b/lib/cli/src/compilers/llvm.rs index fd312a1fdda..b7a7a95e42d 100644 --- a/lib/cli/src/compilers/llvm.rs +++ b/lib/cli/src/compilers/llvm.rs @@ -2,15 +2,15 @@ /// LLVM backend flags. pub struct LLVMCLIOptions { /// Emit LLVM IR before optimization pipeline. - #[clap(long = "llvm-pre-opt-ir", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(long = "llvm-pre-opt-ir", parse(from_os_str))] pre_opt_ir: Option, /// Emit LLVM IR after optimization pipeline. - #[clap(long = "llvm-post-opt-ir", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(long = "llvm-post-opt-ir", parse(from_os_str))] post_opt_ir: Option, /// Emit LLVM generated native code object file. - #[clap(long = "llvm-object-file", value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(long = "llvm-object-file", parse(from_os_str))] obj_file: Option, } diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 76ee227e22e..49b897c7bc3 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -47,7 +47,7 @@ pub struct CompilerOptions { /// LLVM debug directory, where IR and object files will be written to. #[cfg(feature = "llvm")] - #[clap(long, value_parser = clap::value_parser!(std::ffi::OsString))] + #[clap(long, parse(from_os_str))] llvm_debug_dir: Option, #[clap(flatten)] From 5161078d40339932a41d1a1c44e24071d7ef0f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 19:46:58 +0200 Subject: [PATCH 29/83] cargo fmt --- lib/cli/src/cli.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index eaa56828c68..bdf986809cd 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -310,16 +310,16 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { } } else { return WasmerCLIOptions::try_parse()?.execute(); - /* + /* let hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| {})); let cli_result = std::panic::catch_unwind(|| WasmerCLIOptions::try_parse_from(args.iter())); std::panic::set_hook(hook); - + match cli_result { Ok(Ok(opts)) => return opts.execute(), - Ok(Err(e)) => { - return Err(anyhow::anyhow!("Error: {e}")); + Ok(Err(e)) => { + return Err(anyhow::anyhow!("Error: {e}")); }, Err(_) => { return Err(anyhow::anyhow!("Error when trying to parse CLI arguments")); From d0372d5707b210f3027328eff267fdde84f3501d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 23:10:37 +0200 Subject: [PATCH 30/83] Fix last changes from upgrading to clap v4 --- lib/cli/src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 8aa1cf11cc2..c7d6e60c603 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -213,7 +213,7 @@ pub struct Run { debug: bool, #[cfg(feature = "debug")] - #[clap(short, long, num_args(0..))] + #[clap(short, long, parse(from_occurrences))] verbose: u8, /// Application arguments From 5b16e058a2a6fcc086d143df1a57f8540d93b070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 23:16:39 +0200 Subject: [PATCH 31/83] Fix issue in chrono dependency --- Cargo.lock | 26 ++++---------------------- lib/cli/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99ce836cf20..5d370304c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,7 +344,6 @@ dependencies = [ "js-sys", "num-integer", "num-traits", - "time 0.1.44", "wasm-bindgen", "winapi", ] @@ -1201,7 +1200,7 @@ dependencies = [ "cfg-if 1.0.0", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1815,7 +1814,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.36.1", ] @@ -3155,17 +3154,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "time" version = "0.2.27" @@ -3546,12 +3534,6 @@ dependencies = [ "toml", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3965,7 +3947,7 @@ dependencies = [ "lazy_static", "libc", "log", - "time 0.2.27", + "time", "wasmer", "wasmer-types", ] @@ -4173,7 +4155,7 @@ version = "3.0.0-beta.2" dependencies = [ "byteorder", "serde", - "time 0.2.27", + "time", "wasmer-derive", "wasmer-types", ] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 9cd3ec1a84a..91a58bd01b6 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -63,7 +63,7 @@ wapm-toml = "0.1.2" walkdir = "2.3.2" [build-dependencies] -chrono = "0.4.22" +chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] } [target.'cfg(target_os = "linux")'.dependencies] unix_mode = "0.1.3" From 0ed3be1d1b45ca43f00f78425ea85b0cca87962e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 23:37:28 +0200 Subject: [PATCH 32/83] Fix clippy issues --- lib/cli/src/cli.rs | 48 ++++++++++++++++--------------------- lib/cli/src/commands/run.rs | 6 ++--- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index bdf986809cd..a4c3a873f4f 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -199,15 +199,15 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { args_without_first_and_second_arg.remove(0); match (firstarg, secondarg) { - (None, _) | (Some("help"), _) | (Some("--help"), _) => return print_help(true), - (Some("-h"), _) => return print_help(false), + (None, _) | (Some("help"), _) | (Some("--help"), _) => print_help(true), + (Some("-h"), _) => print_help(false), (Some("-vV"), _) | (Some("version"), Some("--verbose")) - | (Some("--version"), Some("--verbose")) => return print_version(true), + | (Some("--version"), Some("--verbose")) => print_version(true), (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { - return print_version(false) + print_version(false) } (Some("list"), _) => { use prettytable::{format, row, Table}; @@ -224,12 +224,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { .collect::>() .join(" \r\n"); - row![ - pkg.registry.clone(), - pkg.name.clone(), - pkg.version.clone(), - commands - ] + row![pkg.registry, pkg.name, pkg.version, commands] }) .collect::>(); @@ -238,7 +233,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { println!("--------------------------------------"); println!("Registry Package Version Commands "); println!("======================================"); - println!(""); + println!(); } else { let mut table = Table::init(rows); table.set_titles(row!["Registry", "Package", "Version", "Commands"]); @@ -250,7 +245,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { Ok(()) } (Some("run"), Some(package)) | (Some(package), _) => { - if package.starts_with("-") { + if package.starts_with('-') { return Err(anyhow!("Unknown CLI argument {package:?}")); } @@ -261,12 +256,11 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { std::panic::set_hook(hook); if let Ok(Ok(run)) = result { - return run.execute(); + run.execute() } else if let Ok((package, version)) = split_version(package) { - if let Some(package) = wasmer_registry::get_local_package( - &package, - version.as_ref().map(|s| s.as_str()), - ) { + if let Some(package) = + wasmer_registry::get_local_package(&package, version.as_deref()) + { let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( &package.registry, &package.name, @@ -278,7 +272,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(local_package_wasm_path, Some(package.manifest.clone())) + .into_run_args(local_package_wasm_path, Some(package.manifest)) .execute(); } @@ -290,7 +284,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { ]) .start(); - let v = version.as_ref().map(|s| s.as_str()); + let v = version.as_deref(); let result = wasmer_registry::install_package(&package, v); sp.close(); print!("\r\n"); @@ -300,16 +294,16 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { let mut args_without_package = args.clone(); args_without_package.remove(1); return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(buf, Some(package.manifest.clone())) + .into_run_args(buf, Some(package.manifest)) .execute(); } Err(e) => { println!("{e}"); - return Ok(()); + Ok(()) } } } else { - return WasmerCLIOptions::try_parse()?.execute(); + WasmerCLIOptions::try_parse()?.execute() /* let hook = std::panic::take_hook(); std::panic::set_hook(Box::new(|_| {})); @@ -346,10 +340,10 @@ fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { if prohibited_package_names.contains(&s.trim()) { return Err(anyhow::anyhow!("Invalid package name {s:?}")); } - let package_version = s.split("@").collect::>(); - match package_version.as_slice() { - &[p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), - &[p] => Ok((p.trim().to_string(), None)), + let package_version = s.split('@').collect::>(); + match *package_version.as_slice() { + [p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), + [p] => Ok((p.trim().to_string(), None)), _ => Err(anyhow!("Invalid package / version: {s:?}")), } } @@ -365,6 +359,7 @@ fn print_help(verbose: bool) -> Result<(), anyhow::Error> { Ok(()) } +#[allow(unused_mut, clippy::vec_init_then_push)] fn print_version(verbose: bool) -> Result<(), anyhow::Error> { if !verbose { println!("{}", env!("CARGO_PKG_VERSION")); @@ -380,7 +375,6 @@ fn print_version(verbose: bool) -> Result<(), anyhow::Error> { println!("commit-date: {}", env!("WASMER_BUILD_DATE")); println!("host: {}", target_lexicon::HOST); println!("compiler: {}", { - #[allow(unused_mut)] let mut s = Vec::<&'static str>::new(); #[cfg(feature = "singlepass")] diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index c7d6e60c603..979cc8ce5d9 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -89,7 +89,7 @@ impl RunWithoutFile { if m == "." { self.wasi.map_dir("/", pkg_fs); } else { - if m.starts_with(".") { + if m.starts_with('.') { m = format!("{}{}", pkg_fs.display(), &m[1..]); } let path = std::path::Path::new(&m).to_path_buf(); @@ -102,7 +102,7 @@ impl RunWithoutFile { if let Some(fs) = manifest.as_ref().and_then(|m| m.fs.as_ref()) { for (alias, real_dir) in fs.iter() { let mut real_dir = format!("{}", real_dir.display()); - if real_dir.starts_with("/") { + if real_dir.starts_with('/') { real_dir = (&real_dir[1..]).to_string(); } let real_dir = if let Some(parent) = pathbuf.parent() { @@ -131,7 +131,7 @@ impl RunWithoutFile { let root_display = format!("{}", alias_pathbuf.display()); for entry in walkdir::WalkDir::new(&alias_pathbuf) .into_iter() - .filter_entry(|e| is_dir(e)) + .filter_entry(is_dir) .filter_map(|e| e.ok()) { let pathbuf = entry.path().canonicalize().unwrap(); From df047b08a41a0320baaaee94548c85276fb73051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 23:46:47 +0200 Subject: [PATCH 33/83] Impl Debug for Binfmt --- lib/cli/src/commands/binfmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/binfmt.rs b/lib/cli/src/commands/binfmt.rs index e403b17be45..bba095e1e5f 100644 --- a/lib/cli/src/commands/binfmt.rs +++ b/lib/cli/src/commands/binfmt.rs @@ -22,7 +22,7 @@ enum Action { /// /// Check the wasmer repository for a systemd service definition example /// to automate the process at start-up. -#[derive(Parser)] +#[derive(Debug, Parser)] pub struct Binfmt { // Might be better to traverse the mount list /// Mount point of binfmt_misc fs From 6eade9bdd06ed6da8db2f24c38e5d2cdd3238ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Sun, 9 Oct 2022 23:59:19 +0200 Subject: [PATCH 34/83] Fix issue in Binfmt::debug --- lib/cli/src/commands/binfmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/binfmt.rs b/lib/cli/src/commands/binfmt.rs index bba095e1e5f..c6cedd9143f 100644 --- a/lib/cli/src/commands/binfmt.rs +++ b/lib/cli/src/commands/binfmt.rs @@ -8,7 +8,7 @@ use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use Action::*; -#[derive(Parser, Clone, Copy)] +#[derive(Debug, Parser, Clone, Copy)] enum Action { /// Register wasmer as binfmt interpreter Register, From 25c4fd4626962c8448a58d4f9a7dd58866244169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 10 Oct 2022 09:59:02 +0200 Subject: [PATCH 35/83] Fix issue with CLI run command not finding PathBuf to wasm file --- lib/cli/src/cli.rs | 170 ++++++++++++++++++------------------ lib/cli/src/commands/run.rs | 2 +- 2 files changed, 87 insertions(+), 85 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index a4c3a873f4f..aa722afbd5d 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -13,7 +13,7 @@ use crate::commands::Wast; use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; use anyhow::Result; -use clap::Parser; +use clap::{ErrorKind, Parser}; use wasmer_registry::get_all_local_packages; #[derive(Parser, Debug)] @@ -174,90 +174,71 @@ pub fn wasmer_main() { #[cfg(windows)] colored::control::set_virtual_terminal(true).unwrap(); - let cmd_output = parse_cli_args(); - - PrettyError::report(cmd_output); + PrettyError::report(wasmer_main_inner()) } -fn parse_cli_args() -> Result<(), anyhow::Error> { - let args = std::env::args().collect::>(); - let binpath = args.get(0).map(|s| s.as_ref()).unwrap_or(""); - +fn wasmer_main_inner() -> Result<(), anyhow::Error> { + // We try to run wasmer with the normal arguments. + // Eg. `wasmer ` + // 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 - if cfg!(target_os = "linux") && binpath.ends_with("wasmer-binfmt-interpreter") { - return Run::from_binfmt_args().execute(); - } + let args = std::env::args().collect::>(); + let binpath = args.get(0).map(|s| s.as_ref()).unwrap_or(""); let firstarg = args.get(1).map(|s| s.as_str()); let secondarg = args.get(2).map(|s| s.as_str()); - let mut args_without_first_arg = args.clone(); - args_without_first_arg.remove(0); - - let mut args_without_first_and_second_arg = args_without_first_arg.clone(); - args_without_first_and_second_arg.remove(0); - match (firstarg, secondarg) { - (None, _) | (Some("help"), _) | (Some("--help"), _) => print_help(true), - (Some("-h"), _) => print_help(false), - + (None, _) | (Some("help"), _) | (Some("--help"), _) => { + return print_help(true); + } + (Some("-h"), _) => { + return print_help(false); + } (Some("-vV"), _) | (Some("version"), Some("--verbose")) - | (Some("--version"), Some("--verbose")) => print_version(true), + | (Some("--version"), Some("--verbose")) => { + return print_version(true); + } (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { - print_version(false) + return print_version(false); } (Some("list"), _) => { - use prettytable::{format, row, Table}; - - let rows = get_all_local_packages() - .into_iter() - .map(|pkg| { - let commands = pkg - .manifest - .command - .unwrap_or_default() - .iter() - .map(|c| c.get_name()) - .collect::>() - .join(" \r\n"); - - row![pkg.registry, pkg.name, pkg.version, commands] + return print_packages(); + } + _ => {} + } + + let command = args.get(1); + let options = if cfg!(target_os = "linux") && binpath.ends_with("wasmer-binfmt-interpreter") { + WasmerCLIOptions::Run(Run::from_binfmt_args()) + } else { + match command.unwrap_or(&"".to_string()).as_ref() { + "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" + | "self-update" | "validate" | "wast" | "binfmt" => WasmerCLIOptions::parse(), + _ => { + WasmerCLIOptions::try_parse_from(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::DisplayVersion | ErrorKind::DisplayHelp => e.exit(), + _ => WasmerCLIOptions::Run(Run::parse()), + } }) - .collect::>(); - - let empty_table = rows.is_empty(); - if empty_table { - println!("--------------------------------------"); - println!("Registry Package Version Commands "); - println!("======================================"); - println!(); - } else { - 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); - let _ = table.printstd(); } - - Ok(()) } - (Some("run"), Some(package)) | (Some(package), _) => { - if package.starts_with('-') { - return Err(anyhow!("Unknown CLI argument {package:?}")); - } - - // Disable printing backtraces in case `Run::try_parse_from` panics - let hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(|_| {})); - let result = std::panic::catch_unwind(|| Run::try_parse_from(args.iter())); - std::panic::set_hook(hook); + }; - if let Ok(Ok(run)) = result { - run.execute() - } else if let Ok((package, version)) = split_version(package) { + // Check if the file is a package name + if let WasmerCLIOptions::Run(r) = &options { + if !r.path.exists() { + let package = format!("{}", r.path.display()); + if let Ok((package, version)) = split_version(&package) { if let Some(package) = wasmer_registry::get_local_package(&package, version.as_deref()) { @@ -299,29 +280,14 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { } Err(e) => { println!("{e}"); - Ok(()) + return Ok(()); } } - } else { - WasmerCLIOptions::try_parse()?.execute() - /* - let hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(|_| {})); - let cli_result = std::panic::catch_unwind(|| WasmerCLIOptions::try_parse_from(args.iter())); - std::panic::set_hook(hook); - - match cli_result { - Ok(Ok(opts)) => return opts.execute(), - Ok(Err(e)) => { - return Err(anyhow::anyhow!("Error: {e}")); - }, - Err(_) => { - return Err(anyhow::anyhow!("Error when trying to parse CLI arguments")); - } - }*/ } } } + + options.execute() } fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { @@ -348,6 +314,42 @@ fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { } } +fn print_packages() -> Result<(), anyhow::Error> { + use prettytable::{format, row, Table}; + + let rows = get_all_local_packages() + .into_iter() + .map(|pkg| { + let commands = pkg + .manifest + .command + .unwrap_or_default() + .iter() + .map(|c| c.get_name()) + .collect::>() + .join(" \r\n"); + + row![pkg.registry, pkg.name, pkg.version, commands] + }) + .collect::>(); + + let empty_table = rows.is_empty(); + if empty_table { + println!("--------------------------------------"); + println!("Registry Package Version Commands "); + println!("======================================"); + println!(); + } else { + 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); + let _ = table.printstd(); + } + + Ok(()) +} + fn print_help(verbose: bool) -> Result<(), anyhow::Error> { use clap::CommandFactory; let mut cmd = WasmerCLIOptions::command(); diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 979cc8ce5d9..4cd4da3d686 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -175,7 +175,7 @@ pub struct Run { /// File to run #[clap(name = "FILE", parse(from_os_str))] - path: PathBuf, + pub(crate) path: PathBuf, /// Invoke a specified function #[clap(long = "invoke", short = 'i')] From 6f15d1a8a15e3d61bee659fa7a93bf83b61f37fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 10 Oct 2022 10:16:44 +0200 Subject: [PATCH 36/83] Fix build issue on linux-musl --- lib/cli/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cli/build.rs b/lib/cli/build.rs index c607120ec6b..f9aecaaf01c 100644 --- a/lib/cli/build.rs +++ b/lib/cli/build.rs @@ -16,6 +16,8 @@ pub fn main() { "cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT={}", &git_hash[..5] ); + } else { + println!("cargo:rustc-env=WASMER_BUILD_GIT_HASH_SHORT=??????"); } let utc: DateTime = Utc::now(); From 03fe253441e28db3a2548b957e39beeb050ae0f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 10 Oct 2022 12:04:48 +0200 Subject: [PATCH 37/83] Fix CI --- lib/cli/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index aa722afbd5d..a2c05f70b38 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -364,7 +364,7 @@ fn print_help(verbose: bool) -> Result<(), anyhow::Error> { #[allow(unused_mut, clippy::vec_init_then_push)] fn print_version(verbose: bool) -> Result<(), anyhow::Error> { if !verbose { - println!("{}", env!("CARGO_PKG_VERSION")); + println!("wasmer {}", env!("CARGO_PKG_VERSION")); } else { println!( "wasmer {} ({} {})", From 5b72f978767a88fe94ba9d2b62f7a339d5ca99af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 10 Oct 2022 15:47:43 +0200 Subject: [PATCH 38/83] Add option to auto-install packages by command name ("wasmer ls") --- lib/cli/src/cli.rs | 129 ++++++++++++--- lib/cli/src/commands/run.rs | 2 +- .../queries/get_package_by_command.graphql | 14 ++ lib/registry/src/lib.rs | 151 +++++++++++------- 4 files changed, 218 insertions(+), 78 deletions(-) create mode 100644 lib/registry/graphql/queries/get_package_by_command.graphql diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index a2c05f70b38..0916c8df49a 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -12,7 +12,6 @@ use crate::commands::CreateObj; use crate::commands::Wast; use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; -use anyhow::Result; use clap::{ErrorKind, Parser}; use wasmer_registry::get_all_local_packages; @@ -146,7 +145,7 @@ enum WasmerCLIOptions { } impl WasmerCLIOptions { - fn execute(&self) -> Result<()> { + fn execute(&self) -> Result<(), anyhow::Error> { match self { Self::Run(options) => options.execute(), Self::SelfUpdate(options) => options.execute(), @@ -238,9 +237,39 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { if let WasmerCLIOptions::Run(r) = &options { if !r.path.exists() { let package = format!("{}", r.path.display()); - if let Ok((package, version)) = split_version(&package) { + if let Ok(mut sv) = split_version(&package) { + let mut package_download_info = None; + if !sv.package.contains('/') { + let sp = spinner::SpinnerBuilder::new(format!( + "Looking up command {} ...", + sv.package + )) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", + "⠐", "⠈", + ]) + .start(); + + for registry in + wasmer_registry::get_all_available_registries().unwrap_or_default() + { + let result = + wasmer_registry::query_command_from_registry(®istry, &sv.package); + print!("\r\n"); + let command = sv.package.clone(); + if let Ok(o) = result { + package_download_info = Some(o.clone()); + sp.close(); + sv.package = o.package; + sv.version = Some(o.version); + sv.command = Some(command); + break; + } + } + } + if let Some(package) = - wasmer_registry::get_local_package(&package, version.as_deref()) + wasmer_registry::get_local_package(&sv.package, sv.version.as_deref()) { let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( &package.registry, @@ -252,21 +281,26 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { // Try finding the local package let mut args_without_package = args.clone(); args_without_package.remove(1); - return RunWithoutFile::try_parse_from(args_without_package.iter())? + + let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; + run_args.command_name = sv.command.clone(); + return run_args .into_run_args(local_package_wasm_path, Some(package.manifest)) .execute(); } // else: local package not found - let sp = spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", - "⠐", "⠈", - ]) - .start(); - - let v = version.as_deref(); - let result = wasmer_registry::install_package(&package, v); + let sp = + spinner::SpinnerBuilder::new(format!("Installing package {} ...", sv.package)) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", + "⠠", "⠐", "⠈", + ]) + .start(); + + let v = sv.version.as_deref(); + let result = + wasmer_registry::install_package(&sv.package, v, package_download_info); sp.close(); print!("\r\n"); match result { @@ -274,7 +308,12 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { // Try auto-installing the remote package let mut args_without_package = args.clone(); args_without_package.remove(1); - return RunWithoutFile::try_parse_from(args_without_package.iter())? + + let mut run_args = + RunWithoutFile::try_parse_from(args_without_package.iter())?; + run_args.command_name = sv.command.clone(); + + return run_args .into_run_args(buf, Some(package.manifest)) .execute(); } @@ -290,7 +329,14 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { options.execute() } -fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { +#[derive(Debug, Clone, PartialEq, Default)] +struct SplitVersion { + package: String, + version: Option, + command: Option, +} + +fn split_version(s: &str) -> Result { let prohibited_package_names = [ "run", "cache", @@ -303,15 +349,52 @@ fn split_version(s: &str) -> Result<(String, Option), anyhow::Error> { "wast", "help", ]; - if prohibited_package_names.contains(&s.trim()) { - return Err(anyhow::anyhow!("Invalid package name {s:?}")); - } + let package_version = s.split('@').collect::>(); - match *package_version.as_slice() { - [p, v] => Ok((p.trim().to_string(), Some(v.trim().to_string()))), - [p] => Ok((p.trim().to_string(), None)), - _ => Err(anyhow!("Invalid package / version: {s:?}")), + let (mut package, mut version) = match *package_version.as_slice() { + [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), + [p] => (p.trim().to_string(), None), + _ => { + return Err(anyhow!("Invalid package / version: {s:?}")); + } + }; + + let version_clone = version.clone().unwrap_or_default(); + let command = if package.contains(':') { + let package_command = version_clone.split('@').collect::>(); + let (p, c) = match package_command.as_slice() { + [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), + [p] => (p.trim().to_string(), None), + _ => { + return Err(anyhow!("Invalid package / command: {s:?}")); + } + }; + package = p; + c + } else if version_clone.contains(':') { + let version_command = version_clone.split('@').collect::>(); + let (v, command) = match version_command.as_slice() { + [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), + [p] => (p.trim().to_string(), None), + _ => { + return Err(anyhow!("Invalid version / command: {s:?}")); + } + }; + version = Some(v); + command + } else { + None + }; + + if prohibited_package_names.contains(&package.trim()) { + return Err(anyhow::anyhow!("Invalid package name {package:?}")); } + + Ok(SplitVersion { + package, + version, + command, + }) } fn print_packages() -> Result<(), anyhow::Error> { diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 4cd4da3d686..651e2be5ca9 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -37,7 +37,7 @@ pub struct RunWithoutFile { /// to the wasm program. This is used in wapm to provide nicer output in /// help commands and error messages of the running wasm program #[clap(long = "command-name", hide = true)] - command_name: Option, + pub(crate) command_name: Option, /// A prehashed string, used to speed up start times by avoiding hashing the /// wasm module. If the specified hash is not found, Wasmer will hash the module diff --git a/lib/registry/graphql/queries/get_package_by_command.graphql b/lib/registry/graphql/queries/get_package_by_command.graphql new file mode 100644 index 00000000000..3d627cd42da --- /dev/null +++ b/lib/registry/graphql/queries/get_package_by_command.graphql @@ -0,0 +1,14 @@ +query GetPackageByCommandQuery ($commandName: String!) { + getCommand(name: $commandName) { + command + packageVersion { + version + distribution { + downloadUrl + } + package { + displayName + } + } + } +} \ No newline at end of file diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 6d6e3d87ace..fb61bb45daf 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -95,6 +95,14 @@ pub mod graphql { )] pub(crate) struct GetPackageVersionQuery; + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/get_package_by_command.graphql", + response_derives = "Debug" + )] + pub(crate) struct GetPackageByCommandQuery; + #[derive(GraphQLQuery)] #[graphql( schema_path = "graphql/schema.graphql", @@ -580,8 +588,35 @@ pub fn get_package_local_wasm_file( Ok(dir.join(&wasm_file_name)) } -pub fn query_command_from_registry(_name: &str) -> Result { - Err("unimplemented".to_string()) +pub fn query_command_from_registry( + registry_url: &str, + command_name: &str, +) -> Result { + use crate::graphql::{execute_query, get_package_by_command_query, GetPackageByCommandQuery}; + use graphql_client::GraphQLQuery; + + let q = GetPackageByCommandQuery::build_query(get_package_by_command_query::Variables { + command_name: command_name.to_string(), + }); + + let response: get_package_by_command_query::ResponseData = execute_query(registry_url, "", &q) + .map_err(|e| format!("Error sending GetPackageByCommandQuery:  {e}"))?; + + let command = response + .get_command + .ok_or_else(|| "GetPackageByCommandQuery: no get_command".to_string())?; + + let package = command.package_version.package.display_name; + let version = command.package_version.version; + let url = command.package_version.distribution.download_url; + + Ok(PackageDownloadInfo { + registry: registry_url.to_string(), + package, + version, + commands: command_name.to_string(), + url, + }) } #[derive(Debug, Clone, PartialEq, PartialOrd)] @@ -766,66 +801,74 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result, + package_download_info: Option, ) -> Result<(LocalPackage, PathBuf), String> { - let registries = get_all_available_registries()?; - let mut url_of_package = None; - let mut error_packages = Vec::new(); - - for r in registries.iter() { - let registry_test = test_if_registry_present(r); - if !registry_test.clone().unwrap_or(false) { - continue; - } - match query_package_from_registry(r, name, version) { - Ok(o) => { - url_of_package = Some((r, o)); - break; - } - Err(e) => { - error_packages.push(e); + let package_info = match package_download_info { + Some(s) => s, + None => { + let registries = get_all_available_registries()?; + let mut url_of_package = None; + let mut error_packages = Vec::new(); + + for r in registries.iter() { + let registry_test = test_if_registry_present(r); + if !registry_test.clone().unwrap_or(false) { + continue; + } + match query_package_from_registry(r, name, version) { + Ok(o) => { + url_of_package = Some((r, o)); + break; + } + Err(e) => { + error_packages.push(e); + } + } } - } - } - let version_str = match version { - None => name.to_string(), - Some(v) => format!("{name}@{v}"), - }; + let version_str = match version { + None => name.to_string(), + Some(v) => format!("{name}@{v}"), + }; - let registries_searched = registries - .iter() - .filter_map(|s| url::Url::parse(s).ok()) - .filter_map(|s| Some(s.host_str()?.to_string())) - .collect::>(); + let registries_searched = registries + .iter() + .filter_map(|s| url::Url::parse(s).ok()) + .filter_map(|s| Some(s.host_str()?.to_string())) + .collect::>(); + + let mut error_str = + format!("Package {version_str} not found in registries {registries_searched:?}."); + let mut did_you_mean = error_packages + .iter() + .flat_map(|error| { + if let QueryPackageError::AmbigouusName { name, packages: _ } = error { + error_str = format!("Ambigouus package name {name:?}. Please specify the package in the namespace/name format."); + } + let packages = error.get_packages(); + packages.iter().filter_map(|f| { + let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); + Some(format!(" {}@{} (from {from})", f.package, f.version)) + }) + .collect::>() + .into_iter() + }) + .collect::>(); + + let did_you_mean = if did_you_mean.is_empty() { + String::new() + } else { + did_you_mean.sort(); + did_you_mean.dedup(); + format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) + }; - let mut error_str = - format!("Package {version_str} not found in registries {registries_searched:?}."); - let mut did_you_mean = error_packages - .iter() - .flat_map(|error| { - if let QueryPackageError::AmbigouusName { name, packages: _ } = error { - error_str = format!("Ambigouus package name {name:?}. Please specify the package in the namespace/name format."); - } - let packages = error.get_packages(); - packages.iter().filter_map(|f| { - let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); - Some(format!(" {}@{} (from {from})", f.package, f.version)) - }) - .collect::>() - .into_iter() - }) - .collect::>(); + let (_, package_info) = url_of_package.ok_or(format!("{error_str}{did_you_mean}"))?; - let did_you_mean = if did_you_mean.is_empty() { - String::new() - } else { - did_you_mean.sort(); - did_you_mean.dedup(); - format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) + package_info + } }; - let (_, package_info) = url_of_package.ok_or(format!("{error_str}{did_you_mean}"))?; - let host = url::Url::parse(&package_info.registry) .map_err(|e| format!("invalid url: {}: {e}", package_info.registry))? .host_str() From 8f29862d72ab49ccab490e568b4b39043d77dffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 11 Oct 2022 11:19:27 +0200 Subject: [PATCH 39/83] Refactor creating SpinnerHandle --- lib/cli/src/cli.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 0916c8df49a..44a930e4f79 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -13,6 +13,7 @@ use crate::commands::Wast; use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; use clap::{ErrorKind, Parser}; +use spinner::SpinnerHandle; use wasmer_registry::get_all_local_packages; #[derive(Parser, Debug)] @@ -240,15 +241,7 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { if let Ok(mut sv) = split_version(&package) { let mut package_download_info = None; if !sv.package.contains('/') { - let sp = spinner::SpinnerBuilder::new(format!( - "Looking up command {} ...", - sv.package - )) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", - "⠐", "⠈", - ]) - .start(); + let sp = start_spinner(format!("Looking up command {} ...", sv.package)); for registry in wasmer_registry::get_all_available_registries().unwrap_or_default() @@ -290,13 +283,7 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { } // else: local package not found - let sp = - spinner::SpinnerBuilder::new(format!("Installing package {} ...", sv.package)) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", - "⠠", "⠐", "⠈", - ]) - .start(); + let sp = start_spinner(format!("Installing package {} ...", sv.package)); let v = sv.version.as_deref(); let result = @@ -329,6 +316,14 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { options.execute() } +fn start_spinner(msg: String) -> SpinnerHandle { + spinner::SpinnerBuilder::new(msg) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", + ]) + .start() +} + #[derive(Debug, Clone, PartialEq, Default)] struct SplitVersion { package: String, From 8d0fbbb93f1fb0da895facb05b0fc7cf9a12cb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 11 Oct 2022 11:57:18 +0200 Subject: [PATCH 40/83] Update Cargo.lock --- Cargo.lock | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4934455a39f..1f9cebd5db7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30275ad0ad84ec1c06dde3b3f7d23c6006b7d76d61a85e7060b426b747eff70d" + [[package]] name = "anyhow" version = "1.0.65" @@ -264,7 +270,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" dependencies = [ - "clap 3.2.21", + "clap 3.2.22", "heck 0.4.0", "indexmap", "log", @@ -467,6 +473,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -1022,6 +1038,30 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.24" @@ -1228,7 +1268,7 @@ checksum = "f290ecfa3bea3e8a157899dc8a1d96ee7dd6405c18c8ddd213fc58939d18a0e9" dependencies = [ "graphql-introspection-query", "graphql-parser", - "heck", + "heck 0.4.0", "lazy_static", "proc-macro2", "quote", @@ -1376,8 +1416,20 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.48" +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ @@ -2021,8 +2073,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] -name = "pest" -version = "2.3.1" +name = "percent-encoding" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" @@ -2863,7 +2915,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3a7cd01625b7e43e62815677d692cb59b221c2fdc2853d1eb86a260ee0c272" dependencies = [ - "ansi_term 0.7.5", + "ansi_term", "term 0.6.1", ] @@ -3166,6 +3218,47 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.9" @@ -3366,6 +3459,23 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.1.0" @@ -4350,6 +4460,17 @@ dependencies = [ "webpki", ] +[[package]] +name = "whoami" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +dependencies = [ + "bumpalo", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" From 471fd367209626455e5c9f48134d7cb5a0513570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 12:54:48 +0200 Subject: [PATCH 41/83] Rework main CLI command flow into smaller functions --- lib/cli/src/cli.rs | 179 ++++++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 76 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 44a930e4f79..d27028d878f 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -14,7 +14,7 @@ use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, V use crate::error::PrettyError; use clap::{ErrorKind, Parser}; use spinner::SpinnerHandle; -use wasmer_registry::get_all_local_packages; +use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; #[derive(Parser, Debug)] #[cfg_attr( @@ -236,86 +236,113 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { // Check if the file is a package name if let WasmerCLIOptions::Run(r) = &options { - if !r.path.exists() { - let package = format!("{}", r.path.display()); - if let Ok(mut sv) = split_version(&package) { - let mut package_download_info = None; - if !sv.package.contains('/') { - let sp = start_spinner(format!("Looking up command {} ...", sv.package)); - - for registry in - wasmer_registry::get_all_available_registries().unwrap_or_default() - { - let result = - wasmer_registry::query_command_from_registry(®istry, &sv.package); - print!("\r\n"); - let command = sv.package.clone(); - if let Ok(o) = result { - package_download_info = Some(o.clone()); - sp.close(); - sv.package = o.package; - sv.version = Some(o.version); - sv.command = Some(command); - break; - } - } - } - - if let Some(package) = - wasmer_registry::get_local_package(&sv.package, sv.version.as_deref()) - { - let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( - &package.registry, - &package.name, - &package.version, - ) - .map_err(|e| anyhow!("{e}"))?; - - // Try finding the local package - let mut args_without_package = args.clone(); - args_without_package.remove(1); - - let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; - run_args.command_name = sv.command.clone(); - return run_args - .into_run_args(local_package_wasm_path, Some(package.manifest)) - .execute(); - } - - // else: local package not found - let sp = start_spinner(format!("Installing package {} ...", sv.package)); - - let v = sv.version.as_deref(); - let result = - wasmer_registry::install_package(&sv.package, v, package_download_info); - sp.close(); - print!("\r\n"); - match result { - Ok((package, buf)) => { - // Try auto-installing the remote package - let mut args_without_package = args.clone(); - args_without_package.remove(1); - - let mut run_args = - RunWithoutFile::try_parse_from(args_without_package.iter())?; - run_args.command_name = sv.command.clone(); - - return run_args - .into_run_args(buf, Some(package.manifest)) - .execute(); - } - Err(e) => { - println!("{e}"); - return Ok(()); - } - } - } - } + return try_run_package_or_file(&args, r); } options.execute() } +fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error> { + // Check "r.path" is a file or a package / command name + if r.path.exists() { + return r.execute(); + } + + let package = format!("{}", r.path.display()); + + let mut sv = match split_version(&package) { + Ok(o) => o, + Err(_) => return r.execute(), + }; + + let mut package_download_info = None; + if !sv.package.contains('/') { + if let Ok(o) = try_lookup_command(&mut sv) { + package_download_info = Some(o); + } + } + + match try_execute_local_package(&args, &sv) { + Ok(o) => return Ok(o), + Err(_) => {} // local package not found + } + + // download and install package + try_autoinstall_package(&args, &sv, package_download_info) +} + +fn try_lookup_command(sv: &mut SplitVersion) -> Result { + let sp = start_spinner(format!("Looking up command {} ...", sv.package)); + + for registry in wasmer_registry::get_all_available_registries().unwrap_or_default() { + let result = wasmer_registry::query_command_from_registry(®istry, &sv.package); + print!("\r\n"); + let command = sv.package.clone(); + if let Ok(o) = result { + sv.package = o.package.clone(); + sv.version = Some(o.version.clone()); + sv.command = Some(command); + return Ok(o); + } + } + + sp.close(); + Err(anyhow::anyhow!("command {sv:?} not found")) +} + +fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), anyhow::Error> { + let package = match wasmer_registry::get_local_package(&sv.package, sv.version.as_deref()) { + Some(p) => p, + None => return Err(anyhow::anyhow!("no local package {sv:?} found")), + }; + + let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( + &package.registry, + &package.name, + &package.version, + ) + .map_err(|e| anyhow!("{e}"))?; + + // Try finding the local package + let mut args_without_package = args.to_vec(); + args_without_package.remove(1); + + let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; + run_args.command_name = sv.command.clone(); + run_args + .into_run_args(local_package_wasm_path, Some(package.manifest)) + .execute() +} + +fn try_autoinstall_package( + args: &[String], + sv: &SplitVersion, + package: Option, +) -> Result<(), anyhow::Error> { + let sp = start_spinner(format!("Installing package {} ...", sv.package)); + let v = sv.version.as_deref(); + let result = wasmer_registry::install_package(&sv.package, v, package); + sp.close(); + print!("\r\n"); + let (package, buf) = match result { + Ok(o) => o, + Err(e) => { + return Err(anyhow::anyhow!("{e}")); + } + }; + + // Try auto-installing the remote package + let mut args_without_package = args.to_vec(); + args_without_package.remove(1); + + let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; + run_args.command_name = sv.command.clone(); + + run_args + .into_run_args(buf, Some(package.manifest)) + .execute() +} + fn start_spinner(msg: String) -> SpinnerHandle { spinner::SpinnerBuilder::new(msg) .spinner(vec![ From 9e278c3a1ee5606cdfda54c3ec9aabb41f850bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 15:54:08 +0200 Subject: [PATCH 42/83] Fix make lint --- lib/cli/src/cli.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index d27028d878f..58d25c90812 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -262,13 +262,12 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error } } - match try_execute_local_package(&args, &sv) { - Ok(o) => return Ok(o), - Err(_) => {} // local package not found + if let Ok(o) = try_execute_local_package(args, &sv) { + return Ok(o); } - // download and install package - try_autoinstall_package(&args, &sv, package_download_info) + // else: local package not found - try to download and install package + try_autoinstall_package(args, &sv, package_download_info) } fn try_lookup_command(sv: &mut SplitVersion) -> Result { From 0da844dc4d81a47f792b51450b7f51b21a6167d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 17:56:55 +0200 Subject: [PATCH 43/83] CLI: use `get_subcommands()` instead of manual definition --- lib/cli/src/cli.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 58d25c90812..7dda9e08386 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -12,7 +12,7 @@ use crate::commands::CreateObj; use crate::commands::Wast; use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; -use clap::{ErrorKind, Parser}; +use clap::{ErrorKind, Parser, CommandFactory}; use spinner::SpinnerHandle; use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; @@ -358,18 +358,11 @@ struct SplitVersion { } fn split_version(s: &str) -> Result { - let prohibited_package_names = [ - "run", - "cache", - "validate", - "compile", - "create-exe", - "create-obj", - "config", - "inspect", - "wast", - "help", - ]; + let command = WasmerCLIOptions::command(); + let prohibited_package_names = command + .get_subcommands() + .map(|s| s.get_name()) + .collect::>(); let package_version = s.split('@').collect::>(); let (mut package, mut version) = match *package_version.as_slice() { @@ -455,7 +448,6 @@ fn print_packages() -> Result<(), anyhow::Error> { } fn print_help(verbose: bool) -> Result<(), anyhow::Error> { - use clap::CommandFactory; let mut cmd = WasmerCLIOptions::command(); if verbose { let _ = cmd.print_long_help(); From 600dc2dfc72596688cee09222cbf030ab0473c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 18:46:18 +0200 Subject: [PATCH 44/83] Replace package parsing with regex --- Cargo.lock | 1 + lib/cli/Cargo.toml | 1 + lib/cli/src/cli.rs | 133 ++++++++++++++++++++++++++++++--------------- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f9cebd5db7..0232bd53486 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3810,6 +3810,7 @@ dependencies = [ "http_req", "log", "prettytable-rs", + "regex", "serde_json", "spinner", "target-lexicon 0.12.4", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 91a58bd01b6..800bcea7fe6 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -61,6 +61,7 @@ target-lexicon = { version = "0.12", features = ["std"] } prettytable-rs = "0.9.0" wapm-toml = "0.1.2" walkdir = "2.3.2" +regex = "1.6.0" [build-dependencies] chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] } diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 7dda9e08386..ecef4507b34 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -12,7 +12,7 @@ use crate::commands::CreateObj; use crate::commands::Wast; use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; use crate::error::PrettyError; -use clap::{ErrorKind, Parser, CommandFactory}; +use clap::{CommandFactory, ErrorKind, Parser}; use spinner::SpinnerHandle; use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; @@ -357,58 +357,105 @@ struct SplitVersion { command: Option, } +#[test] +fn test_split_version() { + assert_eq!( + split_version("namespace/name@version:command").unwrap(), + SplitVersion { + package: "namespace/name".to_string(), + version: Some("version".to_string()), + command: Some("command".to_string()), + } + ); + assert_eq!( + split_version("namespace/name@version").unwrap(), + SplitVersion { + package: "namespace/name".to_string(), + version: Some("version".to_string()), + command: None, + } + ); + assert_eq!( + split_version("namespace/name").unwrap(), + SplitVersion { + package: "namespace/name".to_string(), + version: None, + command: None, + } + ); + assert_eq!( + format!("{}", split_version("namespace").unwrap_err()), + "Invalid package version: \"namespace\"".to_string(), + ); +} + fn split_version(s: &str) -> Result { let command = WasmerCLIOptions::command(); let prohibited_package_names = command - .get_subcommands() - .map(|s| s.get_name()) - .collect::>(); - - let package_version = s.split('@').collect::>(); - let (mut package, mut version) = match *package_version.as_slice() { - [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), - [p] => (p.trim().to_string(), None), - _ => { - return Err(anyhow!("Invalid package / version: {s:?}")); + .get_subcommands() + .map(|s| s.get_name()) + .collect::>(); + + let re1 = regex::Regex::new(r#"(.*)/(.*)@(.*):(.*)"#).unwrap(); + let re2 = regex::Regex::new(r#"(.*)/(.*)@(.*)"#).unwrap(); + let re3 = regex::Regex::new(r#"(.*)/(.*)"#).unwrap(); + + let captures = if re1.is_match(s) { + re1.captures(s) + .map(|c| { + c.iter() + .filter_map(|f| f) + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else if re2.is_match(s) { + re2.captures(s) + .map(|c| { + c.iter() + .filter_map(|f| f) + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else if re3.is_match(s) { + re3.captures(s) + .map(|c| { + c.iter() + .filter_map(|f| f) + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else { + return Err(anyhow::anyhow!("Invalid package version: {s:?}")); + }; + + let namespace = match captures.get(1).cloned() { + Some(s) => s, + None => { + return Err(anyhow::anyhow!( + "Invalid package version: {s:?}: no namespace" + )) } }; - let version_clone = version.clone().unwrap_or_default(); - let command = if package.contains(':') { - let package_command = version_clone.split('@').collect::>(); - let (p, c) = match package_command.as_slice() { - [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), - [p] => (p.trim().to_string(), None), - _ => { - return Err(anyhow!("Invalid package / command: {s:?}")); - } - }; - package = p; - c - } else if version_clone.contains(':') { - let version_command = version_clone.split('@').collect::>(); - let (v, command) = match version_command.as_slice() { - [p, v] => (p.trim().to_string(), Some(v.trim().to_string())), - [p] => (p.trim().to_string(), None), - _ => { - return Err(anyhow!("Invalid version / command: {s:?}")); - } - }; - version = Some(v); - command - } else { - None + let name = match captures.get(2).cloned() { + Some(s) => s, + None => return Err(anyhow::anyhow!("Invalid package version: {s:?}: no name")), + }; + + let sv = SplitVersion { + package: format!("{namespace}/{name}"), + version: captures.get(3).cloned(), + command: captures.get(4).cloned(), }; - if prohibited_package_names.contains(&package.trim()) { - return Err(anyhow::anyhow!("Invalid package name {package:?}")); + if prohibited_package_names.contains(&sv.package.trim()) { + return Err(anyhow::anyhow!("Invalid package name {:?}", sv.package)); } - Ok(SplitVersion { - package, - version, - command, - }) + Ok(sv) } fn print_packages() -> Result<(), anyhow::Error> { From a1548ec7ffb5a988a27f378e11d9e9cd008438c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 18:49:39 +0200 Subject: [PATCH 45/83] Use split_once instead of split().nth(1) --- lib/cli/src/cli.rs | 18 +++++++----------- lib/registry/src/lib.rs | 11 +++-------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index ecef4507b34..9590acec452 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -478,18 +478,14 @@ fn print_packages() -> Result<(), anyhow::Error> { .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 { - println!("--------------------------------------"); - println!("Registry Package Version Commands "); - println!("======================================"); - println!(); - } else { - 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); - let _ = table.printstd(); - } + table.add_empty_row(); + } + let _ = table.printstd(); Ok(()) } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index fb61bb45daf..f03dd99ec12 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -442,14 +442,9 @@ pub fn get_package_local_dir( "package name has to be in the format namespace/package: {name:?}" )); } - let namespace = name - .split('/') - .next() - .ok_or(format!("missing namespace for {name:?}"))?; - let name = name - .split('/') - .nth(1) - .ok_or(format!("missing name for {name:?}"))?; + let (namespace, name) = name + .split_once('/') + .ok_or(format!("missing namespace / name for {name:?}"))?; let install_dir = get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; Ok(install_dir.join(namespace).join(name).join(version)) From 7419be1830e6b0b3148263e8b89009abb623e8b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 18:54:11 +0200 Subject: [PATCH 46/83] Merge RunWithoutFile and Run --- lib/cli/src/cli.rs | 2 +- lib/cli/src/commands/run.rs | 87 +++++++++++-------------------------- 2 files changed, 27 insertions(+), 62 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 9590acec452..53969770065 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -484,7 +484,7 @@ fn print_packages() -> Result<(), anyhow::Error> { table.set_format(*format::consts::FORMAT_NO_COLSEP); if empty_table { table.add_empty_row(); - } + } let _ = table.printstd(); Ok(()) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 651e2be5ca9..55f4f1aa6cf 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -5,6 +5,7 @@ use crate::store::{CompilerType, StoreOptions}; use crate::suggestions::suggest_function_exports; use crate::warning; use anyhow::{anyhow, Context, Result}; +use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; use wasmer::FunctionEnv; @@ -144,23 +145,25 @@ impl RunWithoutFile { } Run { - #[cfg(feature = "cache")] - disable_cache: self.disable_cache, path: pathbuf, - invoke: self.invoke, - command_name: self.command_name, - #[cfg(feature = "cache")] - cache_key: self.cache_key, - store: self.store, - #[cfg(feature = "wasi")] - wasi: self.wasi, - #[cfg(feature = "io-devices")] - enable_experimental_io_devices: self.enable_experimental_io_devices, - #[cfg(feature = "debug")] - debug: self.debug, - #[cfg(feature = "debug")] - verbose: self.verbose.unwrap_or(0), - args: self.args, + options: RunWithoutFile { + #[cfg(feature = "cache")] + disable_cache: self.disable_cache, + invoke: self.invoke, + command_name: self.command_name, + #[cfg(feature = "cache")] + cache_key: self.cache_key, + store: self.store, + #[cfg(feature = "wasi")] + wasi: self.wasi, + #[cfg(feature = "io-devices")] + enable_experimental_io_devices: self.enable_experimental_io_devices, + #[cfg(feature = "debug")] + debug: self.debug, + #[cfg(feature = "debug")] + verbose: self.verbose.unwrap_or(0), + args: self.args, + }, } } } @@ -168,57 +171,19 @@ impl RunWithoutFile { #[derive(Debug, Parser, Clone, Default)] /// The options for the `wasmer run` subcommand pub struct Run { - /// Disable the cache - #[cfg(feature = "cache")] - #[clap(long = "disable-cache")] - disable_cache: bool, - /// File to run #[clap(name = "FILE", parse(from_os_str))] pub(crate) path: PathBuf, - /// Invoke a specified function - #[clap(long = "invoke", short = 'i')] - invoke: Option, - - /// The command name is a string that will override the first argument passed - /// to the wasm program. This is used in wapm to provide nicer output in - /// help commands and error messages of the running wasm program - #[clap(long = "command-name", hide = true)] - command_name: Option, - - /// A prehashed string, used to speed up start times by avoiding hashing the - /// wasm module. If the specified hash is not found, Wasmer will hash the module - /// as if no `cache-key` argument was passed. - #[cfg(feature = "cache")] - #[clap(long = "cache-key", hide = true)] - cache_key: Option, - #[clap(flatten)] - store: StoreOptions, - - // TODO: refactor WASI structure to allow shared options with Emscripten - #[cfg(feature = "wasi")] - #[clap(flatten)] - wasi: Wasi, - - /// Enable non-standard experimental IO devices - #[cfg(feature = "io-devices")] - #[clap(long = "enable-io-devices")] - enable_experimental_io_devices: bool, - - /// Enable debug output - #[cfg(feature = "debug")] - #[clap(long = "debug", short = 'd')] - debug: bool, - - #[cfg(feature = "debug")] - #[clap(short, long, parse(from_occurrences))] - verbose: u8, + pub(crate) options: RunWithoutFile, +} - /// Application arguments - #[clap(value_name = "ARGS")] - args: Vec, +impl Deref for Run { + type Target = RunWithoutFile; + fn deref(&self) -> &Self::Target { + &self.options + } } impl Run { From 04b5c431e38911429b92f9d437e43ed3b34627ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 18:55:48 +0200 Subject: [PATCH 47/83] Lazy-return string on Error --- lib/emscripten/src/utils.rs | 4 ++-- lib/registry/src/lib.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index a8e7cd82b76..88f1b51d151 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -76,11 +76,11 @@ pub fn get_emscripten_metadata(module: &Module) -> Result, St &module.info().global_initializers[max_idx], &module.info().global_initializers[snd_max_idx], ) { - let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or(format!( + let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or_else(|| format!( "emscripten unexpected dynamic_base {}", *dynamic_base as u32 ))?; - let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or(format!( + let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or_else(|| format!( "emscripten unexpected dynamictop_ptr {}", *dynamictop_ptr as u32 ))?; diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index f03dd99ec12..9cddd623aae 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -444,9 +444,9 @@ pub fn get_package_local_dir( } let (namespace, name) = name .split_once('/') - .ok_or(format!("missing namespace / name for {name:?}"))?; + .ok_or_else(|| format!("missing namespace / name for {name:?}"))?; let install_dir = - get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; + get_global_install_dir(registry_host).ok_or_else(|| format!("no install dir for {name:?}"))?; Ok(install_dir.join(namespace).join(name).join(version)) } @@ -565,7 +565,7 @@ pub fn get_package_local_wasm_file( .unwrap_or_default() .first() .map(|m| m.get_module()) - .ok_or(format!( + .ok_or_else(|| format!( "cannot get entrypoint for {name}@{version}: package has no commands" ))?; @@ -576,7 +576,7 @@ pub fn get_package_local_wasm_file( .filter(|m| m.name == module_name) .map(|m| m.source.clone()) .next() - .ok_or(format!( + .ok_or_else(|| format!( "cannot get entrypoint for {name}@{version}: package has no commands" ))?; @@ -858,7 +858,7 @@ pub fn install_package( format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) }; - let (_, package_info) = url_of_package.ok_or(format!("{error_str}{did_you_mean}"))?; + let (_, package_info) = url_of_package.ok_or_else(|| format!("{error_str}{did_you_mean}"))?; package_info } @@ -867,7 +867,7 @@ pub fn install_package( let host = url::Url::parse(&package_info.registry) .map_err(|e| format!("invalid url: {}: {e}", package_info.registry))? .host_str() - .ok_or(format!("invalid url: {}", package_info.registry))? + .ok_or_else(|| format!("invalid url: {}", package_info.registry))? .to_string(); let dir = get_package_local_dir(&host, &package_info.package, &package_info.version)?; @@ -891,7 +891,7 @@ pub fn install_package( .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; let commands = wapm_toml.command.clone().unwrap_or_default(); - let entrypoint_module = commands.first().ok_or(format!( + let entrypoint_module = commands.first().ok_or_else(|| format!( "Cannot run {name}@{version}: package has no commands" ))?; @@ -900,7 +900,7 @@ pub fn install_package( let entrypoint_module = modules .iter() .find(|m| m.name == module_name) - .ok_or(format!( + .ok_or_else(|| format!( "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" ))?; From b96f3546d64a646a33fedf839379ae4985e8289f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 18:57:11 +0200 Subject: [PATCH 48/83] Drop unnecessary loop labels --- lib/emscripten/src/utils.rs | 20 +++++++++++-------- lib/registry/src/lib.rs | 39 +++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 88f1b51d151..41c23fe5e3a 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -76,14 +76,18 @@ pub fn get_emscripten_metadata(module: &Module) -> Result, St &module.info().global_initializers[max_idx], &module.info().global_initializers[snd_max_idx], ) { - let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or_else(|| format!( - "emscripten unexpected dynamic_base {}", - *dynamic_base as u32 - ))?; - let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or_else(|| format!( - "emscripten unexpected dynamictop_ptr {}", - *dynamictop_ptr as u32 - ))?; + let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or_else(|| { + format!( + "emscripten unexpected dynamic_base {}", + *dynamic_base as u32 + ) + })?; + let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or_else(|| { + format!( + "emscripten unexpected dynamictop_ptr {}", + *dynamictop_ptr as u32 + ) + })?; Ok(Some(( align_memory(dynamic_base), align_memory(dynamictop_ptr), diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 9cddd623aae..ca5e2265ded 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -445,8 +445,8 @@ pub fn get_package_local_dir( let (namespace, name) = name .split_once('/') .ok_or_else(|| format!("missing namespace / name for {name:?}"))?; - let install_dir = - get_global_install_dir(registry_host).ok_or_else(|| format!("no install dir for {name:?}"))?; + let install_dir = get_global_install_dir(registry_host) + .ok_or_else(|| format!("no install dir for {name:?}"))?; Ok(install_dir.join(namespace).join(name).join(version)) } @@ -488,20 +488,20 @@ fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { pub fn get_all_local_packages() -> Vec { let mut packages = Vec::new(); - 'outer: for registry in get_all_available_registries().unwrap_or_default() { + for registry in get_all_available_registries().unwrap_or_default() { let host = match url::Url::parse(®istry) { Ok(o) => o.host_str().map(|s| s.to_string()), - Err(_) => continue 'outer, + Err(_) => continue, }; let host = match host { Some(s) => s, - None => continue 'outer, + None => continue, }; let root_dir = match get_global_install_dir(&host) { Some(o) => o, - None => continue 'outer, + None => continue, }; for (username_path, user_name) in get_all_names_in_dir(&root_dir) { @@ -565,9 +565,9 @@ pub fn get_package_local_wasm_file( .unwrap_or_default() .first() .map(|m| m.get_module()) - .ok_or_else(|| format!( - "cannot get entrypoint for {name}@{version}: package has no commands" - ))?; + .ok_or_else(|| { + format!("cannot get entrypoint for {name}@{version}: package has no commands") + })?; let wasm_file_name = wapm .module @@ -576,9 +576,9 @@ pub fn get_package_local_wasm_file( .filter(|m| m.name == module_name) .map(|m| m.source.clone()) .next() - .ok_or_else(|| format!( - "cannot get entrypoint for {name}@{version}: package has no commands" - ))?; + .ok_or_else(|| { + format!("cannot get entrypoint for {name}@{version}: package has no commands") + })?; Ok(dir.join(&wasm_file_name)) } @@ -858,7 +858,8 @@ pub fn install_package( format!("\r\n\r\nDid you mean:\r\n{}\r\n", did_you_mean.join("\r\n")) }; - let (_, package_info) = url_of_package.ok_or_else(|| format!("{error_str}{did_you_mean}"))?; + let (_, package_info) = + url_of_package.ok_or_else(|| format!("{error_str}{did_you_mean}"))?; package_info } @@ -891,18 +892,18 @@ pub fn install_package( .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; let commands = wapm_toml.command.clone().unwrap_or_default(); - let entrypoint_module = commands.first().ok_or_else(|| format!( - "Cannot run {name}@{version}: package has no commands" - ))?; + let entrypoint_module = commands + .first() + .ok_or_else(|| format!("Cannot run {name}@{version}: package has no commands"))?; let module_name = entrypoint_module.get_module(); let modules = wapm_toml.module.clone().unwrap_or_default(); let entrypoint_module = modules .iter() .find(|m| m.name == module_name) - .ok_or_else(|| format!( - "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" - ))?; + .ok_or_else(|| { + format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml") + })?; Ok(( LocalPackage { From b4e84c09f36ba036b447c07bf2c622eca84762e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 19:00:07 +0200 Subject: [PATCH 49/83] Use `unwrap_or(name)` --- lib/registry/src/lib.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index ca5e2265ded..f46f495516c 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -651,16 +651,10 @@ pub fn query_package_from_registry( use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; - let q = if name.contains('/') { - let name = name.split('/').nth(1).unwrap(); - GetPackagesQuery::build_query(get_packages_query::Variables { - names: vec![name.to_string()], - }) - } else { - GetPackagesQuery::build_query(get_packages_query::Variables { - names: vec![name.to_string()], - }) - }; + let name_query = name.split('/').nth(1).unwrap_or(name); + let q = GetPackagesQuery::build_query(get_packages_query::Variables { + names: vec![name_query.to_string()], + }); let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q).map_err(|e| { From 9455b6b5bd03369c8cddb507efaccb47fc7c5549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 19:02:43 +0200 Subject: [PATCH 50/83] Nit: update version == Some(&v.version) --- lib/registry/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index f46f495516c..6c6ebff5a8f 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -715,7 +715,7 @@ pub fn query_package_from_registry( return false; } - if version.is_some() && v.version != version.unwrap() { + if version == Some(&v.version) { return false; } From 5f831fc8869ff68111aa423d4923599019d31b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 19:09:55 +0200 Subject: [PATCH 51/83] Fix make lint --- lib/cli/src/cli.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 53969770065..586cc45fa0c 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -391,10 +391,7 @@ fn test_split_version() { fn split_version(s: &str) -> Result { let command = WasmerCLIOptions::command(); - let prohibited_package_names = command - .get_subcommands() - .map(|s| s.get_name()) - .collect::>(); + let mut prohibited_package_names = command.get_subcommands().map(|s| s.get_name()); let re1 = regex::Regex::new(r#"(.*)/(.*)@(.*):(.*)"#).unwrap(); let re2 = regex::Regex::new(r#"(.*)/(.*)@(.*)"#).unwrap(); @@ -404,7 +401,7 @@ fn split_version(s: &str) -> Result { re1.captures(s) .map(|c| { c.iter() - .filter_map(|f| f) + .flatten() .map(|m| m.as_str().to_owned()) .collect::>() }) @@ -413,7 +410,7 @@ fn split_version(s: &str) -> Result { re2.captures(s) .map(|c| { c.iter() - .filter_map(|f| f) + .flatten() .map(|m| m.as_str().to_owned()) .collect::>() }) @@ -422,7 +419,7 @@ fn split_version(s: &str) -> Result { re3.captures(s) .map(|c| { c.iter() - .filter_map(|f| f) + .flatten() .map(|m| m.as_str().to_owned()) .collect::>() }) @@ -451,7 +448,7 @@ fn split_version(s: &str) -> Result { command: captures.get(4).cloned(), }; - if prohibited_package_names.contains(&sv.package.trim()) { + if prohibited_package_names.any(|s| s == sv.package.trim()) { return Err(anyhow::anyhow!("Invalid package name {:?}", sv.package)); } From 2c3f164ab2cf3d35d4df3049ed2c3ad2d4cd1381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 13 Oct 2022 19:22:36 +0200 Subject: [PATCH 52/83] Use anyhow::ensure --- lib/cli/src/cli.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 586cc45fa0c..f67f0e7b212 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -448,9 +448,11 @@ fn split_version(s: &str) -> Result { command: captures.get(4).cloned(), }; - if prohibited_package_names.any(|s| s == sv.package.trim()) { - return Err(anyhow::anyhow!("Invalid package name {:?}", sv.package)); - } + let svp = sv.package.clone(); + anyhow::ensure!( + !prohibited_package_names.any(|s| s == sv.package.trim()), + "Invalid package name {svp:?}" + ); Ok(sv) } From f5eb1cbe7ab672ed98f2d1975ad43c33a99f91cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 14 Oct 2022 09:06:21 +0200 Subject: [PATCH 53/83] Added timeout of 5 minutes for re-querying packages from registry --- Cargo.lock | 1 + lib/registry/Cargo.toml | 3 +- .../queries/get_package_by_command.graphql | 2 + .../queries/get_package_version.graphql | 1 + .../graphql/queries/get_packages.graphql | 1 + lib/registry/src/lib.rs | 168 +++++++++++++++--- 6 files changed, 155 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0232bd53486..0d13d2bd051 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4036,6 +4036,7 @@ dependencies = [ "flate2", "graphql_client", "reqwest", + "semver 1.0.14", "serde", "serde_json", "tar", diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 3a308ff3db3..f91f1e7e1f2 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -16,4 +16,5 @@ thiserror = "1.0.37" toml = "0.5.9" wapm-toml = "0.1.1" tar = "0.4.38" -flate2 = "1.0.24" \ No newline at end of file +flate2 = "1.0.24" +semver = "1.0.14" \ No newline at end of file diff --git a/lib/registry/graphql/queries/get_package_by_command.graphql b/lib/registry/graphql/queries/get_package_by_command.graphql index 3d627cd42da..36c64eb19bb 100644 --- a/lib/registry/graphql/queries/get_package_by_command.graphql +++ b/lib/registry/graphql/queries/get_package_by_command.graphql @@ -3,6 +3,8 @@ query GetPackageByCommandQuery ($commandName: String!) { command packageVersion { version + isLastVersion + manifest distribution { downloadUrl } diff --git a/lib/registry/graphql/queries/get_package_version.graphql b/lib/registry/graphql/queries/get_package_version.graphql index e70b86ba76e..6fdd84c9ba4 100644 --- a/lib/registry/graphql/queries/get_package_version.graphql +++ b/lib/registry/graphql/queries/get_package_version.graphql @@ -4,6 +4,7 @@ query GetPackageVersionQuery ($name: String!, $version: String) { name } version + isLastVersion distribution { downloadUrl } diff --git a/lib/registry/graphql/queries/get_packages.graphql b/lib/registry/graphql/queries/get_packages.graphql index b6d03e990bc..9bbd6a0e227 100644 --- a/lib/registry/graphql/queries/get_packages.graphql +++ b/lib/registry/graphql/queries/get_packages.graphql @@ -4,6 +4,7 @@ query GetPackagesQuery ($names: [String!]!) { private versions { version + isLastVersion distribution { downloadUrl } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 6c6ebff5a8f..ee38798eefc 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use std::env; use std::path::{Path, PathBuf}; +use std::time::Duration; use serde::Deserialize; use serde::Serialize; @@ -424,7 +425,9 @@ pub struct PackageDownloadInfo { pub registry: String, pub package: String, pub version: String, + pub is_latest_version: bool, pub commands: String, + pub manifest: String, pub url: String, } @@ -609,6 +612,8 @@ pub fn query_command_from_registry( registry: registry_url.to_string(), package, version, + is_latest_version: command.package_version.is_last_version, + manifest: command.package_version.manifest, commands: command_name.to_string(), url, }) @@ -641,13 +646,106 @@ impl QueryPackageError { } } } -/// Returns the download info of the packages, on error returns all the available packages -/// i.e. (("foo/python", "wapm.io"), ("bar/python" "wapm.io"))) -pub fn query_package_from_registry( + +/// Returns true if a package has a newer version +/// +/// Also returns true if the package is not installed yet. +pub fn get_if_package_has_new_version( + registry_url: &str, + name: &str, + version: Option, + max_timeout: Duration, +) -> Result, String> { + if !test_if_registry_present(registry_url).unwrap_or(false) { + return Ok(None); + } + + let host = match url::Url::parse(registry_url) { + Ok(o) => match o.host_str().map(|s| s.to_string()) { + Some(s) => s, + None => return Err(format!("invalid host: {registry_url}")), + }, + Err(_) => return Err(format!("invalid host: {registry_url}")), + }; + + let (namespace, name) = name + .split_once('/') + .ok_or_else(|| format!("missing namespace / name for {name:?}"))?; + + let package_dir = get_global_install_dir(&host).map(|path| path.join(namespace).join(name)); + + let package_dir = match package_dir { + Some(s) => s, + None => return Err(format!("package dir does not exist {name}/{namespace}")), + }; + + // all installed versions of this package + let mut all_installed_versions = Vec::new(); + + let may_have_newer_version = match version { + Some(ref o) => Some(()) + .map(|_| { + let modified = package_dir.join(&o).metadata().ok()?.modified().ok()?; + let version = semver::Version::parse(&o).ok()?; + all_installed_versions.push(version); + if modified.elapsed().ok()? <= max_timeout { + Some(()) + } else { + None + } + }) + .is_some(), + None => { + let read_dir = match std::fs::read_dir(&package_dir) { + Ok(o) => o, + Err(_) => return Err(format!("{}", package_dir.display())), + }; + + // if there is any package younger than $max_timeout, return false + read_dir + .filter_map(|entry| { + let entry = entry.ok()?; + let version = semver::Version::parse(entry.file_name().to_str()?).ok()?; + all_installed_versions.push(version); + let modified = entry.metadata().ok()?.modified().ok()?; + if modified.elapsed().ok()? <= max_timeout { + Some(()) + } else { + None + } + }) + .next() + .is_some() + } + }; + + // If the version is specified, don't check for updates + // (since the package returned from the server would be the same) + if all_installed_versions.is_empty() || (may_have_newer_version && version.is_none()) { + let available_packages = query_available_packages_from_registry( + registry_url, + name, + version.as_ref().map(|s| s.as_str()), + ) + .unwrap_or_default(); + + for p in available_packages { + if p.is_latest_version { + return Ok(Some(p.clone())); + } + } + } + + Err(format!( + "Cannot find package {name:?} either locally or in registry" + )) +} + +pub fn query_available_packages_from_registry( registry_url: &str, name: &str, version: Option<&str>, -) -> Result { +) -> Result, QueryPackageError> { use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; @@ -658,7 +756,7 @@ pub fn query_package_from_registry( let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q).map_err(|e| { - QueryPackageError::ErrorSendingQuery(format!("Error sending GetPackagesQuery:  {e}")) + QueryPackageError::ErrorSendingQuery(format!("Error sending GetPackagesQuery: {e}")) })?; let available_packages = response @@ -675,14 +773,17 @@ pub fn query_package_from_registry( None => continue, }; + let manifest = toml::from_str::(&v.manifest).ok()?; + versions.push(PackageDownloadInfo { registry: registry_url.to_string(), package: p.name.clone(), version: v.version.clone(), + is_latest_version: v.is_last_version, + manifest: v.manifest.clone(), - commands: toml::from_str::(&v.manifest) - .ok()? + commands: manifest .command .unwrap_or_default() .iter() @@ -701,6 +802,18 @@ pub fn query_package_from_registry( .flat_map(|v| v.into_iter()) .collect::>(); + Ok(available_packages) +} + +/// Returns the download info of the packages, on error returns all the available packages +/// i.e. (("foo/python", "wapm.io"), ("bar/python" "wapm.io"))) +pub fn query_package_from_registry( + registry_url: &str, + name: &str, + version: Option<&str>, +) -> Result { + let available_packages = query_available_packages_from_registry(registry_url, name, version)?; + if !name.contains('/') { return Err(QueryPackageError::AmbigouusName { name: name.to_string(), @@ -799,11 +912,39 @@ pub fn install_package( let mut url_of_package = None; let mut error_packages = Vec::new(); + let version_str = match version { + None => name.to_string(), + Some(v) => format!("{name}@{v}"), + }; + + let registries_searched = registries + .iter() + .filter_map(|s| url::Url::parse(s).ok()) + .filter_map(|s| Some(s.host_str()?.to_string())) + .collect::>(); + + let mut error_str = + format!("Package {version_str} not found in registries {registries_searched:?}."); + for r in registries.iter() { let registry_test = test_if_registry_present(r); if !registry_test.clone().unwrap_or(false) { continue; } + + match get_if_package_has_new_version( + r, + name, + version.map(|s| s.to_string()), + Duration::from_secs(60 * 5), + ) { + Ok(Some(package)) => { + url_of_package = Some((r, package)); + break; + } + _ => {} + } + match query_package_from_registry(r, name, version) { Ok(o) => { url_of_package = Some((r, o)); @@ -815,19 +956,6 @@ pub fn install_package( } } - let version_str = match version { - None => name.to_string(), - Some(v) => format!("{name}@{v}"), - }; - - let registries_searched = registries - .iter() - .filter_map(|s| url::Url::parse(s).ok()) - .filter_map(|s| Some(s.host_str()?.to_string())) - .collect::>(); - - let mut error_str = - format!("Package {version_str} not found in registries {registries_searched:?}."); let mut did_you_mean = error_packages .iter() .flat_map(|error| { From f8f33a86ea7a2a185e67380ea1b258b106643b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 14 Oct 2022 09:24:19 +0200 Subject: [PATCH 54/83] Add --force install method (download packages without cooldown) --- lib/cli/src/cli.rs | 5 +++-- lib/cli/src/commands/run.rs | 5 +++++ lib/registry/src/lib.rs | 23 +++++++++++++---------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index f67f0e7b212..a17b1689c86 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -267,7 +267,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error } // else: local package not found - try to download and install package - try_autoinstall_package(args, &sv, package_download_info) + try_autoinstall_package(args, &sv, package_download_info, r.force_install) } fn try_lookup_command(sv: &mut SplitVersion) -> Result { @@ -317,10 +317,11 @@ fn try_autoinstall_package( args: &[String], sv: &SplitVersion, package: Option, + force_install: bool, ) -> Result<(), anyhow::Error> { let sp = start_spinner(format!("Installing package {} ...", sv.package)); let v = sv.version.as_deref(); - let result = wasmer_registry::install_package(&sv.package, v, package); + let result = wasmer_registry::install_package(&sv.package, v, package, force_install); sp.close(); print!("\r\n"); let (package, buf) = match result { diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 55f4f1aa6cf..a62a55c1ef1 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -25,6 +25,10 @@ use wasi::Wasi; /// Same as `wasmer run`, but without the required `path` argument (injected previously) #[derive(Debug, Parser, Clone, Default)] pub struct RunWithoutFile { + /// When installing packages with `wasmer $package`, force re-downloading the package + #[clap(long = "force", short = 'f')] + pub(crate) force_install: bool, + /// Disable the cache #[cfg(feature = "cache")] #[clap(long = "disable-cache")] @@ -147,6 +151,7 @@ impl RunWithoutFile { Run { path: pathbuf, options: RunWithoutFile { + force_install: self.force_install, #[cfg(feature = "cache")] disable_cache: self.disable_cache, invoke: self.invoke, diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index ee38798eefc..43460f96388 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -904,6 +904,7 @@ pub fn install_package( name: &str, version: Option<&str>, package_download_info: Option, + force_install: bool, ) -> Result<(LocalPackage, PathBuf), String> { let package_info = match package_download_info { Some(s) => s, @@ -932,17 +933,19 @@ pub fn install_package( continue; } - match get_if_package_has_new_version( - r, - name, - version.map(|s| s.to_string()), - Duration::from_secs(60 * 5), - ) { - Ok(Some(package)) => { - url_of_package = Some((r, package)); - break; + if !force_install { + match get_if_package_has_new_version( + r, + name, + version.map(|s| s.to_string()), + Duration::from_secs(60 * 5), + ) { + Ok(Some(package)) => { + url_of_package = Some((r, package)); + break; + } + _ => {} } - _ => {} } match query_package_from_registry(r, name, version) { From d518173a9b02973ca00122b4cf2ee7e70ab22498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 14 Oct 2022 21:07:34 +0200 Subject: [PATCH 55/83] Fix CI on Linux --- lib/cli/src/commands/run.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index a62a55c1ef1..7c3a037a29d 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -603,12 +603,14 @@ impl Run { let store = StoreOptions::default(); // TODO: store.compiler.features.all = true; ? Ok(Self { - args, path: executable.into(), - command_name: Some(original_executable), - store, - wasi: Wasi::for_binfmt_interpreter()?, - ..Self::default() + options: RunWithoutFile { + args, + command_name: Some(original_executable), + store, + wasi: Wasi::for_binfmt_interpreter()?, + ..Self::default() + }, }) } #[cfg(not(target_os = "linux"))] From ef1bc7b60891a4dfcf358f0d72794cd5647fee24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 14 Oct 2022 21:18:45 +0200 Subject: [PATCH 56/83] Fix "make lint" --- lib/api/tests/externals.rs | 46 ++++++++++------------- lib/api/tests/reference_types.rs | 10 ++--- lib/registry/src/lib.rs | 25 ++++-------- lib/vm/src/memory.rs | 8 ++-- lib/wasi/tests/stdio.rs | 2 +- tests/integration/cli/tests/create_exe.rs | 4 +- tests/integration/ios/tests/dylib.rs | 3 +- 7 files changed, 41 insertions(+), 57 deletions(-) diff --git a/lib/api/tests/externals.rs b/lib/api/tests/externals.rs index 5c17579a4ab..f593c763b79 100644 --- a/lib/api/tests/externals.rs +++ b/lib/api/tests/externals.rs @@ -210,28 +210,25 @@ fn memory_grow() -> Result<(), String> { fn function_new() -> Result<(), String> { let mut store = Store::default(); let function = Function::new_typed(&mut store, || {}); - assert_eq!( - function.ty(&mut store).clone(), - FunctionType::new(vec![], vec![]) - ); + assert_eq!(function.ty(&mut store), FunctionType::new(vec![], vec![])); let function = Function::new_typed(&mut store, |_a: i32| {}); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![Type::I32], vec![]) ); let function = Function::new_typed(&mut store, |_a: i32, _b: i64, _c: f32, _d: f64| {}); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); let function = Function::new_typed(&mut store, || -> i32 { 1 }); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![], vec![Type::I32]) ); let function = Function::new_typed(&mut store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); Ok(()) @@ -246,14 +243,11 @@ fn function_new_env() -> Result<(), String> { let my_env = MyEnv {}; let env = FunctionEnv::new(&mut store, my_env); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| {}); - assert_eq!( - function.ty(&mut store).clone(), - FunctionType::new(vec![], vec![]) - ); + assert_eq!(function.ty(&mut store), FunctionType::new(vec![], vec![])); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut, _a: i32| {}); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![Type::I32], vec![]) ); let function = Function::new_typed_with_env( @@ -262,13 +256,13 @@ fn function_new_env() -> Result<(), String> { |_env: FunctionEnvMut, _a: i32, _b: i64, _c: f32, _d: f64| {}, ); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]) ); let function = Function::new_typed_with_env(&mut store, &env, |_env: FunctionEnvMut| -> i32 { 1 }); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![], vec![Type::I32]) ); let function = Function::new_typed_with_env( @@ -277,7 +271,7 @@ fn function_new_env() -> Result<(), String> { |_env: FunctionEnvMut| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, ); assert_eq!( - function.ty(&mut store).clone(), + function.ty(&mut store), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) ); Ok(()) @@ -294,35 +288,35 @@ fn function_new_dynamic() -> Result<(), String> { &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); let function = Function::new( &mut store, &function_type, |_values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); @@ -356,7 +350,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![Type::I32], vec![]); let function = Function::new_with_env( &mut store, @@ -364,7 +358,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]); let function = Function::new_with_env( &mut store, @@ -372,7 +366,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32]); let function = Function::new_with_env( &mut store, @@ -380,7 +374,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]); let function = Function::new_with_env( &mut store, @@ -388,7 +382,7 @@ fn function_new_dynamic_env() -> Result<(), String> { &function_type, |_env: FunctionEnvMut, _values: &[Value]| unimplemented!(), ); - assert_eq!(function.ty(&mut store).clone(), function_type); + assert_eq!(function.ty(&mut store), function_type); // Using array signature let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]); diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs index aecd46d48ec..8746936f06f 100644 --- a/lib/api/tests/reference_types.rs +++ b/lib/api/tests/reference_types.rs @@ -367,7 +367,7 @@ pub mod reference_types { let global: &Global = instance.exports.get_global("global")?; { let er = ExternRef::new(&mut store, 3usize); - global.set(&mut store, Value::ExternRef(Some(er.clone())))?; + global.set(&mut store, Value::ExternRef(Some(er)))?; } let get_from_global: TypedFunction<(), Option> = instance .exports @@ -398,7 +398,7 @@ pub mod reference_types { let er = ExternRef::new(&mut store, 3usize); - let result = pass_extern_ref.call(&mut store, Some(er.clone())); + let result = pass_extern_ref.call(&mut store, Some(er)); assert!(result.is_err()); Ok(()) @@ -442,7 +442,7 @@ pub mod reference_types { let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 10_000)?; assert_eq!(result, -1); - let result = grow_table_with_ref.call(&mut store, Some(er1.clone()), 8)?; + let result = grow_table_with_ref.call(&mut store, Some(er1), 8)?; assert_eq!(result, 2); for i in 2..10 { @@ -454,7 +454,7 @@ pub mod reference_types { } { - fill_table_with_ref.call(&mut store, Some(er2.clone()), 0, 2)?; + fill_table_with_ref.call(&mut store, Some(er2), 0, 2)?; } { @@ -462,7 +462,7 @@ pub mod reference_types { table2.set(&mut store, 1, Value::ExternRef(Some(er3.clone())))?; table2.set(&mut store, 2, Value::ExternRef(Some(er3.clone())))?; table2.set(&mut store, 3, Value::ExternRef(Some(er3.clone())))?; - table2.set(&mut store, 4, Value::ExternRef(Some(er3.clone())))?; + table2.set(&mut store, 4, Value::ExternRef(Some(er3)))?; } { diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 43460f96388..e613452094a 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -686,7 +686,7 @@ pub fn get_if_package_has_new_version( Some(ref o) => Some(()) .map(|_| { let modified = package_dir.join(&o).metadata().ok()?.modified().ok()?; - let version = semver::Version::parse(&o).ok()?; + let version = semver::Version::parse(o).ok()?; all_installed_versions.push(version); if modified.elapsed().ok()? <= max_timeout { Some(()) @@ -722,16 +722,12 @@ pub fn get_if_package_has_new_version( // If the version is specified, don't check for updates // (since the package returned from the server would be the same) if all_installed_versions.is_empty() || (may_have_newer_version && version.is_none()) { - let available_packages = query_available_packages_from_registry( - registry_url, - name, - version.as_ref().map(|s| s.as_str()), - ) - .unwrap_or_default(); + let available_packages = + query_available_packages_from_registry(registry_url, name).unwrap_or_default(); for p in available_packages { if p.is_latest_version { - return Ok(Some(p.clone())); + return Ok(Some(p)); } } } @@ -744,7 +740,6 @@ pub fn get_if_package_has_new_version( pub fn query_available_packages_from_registry( registry_url: &str, name: &str, - version: Option<&str>, ) -> Result, QueryPackageError> { use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; @@ -812,7 +807,7 @@ pub fn query_package_from_registry( name: &str, version: Option<&str>, ) -> Result { - let available_packages = query_available_packages_from_registry(registry_url, name, version)?; + let available_packages = query_available_packages_from_registry(registry_url, name)?; if !name.contains('/') { return Err(QueryPackageError::AmbigouusName { @@ -934,17 +929,14 @@ pub fn install_package( } if !force_install { - match get_if_package_has_new_version( + if let Ok(Some(package)) = get_if_package_has_new_version( r, name, version.map(|s| s.to_string()), Duration::from_secs(60 * 5), ) { - Ok(Some(package)) => { - url_of_package = Some((r, package)); - break; - } - _ => {} + url_of_package = Some((r, package)); + break; } } @@ -1045,7 +1037,6 @@ pub fn install_package( pub fn test_if_registry_present(registry: &str) -> Result { use crate::graphql::{test_if_registry_present, TestIfRegistryPresent}; use graphql_client::GraphQLQuery; - use std::time::Duration; let q = TestIfRegistryPresent::build_query(test_if_registry_present::Variables {}); let _ = crate::graphql::execute_query_modifier_inner_check_json( diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index f8b80d2cee4..056f73354d2 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -357,7 +357,7 @@ impl VMMemory { /// /// This creates a `Memory` with owned metadata: this can be used to create a memory /// that will be imported into Wasm modules. - pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?))) } @@ -372,7 +372,7 @@ impl VMMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: NonNull, - ) -> Result { + ) -> Result { Ok(Self(Box::new(VMOwnedMemory::from_definition( memory, style, @@ -384,9 +384,9 @@ impl VMMemory { /// are natively supported /// - VMOwnedMemory -> VMMemory /// - Box -> VMMemory - pub fn from_custom(memory: IntoVMMemory) -> VMMemory + pub fn from_custom(memory: IntoVMMemory) -> Self where - IntoVMMemory: Into, + IntoVMMemory: Into, { memory.into() } diff --git a/lib/wasi/tests/stdio.rs b/lib/wasi/tests/stdio.rs index 78b93520e1a..c5169b6d48b 100644 --- a/lib/wasi/tests/stdio.rs +++ b/lib/wasi/tests/stdio.rs @@ -167,7 +167,7 @@ fn test_stdin() { // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); let result = start.call(&mut store, &[]); - assert!(!result.is_err()); + assert!(result.is_ok()); // We assure stdin is now empty let mut buf = Vec::new(); diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index 45969f7c69e..866bef6a5ae 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -277,7 +277,7 @@ fn create_obj(args: Vec<&'static str>, keyword_needle: &str, keyword: &str) -> a let object_path = operating_dir.join("wasm.obj"); let output: Vec = WasmerCreateObj { - current_dir: operating_dir.clone(), + current_dir: operating_dir, wasm_path, output_object_path: object_path.clone(), compiler: Compiler::Cranelift, @@ -292,7 +292,7 @@ fn create_obj(args: Vec<&'static str>, keyword_needle: &str, keyword: &str) -> a "create-obj successfully completed but object output file `{}` missing", object_path.display() ); - let mut object_header_path = object_path.clone(); + let mut object_header_path = object_path; object_header_path.set_extension("h"); assert!( object_header_path.exists(), diff --git a/tests/integration/ios/tests/dylib.rs b/tests/integration/ios/tests/dylib.rs index 7660a62e4aa..db452fffb60 100644 --- a/tests/integration/ios/tests/dylib.rs +++ b/tests/integration/ios/tests/dylib.rs @@ -40,9 +40,8 @@ mod tests { */ let command_success = command.status.success(); let test_success = !stderr.contains("** TEST FAILED **"); - let success = command_success && test_success; - success + command_success && test_success } fn remove_existing_artificats() -> Output { From b5bf287648a485cf4ca6bc6e69f97646f05537b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 14 Oct 2022 21:40:48 +0200 Subject: [PATCH 57/83] Fix CI on Linux again --- lib/cli/src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 7c3a037a29d..0e0d88e34da 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -609,7 +609,7 @@ impl Run { command_name: Some(original_executable), store, wasi: Wasi::for_binfmt_interpreter()?, - ..Self::default() + ..Default::default() }, }) } From a96049884f98be0bd673c19abb03f388b42b9da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 17 Oct 2022 17:58:37 +0200 Subject: [PATCH 58/83] Add unit test and fix problems when installing package --- lib/cli/src/cli.rs | 7 ++-- lib/registry/src/lib.rs | 80 +++++++++++++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index a17b1689c86..7b3fd6e3cbd 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -290,7 +290,8 @@ fn try_lookup_command(sv: &mut SplitVersion) -> Result Result<(), anyhow::Error> { - let package = match wasmer_registry::get_local_package(&sv.package, sv.version.as_deref()) { + let package = match wasmer_registry::get_local_package(None, &sv.package, sv.version.as_deref()) + { Some(p) => p, None => return Err(anyhow::anyhow!("no local package {sv:?} found")), }; @@ -321,7 +322,7 @@ fn try_autoinstall_package( ) -> Result<(), anyhow::Error> { let sp = start_spinner(format!("Installing package {} ...", sv.package)); let v = sv.version.as_deref(); - let result = wasmer_registry::install_package(&sv.package, v, package, force_install); + let result = wasmer_registry::install_package(None, &sv.package, v, package, force_install); sp.close(); print!("\r\n"); let (package, buf) = match result { @@ -461,7 +462,7 @@ fn split_version(s: &str) -> Result { fn print_packages() -> Result<(), anyhow::Error> { use prettytable::{format, row, Table}; - let rows = get_all_local_packages() + let rows = get_all_local_packages(None) .into_iter() .map(|pkg| { let commands = pkg diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index e613452094a..89a23e517c2 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -488,10 +488,13 @@ fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { } /// Returns a list of all locally installed packages -pub fn get_all_local_packages() -> Vec { +pub fn get_all_local_packages(registry: Option<&str>) -> Vec { let mut packages = Vec::new(); - - for registry in get_all_available_registries().unwrap_or_default() { + let registries = match registry { + Some(s) => vec![s.to_string()], + None => get_all_available_registries().unwrap_or_default(), + }; + for registry in registries { let host = match url::Url::parse(®istry) { Ok(o) => o.host_str().map(|s| s.to_string()), Err(_) => continue, @@ -533,8 +536,12 @@ pub fn get_all_local_packages() -> Vec { packages } -pub fn get_local_package(name: &str, version: Option<&str>) -> Option { - get_all_local_packages() +pub fn get_local_package( + registry: Option<&str>, + name: &str, + version: Option<&str>, +) -> Option { + get_all_local_packages(registry) .iter() .find(|p| { if p.name != name { @@ -744,9 +751,8 @@ pub fn query_available_packages_from_registry( use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; - let name_query = name.split('/').nth(1).unwrap_or(name); let q = GetPackagesQuery::build_query(get_packages_query::Variables { - names: vec![name_query.to_string()], + names: vec![name.to_string()], }); let response: get_packages_query::ResponseData = @@ -783,7 +789,8 @@ pub fn query_available_packages_from_registry( .unwrap_or_default() .iter() .map(|s| s.get_name()) - .collect(), + .collect::>() + .join(", "), url: v.distribution.download_url.clone(), }); @@ -823,7 +830,7 @@ pub fn query_package_from_registry( return false; } - if version == Some(&v.version) { + if version != Some(&v.version) { return false; } @@ -896,6 +903,7 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result, name: &str, version: Option<&str>, package_download_info: Option, @@ -904,7 +912,10 @@ pub fn install_package( let package_info = match package_download_info { Some(s) => s, None => { - let registries = get_all_available_registries()?; + let registries = match registry { + Some(s) => vec![s.to_string()], + None => get_all_available_registries()?, + }; let mut url_of_package = None; let mut error_packages = Vec::new(); @@ -993,7 +1004,7 @@ pub fn install_package( let version = package_info.version; let name = package_info.package; - if !dir.join("wapm.toml").exists() { + if !dir.join("wapm.toml").exists() || force_install { download_and_unpack_targz(&package_info.url, &dir)?; } let target_path = dir; @@ -1066,3 +1077,50 @@ pub fn get_all_available_registries() -> Result, String> { } Ok(registries) } + +#[test] +fn test_install_package() { + let registry = "https://registry.wapm.io/graphql"; + if !test_if_registry_present(registry).unwrap_or(false) { + panic!("registry.wapm.io not reachable, test will fail"); + } + + let wabt = query_package_from_registry(registry, "wasmer/wabt", Some("1.0.29")).unwrap(); + + 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://registry-cdn.wapm.io/packages/wasmer/wabt/wabt-1.0.29.tar.gz".to_string() + ); + + let (package, _) = + install_package(Some(registry), "wasmer/wabt", Some("1.0.29"), None, true).unwrap(); + + assert_eq!( + package.path, + get_global_install_dir("registry.wapm.io") + .unwrap() + .join("wasmer") + .join("wabt") + .join("1.0.29") + ); + + let all_installed_packages = get_all_local_packages(Some(registry)); + 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(Some(registry)) + .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}"); + } +} From db16c2014e1c26c1c9f305220734c647859f0a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 17 Oct 2022 18:39:23 +0200 Subject: [PATCH 59/83] Rework "wasmer list" --- lib/cli/src/cli.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 7b3fd6e3cbd..f580175f5f4 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -37,6 +37,11 @@ use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; )] /// The options for the wasmer Command Line Interface enum WasmerCLIOptions { + + /// List all locally installed packages + #[clap(name = "list")] + List, + /// Run a WebAssembly file. Formats accepted: wasm, wat #[clap(name = "run")] Run(Run), @@ -160,6 +165,7 @@ impl WasmerCLIOptions { Self::CreateObj(create_obj) => create_obj.execute(), Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), + Self::List => print_packages(), #[cfg(feature = "wast")] Self::Wast(wast) => wast.execute(), #[cfg(target_os = "linux")] @@ -207,9 +213,6 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { (Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => { return print_version(false); } - (Some("list"), _) => { - return print_packages(); - } _ => {} } @@ -219,7 +222,7 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { } else { match command.unwrap_or(&"".to_string()).as_ref() { "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" - | "self-update" | "validate" | "wast" | "binfmt" => WasmerCLIOptions::parse(), + | "self-update" | "validate" | "wast" | "binfmt" | "list" => WasmerCLIOptions::parse(), _ => { WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| { match e.kind() { From 7c05da022a07714a8a487b63f35e7730fdccc28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 09:19:05 +0200 Subject: [PATCH 60/83] Fix issue when running wasmer/wit-pack and wabt Fixed duplicated mapped directories when running the .wasm file --- lib/cli/src/cli.rs | 93 +++++++++++++++++++++++++++++++++---- lib/cli/src/commands/run.rs | 41 +++++++++++++--- lib/registry/src/lib.rs | 20 ++++---- 3 files changed, 131 insertions(+), 23 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index f580175f5f4..5b06d554b93 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -14,6 +14,7 @@ use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, V use crate::error::PrettyError; use clap::{CommandFactory, ErrorKind, Parser}; use spinner::SpinnerHandle; +use std::fmt; use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; #[derive(Parser, Debug)] @@ -37,7 +38,6 @@ use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; )] /// The options for the wasmer Command Line Interface enum WasmerCLIOptions { - /// List all locally installed packages #[clap(name = "list")] List, @@ -253,11 +253,41 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error let package = format!("{}", r.path.display()); + let mut is_fake_sv = false; let mut sv = match split_version(&package) { Ok(o) => o, - Err(_) => return r.execute(), + Err(_) => { + let mut fake_sv = SplitVersion { + package: package.to_string(), + version: None, + command: None, + }; + is_fake_sv = true; + match try_lookup_command(&mut fake_sv) { + Ok(o) => SplitVersion { + package: o.package, + version: Some(o.version), + command: r.command_name.clone(), + }, + Err(e) => { + return Err( + anyhow::anyhow!("No package for command {package:?} found, file {package:?} not found either") + .context(e) + .context(anyhow::anyhow!("{}", r.path.display())) + ); + } + } + } }; + if sv.command.is_none() { + sv.command = r.command_name.clone(); + } + + if sv.command.is_none() && is_fake_sv { + sv.command = Some(package.to_string()); + } + let mut package_download_info = None; if !sv.package.contains('/') { if let Ok(o) = try_lookup_command(&mut sv) { @@ -289,7 +319,7 @@ fn try_lookup_command(sv: &mut SplitVersion) -> Result Result<(), anyhow::Error> { @@ -303,6 +333,7 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a &package.registry, &package.name, &package.version, + sv.command.as_ref().map(|s| s.as_str()), ) .map_err(|e| anyhow!("{e}"))?; @@ -310,11 +341,22 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a let mut args_without_package = args.to_vec(); args_without_package.remove(1); - let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; - run_args.command_name = sv.command.clone(); - run_args - .into_run_args(local_package_wasm_path, Some(package.manifest)) + if args_without_package.get(1) == Some(&sv.package) + || args_without_package.get(1) == sv.command.as_ref() + { + args_without_package.remove(1); + } + + if args_without_package.get(0) == Some(&sv.package) + || args_without_package.get(0) == sv.command.as_ref() + { + args_without_package.remove(0); + } + + RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(local_package_wasm_path.clone(), Some(package.manifest)) .execute() + .map_err(|e| e.context(anyhow::anyhow!("{}", local_package_wasm_path.display()))) } fn try_autoinstall_package( @@ -362,6 +404,22 @@ struct SplitVersion { command: Option, } +impl fmt::Display for SplitVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let version = self + .version + .as_ref() + .map(|s| s.as_str()) + .unwrap_or("latest"); + let command = self + .command + .as_ref() + .map(|s| format!(":{s}")) + .unwrap_or_default(); + write!(f, "{}@{version}{command}", self.package) + } +} + #[test] fn test_split_version() { assert_eq!( @@ -401,6 +459,9 @@ fn split_version(s: &str) -> Result { let re1 = regex::Regex::new(r#"(.*)/(.*)@(.*):(.*)"#).unwrap(); let re2 = regex::Regex::new(r#"(.*)/(.*)@(.*)"#).unwrap(); let re3 = regex::Regex::new(r#"(.*)/(.*)"#).unwrap(); + let re4 = regex::Regex::new(r#"(.*)/(.*):(.*)"#).unwrap(); + + let mut no_version = false; let captures = if re1.is_match(s) { re1.captures(s) @@ -420,6 +481,16 @@ fn split_version(s: &str) -> Result { .collect::>() }) .unwrap_or_default() + } else if re4.is_match(s) { + no_version = true; + re4.captures(s) + .map(|c| { + c.iter() + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() } else if re3.is_match(s) { re3.captures(s) .map(|c| { @@ -449,8 +520,12 @@ fn split_version(s: &str) -> Result { let sv = SplitVersion { package: format!("{namespace}/{name}"), - version: captures.get(3).cloned(), - command: captures.get(4).cloned(), + version: if no_version { + None + } else { + captures.get(3).cloned() + }, + command: captures.get(if no_version { 3 } else { 4 }).cloned(), }; let svp = sv.package.clone(); diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 0e0d88e34da..5244ea69ba4 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -5,6 +5,7 @@ use crate::store::{CompilerType, StoreOptions}; use crate::suggestions::suggest_function_exports; use crate::warning; use anyhow::{anyhow, Context, Result}; +use std::collections::BTreeMap; use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; @@ -81,6 +82,9 @@ pub struct RunWithoutFile { impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { + #[cfg(feature = "wasi")] + let mut wasi_map_dir = Vec::new(); + #[cfg(feature = "wasi")] { let pkg_fs = match pathbuf.parent() { @@ -92,13 +96,13 @@ impl RunWithoutFile { .and_then(|m| m.package.pkg_fs_mount_point.clone()) { if m == "." { - self.wasi.map_dir("/", pkg_fs); + wasi_map_dir.push(("/".to_string(), pkg_fs)); } else { if m.starts_with('.') { m = format!("{}{}", pkg_fs.display(), &m[1..]); } let path = std::path::Path::new(&m).to_path_buf(); - self.wasi.map_dir("/", path); + wasi_map_dir.push(("/".to_string(), path)); } } } @@ -123,7 +127,7 @@ impl RunWithoutFile { continue; } let alias_pathbuf = std::path::Path::new(&real_dir).to_path_buf(); - self.wasi.map_dir(alias, alias_pathbuf.clone()); + wasi_map_dir.push((alias.to_string(), alias_pathbuf.clone())); fn is_dir(e: &walkdir::DirEntry) -> bool { let meta = match e.metadata() { @@ -142,9 +146,31 @@ impl RunWithoutFile { let pathbuf = entry.path().canonicalize().unwrap(); let path = format!("{}", pathbuf.display()); let relativepath = path.replacen(&root_display, "", 1); - self.wasi - .map_dir(&format!("/{alias}{relativepath}"), pathbuf); + wasi_map_dir.push((format!("/{alias}{relativepath}"), pathbuf)); + } + } + } + + // If a directory is mapped twice, the WASI runtime will throw an error + // We need to calculate the "key" path, then deduplicate the mapping + #[cfg(feature = "wasi")] + { + let parent = match pathbuf.parent() { + Some(parent) => parent.to_path_buf(), + None => pathbuf.clone(), + }; + let mut wasi_map = BTreeMap::new(); + for (k, v) in wasi_map_dir { + let path_v = v.canonicalize().unwrap_or(v.clone()); + let mut k_path = std::path::Path::new(&k).to_path_buf(); + if k_path.is_relative() { + k_path = parent.join(&k); } + let key = format!("{}", k_path.canonicalize().unwrap_or(k_path).display()); + wasi_map.insert(key, (k, path_v)); + } + for (_, (k, v)) in wasi_map { + self.wasi.map_dir(&k, v); } } @@ -155,7 +181,10 @@ impl RunWithoutFile { #[cfg(feature = "cache")] disable_cache: self.disable_cache, invoke: self.invoke, - command_name: self.command_name, + // If the RunWithoutFile was constructed via a package name, + // the correct syntax is "package:command-name" (--command-name would be + // interpreted as a CLI argument for the .wasm file) + command_name: None, #[cfg(feature = "cache")] cache_key: self.cache_key, store: self.store, diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 89a23e517c2..5fe0dc68b0f 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -561,6 +561,7 @@ pub fn get_package_local_wasm_file( registry_host: &str, name: &str, version: &str, + command: Option<&str>, ) -> Result { let dir = get_package_local_dir(registry_host, name, version)?; let wapm_toml_path = dir.join("wapm.toml"); @@ -570,14 +571,17 @@ pub fn get_package_local_wasm_file( .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}: {e}"))?; // TODO: this will just return the path for the first command, so this might not be correct - let module_name = wapm - .command - .unwrap_or_default() - .first() - .map(|m| m.get_module()) - .ok_or_else(|| { - format!("cannot get entrypoint for {name}@{version}: package has no commands") - })?; + let module_name = match command { + Some(s) => s.to_string(), + None => wapm + .command + .unwrap_or_default() + .first() + .map(|m| m.get_module()) + .ok_or_else(|| { + format!("cannot get entrypoint for {name}@{version}: package has no commands") + })?, + }; let wasm_file_name = wapm .module From 0f59d59eab6fe6a7ab1c6a075c3d062a16f517e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 09:25:50 +0200 Subject: [PATCH 61/83] Fix "make lint" --- lib/cli/src/cli.rs | 10 +++------- lib/cli/src/commands/run.rs | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 5b06d554b93..c3ac818765f 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -285,7 +285,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error } if sv.command.is_none() && is_fake_sv { - sv.command = Some(package.to_string()); + sv.command = Some(package); } let mut package_download_info = None; @@ -333,7 +333,7 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a &package.registry, &package.name, &package.version, - sv.command.as_ref().map(|s| s.as_str()), + sv.command.as_deref(), ) .map_err(|e| anyhow!("{e}"))?; @@ -406,11 +406,7 @@ struct SplitVersion { impl fmt::Display for SplitVersion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let version = self - .version - .as_ref() - .map(|s| s.as_str()) - .unwrap_or("latest"); + let version = self.version.as_deref().unwrap_or("latest"); let command = self .command .as_ref() diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 5244ea69ba4..59cb83a141a 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -161,7 +161,7 @@ impl RunWithoutFile { }; let mut wasi_map = BTreeMap::new(); for (k, v) in wasi_map_dir { - let path_v = v.canonicalize().unwrap_or(v.clone()); + let path_v = v.canonicalize().unwrap_or_else(|_| v.clone()); let mut k_path = std::path::Path::new(&k).to_path_buf(); if k_path.is_relative() { k_path = parent.join(&k); From 1a398c4adc605e5db29e464e0bbe86723394e898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 10:08:25 +0200 Subject: [PATCH 62/83] Fix more wasmer run UX bugs --- lib/cli/src/cli.rs | 7 +++++-- lib/registry/src/lib.rs | 36 +++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index c3ac818765f..fc75c6f126c 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -339,16 +339,19 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a // Try finding the local package let mut args_without_package = args.to_vec(); + + // "wasmer run package arg1 arg2" => "wasmer package arg1 arg2" args_without_package.remove(1); + // "wasmer package arg1 arg2" => "wasmer arg1 arg2" if args_without_package.get(1) == Some(&sv.package) - || args_without_package.get(1) == sv.command.as_ref() + || sv.command.is_some() && args_without_package.get(1) == sv.command.as_ref() { args_without_package.remove(1); } if args_without_package.get(0) == Some(&sv.package) - || args_without_package.get(0) == sv.command.as_ref() + || sv.command.is_some() && args_without_package.get(0) == sv.command.as_ref() { args_without_package.remove(0); } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 5fe0dc68b0f..016a98726b4 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -827,22 +827,35 @@ pub fn query_package_from_registry( }); } - let queried_package = available_packages + let mut queried_packages = available_packages .iter() - .find(|v| { + .filter_map(|v| { if name.contains('/') && v.package != name { - return false; + return None; } - if version != Some(&v.version) { - return false; + if version.is_some() && version != Some(&v.version) { + return None; } - true + Some(v) }) - .cloned(); + .collect::>(); + + let selected_package = match version { + Some(s) => queried_packages.iter().find(|p| p.version == s), + None => { + if let Some(latest) = queried_packages.iter().find(|s| s.is_latest_version) { + Some(latest) + } else { + // sort package by version, select highest + queried_packages.sort_by_key(|k| semver::Version::parse(&k.version).ok()); + queried_packages.first() + } + } + }; - match queried_package { + match selected_package { None => { return Err(QueryPackageError::NoPackageFound { name: name.to_string(), @@ -850,7 +863,7 @@ pub fn query_package_from_registry( packages: available_packages, }); } - Some(s) => Ok(s), + Some(s) => Ok((*s).clone()), } } @@ -944,12 +957,13 @@ pub fn install_package( } if !force_install { - if let Ok(Some(package)) = get_if_package_has_new_version( + let package_has_new_version = get_if_package_has_new_version( r, name, version.map(|s| s.to_string()), Duration::from_secs(60 * 5), - ) { + ); + if let Ok(Some(package)) = package_has_new_version { url_of_package = Some((r, package)); break; } From fd5fa25a917253c51eb5a3fa334b909117fb23f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 18:33:25 +0200 Subject: [PATCH 63/83] Do not auto-download packages if the execution failed --- lib/cli/src/cli.rs | 38 +++++++++++++++++++++++++++----------- lib/registry/src/lib.rs | 8 ++++---- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index fc75c6f126c..f635e0e9b4a 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -295,8 +295,10 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error } } - if let Ok(o) = try_execute_local_package(args, &sv) { - return Ok(o); + match try_execute_local_package(args, &sv) { + Ok(o) => return Ok(o), + Err(ExecuteLocalPackageError::DuringExec(e)) => return Err(e), + _ => {} } // else: local package not found - try to download and install package @@ -322,12 +324,21 @@ fn try_lookup_command(sv: &mut SplitVersion) -> Result Result<(), anyhow::Error> { - let package = match wasmer_registry::get_local_package(None, &sv.package, sv.version.as_deref()) - { - Some(p) => p, - None => return Err(anyhow::anyhow!("no local package {sv:?} found")), - }; +// We need to distinguish between errors that happen +// before vs. during execution +enum ExecuteLocalPackageError { + BeforeExec(anyhow::Error), + DuringExec(anyhow::Error), +} + +fn try_execute_local_package( + args: &[String], + sv: &SplitVersion, +) -> Result<(), ExecuteLocalPackageError> { + let package = wasmer_registry::get_local_package(None, &sv.package, sv.version.as_deref()) + .ok_or_else(|| { + ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("no local package {sv:?} found")) + })?; let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( &package.registry, @@ -335,7 +346,7 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a &package.version, sv.command.as_deref(), ) - .map_err(|e| anyhow!("{e}"))?; + .map_err(|e| ExecuteLocalPackageError::BeforeExec(anyhow!("{e}")))?; // Try finding the local package let mut args_without_package = args.to_vec(); @@ -356,10 +367,15 @@ fn try_execute_local_package(args: &[String], sv: &SplitVersion) -> Result<(), a args_without_package.remove(0); } - RunWithoutFile::try_parse_from(args_without_package.iter())? + RunWithoutFile::try_parse_from(args_without_package.iter()) + .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? .into_run_args(local_package_wasm_path.clone(), Some(package.manifest)) .execute() - .map_err(|e| e.context(anyhow::anyhow!("{}", local_package_wasm_path.display()))) + .map_err(|e| { + ExecuteLocalPackageError::DuringExec( + e.context(anyhow::anyhow!("{}", local_package_wasm_path.display())), + ) + }) } fn try_autoinstall_package( diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 016a98726b4..470f4cbd5ba 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -829,16 +829,16 @@ pub fn query_package_from_registry( let mut queried_packages = available_packages .iter() - .filter_map(|v| { + .filter(|v| { if name.contains('/') && v.package != name { - return None; + return false; } if version.is_some() && version != Some(&v.version) { - return None; + return false; } - Some(v) + true }) .collect::>(); From d6719b790f42297098da0933638497f9c9328dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 19:20:58 +0200 Subject: [PATCH 64/83] Add (failing) integration test for wasmer run --- tests/examples/test.py | 1 + tests/integration/cli/tests/run.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/examples/test.py diff --git a/tests/examples/test.py b/tests/examples/test.py new file mode 100644 index 00000000000..ce47b771f4f --- /dev/null +++ b/tests/examples/test.py @@ -0,0 +1 @@ +print("hello") \ No newline at end of file diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index bb97b67ca04..fb2721eb7ff 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -16,6 +16,10 @@ fn test_no_start_wat_path() -> String { format!("{}/{}", ASSET_PATH, "no_start.wat") } +fn test_python_script() -> String { + format!("{}/{}", ASSET_PATH, "test.py") +} + #[test] fn run_wasi_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) @@ -42,6 +46,29 @@ fn run_wasi_works() -> anyhow::Result<()> { Ok(()) } +#[test] +fn test_wasmer_run_works() -> anyhow::Result<()> { + let output = Command::new(get_wasmer_path()) + .arg("run") + .arg("registry.wapm.io/graphql:python/python") + .arg(test_python_script()) + .output()?; + + let stdout = std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"); + + if !output.status.success() || stdout != "hello" { + bail!( + "running python/python failed with: stdout: {}\n\nstderr: {}", + stdout, + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + Ok(()) +} + #[test] fn run_no_imports_wasm_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) From 3dcd2a6ef8e2ee3058f9d642f9bb8fd42c6b692d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 19:57:01 +0200 Subject: [PATCH 65/83] Add support for specifying registry in CLI arg --- lib/cli/src/cli.rs | 68 ++++++++++++++++++++++++++++-- tests/integration/cli/tests/run.rs | 10 ++--- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index f635e0e9b4a..f0f086d93b9 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -258,6 +258,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error Ok(o) => o, Err(_) => { let mut fake_sv = SplitVersion { + registry: None, package: package.to_string(), version: None, command: None, @@ -265,6 +266,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error is_fake_sv = true; match try_lookup_command(&mut fake_sv) { Ok(o) => SplitVersion { + registry: None, package: o.package, version: Some(o.version), command: r.command_name.clone(), @@ -306,11 +308,13 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error } fn try_lookup_command(sv: &mut SplitVersion) -> Result { + use std::io::Write; let sp = start_spinner(format!("Looking up command {} ...", sv.package)); for registry in wasmer_registry::get_all_available_registries().unwrap_or_default() { let result = wasmer_registry::query_command_from_registry(®istry, &sv.package); - print!("\r\n"); + print!("\r"); + let _ = std::io::stdout().flush(); let command = sv.package.clone(); if let Ok(o) = result { sv.package = o.package.clone(); @@ -321,6 +325,8 @@ fn try_lookup_command(sv: &mut SplitVersion) -> Result, force_install: bool, ) -> Result<(), anyhow::Error> { + use std::io::Write; let sp = start_spinner(format!("Installing package {} ...", sv.package)); let v = sv.version.as_deref(); - let result = wasmer_registry::install_package(None, &sv.package, v, package, force_install); + let result = wasmer_registry::install_package( + sv.registry.as_deref(), + &sv.package, + v, + package, + force_install, + ); sp.close(); - print!("\r\n"); + print!("\r"); + let _ = std::io::stdout().flush(); let (package, buf) = match result { Ok(o) => o, Err(e) => { @@ -418,6 +432,7 @@ fn start_spinner(msg: String) -> SpinnerHandle { #[derive(Debug, Clone, PartialEq, Default)] struct SplitVersion { + registry: Option, package: String, version: Option, command: Option, @@ -437,9 +452,28 @@ impl fmt::Display for SplitVersion { #[test] fn test_split_version() { + assert_eq!( + split_version("registry.wapm.io/graphql/python/python").unwrap(), + SplitVersion { + registry: Some("https://registry.wapm.io/graphql".to_string()), + package: "python/python".to_string(), + version: None, + command: None, + } + ); + assert_eq!( + split_version("registry.wapm.io/python/python").unwrap(), + SplitVersion { + registry: Some("https://registry.wapm.io/graphql".to_string()), + package: "python/python".to_string(), + version: None, + command: None, + } + ); assert_eq!( split_version("namespace/name@version:command").unwrap(), SplitVersion { + registry: None, package: "namespace/name".to_string(), version: Some("version".to_string()), command: Some("command".to_string()), @@ -448,6 +482,7 @@ fn test_split_version() { assert_eq!( split_version("namespace/name@version").unwrap(), SplitVersion { + registry: None, package: "namespace/name".to_string(), version: Some("version".to_string()), command: None, @@ -456,6 +491,16 @@ fn test_split_version() { assert_eq!( split_version("namespace/name").unwrap(), SplitVersion { + registry: None, + package: "namespace/name".to_string(), + version: None, + command: None, + } + ); + assert_eq!( + split_version("registry.wapm.io/namespace/name").unwrap(), + SplitVersion { + registry: Some("https://registry.wapm.io/graphql".to_string()), package: "namespace/name".to_string(), version: None, command: None, @@ -519,7 +564,7 @@ fn split_version(s: &str) -> Result { return Err(anyhow::anyhow!("Invalid package version: {s:?}")); }; - let namespace = match captures.get(1).cloned() { + let mut namespace = match captures.get(1).cloned() { Some(s) => s, None => { return Err(anyhow::anyhow!( @@ -533,7 +578,22 @@ fn split_version(s: &str) -> Result { None => return Err(anyhow::anyhow!("Invalid package version: {s:?}: no name")), }; + let mut registry = None; + if namespace.contains("/") { + let (r, n) = namespace.rsplit_once('/').unwrap(); + let mut real_registry = r.to_string(); + if !real_registry.ends_with("graphql") { + real_registry = format!("{real_registry}/graphql"); + } + if !real_registry.contains("://") { + real_registry = format!("https://{real_registry}"); + } + registry = Some(real_registry); + namespace = n.to_string(); + } + let sv = SplitVersion { + registry, package: format!("{namespace}/{name}"), version: if no_version { None diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index fb2721eb7ff..93a5a40370b 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -49,13 +49,13 @@ fn run_wasi_works() -> anyhow::Result<()> { #[test] fn test_wasmer_run_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) - .arg("run") - .arg("registry.wapm.io/graphql:python/python") - .arg(test_python_script()) - .output()?; + .arg("run") + .arg("registry.wapm.io/python/python") + .arg(test_python_script()) + .output()?; let stdout = std::str::from_utf8(&output.stdout) - .expect("stdout is not utf8! need to handle arbitrary bytes"); + .expect("stdout is not utf8! need to handle arbitrary bytes"); if !output.status.success() || stdout != "hello" { bail!( From 5b165b634badff838eb564d715db92b66a6bc5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 20:16:41 +0200 Subject: [PATCH 66/83] Fix execution of python/python with wrong directory mapping --- lib/cli/src/cli.rs | 14 +++++++++----- lib/cli/src/commands/run.rs | 24 +++++++++--------------- lib/registry/src/lib.rs | 15 +++++++++++---- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index f0f086d93b9..6aaa76192fc 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -346,7 +346,7 @@ fn try_execute_local_package( ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("no local package {sv:?} found")) })?; - let local_package_wasm_path = wasmer_registry::get_package_local_wasm_file( + let (package_dir, local_package_wasm_path) = wasmer_registry::get_package_local_wasm_file( &package.registry, &package.name, &package.version, @@ -375,7 +375,11 @@ fn try_execute_local_package( RunWithoutFile::try_parse_from(args_without_package.iter()) .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? - .into_run_args(local_package_wasm_path.clone(), Some(package.manifest)) + .into_run_args( + package_dir, + local_package_wasm_path.clone(), + Some(package.manifest), + ) .execute() .map_err(|e| { ExecuteLocalPackageError::DuringExec( @@ -403,7 +407,7 @@ fn try_autoinstall_package( sp.close(); print!("\r"); let _ = std::io::stdout().flush(); - let (package, buf) = match result { + let (package, package_dir, buf) = match result { Ok(o) => o, Err(e) => { return Err(anyhow::anyhow!("{e}")); @@ -418,7 +422,7 @@ fn try_autoinstall_package( run_args.command_name = sv.command.clone(); run_args - .into_run_args(buf, Some(package.manifest)) + .into_run_args(package_dir, buf, Some(package.manifest)) .execute() } @@ -579,7 +583,7 @@ fn split_version(s: &str) -> Result { }; let mut registry = None; - if namespace.contains("/") { + if namespace.contains('/') { let (r, n) = namespace.rsplit_once('/').unwrap(); let mut real_registry = r.to_string(); if !real_registry.ends_with("graphql") { diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 59cb83a141a..e7a53b06c3b 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -81,16 +81,18 @@ pub struct RunWithoutFile { impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). - pub fn into_run_args(mut self, pathbuf: PathBuf, manifest: Option) -> Run { + pub fn into_run_args( + mut self, + package_root_dir: PathBuf, + pathbuf: PathBuf, + manifest: Option, + ) -> Run { #[cfg(feature = "wasi")] let mut wasi_map_dir = Vec::new(); #[cfg(feature = "wasi")] { - let pkg_fs = match pathbuf.parent() { - Some(parent) => parent.join("pkg_fs"), - None => pathbuf.join("pkg_fs"), - }; + let pkg_fs = package_root_dir.join("pkg_fs"); if let Some(mut m) = manifest .as_ref() .and_then(|m| m.package.pkg_fs_mount_point.clone()) @@ -114,11 +116,7 @@ impl RunWithoutFile { if real_dir.starts_with('/') { real_dir = (&real_dir[1..]).to_string(); } - let real_dir = if let Some(parent) = pathbuf.parent() { - parent.join(real_dir) - } else { - pathbuf.join(real_dir) - }; + let real_dir = package_root_dir.join(real_dir); if !real_dir.exists() { println!( "warning: cannot map {alias:?} to {}: directory does not exist", @@ -155,16 +153,12 @@ impl RunWithoutFile { // We need to calculate the "key" path, then deduplicate the mapping #[cfg(feature = "wasi")] { - let parent = match pathbuf.parent() { - Some(parent) => parent.to_path_buf(), - None => pathbuf.clone(), - }; let mut wasi_map = BTreeMap::new(); for (k, v) in wasi_map_dir { let path_v = v.canonicalize().unwrap_or_else(|_| v.clone()); let mut k_path = std::path::Path::new(&k).to_path_buf(); if k_path.is_relative() { - k_path = parent.join(&k); + k_path = package_root_dir.join(&k); } let key = format!("{}", k_path.canonicalize().unwrap_or(k_path).display()); wasi_map.insert(key, (k, path_v)); diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 470f4cbd5ba..5ed090220d7 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -557,12 +557,14 @@ pub fn get_local_package( .cloned() } +/// Given a [registry, name, version, command?], returns the +/// (package dir path, .wasm file path) of the command to execute pub fn get_package_local_wasm_file( registry_host: &str, name: &str, version: &str, command: Option<&str>, -) -> Result { +) -> Result<(PathBuf, PathBuf), String> { let dir = get_package_local_dir(registry_host, name, version)?; let wapm_toml_path = dir.join("wapm.toml"); let wapm_toml_str = std::fs::read_to_string(&wapm_toml_path) @@ -594,7 +596,8 @@ pub fn get_package_local_wasm_file( format!("cannot get entrypoint for {name}@{version}: package has no commands") })?; - Ok(dir.join(&wasm_file_name)) + let wasm_path = dir.join(&wasm_file_name); + Ok((dir, wasm_path)) } pub fn query_command_from_registry( @@ -919,13 +922,15 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result, name: &str, version: Option<&str>, package_download_info: Option, force_install: bool, -) -> Result<(LocalPackage, PathBuf), String> { +) -> Result<(LocalPackage, PathBuf, PathBuf), String> { let package_info = match package_download_info { Some(s) => s, None => { @@ -1051,6 +1056,7 @@ pub fn install_package( format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml") })?; + let entrypoint_source = target_path.join(&entrypoint_module.source); Ok(( LocalPackage { registry: package_info.registry, @@ -1059,7 +1065,8 @@ pub fn install_package( manifest: wapm_toml, path: target_path.clone(), }, - target_path.join(&entrypoint_module.source), + target_path, + entrypoint_source, )) } From f761ddc4afbabbe4d359e23da2c6adaff1a114a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 18 Oct 2022 21:04:38 +0200 Subject: [PATCH 67/83] Make python/python run --- lib/cli/src/commands/run.rs | 87 +++++++------------------------- lib/cli/src/commands/run/wasi.rs | 2 - 2 files changed, 18 insertions(+), 71 deletions(-) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index e7a53b06c3b..5626a859e17 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -5,7 +5,7 @@ use crate::store::{CompilerType, StoreOptions}; use crate::suggestions::suggest_function_exports; use crate::warning; use anyhow::{anyhow, Context, Result}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; @@ -79,44 +79,32 @@ pub struct RunWithoutFile { args: Vec, } +#[allow(dead_code)] +fn is_dir(e: &walkdir::DirEntry) -> bool { + let meta = match e.metadata() { + Ok(o) => o, + Err(_) => return false, + }; + meta.is_dir() +} + impl RunWithoutFile { /// Given a local path, returns the `Run` command (overriding the `--path` argument). pub fn into_run_args( mut self, - package_root_dir: PathBuf, - pathbuf: PathBuf, + package_root_dir: PathBuf, // <- package dir + pathbuf: PathBuf, // <- wasm file manifest: Option, ) -> Run { - #[cfg(feature = "wasi")] - let mut wasi_map_dir = Vec::new(); - #[cfg(feature = "wasi")] { - let pkg_fs = package_root_dir.join("pkg_fs"); - if let Some(mut m) = manifest + let default = HashMap::default(); + let fs = manifest .as_ref() - .and_then(|m| m.package.pkg_fs_mount_point.clone()) - { - if m == "." { - wasi_map_dir.push(("/".to_string(), pkg_fs)); - } else { - if m.starts_with('.') { - m = format!("{}{}", pkg_fs.display(), &m[1..]); - } - let path = std::path::Path::new(&m).to_path_buf(); - wasi_map_dir.push(("/".to_string(), path)); - } - } - } - - #[cfg(feature = "wasi")] - if let Some(fs) = manifest.as_ref().and_then(|m| m.fs.as_ref()) { + .and_then(|m| m.fs.as_ref()) + .unwrap_or(&default); for (alias, real_dir) in fs.iter() { - let mut real_dir = format!("{}", real_dir.display()); - if real_dir.starts_with('/') { - real_dir = (&real_dir[1..]).to_string(); - } - let real_dir = package_root_dir.join(real_dir); + let real_dir = package_root_dir.join(&real_dir); if !real_dir.exists() { println!( "warning: cannot map {alias:?} to {}: directory does not exist", @@ -124,47 +112,8 @@ impl RunWithoutFile { ); continue; } - let alias_pathbuf = std::path::Path::new(&real_dir).to_path_buf(); - wasi_map_dir.push((alias.to_string(), alias_pathbuf.clone())); - fn is_dir(e: &walkdir::DirEntry) -> bool { - let meta = match e.metadata() { - Ok(o) => o, - Err(_) => return false, - }; - meta.is_dir() - } - - let root_display = format!("{}", alias_pathbuf.display()); - for entry in walkdir::WalkDir::new(&alias_pathbuf) - .into_iter() - .filter_entry(is_dir) - .filter_map(|e| e.ok()) - { - let pathbuf = entry.path().canonicalize().unwrap(); - let path = format!("{}", pathbuf.display()); - let relativepath = path.replacen(&root_display, "", 1); - wasi_map_dir.push((format!("/{alias}{relativepath}"), pathbuf)); - } - } - } - - // If a directory is mapped twice, the WASI runtime will throw an error - // We need to calculate the "key" path, then deduplicate the mapping - #[cfg(feature = "wasi")] - { - let mut wasi_map = BTreeMap::new(); - for (k, v) in wasi_map_dir { - let path_v = v.canonicalize().unwrap_or_else(|_| v.clone()); - let mut k_path = std::path::Path::new(&k).to_path_buf(); - if k_path.is_relative() { - k_path = package_root_dir.join(&k); - } - let key = format!("{}", k_path.canonicalize().unwrap_or(k_path).display()); - wasi_map.insert(key, (k, path_v)); - } - for (_, (k, v)) in wasi_map { - self.wasi.map_dir(&k, v); + self.wasi.map_dir(alias, real_dir.clone()); } } diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index bbe9ec8c121..ffc70c42036 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -54,8 +54,6 @@ pub struct Wasi { impl Wasi { pub fn map_dir(&mut self, alias: &str, target_on_disk: PathBuf) { self.mapped_dirs.push((alias.to_string(), target_on_disk)); - self.pre_opened_directories - .push(std::path::Path::new(alias).to_path_buf()); } pub fn set_env(&mut self, key: &str, value: &str) { From 1f59a5c81e53266ae98048ade8af81fccc08066b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 14:44:12 +0200 Subject: [PATCH 68/83] Run packages via specifying directory instead of package name --- Cargo.lock | 2 + lib/cli/Cargo.toml | 2 + lib/cli/src/cli.rs | 38 +++----- lib/cli/src/commands/run.rs | 17 ++-- lib/registry/src/lib.rs | 180 ++++++++++++++++-------------------- 5 files changed, 106 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 088494c38da..b266eb81fba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3828,7 +3828,9 @@ dependencies = [ "spinner", "target-lexicon 0.12.4", "tempfile", + "toml", "unix_mode", + "url", "walkdir", "wapm-toml", "wasmer", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 800bcea7fe6..cb5e9e2a0ea 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -62,6 +62,8 @@ prettytable-rs = "0.9.0" wapm-toml = "0.1.2" walkdir = "2.3.2" regex = "1.6.0" +toml = "0.5.9" +url = "2.3.1" [build-dependencies] chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] } diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 6aaa76192fc..738a207246e 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -303,6 +303,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error _ => {} } + println!("finding local package {} failed", sv); // else: local package not found - try to download and install package try_autoinstall_package(args, &sv, package_download_info, r.force_install) } @@ -346,13 +347,9 @@ fn try_execute_local_package( ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("no local package {sv:?} found")) })?; - let (package_dir, local_package_wasm_path) = wasmer_registry::get_package_local_wasm_file( - &package.registry, - &package.name, - &package.version, - sv.command.as_deref(), - ) - .map_err(|e| ExecuteLocalPackageError::BeforeExec(anyhow!("{e}")))?; + let package_dir = package + .get_path() + .map_err(|e| ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("{e}")))?; // Try finding the local package let mut args_without_package = args.to_vec(); @@ -375,17 +372,10 @@ fn try_execute_local_package( RunWithoutFile::try_parse_from(args_without_package.iter()) .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? - .into_run_args( - package_dir, - local_package_wasm_path.clone(), - Some(package.manifest), - ) + .into_run_args(package_dir, sv.command.as_deref()) + .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? .execute() - .map_err(|e| { - ExecuteLocalPackageError::DuringExec( - e.context(anyhow::anyhow!("{}", local_package_wasm_path.display())), - ) - }) + .map_err(|e| ExecuteLocalPackageError::DuringExec(e.context(anyhow::anyhow!("{}", sv)))) } fn try_autoinstall_package( @@ -407,7 +397,7 @@ fn try_autoinstall_package( sp.close(); print!("\r"); let _ = std::io::stdout().flush(); - let (package, package_dir, buf) = match result { + let (_, package_dir) = match result { Ok(o) => o, Err(e) => { return Err(anyhow::anyhow!("{e}")); @@ -422,7 +412,7 @@ fn try_autoinstall_package( run_args.command_name = sv.command.clone(); run_args - .into_run_args(package_dir, buf, Some(package.manifest)) + .into_run_args(package_dir, sv.command.as_deref())? .execute() } @@ -621,9 +611,11 @@ fn print_packages() -> Result<(), anyhow::Error> { let rows = get_all_local_packages(None) .into_iter() - .map(|pkg| { - let commands = pkg - .manifest + .filter_map(|pkg| { + let package_root_path = pkg.get_path().ok()?; + let (manifest, _) = + wasmer_registry::get_executable_file_from_path(&package_root_path, None).ok()?; + let commands = manifest .command .unwrap_or_default() .iter() @@ -631,7 +623,7 @@ fn print_packages() -> Result<(), anyhow::Error> { .collect::>() .join(" \r\n"); - row![pkg.registry, pkg.name, pkg.version, commands] + Some(row![pkg.registry, pkg.name, pkg.version, commands]) }) .collect::>(); diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 5626a859e17..a1bc3e1529b 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -93,16 +93,15 @@ impl RunWithoutFile { pub fn into_run_args( mut self, package_root_dir: PathBuf, // <- package dir - pathbuf: PathBuf, // <- wasm file - manifest: Option, - ) -> Run { + command: Option<&str>, + ) -> Result { + let (manifest, pathbuf) = + wasmer_registry::get_executable_file_from_path(&package_root_dir, command)?; + #[cfg(feature = "wasi")] { let default = HashMap::default(); - let fs = manifest - .as_ref() - .and_then(|m| m.fs.as_ref()) - .unwrap_or(&default); + let fs = manifest.fs.as_ref().unwrap_or(&default); for (alias, real_dir) in fs.iter() { let real_dir = package_root_dir.join(&real_dir); if !real_dir.exists() { @@ -117,7 +116,7 @@ impl RunWithoutFile { } } - Run { + Ok(Run { path: pathbuf, options: RunWithoutFile { force_install: self.force_install, @@ -141,7 +140,7 @@ impl RunWithoutFile { verbose: self.verbose.unwrap_or(0), args: self.args, }, - } + }) } } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 5ed090220d7..a6a0250335a 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -458,8 +458,57 @@ pub struct LocalPackage { pub registry: String, pub name: String, pub version: String, - pub manifest: wapm_toml::Manifest, - pub path: PathBuf, +} + +impl LocalPackage { + pub fn get_path(&self) -> Result { + let host = url::Url::parse(&self.registry) + .ok() + .and_then(|o| o.host_str().map(|s| s.to_string())) + .unwrap_or_else(|| self.registry.clone()); + + get_package_local_dir(&host, &self.name, &self.version) + } +} + +/// Returns the (manifest, .wasm file name), given a package dir +pub fn get_executable_file_from_path( + package_dir: &PathBuf, + command: Option<&str>, +) -> Result<(wapm_toml::Manifest, PathBuf), anyhow::Error> { + let wapm_toml = std::fs::read_to_string(package_dir.join("wapm.toml")) + .map_err(|_| anyhow::anyhow!("Package {package_dir:?} has no wapm.toml"))?; + + let wapm_toml = toml::from_str::(&wapm_toml) + .map_err(|e| anyhow::anyhow!("Could not parse toml for {package_dir:?}: {e}"))?; + + let name = wapm_toml.package.name.clone(); + let version = wapm_toml.package.version.clone(); + + let commands = wapm_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 => commands.first().ok_or_else(|| { + anyhow::anyhow!("Cannot run {name}@{version}: package has no commands") + })?, + }; + + let module_name = entrypoint_module.get_module(); + let modules = wapm_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 wapm.toml" + ) + })?; + + let entrypoint_source = package_dir.join(&entrypoint_module.source); + + Ok((wapm_toml, entrypoint_source)) } fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { @@ -494,17 +543,25 @@ pub fn get_all_local_packages(registry: Option<&str>) -> Vec { Some(s) => vec![s.to_string()], None => get_all_available_registries().unwrap_or_default(), }; - for registry in registries { - let host = match url::Url::parse(®istry) { - Ok(o) => o.host_str().map(|s| s.to_string()), - Err(_) => continue, - }; - let host = match host { - Some(s) => s, - None => continue, - }; + let mut registry_hosts = registries + .into_iter() + .filter_map(|s| url::Url::parse(&s).ok()?.host_str().map(|s| s.to_string())) + .collect::>(); + + let mut registries_in_root_dir = get_checkouts_dir() + .as_ref() + .map(get_all_names_in_dir) + .unwrap_or_default() + .into_iter() + .filter_map(|(path, p)| if path.is_dir() { Some(p) } else { None }) + .collect(); + registry_hosts.append(&mut registries_in_root_dir); + registry_hosts.sort(); + registry_hosts.dedup(); + + for host in registry_hosts { let root_dir = match get_global_install_dir(&host) { Some(o) => o, None => continue, @@ -513,11 +570,7 @@ pub fn get_all_local_packages(registry: Option<&str>) -> Vec { for (username_path, user_name) in get_all_names_in_dir(&root_dir) { for (package_path, package_name) in get_all_names_in_dir(&username_path) { for (version_path, package_version) in get_all_names_in_dir(&package_path) { - let toml_str = match std::fs::read_to_string(version_path.join("wapm.toml")) { - Ok(o) => o, - Err(_) => continue, - }; - let manifest = match toml::from_str(&toml_str) { + let _ = match std::fs::read_to_string(version_path.join("wapm.toml")) { Ok(o) => o, Err(_) => continue, }; @@ -525,8 +578,6 @@ pub fn get_all_local_packages(registry: Option<&str>) -> Vec { registry: host.clone(), name: format!("{user_name}/{package_name}"), version: package_version, - manifest, - path: version_path, }); } } @@ -557,49 +608,6 @@ pub fn get_local_package( .cloned() } -/// Given a [registry, name, version, command?], returns the -/// (package dir path, .wasm file path) of the command to execute -pub fn get_package_local_wasm_file( - registry_host: &str, - name: &str, - version: &str, - command: Option<&str>, -) -> Result<(PathBuf, PathBuf), String> { - let dir = get_package_local_dir(registry_host, name, version)?; - let wapm_toml_path = dir.join("wapm.toml"); - let wapm_toml_str = std::fs::read_to_string(&wapm_toml_path) - .map_err(|e| format!("cannot read wapm.toml for {name}@{version}: {e}"))?; - let wapm = toml::from_str::(&wapm_toml_str) - .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}: {e}"))?; - - // TODO: this will just return the path for the first command, so this might not be correct - let module_name = match command { - Some(s) => s.to_string(), - None => wapm - .command - .unwrap_or_default() - .first() - .map(|m| m.get_module()) - .ok_or_else(|| { - format!("cannot get entrypoint for {name}@{version}: package has no commands") - })?, - }; - - let wasm_file_name = wapm - .module - .unwrap_or_default() - .iter() - .filter(|m| m.name == module_name) - .map(|m| m.source.clone()) - .next() - .ok_or_else(|| { - format!("cannot get entrypoint for {name}@{version}: package has no commands") - })?; - - let wasm_path = dir.join(&wasm_file_name); - Ok((dir, wasm_path)) -} - pub fn query_command_from_registry( registry_url: &str, command_name: &str, @@ -870,14 +878,13 @@ pub fn query_package_from_registry( } } +pub fn get_checkouts_dir() -> Option { + Some(PartialWapmConfig::get_folder().ok()?.join("checkouts")) +} + /// Returs the path to the directory where all packages on this computer are being stored pub fn get_global_install_dir(registry_host: &str) -> Option { - Some( - PartialWapmConfig::get_folder() - .ok()? - .join("checkouts") - .join(registry_host), - ) + Some(get_checkouts_dir()?.join(registry_host)) } pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result { @@ -930,7 +937,7 @@ pub fn install_package( version: Option<&str>, package_download_info: Option, force_install: bool, -) -> Result<(LocalPackage, PathBuf, PathBuf), String> { +) -> Result<(LocalPackage, PathBuf), String> { let package_info = match package_download_info { Some(s) => s, None => { @@ -1030,43 +1037,14 @@ pub fn install_package( if !dir.join("wapm.toml").exists() || force_install { download_and_unpack_targz(&package_info.url, &dir)?; } - let target_path = dir; - - let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")).map_err(|_| { - format!( - "Package {name}@{version} has no wapm.toml (path: {})", - target_path.display() - ) - })?; - - let wapm_toml = toml::from_str::(&wapm_toml) - .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; - - let commands = wapm_toml.command.clone().unwrap_or_default(); - let entrypoint_module = commands - .first() - .ok_or_else(|| format!("Cannot run {name}@{version}: package has no commands"))?; - - let module_name = entrypoint_module.get_module(); - let modules = wapm_toml.module.clone().unwrap_or_default(); - let entrypoint_module = modules - .iter() - .find(|m| m.name == module_name) - .ok_or_else(|| { - format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml") - })?; - let entrypoint_source = target_path.join(&entrypoint_module.source); Ok(( LocalPackage { registry: package_info.registry, - name: wapm_toml.package.name.clone(), - version: wapm_toml.package.version.to_string(), - manifest: wapm_toml, - path: target_path.clone(), + name: name, + version: version, }, - target_path, - entrypoint_source, + dir, )) } @@ -1128,7 +1106,7 @@ fn test_install_package() { install_package(Some(registry), "wasmer/wabt", Some("1.0.29"), None, true).unwrap(); assert_eq!( - package.path, + package.get_path().unwrap(), get_global_install_dir("registry.wapm.io") .unwrap() .join("wasmer") From 3de62868d7897328393b99fa3e21239eb68e17c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 15:05:48 +0200 Subject: [PATCH 69/83] Fix "make lint" --- lib/registry/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index a6a0250335a..681c9a008d6 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1041,8 +1041,8 @@ pub fn install_package( Ok(( LocalPackage { registry: package_info.registry, - name: name, - version: version, + name, + version, }, dir, )) From b887a835af393541475f973e684c527f7a2ed213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 15:15:34 +0200 Subject: [PATCH 70/83] Fix integration test, add option to run directory with wapm.toml --- lib/cli/src/cli.rs | 7 +++++++ tests/integration/cli/tests/run.rs | 10 +++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 738a207246e..36e80b46f2e 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -248,6 +248,13 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error> { // Check "r.path" is a file or a package / command name if r.path.exists() { + if r.path.is_dir() && r.path.join("wapm.toml").exists() { + let mut args_without_package = args.to_vec(); + let _ = args_without_package.remove(1); + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(r.path.clone(), r.command_name.as_deref())? + .execute(); + } return r.execute(); } diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 93a5a40370b..7f922635390 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -16,10 +16,6 @@ fn test_no_start_wat_path() -> String { format!("{}/{}", ASSET_PATH, "no_start.wat") } -fn test_python_script() -> String { - format!("{}/{}", ASSET_PATH, "test.py") -} - #[test] fn run_wasi_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) @@ -49,15 +45,15 @@ fn run_wasi_works() -> anyhow::Result<()> { #[test] fn test_wasmer_run_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) - .arg("run") .arg("registry.wapm.io/python/python") - .arg(test_python_script()) + .arg(format!("--mapdir=.:{}", ASSET_PATH)) + .arg("test.py") .output()?; let stdout = std::str::from_utf8(&output.stdout) .expect("stdout is not utf8! need to handle arbitrary bytes"); - if !output.status.success() || stdout != "hello" { + if stdout != "hello\n" { bail!( "running python/python failed with: stdout: {}\n\nstderr: {}", stdout, From 8eec46ad66ad082e6d610dcfc5eecc99099431a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 15:42:09 +0200 Subject: [PATCH 71/83] Fix "make lint" --- lib/cli/src/cli.rs | 2 +- lib/cli/src/commands/run.rs | 2 +- tests/integration/cli/tests/run.rs | 31 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 36e80b46f2e..d8fc408f690 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -380,7 +380,7 @@ fn try_execute_local_package( RunWithoutFile::try_parse_from(args_without_package.iter()) .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? .into_run_args(package_dir, sv.command.as_deref()) - .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? + .map_err(ExecuteLocalPackageError::DuringExec)? .execute() .map_err(|e| ExecuteLocalPackageError::DuringExec(e.context(anyhow::anyhow!("{}", sv)))) } diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index a1bc3e1529b..3fccf15b678 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -5,7 +5,7 @@ use crate::store::{CompilerType, StoreOptions}; use crate::suggestions::suggest_function_exports; use crate::warning; use anyhow::{anyhow, Context, Result}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 7f922635390..8f1e43f57ea 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -42,6 +42,37 @@ fn run_wasi_works() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn test_wasmer_run_works() -> anyhow::Result<()> { + + let temp_dir = tempfile::TempDir::new()?; + let qjs_path = temp_dir.path().join("qjs.wasm"); + + std::fs::copy(wasi_test_wasm_path(), &qjs_path)?; + std::fs::write(format!("{}/{}", C_ASSET_PATH, "qjs-wapm.toml"), temp_dir.path().join("wapm.toml"))?; + + let output = Command::new(get_wasmer_path()) + .arg("run") + .arg(temp_dir.path()) + .arg(format!("--mapdir=.:{}", temp_dir.path())) + .output()?; + + let stdout = std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"); + + if !output.status.success() { + bail!( + "running {} failed with: stdout: {}\n\nstderr: {}", + qjs_path.display(), + stdout, + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + Ok(()) +} #[test] fn test_wasmer_run_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) From 2f61896e6d3b2317e07ae9e04f3872095f4608e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 15:59:18 +0200 Subject: [PATCH 72/83] Add integration test for running directory --- lib/c-api/examples/assets/qjs-wapm.toml | 13 +++++++ tests/integration/cli/tests/run.rs | 45 ++++++++++++++++++++----- 2 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 lib/c-api/examples/assets/qjs-wapm.toml diff --git a/lib/c-api/examples/assets/qjs-wapm.toml b/lib/c-api/examples/assets/qjs-wapm.toml new file mode 100644 index 00000000000..53751a20d8a --- /dev/null +++ b/lib/c-api/examples/assets/qjs-wapm.toml @@ -0,0 +1,13 @@ +[package] +name = "adamz/qjs" +version = "0.0.1" +description = "https://github.com/bellard/quickjs" +license = "MIT" + +[[command]] +name = "qjs" +module = "qjs" + +[[module]] +name = "qjs" +source = "qjs.wasm" diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 8f1e43f57ea..ffe1e8cdf34 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -42,24 +42,51 @@ fn run_wasi_works() -> anyhow::Result<()> { Ok(()) } - #[test] -fn test_wasmer_run_works() -> anyhow::Result<()> { - +fn test_wasmer_run_works_with_dir() -> anyhow::Result<()> { let temp_dir = tempfile::TempDir::new()?; let qjs_path = temp_dir.path().join("qjs.wasm"); - + std::fs::copy(wasi_test_wasm_path(), &qjs_path)?; - std::fs::write(format!("{}/{}", C_ASSET_PATH, "qjs-wapm.toml"), temp_dir.path().join("wapm.toml"))?; - + std::fs::copy( + format!("{}/{}", C_ASSET_PATH, "qjs-wapm.toml"), + temp_dir.path().join("wapm.toml"), + )?; + + assert!(temp_dir.path().exists()); + assert!(temp_dir.path().join("wapm.toml").exists()); + assert!(temp_dir.path().join("qjs.wasm").exists()); + + // test with "wasmer qjs.wasm" let output = Command::new(get_wasmer_path()) - .arg("run") .arg(temp_dir.path()) - .arg(format!("--mapdir=.:{}", temp_dir.path())) + .arg("--") + .arg("--quit") .output()?; let stdout = std::str::from_utf8(&output.stdout) - .expect("stdout is not utf8! need to handle arbitrary bytes"); + .expect("stdout is not utf8! need to handle arbitrary bytes"); + + if !output.status.success() { + bail!( + "running {} failed with: stdout: {}\n\nstderr: {}", + qjs_path.display(), + stdout, + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + // test again with "wasmer run qjs.wasm" + let output = Command::new(get_wasmer_path()) + .arg("run") + .arg(temp_dir.path()) + .arg("--") + .arg("--quit") + .output()?; + + let stdout = std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"); if !output.status.success() { bail!( From d89db1f020befe851dfc1b166b3165dea87919dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 16:07:12 +0200 Subject: [PATCH 73/83] Fix bug when running file with directory --- lib/cli/src/cli.rs | 7 ++++++- tests/integration/cli/tests/run.rs | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index d8fc408f690..0665e81964a 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -250,7 +250,12 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error if r.path.exists() { if r.path.is_dir() && r.path.join("wapm.toml").exists() { let mut args_without_package = args.to_vec(); - let _ = args_without_package.remove(1); + if args_without_package.get(1) == Some(&format!("{}", r.path.display())) { + let _ = args_without_package.remove(1); + } else if args_without_package.get(2) == Some(&format!("{}", r.path.display())) { + let _ = args_without_package.remove(1); + let _ = args_without_package.remove(1); + } return RunWithoutFile::try_parse_from(args_without_package.iter())? .into_run_args(r.path.clone(), r.command_name.as_deref())? .execute(); diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index ffe1e8cdf34..69b0eae0c95 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -79,11 +79,11 @@ fn test_wasmer_run_works_with_dir() -> anyhow::Result<()> { // test again with "wasmer run qjs.wasm" let output = Command::new(get_wasmer_path()) - .arg("run") - .arg(temp_dir.path()) - .arg("--") - .arg("--quit") - .output()?; + .arg("run") + .arg(temp_dir.path()) + .arg("--") + .arg("--quit") + .output()?; let stdout = std::str::from_utf8(&output.stdout) .expect("stdout is not utf8! need to handle arbitrary bytes"); From 69cd38014254118c27429f9ce94a8096f24b1319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 16:33:45 +0200 Subject: [PATCH 74/83] Fix issue with wasmer run not working correctly --- lib/cli/src/cli.rs | 16 +++++------ tests/integration/cli/tests/run.rs | 43 +++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 0665e81964a..65fe6baede3 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -270,6 +270,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error Ok(o) => o, Err(_) => { let mut fake_sv = SplitVersion { + original: package.to_string(), registry: None, package: package.to_string(), version: None, @@ -278,6 +279,7 @@ fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error is_fake_sv = true; match try_lookup_command(&mut fake_sv) { Ok(o) => SplitVersion { + original: format!("{}@{}", o.package, o.version), registry: None, package: o.package, version: Some(o.version), @@ -366,22 +368,16 @@ fn try_execute_local_package( // Try finding the local package let mut args_without_package = args.to_vec(); - // "wasmer run package arg1 arg2" => "wasmer package arg1 arg2" + // remove either "run" or $package args_without_package.remove(1); // "wasmer package arg1 arg2" => "wasmer arg1 arg2" - if args_without_package.get(1) == Some(&sv.package) - || sv.command.is_some() && args_without_package.get(1) == sv.command.as_ref() + if (args_without_package.get(1).is_some() && args_without_package[1].starts_with(&sv.original)) + || (sv.command.is_some() && args_without_package[1].ends_with(sv.command.as_ref().unwrap())) { args_without_package.remove(1); } - if args_without_package.get(0) == Some(&sv.package) - || sv.command.is_some() && args_without_package.get(0) == sv.command.as_ref() - { - args_without_package.remove(0); - } - RunWithoutFile::try_parse_from(args_without_package.iter()) .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? .into_run_args(package_dir, sv.command.as_deref()) @@ -438,6 +434,7 @@ fn start_spinner(msg: String) -> SpinnerHandle { #[derive(Debug, Clone, PartialEq, Default)] struct SplitVersion { + original: String, registry: Option, package: String, version: Option, @@ -599,6 +596,7 @@ fn split_version(s: &str) -> Result { } let sv = SplitVersion { + original: s.to_string(), registry, package: format!("{namespace}/{name}"), version: if no_version { diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 69b0eae0c95..e583dd44088 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -100,6 +100,7 @@ fn test_wasmer_run_works_with_dir() -> anyhow::Result<()> { Ok(()) } + #[test] fn test_wasmer_run_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path()) @@ -113,7 +114,47 @@ fn test_wasmer_run_works() -> anyhow::Result<()> { if stdout != "hello\n" { bail!( - "running python/python failed with: stdout: {}\n\nstderr: {}", + "1 running python/python failed with: stdout: {}\n\nstderr: {}", + stdout, + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + // same test again, but this time with "wasmer run ..." + let output = Command::new(get_wasmer_path()) + .arg("run") + .arg("registry.wapm.io/python/python") + .arg(format!("--mapdir=.:{}", ASSET_PATH)) + .arg("test.py") + .output()?; + + let stdout = std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"); + + if stdout != "hello\n" { + bail!( + "2 running python/python failed with: stdout: {}\n\nstderr: {}", + stdout, + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + // same test again, but this time without specifying the registry + let output = Command::new(get_wasmer_path()) + .arg("run") + .arg("python/python") + .arg(format!("--mapdir=.:{}", ASSET_PATH)) + .arg("test.py") + .output()?; + + let stdout = std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"); + + if stdout != "hello\n" { + bail!( + "3 running python/python failed with: stdout: {}\n\nstderr: {}", stdout, std::str::from_utf8(&output.stderr) .expect("stderr is not utf8! need to handle arbitrary bytes") From 71e7ee12b8c91e7422f0a9637079e263f986ba24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 19 Oct 2022 17:49:48 +0200 Subject: [PATCH 75/83] Fix CI --- lib/cli/src/cli.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 65fe6baede3..a5b2131907b 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -458,6 +458,7 @@ fn test_split_version() { assert_eq!( split_version("registry.wapm.io/graphql/python/python").unwrap(), SplitVersion { + original: "registry.wapm.io/graphql/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), package: "python/python".to_string(), version: None, @@ -467,6 +468,7 @@ fn test_split_version() { assert_eq!( split_version("registry.wapm.io/python/python").unwrap(), SplitVersion { + original: "registry.wapm.io/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), package: "python/python".to_string(), version: None, @@ -476,6 +478,7 @@ fn test_split_version() { assert_eq!( split_version("namespace/name@version:command").unwrap(), SplitVersion { + original: "namespace/name@version:command".to_string(), registry: None, package: "namespace/name".to_string(), version: Some("version".to_string()), @@ -485,6 +488,7 @@ fn test_split_version() { assert_eq!( split_version("namespace/name@version").unwrap(), SplitVersion { + original: "namespace/name@version".to_string(), registry: None, package: "namespace/name".to_string(), version: Some("version".to_string()), @@ -494,6 +498,7 @@ fn test_split_version() { assert_eq!( split_version("namespace/name").unwrap(), SplitVersion { + original: "namespace/name".to_string(), registry: None, package: "namespace/name".to_string(), version: None, @@ -503,6 +508,7 @@ fn test_split_version() { assert_eq!( split_version("registry.wapm.io/namespace/name").unwrap(), SplitVersion { + original: "registry.wapm.io/namespace/name".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), package: "namespace/name".to_string(), version: None, From ab4b00126b752af796cd00229268171fbdfe4704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 20 Oct 2022 19:01:58 +0200 Subject: [PATCH 76/83] Fix integration test The stdout might print something else other than just "hello", so we need to check for the ending, not the stdout to be equal --- tests/integration/cli/tests/run.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index e583dd44088..b4d2615a64d 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -112,7 +112,7 @@ fn test_wasmer_run_works() -> anyhow::Result<()> { let stdout = std::str::from_utf8(&output.stdout) .expect("stdout is not utf8! need to handle arbitrary bytes"); - if stdout != "hello\n" { + if !stdout.ends_with("hello\n") { bail!( "1 running python/python failed with: stdout: {}\n\nstderr: {}", stdout, @@ -132,7 +132,7 @@ fn test_wasmer_run_works() -> anyhow::Result<()> { let stdout = std::str::from_utf8(&output.stdout) .expect("stdout is not utf8! need to handle arbitrary bytes"); - if stdout != "hello\n" { + if !stdout.ends_with("hello\n") { bail!( "2 running python/python failed with: stdout: {}\n\nstderr: {}", stdout, @@ -152,7 +152,7 @@ fn test_wasmer_run_works() -> anyhow::Result<()> { let stdout = std::str::from_utf8(&output.stdout) .expect("stdout is not utf8! need to handle arbitrary bytes"); - if stdout != "hello\n" { + if !stdout.ends_with("hello\n") { bail!( "3 running python/python failed with: stdout: {}\n\nstderr: {}", stdout, From fcc796225dc0ba3b1286e83608a6e7ee7de704b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Thu, 20 Oct 2022 19:04:41 +0200 Subject: [PATCH 77/83] Debug failing test on linux-musl --- lib/registry/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 681c9a008d6..be02817e189 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1083,13 +1083,17 @@ pub fn get_all_available_registries() -> Result, String> { #[test] fn test_install_package() { + println!("test install package..."); let registry = "https://registry.wapm.io/graphql"; if !test_if_registry_present(registry).unwrap_or(false) { panic!("registry.wapm.io not reachable, test will fail"); } + 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"); @@ -1104,6 +1108,8 @@ fn test_install_package() { let (package, _) = install_package(Some(registry), "wasmer/wabt", Some("1.0.29"), None, true).unwrap(); + + println!("package installed: {package:#?}"); assert_eq!( package.get_path().unwrap(), @@ -1115,9 +1121,15 @@ fn test_install_package() { ); let all_installed_packages = get_all_local_packages(Some(registry)); + + println!("all_installed_packages: {all_installed_packages:#?}"); + let is_installed = all_installed_packages .iter() .any(|p| p.name == "wasmer/wabt" && p.version == "1.0.29"); + + println!("is_installed: {is_installed:#?}"); + if !is_installed { let panic_str = get_all_local_packages(Some(registry)) .iter() @@ -1126,4 +1138,6 @@ fn test_install_package() { .join("\r\n"); panic!("get all local packages: failed to install:\r\n{panic_str}"); } + + println!("ok, done"); } From e57b7bb25aa16928d3a3f1fc52bb38e0c969f6a1 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Fri, 21 Oct 2022 16:49:33 +0800 Subject: [PATCH 78/83] Ran rustfmt --- lib/registry/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index be02817e189..7707fef5a30 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1108,7 +1108,7 @@ fn test_install_package() { let (package, _) = install_package(Some(registry), "wasmer/wabt", Some("1.0.29"), None, true).unwrap(); - + println!("package installed: {package:#?}"); assert_eq!( @@ -1121,7 +1121,7 @@ fn test_install_package() { ); let all_installed_packages = get_all_local_packages(Some(registry)); - + println!("all_installed_packages: {all_installed_packages:#?}"); let is_installed = all_installed_packages From 81350bc2a100c13f5d98d4a7fb906d9c20673411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 24 Oct 2022 17:38:59 +0200 Subject: [PATCH 79/83] Adress review comments --- lib/cli/src/cli.rs | 435 +++++++++-------------------------- lib/cli/src/commands.rs | 3 +- lib/cli/src/commands/list.rs | 43 ++++ lib/cli/src/commands/run.rs | 189 +++++++++++++++ 4 files changed, 341 insertions(+), 329 deletions(-) create mode 100644 lib/cli/src/commands/list.rs diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index a5b2131907b..e38fd5219ee 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -10,12 +10,10 @@ use crate::commands::CreateExe; use crate::commands::CreateObj; #[cfg(feature = "wast")] use crate::commands::Wast; -use crate::commands::{Cache, Config, Inspect, Run, RunWithoutFile, SelfUpdate, Validate}; +use crate::commands::{Cache, Config, Inspect, List, Run, SelfUpdate, Validate}; use crate::error::PrettyError; use clap::{CommandFactory, ErrorKind, Parser}; -use spinner::SpinnerHandle; use std::fmt; -use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; #[derive(Parser, Debug)] #[cfg_attr( @@ -40,7 +38,7 @@ use wasmer_registry::{get_all_local_packages, PackageDownloadInfo}; enum WasmerCLIOptions { /// List all locally installed packages #[clap(name = "list")] - List, + List(List), /// Run a WebAssembly file. Formats accepted: wasm, wat #[clap(name = "run")] @@ -165,7 +163,7 @@ impl WasmerCLIOptions { Self::CreateObj(create_obj) => create_obj.execute(), Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), - Self::List => print_packages(), + Self::List(list) => list.execute(), #[cfg(feature = "wast")] Self::Wast(wast) => wast.execute(), #[cfg(target_os = "linux")] @@ -239,206 +237,19 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { // Check if the file is a package name if let WasmerCLIOptions::Run(r) = &options { - return try_run_package_or_file(&args, r); + return crate::commands::try_run_package_or_file(&args, r); } options.execute() } -fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error> { - // Check "r.path" is a file or a package / command name - if r.path.exists() { - if r.path.is_dir() && r.path.join("wapm.toml").exists() { - let mut args_without_package = args.to_vec(); - if args_without_package.get(1) == Some(&format!("{}", r.path.display())) { - let _ = args_without_package.remove(1); - } else if args_without_package.get(2) == Some(&format!("{}", r.path.display())) { - let _ = args_without_package.remove(1); - let _ = args_without_package.remove(1); - } - return RunWithoutFile::try_parse_from(args_without_package.iter())? - .into_run_args(r.path.clone(), r.command_name.as_deref())? - .execute(); - } - return r.execute(); - } - - let package = format!("{}", r.path.display()); - - let mut is_fake_sv = false; - let mut sv = match split_version(&package) { - Ok(o) => o, - Err(_) => { - let mut fake_sv = SplitVersion { - original: package.to_string(), - registry: None, - package: package.to_string(), - version: None, - command: None, - }; - is_fake_sv = true; - match try_lookup_command(&mut fake_sv) { - Ok(o) => SplitVersion { - original: format!("{}@{}", o.package, o.version), - registry: None, - package: o.package, - version: Some(o.version), - command: r.command_name.clone(), - }, - Err(e) => { - return Err( - anyhow::anyhow!("No package for command {package:?} found, file {package:?} not found either") - .context(e) - .context(anyhow::anyhow!("{}", r.path.display())) - ); - } - } - } - }; - - if sv.command.is_none() { - sv.command = r.command_name.clone(); - } - - if sv.command.is_none() && is_fake_sv { - sv.command = Some(package); - } - - let mut package_download_info = None; - if !sv.package.contains('/') { - if let Ok(o) = try_lookup_command(&mut sv) { - package_download_info = Some(o); - } - } - - match try_execute_local_package(args, &sv) { - Ok(o) => return Ok(o), - Err(ExecuteLocalPackageError::DuringExec(e)) => return Err(e), - _ => {} - } - - println!("finding local package {} failed", sv); - // else: local package not found - try to download and install package - try_autoinstall_package(args, &sv, package_download_info, r.force_install) -} - -fn try_lookup_command(sv: &mut SplitVersion) -> Result { - use std::io::Write; - let sp = start_spinner(format!("Looking up command {} ...", sv.package)); - - for registry in wasmer_registry::get_all_available_registries().unwrap_or_default() { - let result = wasmer_registry::query_command_from_registry(®istry, &sv.package); - print!("\r"); - let _ = std::io::stdout().flush(); - let command = sv.package.clone(); - if let Ok(o) = result { - sv.package = o.package.clone(); - sv.version = Some(o.version.clone()); - sv.command = Some(command); - return Ok(o); - } - } - - sp.close(); - print!("\r"); - let _ = std::io::stdout().flush(); - Err(anyhow::anyhow!("command {sv} not found")) -} - -// We need to distinguish between errors that happen -// before vs. during execution -enum ExecuteLocalPackageError { - BeforeExec(anyhow::Error), - DuringExec(anyhow::Error), -} - -fn try_execute_local_package( - args: &[String], - sv: &SplitVersion, -) -> Result<(), ExecuteLocalPackageError> { - let package = wasmer_registry::get_local_package(None, &sv.package, sv.version.as_deref()) - .ok_or_else(|| { - ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("no local package {sv:?} found")) - })?; - - let package_dir = package - .get_path() - .map_err(|e| ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("{e}")))?; - - // Try finding the local package - let mut args_without_package = args.to_vec(); - - // remove either "run" or $package - args_without_package.remove(1); - - // "wasmer package arg1 arg2" => "wasmer arg1 arg2" - if (args_without_package.get(1).is_some() && args_without_package[1].starts_with(&sv.original)) - || (sv.command.is_some() && args_without_package[1].ends_with(sv.command.as_ref().unwrap())) - { - args_without_package.remove(1); - } - - RunWithoutFile::try_parse_from(args_without_package.iter()) - .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? - .into_run_args(package_dir, sv.command.as_deref()) - .map_err(ExecuteLocalPackageError::DuringExec)? - .execute() - .map_err(|e| ExecuteLocalPackageError::DuringExec(e.context(anyhow::anyhow!("{}", sv)))) -} - -fn try_autoinstall_package( - args: &[String], - sv: &SplitVersion, - package: Option, - force_install: bool, -) -> Result<(), anyhow::Error> { - use std::io::Write; - let sp = start_spinner(format!("Installing package {} ...", sv.package)); - let v = sv.version.as_deref(); - let result = wasmer_registry::install_package( - sv.registry.as_deref(), - &sv.package, - v, - package, - force_install, - ); - sp.close(); - print!("\r"); - let _ = std::io::stdout().flush(); - let (_, package_dir) = match result { - Ok(o) => o, - Err(e) => { - return Err(anyhow::anyhow!("{e}")); - } - }; - - // Try auto-installing the remote package - let mut args_without_package = args.to_vec(); - args_without_package.remove(1); - - let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; - run_args.command_name = sv.command.clone(); - - run_args - .into_run_args(package_dir, sv.command.as_deref())? - .execute() -} - -fn start_spinner(msg: String) -> SpinnerHandle { - spinner::SpinnerBuilder::new(msg) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", - ]) - .start() -} - #[derive(Debug, Clone, PartialEq, Default)] -struct SplitVersion { - original: String, - registry: Option, - package: String, - version: Option, - command: Option, +pub(crate) struct SplitVersion { + pub(crate) original: String, + pub(crate) registry: Option, + pub(crate) package: String, + pub(crate) version: Option, + pub(crate) command: Option, } impl fmt::Display for SplitVersion { @@ -521,139 +332,107 @@ fn test_split_version() { ); } -fn split_version(s: &str) -> Result { - let command = WasmerCLIOptions::command(); - let mut prohibited_package_names = command.get_subcommands().map(|s| s.get_name()); - - let re1 = regex::Regex::new(r#"(.*)/(.*)@(.*):(.*)"#).unwrap(); - let re2 = regex::Regex::new(r#"(.*)/(.*)@(.*)"#).unwrap(); - let re3 = regex::Regex::new(r#"(.*)/(.*)"#).unwrap(); - let re4 = regex::Regex::new(r#"(.*)/(.*):(.*)"#).unwrap(); - - let mut no_version = false; - - let captures = if re1.is_match(s) { - re1.captures(s) - .map(|c| { - c.iter() - .flatten() - .map(|m| m.as_str().to_owned()) - .collect::>() - }) - .unwrap_or_default() - } else if re2.is_match(s) { - re2.captures(s) - .map(|c| { - c.iter() - .flatten() - .map(|m| m.as_str().to_owned()) - .collect::>() - }) - .unwrap_or_default() - } else if re4.is_match(s) { - no_version = true; - re4.captures(s) - .map(|c| { - c.iter() - .flatten() - .map(|m| m.as_str().to_owned()) - .collect::>() - }) - .unwrap_or_default() - } else if re3.is_match(s) { - re3.captures(s) - .map(|c| { - c.iter() - .flatten() - .map(|m| m.as_str().to_owned()) - .collect::>() - }) - .unwrap_or_default() - } else { - return Err(anyhow::anyhow!("Invalid package version: {s:?}")); - }; - - let mut namespace = match captures.get(1).cloned() { - Some(s) => s, - None => { - return Err(anyhow::anyhow!( - "Invalid package version: {s:?}: no namespace" - )) - } - }; - - let name = match captures.get(2).cloned() { - Some(s) => s, - None => return Err(anyhow::anyhow!("Invalid package version: {s:?}: no name")), - }; - - let mut registry = None; - if namespace.contains('/') { - let (r, n) = namespace.rsplit_once('/').unwrap(); - let mut real_registry = r.to_string(); - if !real_registry.ends_with("graphql") { - real_registry = format!("{real_registry}/graphql"); - } - if !real_registry.contains("://") { - real_registry = format!("https://{real_registry}"); - } - registry = Some(real_registry); - namespace = n.to_string(); - } - - let sv = SplitVersion { - original: s.to_string(), - registry, - package: format!("{namespace}/{name}"), - version: if no_version { - None +impl SplitVersion { + pub fn new(s: &str) -> Result { + let command = WasmerCLIOptions::command(); + let mut prohibited_package_names = command.get_subcommands().map(|s| s.get_name()); + + let re1 = regex::Regex::new(r#"(.*)/(.*)@(.*):(.*)"#).unwrap(); + let re2 = regex::Regex::new(r#"(.*)/(.*)@(.*)"#).unwrap(); + let re3 = regex::Regex::new(r#"(.*)/(.*)"#).unwrap(); + let re4 = regex::Regex::new(r#"(.*)/(.*):(.*)"#).unwrap(); + + let mut no_version = false; + + let captures = if re1.is_match(s) { + re1.captures(s) + .map(|c| { + c.iter() + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else if re2.is_match(s) { + re2.captures(s) + .map(|c| { + c.iter() + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else if re4.is_match(s) { + no_version = true; + re4.captures(s) + .map(|c| { + c.iter() + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() + } else if re3.is_match(s) { + re3.captures(s) + .map(|c| { + c.iter() + .flatten() + .map(|m| m.as_str().to_owned()) + .collect::>() + }) + .unwrap_or_default() } else { - captures.get(3).cloned() - }, - command: captures.get(if no_version { 3 } else { 4 }).cloned(), - }; - - let svp = sv.package.clone(); - anyhow::ensure!( - !prohibited_package_names.any(|s| s == sv.package.trim()), - "Invalid package name {svp:?}" - ); + return Err(anyhow::anyhow!("Invalid package version: {s:?}")); + }; + + let mut namespace = match captures.get(1).cloned() { + Some(s) => s, + None => { + return Err(anyhow::anyhow!( + "Invalid package version: {s:?}: no namespace" + )) + } + }; + + let name = match captures.get(2).cloned() { + Some(s) => s, + None => return Err(anyhow::anyhow!("Invalid package version: {s:?}: no name")), + }; + + let mut registry = None; + if namespace.contains('/') { + let (r, n) = namespace.rsplit_once('/').unwrap(); + let mut real_registry = r.to_string(); + if !real_registry.ends_with("graphql") { + real_registry = format!("{real_registry}/graphql"); + } + if !real_registry.contains("://") { + real_registry = format!("https://{real_registry}"); + } + registry = Some(real_registry); + namespace = n.to_string(); + } - Ok(sv) -} + let sv = SplitVersion { + original: s.to_string(), + registry, + package: format!("{namespace}/{name}"), + version: if no_version { + None + } else { + captures.get(3).cloned() + }, + command: captures.get(if no_version { 3 } else { 4 }).cloned(), + }; + + let svp = sv.package.clone(); + anyhow::ensure!( + !prohibited_package_names.any(|s| s == sv.package.trim()), + "Invalid package name {svp:?}" + ); -fn print_packages() -> Result<(), anyhow::Error> { - use prettytable::{format, row, Table}; - - let rows = get_all_local_packages(None) - .into_iter() - .filter_map(|pkg| { - let package_root_path = pkg.get_path().ok()?; - 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(); + Ok(sv) } - let _ = table.printstd(); - - Ok(()) } fn print_help(verbose: bool) -> Result<(), anyhow::Error> { diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index 33194feee94..ad5dc013572 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -10,6 +10,7 @@ mod create_exe; #[cfg(feature = "static-artifact-create")] mod create_obj; mod inspect; +mod list; mod run; mod self_update; mod validate; @@ -26,7 +27,7 @@ pub use create_exe::*; pub use create_obj::*; #[cfg(feature = "wast")] pub use wast::*; -pub use {cache::*, config::*, inspect::*, run::*, self_update::*, validate::*}; +pub use {cache::*, config::*, inspect::*, list::*, run::*, self_update::*, validate::*}; /// The kind of object format to emit. #[derive(Debug, Copy, Clone, clap::Parser)] diff --git a/lib/cli/src/commands/list.rs b/lib/cli/src/commands/list.rs new file mode 100644 index 00000000000..332129b3912 --- /dev/null +++ b/lib/cli/src/commands/list.rs @@ -0,0 +1,43 @@ +use clap::Parser; + +/// 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 rows = wasmer_registry::get_all_local_packages(None) + .into_iter() + .filter_map(|pkg| { + let package_root_path = pkg.get_path().ok()?; + 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(); + } + let _ = table.printstd(); + + Ok(()) + } +} diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 3fccf15b678..569b37394bc 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -15,7 +15,9 @@ use wasmer::*; use wasmer_cache::{Cache, FileSystemCache, Hash}; use wasmer_types::Type as ValueType; +use crate::cli::SplitVersion; use clap::Parser; +use wasmer_registry::PackageDownloadInfo; #[cfg(feature = "wasi")] mod wasi; @@ -589,3 +591,190 @@ impl Run { bail!("binfmt_misc is only available on linux.") } } + +fn start_spinner(msg: String) -> spinner::SpinnerHandle { + spinner::SpinnerBuilder::new(msg) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", + ]) + .start() +} + +pub(crate) fn try_autoinstall_package( + args: &[String], + sv: &SplitVersion, + package: Option, + force_install: bool, +) -> Result<(), anyhow::Error> { + use std::io::Write; + let sp = start_spinner(format!("Installing package {} ...", sv.package)); + let v = sv.version.as_deref(); + let result = wasmer_registry::install_package( + sv.registry.as_deref(), + &sv.package, + v, + package, + force_install, + ); + sp.close(); + print!("\r"); + let _ = std::io::stdout().flush(); + let (_, package_dir) = match result { + Ok(o) => o, + Err(e) => { + return Err(anyhow::anyhow!("{e}")); + } + }; + + // Try auto-installing the remote package + let mut args_without_package = args.to_vec(); + args_without_package.remove(1); + + let mut run_args = RunWithoutFile::try_parse_from(args_without_package.iter())?; + run_args.command_name = sv.command.clone(); + + run_args + .into_run_args(package_dir, sv.command.as_deref())? + .execute() +} + +// We need to distinguish between errors that happen +// before vs. during execution +enum ExecuteLocalPackageError { + BeforeExec(anyhow::Error), + DuringExec(anyhow::Error), +} + +fn try_execute_local_package( + args: &[String], + sv: &SplitVersion, +) -> Result<(), ExecuteLocalPackageError> { + let package = wasmer_registry::get_local_package(None, &sv.package, sv.version.as_deref()) + .ok_or_else(|| { + ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("no local package {sv:?} found")) + })?; + + let package_dir = package + .get_path() + .map_err(|e| ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("{e}")))?; + + // Try finding the local package + let mut args_without_package = args.to_vec(); + + // remove either "run" or $package + args_without_package.remove(1); + + // "wasmer package arg1 arg2" => "wasmer arg1 arg2" + if (args_without_package.get(1).is_some() && args_without_package[1].starts_with(&sv.original)) + || (sv.command.is_some() && args_without_package[1].ends_with(sv.command.as_ref().unwrap())) + { + args_without_package.remove(1); + } + + RunWithoutFile::try_parse_from(args_without_package.iter()) + .map_err(|e| ExecuteLocalPackageError::DuringExec(e.into()))? + .into_run_args(package_dir, sv.command.as_deref()) + .map_err(ExecuteLocalPackageError::DuringExec)? + .execute() + .map_err(|e| ExecuteLocalPackageError::DuringExec(e.context(anyhow::anyhow!("{}", sv)))) +} + +fn try_lookup_command(sv: &mut SplitVersion) -> Result { + use std::io::Write; + let sp = start_spinner(format!("Looking up command {} ...", sv.package)); + + for registry in wasmer_registry::get_all_available_registries().unwrap_or_default() { + let result = wasmer_registry::query_command_from_registry(®istry, &sv.package); + print!("\r"); + let _ = std::io::stdout().flush(); + let command = sv.package.clone(); + if let Ok(o) = result { + sv.package = o.package.clone(); + sv.version = Some(o.version.clone()); + sv.command = Some(command); + return Ok(o); + } + } + + sp.close(); + print!("\r"); + let _ = std::io::stdout().flush(); + Err(anyhow::anyhow!("command {sv} not found")) +} + +pub(crate) fn try_run_package_or_file(args: &[String], r: &Run) -> Result<(), anyhow::Error> { + // Check "r.path" is a file or a package / command name + if r.path.exists() { + if r.path.is_dir() && r.path.join("wapm.toml").exists() { + let mut args_without_package = args.to_vec(); + if args_without_package.get(1) == Some(&format!("{}", r.path.display())) { + let _ = args_without_package.remove(1); + } else if args_without_package.get(2) == Some(&format!("{}", r.path.display())) { + let _ = args_without_package.remove(1); + let _ = args_without_package.remove(1); + } + return RunWithoutFile::try_parse_from(args_without_package.iter())? + .into_run_args(r.path.clone(), r.command_name.as_deref())? + .execute(); + } + return r.execute(); + } + + let package = format!("{}", r.path.display()); + + let mut is_fake_sv = false; + let mut sv = match SplitVersion::new(&package) { + Ok(o) => o, + Err(_) => { + let mut fake_sv = SplitVersion { + original: package.to_string(), + registry: None, + package: package.to_string(), + version: None, + command: None, + }; + is_fake_sv = true; + match try_lookup_command(&mut fake_sv) { + Ok(o) => SplitVersion { + original: format!("{}@{}", o.package, o.version), + registry: None, + package: o.package, + version: Some(o.version), + command: r.command_name.clone(), + }, + Err(e) => { + return Err( + anyhow::anyhow!("No package for command {package:?} found, file {package:?} not found either") + .context(e) + .context(anyhow::anyhow!("{}", r.path.display())) + ); + } + } + } + }; + + if sv.command.is_none() { + sv.command = r.command_name.clone(); + } + + if sv.command.is_none() && is_fake_sv { + sv.command = Some(package); + } + + let mut package_download_info = None; + if !sv.package.contains('/') { + if let Ok(o) = try_lookup_command(&mut sv) { + package_download_info = Some(o); + } + } + + match try_execute_local_package(args, &sv) { + Ok(o) => return Ok(o), + Err(ExecuteLocalPackageError::DuringExec(e)) => return Err(e), + _ => {} + } + + println!("finding local package {} failed", sv); + // else: local package not found - try to download and install package + try_autoinstall_package(args, &sv, package_download_info, r.force_install) +} From acb529aa392f5a1010572ea89a1f32c1ecc9734e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 24 Oct 2022 18:13:46 +0200 Subject: [PATCH 80/83] Disable segfaulting unit test on linux-musl --- lib/registry/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 7707fef5a30..6aa73316b28 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1081,6 +1081,9 @@ pub fn get_all_available_registries() -> Result, String> { Ok(registries) } +// TODO: this test is segfaulting only on linux-musl, no other OS +// See https://github.com/wasmerio/wasmer/pull/3215 +#[cfg(not(target = "x86_64-unknown-linux-musl"))] #[test] fn test_install_package() { println!("test install package..."); From 9b0ac57e89e05cbe36528e563f599b247c6523da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 24 Oct 2022 18:18:17 +0200 Subject: [PATCH 81/83] Fix failing unit test (refactor) --- lib/cli/src/cli.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index e38fd5219ee..b276fc6a74e 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -267,7 +267,7 @@ impl fmt::Display for SplitVersion { #[test] fn test_split_version() { assert_eq!( - split_version("registry.wapm.io/graphql/python/python").unwrap(), + SplitVersion::new("registry.wapm.io/graphql/python/python").unwrap(), SplitVersion { original: "registry.wapm.io/graphql/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -277,7 +277,7 @@ fn test_split_version() { } ); assert_eq!( - split_version("registry.wapm.io/python/python").unwrap(), + SplitVersion::new("registry.wapm.io/python/python").unwrap(), SplitVersion { original: "registry.wapm.io/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -287,7 +287,7 @@ fn test_split_version() { } ); assert_eq!( - split_version("namespace/name@version:command").unwrap(), + SplitVersion::new("namespace/name@version:command").unwrap(), SplitVersion { original: "namespace/name@version:command".to_string(), registry: None, @@ -297,7 +297,7 @@ fn test_split_version() { } ); assert_eq!( - split_version("namespace/name@version").unwrap(), + SplitVersion::new("namespace/name@version").unwrap(), SplitVersion { original: "namespace/name@version".to_string(), registry: None, @@ -307,7 +307,7 @@ fn test_split_version() { } ); assert_eq!( - split_version("namespace/name").unwrap(), + SplitVersion::new("namespace/name").unwrap(), SplitVersion { original: "namespace/name".to_string(), registry: None, @@ -317,7 +317,7 @@ fn test_split_version() { } ); assert_eq!( - split_version("registry.wapm.io/namespace/name").unwrap(), + SplitVersion::new("registry.wapm.io/namespace/name").unwrap(), SplitVersion { original: "registry.wapm.io/namespace/name".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -327,7 +327,7 @@ fn test_split_version() { } ); assert_eq!( - format!("{}", split_version("namespace").unwrap_err()), + format!("{}", SplitVersion::new("namespace").unwrap_err()), "Invalid package version: \"namespace\"".to_string(), ); } From 90ab3e6bb2faea4bb5cdf2bc293f2c05191fa7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 24 Oct 2022 19:10:51 +0200 Subject: [PATCH 82/83] Properly disable unit test on -musl --- lib/registry/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 6aa73316b28..61d4cd86fcd 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1083,7 +1083,7 @@ pub fn get_all_available_registries() -> Result, String> { // TODO: this test is segfaulting only on linux-musl, no other OS // See https://github.com/wasmerio/wasmer/pull/3215 -#[cfg(not(target = "x86_64-unknown-linux-musl"))] +#[cfg(not(target_env = "musl"))] #[test] fn test_install_package() { println!("test install package..."); From 9e4b445af0a884192e70068cf81d0df3df42169b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Mon, 24 Oct 2022 23:40:01 +0200 Subject: [PATCH 83/83] Disable test_wasmer_run_works on musl due to failing network access --- tests/integration/cli/tests/run.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index b4d2615a64d..140f13b8b7a 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -101,6 +101,7 @@ fn test_wasmer_run_works_with_dir() -> anyhow::Result<()> { Ok(()) } +#[cfg(not(target_env = "musl"))] #[test] fn test_wasmer_run_works() -> anyhow::Result<()> { let output = Command::new(get_wasmer_path())