From f751497b2859ca31392bd005f0583cb6dad18bd0 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Thu, 31 Oct 2024 10:20:05 -0500 Subject: [PATCH 1/2] use wkg.lock instead of Cargo-component.lock --- Cargo.lock | 250 +++++-------- Cargo.toml | 21 +- crates/core/Cargo.toml | 1 + crates/core/src/lib.rs | 2 - crates/core/src/lock.rs | 571 ------------------------------ crates/core/src/registry.rs | 685 ------------------------------------ src/bindings.rs | 7 +- src/commands/add.rs | 9 +- src/commands/new.rs | 16 +- src/commands/update.rs | 169 ++++++++- src/generator.rs | 2 +- src/lib.rs | 246 ++----------- src/lock.rs | 67 ---- src/metadata.rs | 2 +- src/registry.rs | 28 +- tests/build.rs | 4 - tests/publish.rs | 2 +- tests/support/mod.rs | 4 +- 18 files changed, 321 insertions(+), 1765 deletions(-) delete mode 100644 crates/core/src/lock.rs delete mode 100644 crates/core/src/registry.rs delete mode 100644 src/lock.rs diff --git a/Cargo.lock b/Cargo.lock index eefe0f86..b8af28f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -578,6 +578,7 @@ dependencies = [ "serde_json", "shell-escape", "tempfile", + "terminal-link", "tokio", "tokio-util", "toml_edit 0.22.20", @@ -587,16 +588,17 @@ dependencies = [ "warg-protocol", "warg-server", "wasi-preview1-component-adapter-provider", - "wasm-metadata 0.216.0", + "wasm-metadata 0.219.1", "wasm-pkg-client", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasm-pkg-core", + "wasmparser 0.219.1", + "wasmprinter 0.219.1", "wat", "which", "wit-bindgen-core", "wit-bindgen-rust", - "wit-component 0.216.0", - "wit-parser 0.216.0", + "wit-component 0.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -619,9 +621,10 @@ dependencies = [ "unicode-width", "url", "wasm-pkg-client", + "wasm-pkg-core", "windows-sys 0.52.0", - "wit-component 0.216.0", - "wit-parser 0.216.0", + "wit-component 0.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -918,37 +921,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.77", -] - [[package]] name = "dialoguer" version = "0.11.0" @@ -1143,17 +1115,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1386,18 +1347,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "getset" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" -dependencies = [ - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "gimli" version = "0.29.0" @@ -2214,9 +2163,9 @@ dependencies = [ [[package]] name = "oci-client" -version = "0.13.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e050f8bab3f561aa5ab537e1a6ed24006d58b4ee93cd6bb2c0cb822726f306" +checksum = "0f5098b86f972ac3484f7c9011bbbbd64aaa7e21d10d2c1a91fefb4ad0ba2ad9" dependencies = [ "bytes", "chrono", @@ -2225,7 +2174,6 @@ dependencies = [ "http-auth", "jwt", "lazy_static 1.5.0", - "oci-spec", "olpc-cjson", "regex", "reqwest", @@ -2238,27 +2186,11 @@ dependencies = [ "unicase", ] -[[package]] -name = "oci-spec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee185ce7cf1cce45e194e34cd87c0bad7ff0aa2e8917009a2da4f7b31fb363" -dependencies = [ - "derive_builder", - "getset", - "regex", - "serde 1.0.209", - "serde_json", - "strum", - "strum_macros", - "thiserror", -] - [[package]] name = "oci-wasm" -version = "0.1.0" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8403a1f0598a12452babb25a7bdf815269c4d83b064862a560879e66e3e85ee" +checksum = "1d3493d1985a31c5fbd4b37f72a319aab88b55908185a5a799219c6152e9da9b" dependencies = [ "anyhow", "chrono", @@ -2267,8 +2199,8 @@ dependencies = [ "serde_json", "sha2", "tokio", - "wit-component 0.219.1", - "wit-parser 0.219.1", + "wit-component 0.215.0", + "wit-parser 0.215.0", ] [[package]] @@ -2608,28 +2540,6 @@ dependencies = [ "toml_edit 0.19.15", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -3498,25 +3408,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.77", -] - [[package]] name = "subtle" version = "2.6.1" @@ -3603,6 +3494,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253bcead4f3aa96243b0f8fa46f9010e87ca23bd5d0c723d474ff1d2417bbdf8" + [[package]] name = "termtree" version = "0.4.1" @@ -4350,12 +4247,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.216.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" +checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" dependencies = [ "leb128", - "wasmparser 0.216.0", + "wasmparser 0.215.0", ] [[package]] @@ -4370,9 +4267,9 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.216.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8154d703a6b0e45acf6bd172fa002fc3c7058a9f7615e517220aeca27c638" +checksum = "0c6bb07c5576b608f7a2a9baa2294c1a3584a249965d695a9814a496cb6d232f" dependencies = [ "anyhow", "indexmap 2.5.0", @@ -4380,8 +4277,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", + "wasm-encoder 0.215.0", + "wasmparser 0.215.0", ] [[package]] @@ -4402,16 +4299,14 @@ dependencies = [ [[package]] name = "wasm-pkg-client" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff3e17576a2a2170f5ec4c2834c3a05504b3c38f1cdd669e48c4b570e47bfb4" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", + "dirs", "docker_credential", - "etcetera", "futures-util", "oci-client", "oci-wasm", @@ -4429,20 +4324,17 @@ dependencies = [ "warg-client", "warg-crypto", "warg-protocol", - "wasm-metadata 0.219.1", "wasm-pkg-common", "wit-component 0.219.1", ] [[package]] name = "wasm-pkg-common" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da50c3adeef70b9ffbef0000eedff727ea050148a42cfc8a27f25f1e3bb100ae" +version = "0.6.0" dependencies = [ "anyhow", "bytes", - "etcetera", + "dirs", "futures-util", "http", "reqwest", @@ -4456,6 +4348,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "wasm-pkg-core" +version = "0.6.0" +dependencies = [ + "anyhow", + "futures-util", + "indexmap 2.5.0", + "libc", + "semver", + "serde 1.0.209", + "tokio", + "tokio-util", + "toml 0.8.19", + "tracing", + "wasm-metadata 0.219.1", + "wasm-pkg-client", + "wasm-pkg-common", + "windows-sys 0.52.0", + "wit-component 0.219.1", + "wit-parser 0.219.1", +] + [[package]] name = "wasm-streams" version = "0.4.0" @@ -4482,16 +4396,15 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.216.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" +checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" dependencies = [ "ahash", "bitflags 2.6.0", "hashbrown 0.14.5", "indexmap 2.5.0", "semver", - "serde 1.0.209", ] [[package]] @@ -4505,6 +4418,7 @@ dependencies = [ "hashbrown 0.14.5", "indexmap 2.5.0", "semver", + "serde 1.0.209", ] [[package]] @@ -4519,33 +4433,33 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.216.0" +version = "0.219.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f82916f3892e53620639217d6ec78fe15c678352a3fbf3f3745b6417d0bd70f" +checksum = "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.216.0", + "wasmparser 0.219.1", ] [[package]] name = "wast" -version = "216.0.0" +version = "219.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" +checksum = "4f79a9d9df79986a68689a6b40bcc8d5d40d807487b235bebc2ac69a242b54a1" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.216.0", + "wasm-encoder 0.219.1", ] [[package]] name = "wat" -version = "1.216.0" +version = "1.219.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" +checksum = "8bc3cf014fb336883a411cd662f987abf6a1d2a27f2f0008616a0070bbf6bd0d" dependencies = [ "wast", ] @@ -4825,36 +4739,36 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wit-bindgen-core" -version = "0.31.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175a2edcf18d1efd1412cb560b895dd09903f4fe73c3a597fbb0075ad86a03e8" +checksum = "163cee59d3d5ceec0b256735f3ab0dccac434afb0ec38c406276de9c5a11e906" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.216.0", + "wit-parser 0.219.1", ] [[package]] name = "wit-bindgen-rust" -version = "0.31.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1c119b99edc4fc44ebfd3c461337b18f9db35388358344750b4fcfc788986b" +checksum = "f6919521fc7807f927a739181db93100ca7ed03c29509b84d5f96b27b2e49a9a" dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.5.0", "prettyplease", "syn 2.0.77", - "wasm-metadata 0.216.0", + "wasm-metadata 0.219.1", "wit-bindgen-core", - "wit-component 0.216.0", + "wit-component 0.219.1", ] [[package]] name = "wit-component" -version = "0.216.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2ca3ece38ea2447a9069b43074ba73d96dde1944cba276c54e41371745f9dc" +checksum = "f725e3885fc5890648be5c5cbc1353b755dc932aa5f1aa7de968b912a3280743" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -4863,10 +4777,10 @@ dependencies = [ "serde 1.0.209", "serde_derive", "serde_json", - "wasm-encoder 0.216.0", - "wasm-metadata 0.216.0", - "wasmparser 0.216.0", - "wit-parser 0.216.0", + "wasm-encoder 0.215.0", + "wasm-metadata 0.215.0", + "wasmparser 0.215.0", + "wit-parser 0.215.0", ] [[package]] @@ -4890,9 +4804,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.216.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d108165c1167a4ccc8a803dcf5c28e0a51d6739fd228cc7adce768632c764c" +checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" dependencies = [ "anyhow", "id-arena", @@ -4903,7 +4817,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.216.0", + "wasmparser 0.215.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 13e76a06..b0196929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,12 +46,14 @@ url = { workspace = true } wasi-preview1-component-adapter-provider = { workspace = true } wasm-metadata = { workspace = true } wasm-pkg-client = { workspace = true } +wasm-pkg-core = { workspace = true } wasmparser = { workspace = true } which = { workspace = true } wit-bindgen-core = { workspace = true } wit-bindgen-rust = { workspace = true } wit-component = { workspace = true } wit-parser = { workspace = true } +terminal-link = "0.1.0" [dev-dependencies] assert_cmd = { workspace = true } @@ -106,16 +108,17 @@ warg-crypto = "0.9.0" warg-protocol = "0.9.0" warg-server = "0.9.0" wasi-preview1-component-adapter-provider = "24" -wasm-metadata = "0.216.0" -wasm-pkg-client = "0.8.0" -wasmparser = "0.216.0" -wasmprinter = "0.216.0" -wat = "1.216.0" +wasm-metadata = "0.219.0" +wasm-pkg-client = { path = "../wasm-pkg-tools/crates/wasm-pkg-client" } +wasm-pkg-core = { path = "../wasm-pkg-tools/crates/wasm-pkg-core" } +wasmparser = "0.219.0" +wasmprinter = "0.219.0" +wat = "1.219.0" which = "6.0.1" -wit-bindgen-core = "0.31.0" -wit-bindgen-rust = "0.31.0" -wit-component = "0.216.0" -wit-parser = "0.216.0" +wit-bindgen-core = "0.34.0" +wit-bindgen-rust = "0.34.0" +wit-component = "0.219.0" +wit-parser = "0.219.0" [profile.release] panic = "abort" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 302ef994..51359fb5 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -26,6 +26,7 @@ toml_edit = { workspace = true } unicode-width = { workspace = true } url = { workspace = true } wasm-pkg-client = { workspace = true } +wasm-pkg-core = { workspace = true } wit-component = { workspace = true } wit-parser = { workspace = true } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 870258f1..0a392230 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -10,9 +10,7 @@ use semver::VersionReq; use wasm_pkg_client::PackageRef; pub mod command; -pub mod lock; pub mod progress; -pub mod registry; pub mod terminal; /// The root directory name used for default cargo component directories diff --git a/crates/core/src/lock.rs b/crates/core/src/lock.rs deleted file mode 100644 index 30f9a51a..00000000 --- a/crates/core/src/lock.rs +++ /dev/null @@ -1,571 +0,0 @@ -//! Module for the lock file implementation. - -use crate::registry::DEFAULT_REGISTRY_NAME; -use anyhow::{anyhow, bail, Context, Result}; -use semver::{Version, VersionReq}; -use serde::{de::IntoDeserializer, Deserialize, Serialize}; -use std::{ - fs::{File, OpenOptions}, - io::{self, Read, Seek, SeekFrom, Write}, - path::{Path, PathBuf}, -}; -use toml_edit::{DocumentMut, Item, Value}; -use wasm_pkg_client::{ContentDigest, PackageRef}; - -/// The file format version of the lock file. -const LOCK_FILE_VERSION: i64 = 1; - -/// Represents a locked package in a lock file. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -pub struct LockedPackage { - /// The name of the locked package. - pub name: PackageRef, - /// The registry the package was resolved from. - /// - /// Defaults to the default registry if not specified. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub registry: Option, - /// The locked versions of a package. - /// - /// A package may have multiple locked versions if more than one - /// version requirement was specified for the package in `wit.toml`. - #[serde(rename = "version", default, skip_serializing_if = "Vec::is_empty")] - pub versions: Vec, -} - -impl LockedPackage { - /// Gets the key used in sorting and searching the package list. - pub fn key(&self) -> (&str, &str, &str) { - ( - self.name.namespace().as_ref(), - self.name.name().as_ref(), - self.registry.as_deref().unwrap_or(DEFAULT_REGISTRY_NAME), - ) - } -} - -/// Represents version information for a locked package. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct LockedPackageVersion { - /// The version requirement used to resolve this version. - pub requirement: String, - /// The version the package is locked to. - pub version: Version, - /// The digest of the package contents. - pub digest: ContentDigest, -} - -impl LockedPackageVersion { - /// Gets the sort key for the locked package version. - pub fn key(&self) -> &str { - &self.requirement - } -} - -/// Represents a resolver for a lock file. -#[derive(Clone, Copy, Debug)] -pub struct LockFileResolver<'a>(&'a LockFile); - -impl<'a> LockFileResolver<'a> { - /// Creates a new lock file resolver for the given workspace and lock file. - pub fn new(lock_file: &'a LockFile) -> Self { - Self(lock_file) - } - - /// Resolves a package from the lock file. - /// - /// Returns `Ok(None)` if the package cannot be resolved. - /// - /// Fails if the package cannot be resolved and the lock file is not allowed to be updated. - pub fn resolve( - &'a self, - registry: &str, - package_ref: &PackageRef, - requirement: &VersionReq, - ) -> Result> { - if let Some(pkg) = self - .0 - .packages - .binary_search_by_key( - &( - package_ref.namespace().as_ref(), - package_ref.name().as_ref(), - registry, - ), - LockedPackage::key, - ) - .ok() - .map(|i| &self.0.packages[i]) - { - if let Ok(index) = pkg - .versions - .binary_search_by_key(&requirement.to_string().as_str(), LockedPackageVersion::key) - { - let locked = &pkg.versions[index]; - log::info!("dependency package `{package_ref}` from registry `{registry}` with requirement `{requirement}` was resolved by the lock file to version {version}", version = locked.version); - return Ok(Some(locked)); - } - } - - log::info!("dependency package `{package_ref}` from registry `{registry}` with requirement `{requirement}` was not in the lock file"); - Ok(None) - } -} - -/// Represents a resolved dependency lock file. -/// -/// This is a TOML file that contains the resolved dependency information from -/// a previous build. -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "kebab-case")] -pub struct LockFile { - /// The version of the lock file. - /// - /// Currently this is always `1`. - pub version: i64, - /// The locked dependencies in the lock file. - /// - /// This list is sorted by the key of the locked package. - #[serde(rename = "package", default, skip_serializing_if = "Vec::is_empty")] - pub packages: Vec, -} - -impl LockFile { - /// Constructs a new lock file from a list of locked packages. - /// - /// It is expected that the packages will be already sorted. - pub fn new(packages: impl Into>) -> Self { - Self { - version: LOCK_FILE_VERSION, - packages: packages.into(), - } - } - - /// Reads the lock file from the given file object. - pub fn read(mut file: &File) -> Result { - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - let document: DocumentMut = contents.parse()?; - - match document.as_table().get("version") { - Some(Item::Value(Value::Integer(v))) => { - if *v.value() != LOCK_FILE_VERSION { - bail!( - "unsupported file format version {version}", - version = v.value() - ); - } - - // In the future, we should convert between supported versions here. - } - Some(_) => bail!("file format version is not an integer"), - None => bail!("missing file format version"), - } - - Self::deserialize(document.into_deserializer()).context("invalid file format") - } - - /// Writes the lock file to the given file object. - /// - /// The app name is used to generate a header comment. - pub fn write(&self, mut file: &File, app: &str) -> Result<()> { - let content = toml_edit::ser::to_string_pretty(&self)?; - - file.set_len(0)?; - write!(file, "# This file is automatically generated by {app}.\n# It is not intended for manual editing.\n")?; - file.write_all(content.as_bytes())?; - - Ok(()) - } -} - -impl Default for LockFile { - fn default() -> Self { - Self { - version: LOCK_FILE_VERSION, - packages: Vec::new(), - } - } -} - -/// Implements a file lock. -#[derive(Debug)] -pub struct FileLock { - file: File, - path: PathBuf, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum Access { - Shared, - Exclusive, -} - -impl FileLock { - /// Gets the path associated with the file lock. - pub fn path(&self) -> &Path { - &self.path - } - - /// Attempts to acquire exclusive access to a file, returning the locked - /// version of a file. - /// - /// This function will create a file at `path` if it doesn't already exist - /// (including intermediate directories), and then it will try to acquire an - /// exclusive lock on `path`. - /// - /// If the lock cannot be immediately acquired, `Ok(None)` is returned. - /// - /// The returned file can be accessed to look at the path and also has - /// read/write access to the underlying file. - pub fn try_open_rw(path: impl Into) -> Result> { - Self::open( - path.into(), - OpenOptions::new().read(true).write(true).create(true), - Access::Exclusive, - true, - ) - } - - /// Opens exclusive access to a file, returning the locked version of a - /// file. - /// - /// This function will create a file at `path` if it doesn't already exist - /// (including intermediate directories), and then it will acquire an - /// exclusive lock on `path`. - /// - /// If the lock cannot be acquired, this function will block until it is - /// acquired. - /// - /// The returned file can be accessed to look at the path and also has - /// read/write access to the underlying file. - pub fn open_rw(path: impl Into) -> Result { - Ok(Self::open( - path.into(), - OpenOptions::new().read(true).write(true).create(true), - Access::Exclusive, - false, - )? - .unwrap()) - } - - /// Attempts to acquire shared access to a file, returning the locked version - /// of a file. - /// - /// This function will fail if `path` doesn't already exist, but if it does - /// then it will acquire a shared lock on `path`. - /// - /// If the lock cannot be immediately acquired, `Ok(None)` is returned. - /// - /// The returned file can be accessed to look at the path and also has read - /// access to the underlying file. Any writes to the file will return an - /// error. - pub fn try_open_ro(path: impl Into) -> Result> { - Self::open( - path.into(), - OpenOptions::new().read(true), - Access::Shared, - true, - ) - } - - /// Opens shared access to a file, returning the locked version of a file. - /// - /// This function will fail if `path` doesn't already exist, but if it does - /// then it will acquire a shared lock on `path`. - /// - /// If the lock cannot be acquired, this function will block until it is - /// acquired. - /// - /// The returned file can be accessed to look at the path and also has read - /// access to the underlying file. Any writes to the file will return an - /// error. - pub fn open_ro(path: impl Into) -> Result { - Ok(Self::open( - path.into(), - OpenOptions::new().read(true), - Access::Shared, - false, - )? - .unwrap()) - } - - fn open( - path: PathBuf, - opts: &OpenOptions, - access: Access, - try_lock: bool, - ) -> Result> { - // If we want an exclusive lock then if we fail because of NotFound it's - // likely because an intermediate directory didn't exist, so try to - // create the directory and then continue. - let file = opts - .open(&path) - .or_else(|e| { - if e.kind() == io::ErrorKind::NotFound && access == Access::Exclusive { - std::fs::create_dir_all(path.parent().unwrap())?; - Ok(opts.open(&path)?) - } else { - Err(anyhow::Error::from(e)) - } - }) - .with_context(|| format!("failed to open `{path}`", path = path.display()))?; - - let lock = Self { file, path }; - - // File locking on Unix is currently implemented via `flock`, which is known - // to be broken on NFS. We could in theory just ignore errors that happen on - // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking - // forever**, even if the "non-blocking" flag is passed! - // - // As a result, we just skip all file locks entirely on NFS mounts. That - // should avoid calling any `flock` functions at all, and it wouldn't work - // there anyway. - // - // [1]: https://github.com/rust-lang/cargo/issues/2615 - if is_on_nfs_mount(&lock.path) { - return Ok(Some(lock)); - } - - let res = match (access, try_lock) { - (Access::Shared, true) => sys::try_lock_shared(&lock.file), - (Access::Exclusive, true) => sys::try_lock_exclusive(&lock.file), - (Access::Shared, false) => sys::lock_shared(&lock.file), - (Access::Exclusive, false) => sys::lock_exclusive(&lock.file), - }; - - return match res { - Ok(_) => Ok(Some(lock)), - - // In addition to ignoring NFS which is commonly not working we also - // just ignore locking on file systems that look like they don't - // implement file locking. - Err(e) if sys::error_unsupported(&e) => Ok(Some(lock)), - - // Check to see if it was a contention error - Err(e) if try_lock && sys::error_contended(&e) => Ok(None), - - Err(e) => Err(anyhow!(e).context(format!( - "failed to lock file `{path}`", - path = lock.path.display() - ))), - }; - - #[cfg(all(target_os = "linux", not(target_env = "musl")))] - fn is_on_nfs_mount(path: &Path) -> bool { - use std::ffi::CString; - use std::mem; - use std::os::unix::prelude::*; - - let path = match CString::new(path.as_os_str().as_bytes()) { - Ok(path) => path, - Err(_) => return false, - }; - - unsafe { - let mut buf: libc::statfs = mem::zeroed(); - let r = libc::statfs(path.as_ptr(), &mut buf); - - r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32 - } - } - - #[cfg(any(not(target_os = "linux"), target_env = "musl"))] - fn is_on_nfs_mount(_path: &Path) -> bool { - false - } - } - - /// Returns the underlying file handle of this lock. - pub fn file(&self) -> &File { - &self.file - } -} - -impl Read for FileLock { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.file().read(buf) - } -} - -impl Seek for FileLock { - fn seek(&mut self, to: SeekFrom) -> io::Result { - self.file().seek(to) - } -} - -impl Write for FileLock { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.file().write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.file().flush() - } -} - -impl Drop for FileLock { - fn drop(&mut self) { - let _ = sys::unlock(&self.file); - } -} - -#[cfg(unix)] -mod sys { - use std::fs::File; - use std::io::{Error, Result}; - use std::os::unix::io::AsRawFd; - - pub(super) fn lock_shared(file: &File) -> Result<()> { - flock(file, libc::LOCK_SH) - } - - pub(super) fn lock_exclusive(file: &File) -> Result<()> { - flock(file, libc::LOCK_EX) - } - - pub(super) fn try_lock_shared(file: &File) -> Result<()> { - flock(file, libc::LOCK_SH | libc::LOCK_NB) - } - - pub(super) fn try_lock_exclusive(file: &File) -> Result<()> { - flock(file, libc::LOCK_EX | libc::LOCK_NB) - } - - pub(super) fn unlock(file: &File) -> Result<()> { - flock(file, libc::LOCK_UN) - } - - pub(super) fn error_contended(err: &Error) -> bool { - err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK) - } - - pub(super) fn error_unsupported(err: &Error) -> bool { - match err.raw_os_error() { - // Unfortunately, depending on the target, these may or may not be the same. - // For targets in which they are the same, the duplicate pattern causes a warning. - #[allow(unreachable_patterns)] - Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true, - Some(libc::ENOSYS) => true, - _ => false, - } - } - - #[cfg(not(target_os = "solaris"))] - fn flock(file: &File, flag: libc::c_int) -> Result<()> { - let ret = unsafe { libc::flock(file.as_raw_fd(), flag) }; - if ret < 0 { - Err(Error::last_os_error()) - } else { - Ok(()) - } - } - - #[cfg(target_os = "solaris")] - fn flock(file: &File, flag: libc::c_int) -> Result<()> { - // Solaris lacks flock(), so try to emulate using fcntl() - let mut flock = libc::flock { - l_type: 0, - l_whence: 0, - l_start: 0, - l_len: 0, - l_sysid: 0, - l_pid: 0, - l_pad: [0, 0, 0, 0], - }; - flock.l_type = if flag & libc::LOCK_UN != 0 { - libc::F_UNLCK - } else if flag & libc::LOCK_EX != 0 { - libc::F_WRLCK - } else if flag & libc::LOCK_SH != 0 { - libc::F_RDLCK - } else { - panic!("unexpected flock() operation") - }; - - let mut cmd = libc::F_SETLKW; - if (flag & libc::LOCK_NB) != 0 { - cmd = libc::F_SETLK; - } - - let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) }; - - if ret < 0 { - Err(Error::last_os_error()) - } else { - Ok(()) - } - } -} - -#[cfg(windows)] -mod sys { - use std::fs::File; - use std::io::{Error, Result}; - use std::mem; - use std::os::windows::io::AsRawHandle; - - use windows_sys::Win32::Foundation::HANDLE; - use windows_sys::Win32::Foundation::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION}; - use windows_sys::Win32::Storage::FileSystem::{ - LockFileEx, UnlockFile, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, - }; - - pub(super) fn lock_shared(file: &File) -> Result<()> { - lock_file(file, 0) - } - - pub(super) fn lock_exclusive(file: &File) -> Result<()> { - lock_file(file, LOCKFILE_EXCLUSIVE_LOCK) - } - - pub(super) fn try_lock_shared(file: &File) -> Result<()> { - lock_file(file, LOCKFILE_FAIL_IMMEDIATELY) - } - - pub(super) fn try_lock_exclusive(file: &File) -> Result<()> { - lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY) - } - - pub(super) fn error_contended(err: &Error) -> bool { - err.raw_os_error() - .map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32) - } - - pub(super) fn error_unsupported(err: &Error) -> bool { - err.raw_os_error() - .map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32) - } - - pub(super) fn unlock(file: &File) -> Result<()> { - unsafe { - let ret = UnlockFile(file.as_raw_handle() as HANDLE, 0, 0, !0, !0); - if ret == 0 { - Err(Error::last_os_error()) - } else { - Ok(()) - } - } - } - - fn lock_file(file: &File, flags: u32) -> Result<()> { - unsafe { - let mut overlapped = mem::zeroed(); - let ret = LockFileEx( - file.as_raw_handle() as HANDLE, - flags, - 0, - !0, - !0, - &mut overlapped, - ); - if ret == 0 { - Err(Error::last_os_error()) - } else { - Ok(()) - } - } - } -} diff --git a/crates/core/src/registry.rs b/crates/core/src/registry.rs deleted file mode 100644 index a494d395..00000000 --- a/crates/core/src/registry.rs +++ /dev/null @@ -1,685 +0,0 @@ -//! Module for resolving dependencies from a component registry. -use std::{ - collections::{hash_map, HashMap}, - fmt::Debug, - path::{Path, PathBuf}, - str::FromStr, - sync::Arc, -}; - -use anyhow::{bail, Context, Result}; -use futures::TryStreamExt; -use indexmap::IndexMap; -use semver::{Comparator, Op, Version, VersionReq}; -use serde::{ - de::{self, value::MapAccessDeserializer}, - Deserialize, Serialize, -}; - -use tokio::io::AsyncReadExt; -use wasm_pkg_client::{ - caching::{CachingClient, FileCache}, - Client, Config, ContentDigest, Error as WasmPkgError, PackageRef, Release, VersionInfo, -}; -use wit_component::DecodedWasm; -use wit_parser::{PackageId, PackageName, Resolve, UnresolvedPackageGroup, WorldId}; - -use crate::lock::{LockFileResolver, LockedPackageVersion}; - -/// The name of the default registry. -pub const DEFAULT_REGISTRY_NAME: &str = "default"; - -/// Represents a WIT package dependency. -#[derive(Debug, Clone)] -pub enum Dependency { - /// The dependency is a registry package. - Package(RegistryPackage), - - /// The dependency is a path to a local directory or file. - Local(PathBuf), -} - -impl Serialize for Dependency { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - Self::Package(package) => { - if package.name.is_none() && package.registry.is_none() { - let version = package.version.to_string(); - version.trim_start_matches('^').serialize(serializer) - } else { - #[derive(Serialize)] - struct Entry<'a> { - package: Option<&'a PackageRef>, - version: &'a str, - registry: Option<&'a str>, - } - - Entry { - package: package.name.as_ref(), - version: package.version.to_string().trim_start_matches('^'), - registry: package.registry.as_deref(), - } - .serialize(serializer) - } - } - Self::Local(path) => { - #[derive(Serialize)] - struct Entry<'a> { - path: &'a PathBuf, - } - - Entry { path }.serialize(serializer) - } - } - } -} - -impl<'de> Deserialize<'de> for Dependency { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = Dependency; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a string or a table") - } - - fn visit_str(self, s: &str) -> Result - where - E: de::Error, - { - Ok(Self::Value::Package(s.parse().map_err(de::Error::custom)?)) - } - - fn visit_map(self, map: A) -> Result - where - A: de::MapAccess<'de>, - { - #[derive(Default, Deserialize)] - #[serde(default, deny_unknown_fields)] - struct Entry { - path: Option, - package: Option, - version: Option, - registry: Option, - } - - let entry = Entry::deserialize(MapAccessDeserializer::new(map))?; - - match (entry.path, entry.package, entry.version, entry.registry) { - (Some(path), None, None, None) => Ok(Self::Value::Local(path)), - (None, name, Some(version), registry) => { - Ok(Self::Value::Package(RegistryPackage { - name, - version, - registry, - })) - } - (Some(_), None, Some(_), _) => Err(de::Error::custom( - "cannot specify both `path` and `version` fields in a dependency entry", - )), - (Some(_), None, None, Some(_)) => Err(de::Error::custom( - "cannot specify both `path` and `registry` fields in a dependency entry", - )), - (Some(_), Some(_), _, _) => Err(de::Error::custom( - "cannot specify both `path` and `package` fields in a dependency entry", - )), - (None, None, _, _) => Err(de::Error::missing_field("package")), - (None, Some(_), None, _) => Err(de::Error::missing_field("version")), - } - } - } - - deserializer.deserialize_any(Visitor) - } -} - -impl FromStr for Dependency { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - Ok(Self::Package(s.parse()?)) - } -} - -/// Represents a reference to a registry package. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RegistryPackage { - /// The name of the package. - /// - /// If not specified, the name from the mapping will be used. - pub name: Option, - - /// The version requirement of the package. - pub version: VersionReq, - - /// The name of the component registry containing the package. - /// - /// If not specified, the default registry is used. - pub registry: Option, -} - -impl FromStr for RegistryPackage { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - Ok(Self { - name: None, - version: s - .parse() - .with_context(|| format!("'{s}' is an invalid registry package version"))?, - registry: None, - }) - } -} - -/// Represents information about a resolution of a registry package. -#[derive(Clone)] -pub struct RegistryResolution { - /// The name of the dependency that was resolved. - /// - /// This may differ from `package` if the dependency was renamed. - pub name: PackageRef, - /// The name of the package from the registry that was resolved. - pub package: PackageRef, - /// The name of the registry used to resolve the package. - /// - /// A value of `None` indicates that the default registry was used. - pub registry: Option, - /// The version requirement that was used to resolve the package. - pub requirement: VersionReq, - /// The package version that was resolved. - pub version: Version, - /// The digest of the package contents. - pub digest: ContentDigest, - /// The client to use for fetching the package contents. - client: Arc>, -} - -impl Debug for RegistryResolution { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("RegistryResolution") - .field("name", &self.name) - .field("package", &self.package) - .field("registry", &self.registry) - .field("requirement", &self.requirement) - .field("version", &self.version) - .field("digest", &self.digest) - .finish() - } -} - -/// Represents information about a resolution of a local file. -#[derive(Clone, Debug)] -pub struct LocalResolution { - /// The name of the dependency that was resolved. - pub name: PackageRef, - /// The path to the resolved dependency. - pub path: PathBuf, -} - -/// Represents a resolution of a dependency. -#[derive(Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum DependencyResolution { - /// The dependency is resolved from a registry package. - Registry(RegistryResolution), - /// The dependency is resolved from a local path. - Local(LocalResolution), -} - -impl DependencyResolution { - /// Gets the name of the dependency that was resolved. - pub fn name(&self) -> &PackageRef { - match self { - Self::Registry(res) => &res.name, - Self::Local(res) => &res.name, - } - } - - /// Gets the resolved version. - /// - /// Returns `None` if the dependency is not resolved from a registry package. - pub fn version(&self) -> Option<&Version> { - match self { - Self::Registry(res) => Some(&res.version), - Self::Local(_) => None, - } - } - - /// The key used in sorting and searching the lock file package list. - /// - /// Returns `None` if the dependency is not resolved from a registry package. - pub fn key(&self) -> Option<(&PackageRef, Option<&str>)> { - match self { - DependencyResolution::Registry(pkg) => Some((&pkg.package, pkg.registry.as_deref())), - DependencyResolution::Local(_) => None, - } - } - - /// Decodes the resolved dependency. - pub async fn decode(&self) -> Result { - // If the dependency path is a directory, assume it contains wit to parse as a package. - let bytes = match self { - DependencyResolution::Local(LocalResolution { path, .. }) - if tokio::fs::metadata(path).await?.is_dir() => - { - return Ok(DecodedDependency::Wit { - resolution: self, - package: UnresolvedPackageGroup::parse_dir(path).with_context(|| { - format!("failed to parse dependency `{path}`", path = path.display()) - })?, - }); - } - DependencyResolution::Local(LocalResolution { path, .. }) => { - tokio::fs::read(path).await.with_context(|| { - format!( - "failed to read content of dependency `{name}` at path `{path}`", - name = self.name(), - path = path.display() - ) - })? - } - DependencyResolution::Registry(res) => { - let stream = res - .client - .get_content( - &res.package, - &Release { - version: res.version.clone(), - content_digest: res.digest.clone(), - }, - ) - .await?; - - let mut buf = Vec::new(); - tokio_util::io::StreamReader::new( - stream.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)), - ) - .read_to_end(&mut buf) - .await?; - buf - } - }; - - if &bytes[0..4] != b"\0asm" { - return Ok(DecodedDependency::Wit { - resolution: self, - package: UnresolvedPackageGroup::parse( - // This is fake, but it's needed for the parser to work. - self.name().to_string(), - std::str::from_utf8(&bytes).with_context(|| { - format!( - "dependency `{name}` is not UTF-8 encoded", - name = self.name() - ) - })?, - )?, - }); - } - - Ok(DecodedDependency::Wasm { - resolution: self, - decoded: wit_component::decode(&bytes).with_context(|| { - format!( - "failed to decode content of dependency `{name}`", - name = self.name(), - ) - })?, - }) - } -} - -/// Represents a decoded dependency. -pub enum DecodedDependency<'a> { - /// The dependency decoded from an unresolved WIT package. - Wit { - /// The resolution related to the decoded dependency. - resolution: &'a DependencyResolution, - /// The unresolved WIT package. - package: UnresolvedPackageGroup, - }, - /// The dependency decoded from a Wasm file. - Wasm { - /// The resolution related to the decoded dependency. - resolution: &'a DependencyResolution, - /// The decoded Wasm file. - decoded: DecodedWasm, - }, -} - -impl<'a> DecodedDependency<'a> { - /// Fully resolves the dependency. - /// - /// If the dependency is an unresolved WIT package, it will assume that the - /// package has no foreign dependencies. - pub fn resolve(self) -> Result<(Resolve, PackageId, Vec)> { - match self { - Self::Wit { package, .. } => { - let mut resolve = Resolve::new(); - let source_files = package - .source_map - .source_files() - .map(Path::to_path_buf) - .collect(); - let pkg = resolve.push_group(package)?; - Ok((resolve, pkg, source_files)) - } - Self::Wasm { decoded, .. } => match decoded { - DecodedWasm::WitPackage(resolve, pkg) => Ok((resolve, pkg, Vec::new())), - DecodedWasm::Component(resolve, world) => { - let pkg = resolve.worlds[world].package.unwrap(); - Ok((resolve, pkg, Vec::new())) - } - }, - } - } - - /// Gets the package name of the decoded dependency. - pub fn package_name(&self) -> &PackageName { - match self { - Self::Wit { package, .. } => &package.main.name, - Self::Wasm { decoded, .. } => &decoded.resolve().packages[decoded.package()].name, - } - } - - /// Converts the decoded dependency into a component world. - /// - /// Returns an error if the dependency is not a decoded component. - pub fn into_component_world(self) -> Result<(Resolve, WorldId)> { - match self { - Self::Wasm { - decoded: DecodedWasm::Component(resolve, world), - .. - } => Ok((resolve, world)), - _ => bail!("dependency is not a WebAssembly component"), - } - } -} - -/// Used to resolve dependencies for a WIT package. -pub struct DependencyResolver<'a> { - client: Arc>, - lock_file: Option>, - registries: IndexMap<&'a str, Registry<'a>>, - resolutions: HashMap, -} - -impl<'a> DependencyResolver<'a> { - /// Creates a new dependency resolver. If `config` is `None`, then the resolver will be set to - /// offline mode and a lock file must be given as well. Anything that will require network - /// access will fail in offline mode. - pub fn new( - config: Option, - lock_file: Option>, - cache: FileCache, - ) -> anyhow::Result { - if config.is_none() && lock_file.is_none() { - anyhow::bail!("lock file must be provided when offline mode is enabled"); - } - let client = CachingClient::new(config.map(Client::new), cache); - Ok(DependencyResolver { - client: Arc::new(client), - lock_file, - registries: Default::default(), - resolutions: Default::default(), - }) - } - - /// Creates a new dependency resolver with the given client. This is useful when you already - /// have a client available. If the client is set to offline mode, then a lock file must be - /// given or this will error - pub fn new_with_client( - client: Arc>, - lock_file: Option>, - ) -> anyhow::Result { - if client.is_readonly() && lock_file.is_none() { - anyhow::bail!("lock file must be provided when offline mode is enabled"); - } - Ok(DependencyResolver { - client, - lock_file, - registries: Default::default(), - resolutions: Default::default(), - }) - } - - /// Add a dependency to the resolver. - pub async fn add_dependency( - &mut self, - name: &'a PackageRef, - dependency: &'a Dependency, - ) -> Result<()> { - match dependency { - Dependency::Package(package) => { - // Dependency comes from a registry, add a dependency to the resolver - let registry_name = package.registry.as_deref().unwrap_or(DEFAULT_REGISTRY_NAME); - let package_name = package.name.clone().unwrap_or_else(|| name.clone()); - - // Resolve the version from the lock file if there is one - let locked = match self.lock_file.as_ref().and_then(|resolver| { - resolver - .resolve(registry_name, &package_name, &package.version) - .transpose() - }) { - Some(Ok(locked)) => Some(locked), - Some(Err(e)) => return Err(e), - _ => None, - }; - - let registry = match self.registries.entry(registry_name) { - indexmap::map::Entry::Occupied(e) => e.into_mut(), - indexmap::map::Entry::Vacant(e) => e.insert(Registry { - client: self.client.clone(), - packages: HashMap::new(), - dependencies: Vec::new(), - }), - }; - - registry - .add_dependency(name, package_name, &package.version, locked) - .await?; - } - Dependency::Local(p) => { - // A local path dependency, insert a resolution immediately - let res = DependencyResolution::Local(LocalResolution { - name: name.clone(), - path: p.clone(), - }); - - let prev = self.resolutions.insert(name.clone(), res); - assert!(prev.is_none()); - } - } - - Ok(()) - } - - /// Resolve all dependencies. - /// - /// This will download all dependencies that are not already present in client storage. - /// - /// Returns the dependency resolution map. - pub async fn resolve(mut self) -> Result { - // Resolve all dependencies - for (name, registry) in self.registries.iter_mut() { - registry.resolve(name).await?; - } - - for resolution in self - .registries - .into_values() - .flat_map(|r| r.dependencies.into_iter()) - .map(|d| { - DependencyResolution::Registry( - d.resolution.expect("dependency should have been resolved"), - ) - }) - { - let prev = self - .resolutions - .insert(resolution.name().clone(), resolution); - assert!(prev.is_none()); - } - - Ok(self.resolutions) - } -} - -struct Registry<'a> { - client: Arc>, - packages: HashMap>, - dependencies: Vec>, -} - -impl<'a> Registry<'a> { - async fn add_dependency( - &mut self, - name: &'a PackageRef, - package: PackageRef, - version: &'a VersionReq, - locked: Option<&LockedPackageVersion>, - ) -> Result<()> { - let dep = RegistryDependency { - name, - package: package.clone(), - version, - locked: locked.map(|l| (l.version.clone(), l.digest.clone())), - resolution: None, - }; - - self.dependencies.push(dep); - - Ok(()) - } - - async fn resolve(&mut self, registry: &'a str) -> Result<()> { - for dependency in self.dependencies.iter_mut() { - // We need to clone a handle to the client because we mutably borrow self below. Might - // be worth replacing the mutable borrow with a RwLock down the line. - let client = self.client.clone(); - - let (selected_version, digest) = if client.is_readonly() { - dependency - .locked - .as_ref() - .map(|(ver, digest)| (ver, Some(digest))) - .ok_or_else(|| { - anyhow::anyhow!("Couldn't find locked dependency while in offline mode") - })? - } else { - let versions = - load_package(&mut self.packages, &self.client, dependency.package.clone()) - .await? - .with_context(|| { - format!( - "package `{name}` was not found in component registry `{registry}`", - name = dependency.package - ) - })?; - - match &dependency.locked { - Some((version, digest)) => { - // The dependency had a lock file entry, so attempt to do an exact match first - let exact_req = VersionReq { - comparators: vec![Comparator { - op: Op::Exact, - major: version.major, - minor: Some(version.minor), - patch: Some(version.patch), - pre: version.pre.clone(), - }], - }; - - // If an exact match can't be found, fallback to the latest release to satisfy - // the version requirement; this can happen when packages are yanked. If we did - // find an exact match, return the digest for comparison after fetching the - // release - find_latest_release(versions, &exact_req).map(|v| (&v.version, Some(digest))).or_else(|| find_latest_release(versions, dependency.version).map(|v| (&v.version, None))) - } - None => find_latest_release(versions, dependency.version).map(|v| (&v.version, None)), - }.with_context(|| format!("component registry package `{name}` has no release matching version requirement `{version}`", name = dependency.package, version = dependency.version))? - }; - - // We need to clone a handle to the client because we mutably borrow self above. Might - // be worth replacing the mutable borrow with a RwLock down the line. - let release = client - .get_release(&dependency.package, selected_version) - .await?; - if let Some(digest) = digest { - if &release.content_digest != digest { - bail!( - "component registry package `{name}` (v`{version}`) has digest `{content}` but the lock file specifies digest `{digest}`", - name = dependency.package, - version = release.version, - content = release.content_digest, - ); - } - } - - dependency.resolution = Some(RegistryResolution { - name: dependency.name.clone(), - package: dependency.package.clone(), - registry: if registry == DEFAULT_REGISTRY_NAME { - None - } else { - Some(registry.to_string()) - }, - requirement: dependency.version.clone(), - version: release.version.clone(), - digest: release.content_digest.clone(), - client: self.client.clone(), - }); - } - - Ok(()) - } -} - -async fn load_package<'b>( - packages: &'b mut HashMap>, - client: &CachingClient, - package: PackageRef, -) -> Result>> { - match packages.entry(package) { - hash_map::Entry::Occupied(e) => Ok(Some(e.into_mut())), - hash_map::Entry::Vacant(e) => match client.list_all_versions(e.key()).await { - Ok(p) => Ok(Some(e.insert(p))), - Err(WasmPkgError::PackageNotFound) => Ok(None), - Err(err) => Err(err.into()), - }, - } -} - -struct RegistryDependency<'a> { - /// The package name assigned in the configuration file. - name: &'a PackageRef, - /// The package name of the registry package. - package: PackageRef, - version: &'a VersionReq, - locked: Option<(Version, ContentDigest)>, - resolution: Option, -} - -/// Represents a map of dependency resolutions. -/// -/// The key to the map is the package name of the dependency. -pub type DependencyResolutionMap = HashMap; - -fn find_latest_release<'a>( - versions: &'a [VersionInfo], - req: &VersionReq, -) -> Option<&'a VersionInfo> { - versions - .iter() - .filter(|info| !info.yanked && req.matches(&info.version)) - .max_by(|a, b| a.version.cmp(&b.version)) -} diff --git a/src/bindings.rs b/src/bindings.rs index 3930225a..2d939286 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -5,11 +5,11 @@ use std::{ }; use anyhow::{bail, Context, Result}; -use cargo_component_core::registry::DecodedDependency; use heck::ToKebabCase; use indexmap::{IndexMap, IndexSet}; use semver::Version; use wasm_pkg_client::PackageRef; +use wasm_pkg_core::resolver::DecodedDependency; use wit_bindgen_core::Files; use wit_bindgen_rust::{Opts, WithOption}; use wit_component::DecodedWasm; @@ -173,7 +173,7 @@ impl<'a> BindingsGenerator<'a> { }; // Merge all component dependencies as interface imports - for (id, dependency) in &resolution.resolutions { + for (id, dependency) in resolution.resolutions.iter() { log::debug!("importing component dependency `{id}`"); empty_target = false; @@ -186,7 +186,6 @@ impl<'a> BindingsGenerator<'a> { // Set the world name as currently it defaults to "root" // For now, set it to the name from the id let world = &mut resolve.worlds[component_world_id]; - world.name = id.name().to_string(); let pkg = &mut resolve.packages[world.package.unwrap()]; pkg.name.namespace = id.namespace().to_string(); @@ -246,7 +245,7 @@ impl<'a> BindingsGenerator<'a> { // Start by decoding all of the target dependencies let mut deps = IndexMap::new(); - for (id, resolution) in &resolution.target_resolutions { + for (id, resolution) in resolution.target_resolutions.iter() { let decoded = resolution.decode().await?; let name = decoded.package_name(); diff --git a/src/commands/add.rs b/src/commands/add.rs index f4614bf3..c39a5df7 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -5,11 +5,7 @@ use std::{ }; use anyhow::{bail, Context, Result}; -use cargo_component_core::{ - command::CommonOptions, - registry::{Dependency, DependencyResolution, DependencyResolver, RegistryPackage}, - VersionedPackageName, -}; +use cargo_component_core::{command::CommonOptions, VersionedPackageName}; use cargo_metadata::Package; use clap::Args; use semver::VersionReq; @@ -18,6 +14,9 @@ use wasm_pkg_client::{ caching::{CachingClient, FileCache}, PackageRef, }; +use wasm_pkg_core::resolver::{ + Dependency, DependencyResolution, DependencyResolver, RegistryPackage, +}; use crate::{ config::CargoPackageSpec, diff --git a/src/commands/new.rs b/src/commands/new.rs index 51fa9906..681342a8 100644 --- a/src/commands/new.rs +++ b/src/commands/new.rs @@ -1,5 +1,6 @@ use std::{ borrow::Cow, + collections::HashMap, fs, path::{Path, PathBuf}, process::Command, @@ -7,15 +8,18 @@ use std::{ }; use anyhow::{bail, Context, Result}; -use cargo_component_core::{ - command::CommonOptions, - registry::{Dependency, DependencyResolution, DependencyResolver, RegistryResolution}, -}; +use cargo_component_core::command::CommonOptions; use clap::Args; use heck::ToKebabCase; use semver::VersionReq; use toml_edit::{table, value, DocumentMut, Item, Table, Value}; -use wasm_pkg_client::caching::{CachingClient, FileCache}; +use wasm_pkg_client::{ + caching::{CachingClient, FileCache}, + PackageRef, +}; +use wasm_pkg_core::resolver::{ + Dependency, DependencyResolution, DependencyResolver, RegistryResolution, +}; use crate::{config::Config, generator::SourceGenerator, metadata, metadata::DEFAULT_WIT_DIR}; @@ -540,7 +544,7 @@ world example {{ assert_eq!(dependencies.len(), 1); Ok(Some(( - dependencies + as Clone>::clone(&dependencies) .into_values() .next() .expect("expected a target resolution"), diff --git a/src/commands/update.rs b/src/commands/update.rs index 6a2e57b2..206d226f 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,9 +1,21 @@ use anyhow::Result; -use cargo_component_core::command::CommonOptions; +use cargo_component_core::{command::CommonOptions, terminal::Colors}; use clap::Args; -use std::path::PathBuf; +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; +use terminal_link::Link as TerminalLink; +use wasm_pkg_core::{ + lock::{LockFile, LockedPackageVersion}, + resolver::{DependencyResolver, RegistryPackage}, + wit::add_packages_to_resolver, +}; -use crate::{load_component_metadata, load_metadata, Config}; +use crate::{ + load_component_metadata, load_metadata, metadata::ComponentMetadata, Config, + PackageComponentMetadata, +}; /// Update dependencies as recorded in the component lock file #[derive(Args)] @@ -42,17 +54,146 @@ impl UpdateCommand { let metadata = load_metadata(self.manifest_path.as_deref())?; let packages = load_component_metadata(&metadata, [].iter(), true)?; - let lock_update_allowed = !self.frozen && !self.locked; let client = config.client(self.common.cache_dir, false).await?; - crate::update_lockfile( - client, - &config, - &metadata, - &packages, - lock_update_allowed, - self.locked, - self.dry_run, - ) - .await + let lock_file = if Path::exists(&PathBuf::from("Cargo-component.lock")) { + config.terminal().status_with_color( + "Warning", + format!( + "It seems you are using `Cargo-component.lock` for your lock file. + As of version 0.19.0, cargo-component uses `wkg.lock` from {}. + You will notice that your lock file has been renamed.", + TerminalLink::new( + "wasm-pkg-tools", + "https://github.com/bytecodealliance/wasm-pkg-tools" + ) + ), + Colors::Yellow, + )?; + let lock = LockFile::load_from_path("Cargo-component.lock", true).await?; + let mut new_lock = LockFile::new_with_path(lock.packages, "wkg.lock").await?; + new_lock.write().await?; + std::fs::remove_file("Cargo-component.lock")?; + new_lock + } else { + LockFile::load(true).await? + }; + let old_pkgs = lock_file.packages.clone(); + drop(lock_file); + + let mut new_packages = HashSet::new(); + for PackageComponentMetadata { + metadata: ComponentMetadata { section, .. }, + .. + } in &packages + { + let target_deps = section.target.dependencies(); + for (name, dep) in target_deps.iter() { + match dep { + wasm_pkg_core::resolver::Dependency::Package(RegistryPackage { + version, + .. + }) => { + new_packages.insert((name.clone(), version.clone())); + } + wasm_pkg_core::resolver::Dependency::Local(_) => todo!(), + } + } + for (name, dep) in section.dependencies.iter() { + match dep { + wasm_pkg_core::resolver::Dependency::Package(RegistryPackage { + version, + .. + }) => { + new_packages.insert((name.clone(), version.clone())); + } + wasm_pkg_core::resolver::Dependency::Local(_) => todo!(), + } + } + } + let mut resolver = DependencyResolver::new_with_client(client, None)?; + add_packages_to_resolver(&mut resolver, new_packages).await?; + let deps = resolver.resolve().await?; + + let mut new_lock_file = LockFile::from_dependencies(&deps, "wkg.lock").await?; + + for old_pkg in &old_pkgs { + if let Some(new_pkg) = new_lock_file + .packages + .iter() + .find(|p| p.name == old_pkg.name) + { + for old_ver in &old_pkg.versions { + let new_ver = match new_pkg + .versions + .binary_search_by_key(&old_ver.key(), LockedPackageVersion::key) + .map(|index| &new_pkg.versions[index]) + { + Ok(ver) => ver, + Err(_) => { + config.terminal().status_with_color( + if self.dry_run { + "Would remove" + } else { + "Removing" + }, + format!( + "dependency `{name}` v{version}", + name = old_pkg.name, + version = old_ver.version, + ), + Colors::Red, + )?; + continue; + } + }; + if old_ver.version != new_ver.version { + config.terminal().status_with_color( + if self.dry_run { + "Would update" + } else { + "Updating" + }, + format!( + "dependency `{name}` v{old} -> v{new}", + name = old_pkg.name, + old = old_ver.version, + new = new_ver.version + ), + Colors::Cyan, + )?; + } + } + } else { + for old_ver in &old_pkg.versions { + config.terminal().status_with_color( + if self.dry_run { + "Would remove" + } else { + "Removing" + }, + format!("dependency `{}` v{}", old_pkg.name, old_ver.version), + Colors::Red, + )?; + } + } + } + for new_pkg in &new_lock_file.packages { + if old_pkgs.iter().find(|p| p.name == new_pkg.name).is_none() { + for new_ver in &new_pkg.versions { + config.terminal().status_with_color( + if self.dry_run { "Would add" } else { "Adding" }, + format!( + "dependency `{name}` v{version}", + name = new_pkg.name, + version = new_ver.version, + ), + Colors::Green, + )?; + } + } + } + + new_lock_file.write().await?; + Ok(()) } } diff --git a/src/generator.rs b/src/generator.rs index 140472ef..2b2859bc 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -9,10 +9,10 @@ use std::{ }; use anyhow::{bail, Context, Result}; -use cargo_component_core::registry::DependencyResolution; use heck::{AsSnakeCase, ToSnakeCase, ToUpperCamelCase}; use indexmap::{map::Entry, IndexMap, IndexSet}; use wasm_pkg_client::PackageRef; +use wasm_pkg_core::resolver::DependencyResolution; use wit_bindgen_rust::to_rust_ident; use wit_parser::{ Function, FunctionKind, Handle, Interface, Resolve, Type, TypeDef, TypeDefKind, TypeId, diff --git a/src/lib.rs b/src/lib.rs index fb0b04d3..b2bb22d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,27 +17,25 @@ use std::{ use anyhow::{bail, Context, Result}; use bindings::BindingsGenerator; -use cargo_component_core::{ - lock::{LockFile, LockFileResolver, LockedPackage, LockedPackageVersion}, - terminal::Colors, -}; +use cargo_component_core::terminal::Colors; use cargo_config2::{PathAndArgs, TargetTripleRef}; use cargo_metadata::{Artifact, Message, Metadata, MetadataCommand, Package}; use semver::Version; use shell_escape::escape; use tempfile::NamedTempFile; +use terminal_link::Link as TerminalLink; use wasm_metadata::{Link, LinkType, RegistryMetadata}; use wasm_pkg_client::{ caching::{CachingClient, FileCache}, PackageRef, PublishOpts, Registry, }; +use wasm_pkg_core::lock::LockFile; use wasmparser::{Parser, Payload}; use wit_component::ComponentEncoder; use crate::target::install_wasm32_wasip1; use config::{CargoArguments, CargoPackageSpec, Config}; -use lock::{acquire_lock_file_ro, acquire_lock_file_rw}; use metadata::ComponentMetadata; use registry::{PackageDependencyResolution, PackageResolutionMap}; @@ -45,7 +43,6 @@ mod bindings; pub mod commands; pub mod config; mod generator; -mod lock; mod metadata; mod registry; mod target; @@ -146,7 +143,7 @@ pub async fn run_cargo_command( cargo_args: &CargoArguments, spawn_args: &[String], ) -> Result> { - let import_name_map = generate_bindings(client, config, metadata, packages, cargo_args).await?; + let import_name_map = generate_bindings(client, config, packages).await?; let cargo_path = std::env::var("CARGO") .map(PathBuf::from) @@ -260,7 +257,8 @@ pub async fn run_cargo_command( &import_name_map, command, output_args, - )?; + ) + .await?; if let Some(runner) = runner { spawn_outputs(config, &runner, output_args, &outputs, command)?; @@ -392,7 +390,7 @@ struct Output { display: Option, } -fn componentize_artifacts( +async fn componentize_artifacts( config: &Config, cargo_metadata: &Metadata, artifacts: &[Artifact], @@ -406,7 +404,7 @@ fn componentize_artifacts( env::current_dir().with_context(|| "couldn't get the current directory of the process")?; // Acquire the lock file to ensure any other cargo-component process waits for this to complete - let _file_lock = acquire_lock_file_ro(config.terminal(), cargo_metadata)?; + let _file_lock = LockFile::load(false).await?; for artifact in artifacts { for path in artifact @@ -735,28 +733,34 @@ pub fn load_component_metadata<'a>( async fn generate_bindings( client: Arc>, config: &Config, - metadata: &Metadata, packages: &[PackageComponentMetadata<'_>], - cargo_args: &CargoArguments, ) -> Result>> { - let file_lock = acquire_lock_file_ro(config.terminal(), metadata)?; - let lock_file = file_lock - .as_ref() - .map(|f| { - LockFile::read(f.file()).with_context(|| { - format!( - "failed to read lock file `{path}`", - path = f.path().display() + let lock_file = if Path::exists(&PathBuf::from("Cargo-component.lock")) { + config.terminal().status_with_color( + "Warning", + format!( + "It seems you are using `Cargo-component.lock` for your lock file. + As of version 0.19.0, cargo-component uses `wkg.lock` from {}. + You will notice that your lock file has been renamed.", + TerminalLink::new( + "wasm-pkg-tools", + "https://github.com/bytecodealliance/wasm-pkg-tools" ) - }) - }) - .transpose()?; + ), + Colors::Yellow, + )?; + let lock = LockFile::load_from_path("Cargo-component.lock", true).await?; + let mut new_lock = LockFile::new_with_path(lock.packages, "wkg.lock").await?; + new_lock.write().await?; + std::fs::remove_file("Cargo-component.lock")?; + new_lock + } else { + LockFile::load(true).await? + }; let cwd = env::current_dir().with_context(|| "couldn't get the current directory of the process")?; - - let resolver = lock_file.as_ref().map(LockFileResolver::new); - let resolution_map = create_resolution_map(client, packages, resolver).await?; + let resolution_map = create_resolution_map(client, packages, &lock_file).await?; let mut import_name_map = HashMap::new(); for PackageComponentMetadata { package, .. } in packages { let resolution = resolution_map.get(&package.id).expect("missing resolution"); @@ -766,27 +770,9 @@ async fn generate_bindings( ); } - // Update the lock file if it exists or if the new lock file is non-empty - let new_lock_file = resolution_map.to_lock_file(); - if (lock_file.is_some() || !new_lock_file.packages.is_empty()) - && Some(&new_lock_file) != lock_file.as_ref() - { - drop(file_lock); - let file_lock = acquire_lock_file_rw( - config.terminal(), - metadata, - cargo_args.lock_update_allowed(), - cargo_args.locked, - )?; - new_lock_file - .write(file_lock.file(), "cargo-component") - .with_context(|| { - format!( - "failed to write lock file `{path}`", - path = file_lock.path().display() - ) - })?; - } + drop(lock_file); + let mut new_lock_file = resolution_map.to_lock_file().await; + new_lock_file.write().await?; Ok(import_name_map) } @@ -794,7 +780,7 @@ async fn generate_bindings( async fn create_resolution_map<'a>( client: Arc>, packages: &'a [PackageComponentMetadata<'_>], - lock_file: Option>, + lock_file: &LockFile, ) -> Result> { let mut map = PackageResolutionMap::default(); @@ -946,7 +932,7 @@ fn componentize( )?; } - let encoder = ComponentEncoder::default() + let mut encoder = ComponentEncoder::default() .module(bytes)? .import_name_map(import_name_map.clone()) .adapter( @@ -1119,165 +1105,3 @@ pub async fn publish( Ok(()) } - -/// Update the dependencies in the lock file. -/// -/// This updates only `Cargo-component.lock`. -pub async fn update_lockfile( - client: Arc>, - config: &Config, - metadata: &Metadata, - packages: &[PackageComponentMetadata<'_>], - lock_update_allowed: bool, - locked: bool, - dry_run: bool, -) -> Result<()> { - // Read the current lock file and generate a new one - let map = create_resolution_map(client, packages, None).await?; - - let file_lock = acquire_lock_file_ro(config.terminal(), metadata)?; - let orig_lock_file = file_lock - .as_ref() - .map(|f| { - LockFile::read(f.file()).with_context(|| { - format!( - "failed to read lock file `{path}`", - path = f.path().display() - ) - }) - }) - .transpose()? - .unwrap_or_default(); - - let new_lock_file = map.to_lock_file(); - - for old_pkg in &orig_lock_file.packages { - let new_pkg = match new_lock_file - .packages - .binary_search_by_key(&old_pkg.key(), LockedPackage::key) - .map(|index| &new_lock_file.packages[index]) - { - Ok(pkg) => pkg, - Err(_) => { - // The package is no longer a dependency - for old_ver in &old_pkg.versions { - config.terminal().status_with_color( - if dry_run { "Would remove" } else { "Removing" }, - format!( - "dependency `{name}` v{version}", - name = old_pkg.name, - version = old_ver.version, - ), - Colors::Red, - )?; - } - continue; - } - }; - - for old_ver in &old_pkg.versions { - let new_ver = match new_pkg - .versions - .binary_search_by_key(&old_ver.key(), LockedPackageVersion::key) - .map(|index| &new_pkg.versions[index]) - { - Ok(ver) => ver, - Err(_) => { - // The version of the package is no longer a dependency - config.terminal().status_with_color( - if dry_run { "Would remove" } else { "Removing" }, - format!( - "dependency `{name}` v{version}", - name = old_pkg.name, - version = old_ver.version, - ), - Colors::Red, - )?; - continue; - } - }; - - // The version has changed - if old_ver.version != new_ver.version { - config.terminal().status_with_color( - if dry_run { "Would update" } else { "Updating" }, - format!( - "dependency `{name}` v{old} -> v{new}", - name = old_pkg.name, - old = old_ver.version, - new = new_ver.version - ), - Colors::Cyan, - )?; - } - } - } - - for new_pkg in &new_lock_file.packages { - let old_pkg = match orig_lock_file - .packages - .binary_search_by_key(&new_pkg.key(), LockedPackage::key) - .map(|index| &orig_lock_file.packages[index]) - { - Ok(pkg) => pkg, - Err(_) => { - // The package is new - for new_ver in &new_pkg.versions { - config.terminal().status_with_color( - if dry_run { "Would add" } else { "Adding" }, - format!( - "dependency `{name}` v{version}", - name = new_pkg.name, - version = new_ver.version, - ), - Colors::Green, - )?; - } - continue; - } - }; - - for new_ver in &new_pkg.versions { - if old_pkg - .versions - .binary_search_by_key(&new_ver.key(), LockedPackageVersion::key) - .map(|index| &old_pkg.versions[index]) - .is_err() - { - // The version is new - config.terminal().status_with_color( - if dry_run { "Would add" } else { "Adding" }, - format!( - "dependency `{name}` v{version}", - name = new_pkg.name, - version = new_ver.version, - ), - Colors::Green, - )?; - } - } - } - - if dry_run { - config - .terminal() - .warn("not updating component lock file due to --dry-run option")?; - } else { - // Update the lock file - if new_lock_file != orig_lock_file { - drop(file_lock); - let file_lock = - acquire_lock_file_rw(config.terminal(), metadata, lock_update_allowed, locked)?; - new_lock_file - .write(file_lock.file(), "cargo-component") - .with_context(|| { - format!( - "failed to write lock file `{path}`", - path = file_lock.path().display() - ) - })?; - } - } - - Ok(()) -} diff --git a/src/lock.rs b/src/lock.rs deleted file mode 100644 index f045bfad..00000000 --- a/src/lock.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Module for the lock file implementation. - -use anyhow::Result; -use cargo_component_core::{ - lock::FileLock, - terminal::{Colors, Terminal}, -}; -use cargo_metadata::Metadata; - -/// The name of the lock file. -pub const LOCK_FILE_NAME: &str = "Cargo-component.lock"; - -pub(crate) fn acquire_lock_file_ro( - terminal: &Terminal, - metadata: &Metadata, -) -> Result> { - let path = metadata.workspace_root.join(LOCK_FILE_NAME); - if !path.exists() { - return Ok(None); - } - - log::info!("opening lock file `{path}`"); - match FileLock::try_open_ro(&path)? { - Some(lock) => Ok(Some(lock)), - None => { - terminal.status_with_color( - "Blocking", - format!("on access to lock file `{path}`"), - Colors::Cyan, - )?; - - FileLock::open_ro(&path).map(Some) - } - } -} - -pub(crate) fn acquire_lock_file_rw( - terminal: &Terminal, - metadata: &Metadata, - lock_update_allowed: bool, - locked: bool, -) -> Result { - if !lock_update_allowed { - let flag = if locked { "--locked" } else { "--frozen" }; - anyhow::bail!( - "the lock file {path} needs to be updated but {flag} was passed to prevent this\n\ - If you want to try to generate the lock file without accessing the network, \ - remove the {flag} flag and use --offline instead.", - path = metadata.workspace_root.join(LOCK_FILE_NAME) - ); - } - - let path = metadata.workspace_root.join(LOCK_FILE_NAME); - log::info!("creating lock file `{path}`"); - match FileLock::try_open_rw(&path)? { - Some(lock) => Ok(lock), - None => { - terminal.status_with_color( - "Blocking", - format!("on access to lock file `{path}`"), - Colors::Cyan, - )?; - - FileLock::open_rw(&path) - } - } -} diff --git a/src/metadata.rs b/src/metadata.rs index a9abdb76..3ab24126 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -8,7 +8,6 @@ use std::{ }; use anyhow::{bail, Context, Result}; -use cargo_component_core::registry::{Dependency, RegistryPackage}; use cargo_metadata::Package; use semver::{Version, VersionReq}; use serde::{ @@ -18,6 +17,7 @@ use serde::{ use serde_json::from_value; use url::Url; use wasm_pkg_client::PackageRef; +use wasm_pkg_core::resolver::{Dependency, RegistryPackage}; /// The default directory to look for a target WIT file. pub const DEFAULT_WIT_DIR: &str = "wit"; diff --git a/src/registry.rs b/src/registry.rs index e65c21d4..9c8c2ca3 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -2,16 +2,16 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::Result; -use cargo_component_core::{ - lock::{LockFile, LockFileResolver, LockedPackage, LockedPackageVersion}, - registry::{DependencyResolution, DependencyResolutionMap, DependencyResolver}, -}; use cargo_metadata::PackageId; -use semver::Version; +use semver::{Version, VersionReq}; use wasm_pkg_client::{ caching::{CachingClient, FileCache}, ContentDigest, PackageRef, }; +use wasm_pkg_core::{ + lock::{LockFile, LockedPackage, LockedPackageVersion}, + resolver::{DependencyResolution, DependencyResolutionMap, DependencyResolver}, +}; use crate::metadata::ComponentMetadata; @@ -33,7 +33,7 @@ impl<'a> PackageDependencyResolution<'a> { pub async fn new( client: Arc>, metadata: &'a ComponentMetadata, - lock_file: Option>, + lock_file: &LockFile, ) -> Result> { Ok(Self { metadata, @@ -53,14 +53,14 @@ impl<'a> PackageDependencyResolution<'a> { async fn resolve_target_deps( client: Arc>, metadata: &ComponentMetadata, - lock_file: Option>, + lock_file: &LockFile, ) -> Result { let target_deps = metadata.section.target.dependencies(); if target_deps.is_empty() { return Ok(Default::default()); } - let mut resolver = DependencyResolver::new_with_client(client, lock_file)?; + let mut resolver = DependencyResolver::new_with_client(client, Some(lock_file))?; for (name, dependency) in target_deps.iter() { resolver.add_dependency(name, dependency).await?; @@ -72,13 +72,13 @@ impl<'a> PackageDependencyResolution<'a> { async fn resolve_deps( client: Arc>, metadata: &ComponentMetadata, - lock_file: Option>, + lock_file: &LockFile, ) -> Result { if metadata.section.dependencies.is_empty() { return Ok(Default::default()); } - let mut resolver = DependencyResolver::new_with_client(client, lock_file)?; + let mut resolver = DependencyResolver::new_with_client(client, Some(lock_file))?; for (name, dependency) in &metadata.section.dependencies { resolver.add_dependency(name, dependency).await?; @@ -111,7 +111,7 @@ impl<'a> PackageResolutionMap<'a> { } /// Converts the resolution map into a lock file. - pub fn to_lock_file(&self) -> LockFile { + pub async fn to_lock_file(&self) -> LockFile { type PackageKey = (PackageRef, Option); type VersionsMap = HashMap; let mut packages: HashMap = HashMap::new(); @@ -149,13 +149,13 @@ impl<'a> PackageResolutionMap<'a> { let mut versions: Vec = versions .into_iter() .map(|(requirement, (version, digest))| LockedPackageVersion { - requirement, + requirement: VersionReq::parse(&requirement).unwrap(), version, digest, }) .collect(); - versions.sort_by(|a, b| a.key().cmp(b.key())); + versions.sort_by(|a, b| a.key().cmp(&b.key())); LockedPackage { name, @@ -167,6 +167,6 @@ impl<'a> PackageResolutionMap<'a> { packages.sort_by(|a, b| a.key().cmp(&b.key())); - LockFile::new(packages) + LockFile::new_with_path(packages, "wkg.lock").await.unwrap() } } diff --git a/tests/build.rs b/tests/build.rs index 453d7473..218286bc 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -24,10 +24,6 @@ fn it_builds_debug() -> Result<()> { validate_component(&project.debug_wasm("foo"))?; - // A lock file should only be generated for projects with - // registry dependencies - assert!(!project.root().join("Cargo-component.lock").exists()); - Ok(()) } diff --git a/tests/publish.rs b/tests/publish.rs index 0c5b5566..25fe5416 100644 --- a/tests/publish.rs +++ b/tests/publish.rs @@ -57,7 +57,7 @@ world foo { validate_component(&project.release_wasm("foo"))?; - let path = project.root().join("Cargo-component.lock"); + let path = project.root().join("wkg.lock"); let contents = fs::read_to_string(&path) .with_context(|| format!("failed to read lock file `{path}`", path = path.display()))?; diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 451c2574..5525594d 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -103,8 +103,8 @@ pub async fn publish_wit( ) .context("failed to resolve wit for publishing")?; - let bytes = wit_component::encode(Some(true), &resolve, pkg) - .context("failed to encode wit for publishing")?; + let bytes = + wit_component::encode(&resolve, pkg).context("failed to encode wit for publishing")?; publish(config, &id.parse()?, version, bytes).await } From dab9549bab65f48b52cf3b96a1b296fecad668c7 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Tue, 19 Nov 2024 14:32:19 -0600 Subject: [PATCH 2/2] updated wasm-pkg-tools --- Cargo.lock | 357 ++++++++++++++++++++++++----------------- src/commands/add.rs | 3 +- src/commands/new.rs | 3 +- src/commands/update.rs | 7 +- src/config.rs | 7 +- src/lib.rs | 9 +- src/metadata.rs | 122 +++++++++++++- src/registry.rs | 21 ++- 8 files changed, 347 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8af28f0..adc1fb06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -281,7 +281,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -316,7 +316,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -407,7 +407,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -538,9 +538,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "camino" @@ -588,7 +588,7 @@ dependencies = [ "warg-protocol", "warg-server", "wasi-preview1-component-adapter-provider", - "wasm-metadata 0.219.1", + "wasm-metadata", "wasm-pkg-client", "wasm-pkg-core", "wasmparser 0.219.1", @@ -597,8 +597,8 @@ dependencies = [ "which", "wit-bindgen-core", "wit-bindgen-rust", - "wit-component 0.219.1", - "wit-parser 0.219.1", + "wit-component", + "wit-parser", ] [[package]] @@ -623,8 +623,8 @@ dependencies = [ "wasm-pkg-client", "wasm-pkg-core", "windows-sys 0.52.0", - "wit-component 0.219.1", - "wit-parser 0.219.1", + "wit-component", + "wit-parser", ] [[package]] @@ -659,7 +659,7 @@ dependencies = [ "semver", "serde 1.0.209", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -742,7 +742,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -875,7 +875,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -886,7 +886,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -921,6 +921,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.87", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -930,7 +961,7 @@ dependencies = [ "console", "shell-words", "tempfile", - "thiserror", + "thiserror 1.0.63", "zeroize", ] @@ -1083,7 +1114,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1115,6 +1146,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1292,7 +1334,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1347,6 +1389,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getset" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "gimli" version = "0.29.0" @@ -1899,7 +1953,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.4", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1958,7 +2012,7 @@ checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ "cfg-if", "miette-derive", - "thiserror", + "thiserror 1.0.63", "unicode-width", ] @@ -1970,7 +2024,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2163,9 +2217,9 @@ dependencies = [ [[package]] name = "oci-client" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f5098b86f972ac3484f7c9011bbbbd64aaa7e21d10d2c1a91fefb4ad0ba2ad9" +checksum = "474675fdc023fbcc9dcf4782e938a3a1ae5fd469c728d8db40599bd25c77e1ba" dependencies = [ "bytes", "chrono", @@ -2174,23 +2228,40 @@ dependencies = [ "http-auth", "jwt", "lazy_static 1.5.0", + "oci-spec", "olpc-cjson", "regex", "reqwest", "serde 1.0.209", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "unicase", ] +[[package]] +name = "oci-spec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da406e58efe2eb5986a6139626d611ce426e5324a824133d76367c765cf0b882" +dependencies = [ + "derive_builder", + "getset", + "regex", + "serde 1.0.209", + "serde_json", + "strum", + "strum_macros", + "thiserror 2.0.3", +] + [[package]] name = "oci-wasm" -version = "0.0.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3493d1985a31c5fbd4b37f72a319aab88b55908185a5a799219c6152e9da9b" +checksum = "f147e207436277483c23cb8e55ccd039ee1657c6a8d19471a6de187da6973ef8" dependencies = [ "anyhow", "chrono", @@ -2199,8 +2270,8 @@ dependencies = [ "serde_json", "sha2", "tokio", - "wit-component 0.215.0", - "wit-parser 0.215.0", + "wit-component", + "wit-parser", ] [[package]] @@ -2389,7 +2460,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2518,7 +2589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2540,6 +2611,28 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -2576,7 +2669,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.77", + "syn 2.0.87", "tempfile", ] @@ -2590,7 +2683,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2627,7 +2720,7 @@ dependencies = [ "prost-reflect", "prost-types", "protox-parse", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2639,7 +2732,7 @@ dependencies = [ "logos", "miette", "prost-types", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2671,7 +2764,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -2688,7 +2781,7 @@ dependencies = [ "rustc-hash", "rustls", "slab", - "thiserror", + "thiserror 1.0.63", "tinyvec", "tracing", ] @@ -2771,7 +2864,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3145,7 +3238,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3178,7 +3271,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3229,7 +3322,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3408,6 +3501,25 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.87", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3427,9 +3539,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3512,7 +3624,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] @@ -3523,7 +3644,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -3617,7 +3749,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3639,7 +3771,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -3783,7 +3915,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -3976,7 +4108,7 @@ dependencies = [ "itertools 0.12.1", "serde 1.0.209", "serde_with", - "thiserror", + "thiserror 1.0.63", "warg-crypto", "warg-protocol", ] @@ -4010,7 +4142,7 @@ dependencies = [ "serde_json", "sha256", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "tracing", @@ -4045,7 +4177,7 @@ dependencies = [ "serde 1.0.209", "sha2", "signature", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -4083,7 +4215,7 @@ dependencies = [ "semver", "serde 1.0.209", "serde_with", - "thiserror", + "thiserror 1.0.63", "warg-crypto", "warg-protobuf", "warg-transparency", @@ -4105,7 +4237,7 @@ dependencies = [ "secrecy", "serde 1.0.209", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "toml 0.8.19", @@ -4130,7 +4262,7 @@ dependencies = [ "anyhow", "indexmap 2.5.0", "prost", - "thiserror", + "thiserror 1.0.63", "warg-crypto", "warg-protobuf", ] @@ -4169,7 +4301,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -4203,7 +4335,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4245,16 +4377,6 @@ dependencies = [ "wasmparser 0.121.2", ] -[[package]] -name = "wasm-encoder" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" -dependencies = [ - "leb128", - "wasmparser 0.215.0", -] - [[package]] name = "wasm-encoder" version = "0.219.1" @@ -4265,22 +4387,6 @@ dependencies = [ "wasmparser 0.219.1", ] -[[package]] -name = "wasm-metadata" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6bb07c5576b608f7a2a9baa2294c1a3584a249965d695a9814a496cb6d232f" -dependencies = [ - "anyhow", - "indexmap 2.5.0", - "serde 1.0.209", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder 0.215.0", - "wasmparser 0.215.0", -] - [[package]] name = "wasm-metadata" version = "0.219.1" @@ -4299,14 +4405,14 @@ dependencies = [ [[package]] name = "wasm-pkg-client" -version = "0.6.0" +version = "0.8.2" dependencies = [ "anyhow", "async-trait", "base64 0.22.1", "bytes", - "dirs", "docker_credential", + "etcetera", "futures-util", "oci-client", "oci-wasm", @@ -4314,7 +4420,7 @@ dependencies = [ "serde 1.0.209", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "toml 0.8.19", @@ -4324,17 +4430,18 @@ dependencies = [ "warg-client", "warg-crypto", "warg-protocol", + "wasm-metadata", "wasm-pkg-common", - "wit-component 0.219.1", + "wit-component", ] [[package]] name = "wasm-pkg-common" -version = "0.6.0" +version = "0.8.2" dependencies = [ "anyhow", "bytes", - "dirs", + "etcetera", "futures-util", "http", "reqwest", @@ -4342,7 +4449,7 @@ dependencies = [ "serde 1.0.209", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "tracing", @@ -4350,7 +4457,7 @@ dependencies = [ [[package]] name = "wasm-pkg-core" -version = "0.6.0" +version = "0.8.2" dependencies = [ "anyhow", "futures-util", @@ -4362,12 +4469,12 @@ dependencies = [ "tokio-util", "toml 0.8.19", "tracing", - "wasm-metadata 0.219.1", + "wasm-metadata", "wasm-pkg-client", "wasm-pkg-common", - "windows-sys 0.52.0", - "wit-component 0.219.1", - "wit-parser 0.219.1", + "windows-sys 0.59.0", + "wit-component", + "wit-parser", ] [[package]] @@ -4394,19 +4501,6 @@ dependencies = [ "semver", ] -[[package]] -name = "wasmparser" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" -dependencies = [ - "ahash", - "bitflags 2.6.0", - "hashbrown 0.14.5", - "indexmap 2.5.0", - "semver", -] - [[package]] name = "wasmparser" version = "0.219.1" @@ -4745,7 +4839,7 @@ checksum = "163cee59d3d5ceec0b256735f3ab0dccac434afb0ec38c406276de9c5a11e906" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.219.1", + "wit-parser", ] [[package]] @@ -4758,29 +4852,10 @@ dependencies = [ "heck 0.5.0", "indexmap 2.5.0", "prettyplease", - "syn 2.0.77", - "wasm-metadata 0.219.1", + "syn 2.0.87", + "wasm-metadata", "wit-bindgen-core", - "wit-component 0.219.1", -] - -[[package]] -name = "wit-component" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f725e3885fc5890648be5c5cbc1353b755dc932aa5f1aa7de968b912a3280743" -dependencies = [ - "anyhow", - "bitflags 2.6.0", - "indexmap 2.5.0", - "log", - "serde 1.0.209", - "serde_derive", - "serde_json", - "wasm-encoder 0.215.0", - "wasm-metadata 0.215.0", - "wasmparser 0.215.0", - "wit-parser 0.215.0", + "wit-component", ] [[package]] @@ -4797,27 +4872,9 @@ dependencies = [ "serde_derive", "serde_json", "wasm-encoder 0.219.1", - "wasm-metadata 0.219.1", + "wasm-metadata", "wasmparser 0.219.1", - "wit-parser 0.219.1", -] - -[[package]] -name = "wit-parser" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.5.0", - "log", - "semver", - "serde 1.0.209", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.215.0", + "wit-parser", ] [[package]] @@ -4941,7 +4998,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] diff --git a/src/commands/add.rs b/src/commands/add.rs index c39a5df7..bda1d26c 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -1,7 +1,6 @@ use std::{ fs, path::{Path, PathBuf}, - sync::Arc, }; use anyhow::{bail, Context, Result}; @@ -126,7 +125,7 @@ impl AddCommand { async fn resolve_version( &self, - client: Arc>, + client: CachingClient, name: &PackageRef, ) -> Result { let mut resolver = DependencyResolver::new_with_client(client, None)?; diff --git a/src/commands/new.rs b/src/commands/new.rs index 681342a8..d41a54f8 100644 --- a/src/commands/new.rs +++ b/src/commands/new.rs @@ -4,7 +4,6 @@ use std::{ fs, path::{Path, PathBuf}, process::Command, - sync::Arc, }; use anyhow::{bail, Context, Result}; @@ -526,7 +525,7 @@ world example {{ /// `DependencyResolution` instead so we can actually resolve the dependency. async fn resolve_target( &self, - client: Arc>, + client: CachingClient, target: Option, ) -> Result)>> { match target { diff --git a/src/commands/update.rs b/src/commands/update.rs index 206d226f..4848fb26 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -9,7 +9,6 @@ use terminal_link::Link as TerminalLink; use wasm_pkg_core::{ lock::{LockFile, LockedPackageVersion}, resolver::{DependencyResolver, RegistryPackage}, - wit::add_packages_to_resolver, }; use crate::{ @@ -88,7 +87,7 @@ impl UpdateCommand { { let target_deps = section.target.dependencies(); for (name, dep) in target_deps.iter() { - match dep { + match &dep.0 { wasm_pkg_core::resolver::Dependency::Package(RegistryPackage { version, .. @@ -99,7 +98,7 @@ impl UpdateCommand { } } for (name, dep) in section.dependencies.iter() { - match dep { + match &dep.0 { wasm_pkg_core::resolver::Dependency::Package(RegistryPackage { version, .. @@ -111,7 +110,7 @@ impl UpdateCommand { } } let mut resolver = DependencyResolver::new_with_client(client, None)?; - add_packages_to_resolver(&mut resolver, new_packages).await?; + resolver.add_packages(new_packages).await?; let deps = resolver.resolve().await?; let mut new_lock_file = LockFile::from_dependencies(&deps, "wkg.lock").await?; diff --git a/src/config.rs b/src/config.rs index b6f54333..a82ff347 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,7 +26,6 @@ use parse_arg::{iter_short, match_arg}; use semver::Version; use std::fmt; use std::str::FromStr; -use std::sync::Arc; use std::{collections::BTreeMap, fmt::Display, path::PathBuf}; use toml_edit::DocumentMut; use wasm_pkg_client::caching::{CachingClient, FileCache}; @@ -525,11 +524,11 @@ impl Config { &self, cache: Option, offline: bool, - ) -> anyhow::Result>> { - Ok(Arc::new(CachingClient::new( + ) -> anyhow::Result> { + Ok(CachingClient::new( (!offline).then(|| Client::new(self.pkg_config.clone())), FileCache::new(cache_dir(cache)?).await?, - ))) + )) } } diff --git a/src/lib.rs b/src/lib.rs index b2bb22d3..1e63dd15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ use std::{ io::{BufRead, BufReader, Read, Seek, SeekFrom}, path::{Path, PathBuf}, process::{Command, Stdio}, - sync::Arc, time::SystemTime, }; @@ -135,7 +134,7 @@ impl From<&str> for CargoCommand { /// /// Returns any relevant output components. pub async fn run_cargo_command( - client: Arc>, + client: CachingClient, config: &Config, metadata: &Metadata, packages: &[PackageComponentMetadata<'_>], @@ -731,7 +730,7 @@ pub fn load_component_metadata<'a>( } async fn generate_bindings( - client: Arc>, + client: CachingClient, config: &Config, packages: &[PackageComponentMetadata<'_>], ) -> Result>> { @@ -778,7 +777,7 @@ async fn generate_bindings( } async fn create_resolution_map<'a>( - client: Arc>, + client: CachingClient, packages: &'a [PackageComponentMetadata<'_>], lock_file: &LockFile, ) -> Result> { @@ -1064,7 +1063,7 @@ fn add_registry_metadata(package: &Package, bytes: &[u8], path: &Path) -> Result /// Publish a component for the given workspace and publish options. pub async fn publish( config: &Config, - client: Arc>, + client: CachingClient, options: &PublishOptions<'_>, ) -> Result<()> { if options.dry_run { diff --git a/src/metadata.rs b/src/metadata.rs index 3ab24126..8554928d 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -12,7 +12,7 @@ use cargo_metadata::Package; use semver::{Version, VersionReq}; use serde::{ de::{self, value::MapAccessDeserializer}, - Deserialize, + Deserialize, Serialize }; use serde_json::from_value; use url::Url; @@ -169,17 +169,17 @@ pub enum Target { /// [select-world]: https://docs.rs/wit-parser/latest/wit_parser/struct.Resolve.html#method.select_world world: Option, /// The dependencies of the wit document being targeted. - dependencies: HashMap, + dependencies: HashMap, }, } impl Target { /// Gets the dependencies of the target. - pub fn dependencies(&self) -> Cow> { + pub fn dependencies(&self) -> Cow> { match self { Self::Package { name, package, .. } => Cow::Owned(HashMap::from_iter([( name.clone(), - Dependency::Package(package.clone()), + WasmDependency(Dependency::Package(package.clone())), )])), Self::Local { dependencies, .. } => Cow::Borrowed(dependencies), } @@ -271,7 +271,7 @@ impl<'de> Deserialize<'de> for Target { world: Option, registry: Option, path: Option, - dependencies: HashMap, + dependencies: HashMap, } let entry = Entry::deserialize(MapAccessDeserializer::new(map))?; @@ -326,6 +326,112 @@ impl<'de> Deserialize<'de> for Target { } } +#[derive(Debug, Clone)] +pub struct WasmDependency(pub Dependency); + +impl Serialize for WasmDependency { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match &self.0 { + Dependency::Package(package) => { + if package.name.is_none() && package.registry.is_none() { + let version = package.version.to_string(); + version.trim_start_matches('^').serialize(serializer) + } else { + #[derive(Serialize)] + struct Entry<'a> { + package: Option<&'a PackageRef>, + version: &'a str, + registry: Option<&'a str>, + } + + Entry { + package: package.name.as_ref(), + version: package.version.to_string().trim_start_matches('^'), + registry: package.registry.as_deref(), + } + .serialize(serializer) + } + } + Dependency::Local(path) => { + #[derive(Serialize)] + struct Entry<'a> { + path: &'a PathBuf, + } + + Entry { path }.serialize(serializer) + } + } + } +} + +impl<'de> Deserialize<'de> for WasmDependency { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = WasmDependency; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a string or a table") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + Ok(WasmDependency(Dependency::Package( + s.parse().map_err(de::Error::custom)?, + ))) + } + + fn visit_map(self, map: A) -> Result + where + A: de::MapAccess<'de>, + { + #[derive(Default, Deserialize)] + #[serde(default, deny_unknown_fields)] + struct Entry { + path: Option, + package: Option, + version: Option, + registry: Option, + } + + let entry = Entry::deserialize(MapAccessDeserializer::new(map))?; + + match (entry.path, entry.package, entry.version, entry.registry) { + (Some(path), None, None, None) => Ok(WasmDependency(Dependency::Local(path))), + (None, name, Some(version), registry) => { + Ok(WasmDependency(Dependency::Package(RegistryPackage { + name, + version, + registry, + }))) + } + (Some(_), None, Some(_), _) => Err(de::Error::custom( + "cannot specify both `path` and `version` fields in a dependency entry", + )), + (Some(_), None, None, Some(_)) => Err(de::Error::custom( + "cannot specify both `path` and `registry` fields in a dependency entry", + )), + (Some(_), Some(_), _, _) => Err(de::Error::custom( + "cannot specify both `path` and `package` fields in a dependency entry", + )), + (None, None, _, _) => Err(de::Error::missing_field("package")), + (None, Some(_), None, _) => Err(de::Error::missing_field("version")), + } + } + } + + deserializer.deserialize_any(Visitor) + } +} /// Represents the `package.metadata.component` section in `Cargo.toml`. #[derive(Default, Debug, Clone, Deserialize)] #[serde(default, deny_unknown_fields)] @@ -337,7 +443,7 @@ pub struct ComponentSection { /// The path to the WASI adapter to use. pub adapter: Option, /// The dependencies of the component. - pub dependencies: HashMap, + pub dependencies: HashMap, /// The registries to use for the component. pub registries: HashMap, /// The configuration for bindings generation. @@ -415,14 +521,14 @@ impl ComponentMetadata { } for dependency in dependencies.values_mut() { - if let Dependency::Local(path) = dependency { + if let Dependency::Local(path) = &mut dependency.0 { *path = manifest_dir.join(path.as_path()); } } } for dependency in section.dependencies.values_mut() { - if let Dependency::Local(path) = dependency { + if let Dependency::Local(path) = &mut dependency.0 { *path = manifest_dir.join(path.as_path()); } } diff --git a/src/registry.rs b/src/registry.rs index 9c8c2ca3..f83bd661 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -1,5 +1,5 @@ //! Module for interacting with component registries. -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; use anyhow::Result; use cargo_metadata::PackageId; @@ -10,7 +10,7 @@ use wasm_pkg_client::{ }; use wasm_pkg_core::{ lock::{LockFile, LockedPackage, LockedPackageVersion}, - resolver::{DependencyResolution, DependencyResolutionMap, DependencyResolver}, + resolver::{Dependency, DependencyResolution, DependencyResolutionMap, DependencyResolver}, }; use crate::metadata::ComponentMetadata; @@ -31,7 +31,7 @@ impl<'a> PackageDependencyResolution<'a> { /// /// Returns `Ok(None)` if the package is not a component package. pub async fn new( - client: Arc>, + client: CachingClient, metadata: &'a ComponentMetadata, lock_file: &LockFile, ) -> Result> { @@ -51,7 +51,7 @@ impl<'a> PackageDependencyResolution<'a> { } async fn resolve_target_deps( - client: Arc>, + client: CachingClient, metadata: &ComponentMetadata, lock_file: &LockFile, ) -> Result { @@ -63,14 +63,17 @@ impl<'a> PackageDependencyResolution<'a> { let mut resolver = DependencyResolver::new_with_client(client, Some(lock_file))?; for (name, dependency) in target_deps.iter() { - resolver.add_dependency(name, dependency).await?; + resolver.add_shallow_dependency(name, &dependency.0).await?; } + // for (name, dependency) in target_deps.iter() { + // resolver.add_dependency(name, &dependency.0).await?; + // } resolver.resolve().await } async fn resolve_deps( - client: Arc>, + client: CachingClient, metadata: &ComponentMetadata, lock_file: &LockFile, ) -> Result { @@ -81,7 +84,11 @@ impl<'a> PackageDependencyResolution<'a> { let mut resolver = DependencyResolver::new_with_client(client, Some(lock_file))?; for (name, dependency) in &metadata.section.dependencies { - resolver.add_dependency(name, dependency).await?; + if let Dependency::Local(path) = dependency.clone().0 { + resolver.add_shallow_dependency(name, &Dependency::Local(path)).await?; + + } + resolver.add_dependency(name, &dependency.0).await?; } resolver.resolve().await