diff --git a/Cargo.lock b/Cargo.lock index b36842a027e..754f1f254d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + [[package]] name = "any_ascii" version = "0.1.7" @@ -63,24 +69,24 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arbitrary" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" dependencies = [ "derive_arbitrary", ] [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -106,9 +112,24 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe" dependencies = [ - "bstr", + "bstr 0.2.17", "doc-comment", - "predicates", + "predicates 2.1.5", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_cmd" +version = "2.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" +dependencies = [ + "anstyle", + "bstr 1.4.0", + "doc-comment", + "predicates 3.0.1", "predicates-core", "predicates-tree", "wait-timeout", @@ -116,13 +137,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.66" +version = "0.1.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" +checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] @@ -224,9 +245,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -242,6 +263,18 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "bstr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +dependencies = [ + "memchr", + "once_cell", + "regex-automata", + "serde", +] + [[package]] name = "build-deps" version = "0.1.4" @@ -282,7 +315,7 @@ checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -308,9 +341,9 @@ checksum = "38fcc2979eff34a4b84e1cf9a1e3da42a7d44b3b690a40cdcb23e3d556cfb2e5" [[package]] name = "camino" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -332,7 +365,7 @@ checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "thiserror", @@ -358,7 +391,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn", + "syn 1.0.109", "tempfile", "toml", ] @@ -386,9 +419,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -400,9 +433,9 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", @@ -436,6 +469,16 @@ dependencies = [ "textwrap 0.16.0", ] +[[package]] +name = "clap-verbosity-flag" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0636f9c040082f8e161555a305f8cec1a1c2828b3d981c812b8c39f4ac00c42c" +dependencies = [ + "clap 3.2.23", + "log", +] + [[package]] name = "clap_derive" version = "3.2.18" @@ -446,7 +489,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -519,7 +562,7 @@ dependencies = [ "pretty_assertions", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "trybuild", ] @@ -576,9 +619,9 @@ checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "cooked-waker" @@ -824,9 +867,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359" +checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" dependencies = [ "csv-core", "itoa", @@ -850,7 +893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -861,9 +904,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" dependencies = [ "cc", "cxxbridge-flags", @@ -873,9 +916,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" dependencies = [ "cc", "codespan-reporting", @@ -883,31 +926,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.3", ] [[package]] name = "cxxbridge-flags" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" [[package]] name = "cxxbridge-macro" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] name = "darling" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -915,26 +958,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -945,18 +988,18 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "derive_arbitrary" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8beee4701e2e229e8098bbdecdca12449bc3e322f137d269182fa1291e20bd00" +checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1080,7 +1123,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1138,7 +1181,7 @@ checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1159,7 +1202,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1300,11 +1343,17 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" dependencies = [ "futures-channel", "futures-core", @@ -1317,9 +1366,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" dependencies = [ "futures-core", "futures-sink", @@ -1327,15 +1376,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" dependencies = [ "futures-core", "futures-task", @@ -1344,38 +1393,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" dependencies = [ "futures-channel", "futures-core", @@ -1432,13 +1481,13 @@ dependencies = [ [[package]] name = "ghost" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e0cd8a998937e25c6ba7cc276b96ec5cc3f4dc4ab5de9ede4fb152bdd5c5eb" +checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] @@ -1508,7 +1557,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn", + "syn 1.0.109", ] [[package]] @@ -1519,7 +1568,7 @@ checksum = "a755cc59cda2641ea3037b4f9f7ef40471c329f55c1fa2db6fa0bb7ae6c1f7ce" dependencies = [ "graphql_client_codegen", "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] @@ -1539,7 +1588,7 @@ checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1692,6 +1741,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "http-serde" version = "1.1.2" @@ -1728,9 +1783,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -1878,7 +1933,7 @@ checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1887,11 +1942,11 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340dd3d6102fa919bd20987024a6d84954c36ec691ac1efea37742ee983c8dd5" dependencies = [ - "assert_cmd", + "assert_cmd 1.0.8", "cc", "inline-c-macro", "lazy_static", - "predicates", + "predicates 2.1.5", "regex", "rustc_version 0.3.3", "target-lexicon 0.11.2", @@ -1956,10 +2011,11 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] @@ -1972,9 +2028,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -2027,6 +2083,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2063,9 +2130,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libfuzzer-sys" @@ -2139,7 +2206,7 @@ dependencies = [ "lazy_static", "libc", "regex", - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -2187,7 +2254,7 @@ dependencies = [ "proc-macro2", "proc-quote", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2261,9 +2328,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -2309,9 +2376,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "minisign" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce49953dd06a44e1034590bb619bfe8900c29500053c0c0f83e9260a34466aa5" +checksum = "b23ef13ff1d745b1e52397daaa247e333c607f3cff96d4df2b798dc252db974b" dependencies = [ "getrandom", "rpassword", @@ -2488,7 +2555,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2533,9 +2600,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.46" +version = "0.10.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2523381e46256e40930512c7fd25562b9eae4812cb52078f155e87217c9d1e" +checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2554,7 +2621,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2565,9 +2632,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.81" +version = "0.9.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67" +checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" dependencies = [ "autocfg", "cc", @@ -2578,9 +2645,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba683f1641c11041c59d5d93689187abcab3c1349dc6d9d70c550c9f9360802f" +checksum = "974465c5e83cf9df05c1e4137b271d29035c902e39e5ad4c1939837e22160af8" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2594,9 +2661,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -2681,11 +2748,12 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" dependencies = [ "digest", + "hmac", ] [[package]] @@ -2704,6 +2772,40 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pest_derive" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pest_meta" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -2721,7 +2823,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2796,17 +2898,29 @@ dependencies = [ "regex", ] +[[package]] +name = "predicates" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba7d6ead3e3966038f68caa9fc1f860185d95a793180bbcfe0d0da47b3961ed" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -2857,7 +2971,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -2880,9 +2994,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" dependencies = [ "unicode-ident", ] @@ -2897,7 +3011,7 @@ dependencies = [ "proc-macro2", "proc-quote-impl", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2928,7 +3042,7 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2944,13 +3058,26 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -2959,7 +3086,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2969,9 +3096,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -3022,6 +3164,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -3104,6 +3255,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rend" version = "0.4.0" @@ -3115,9 +3275,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" dependencies = [ "base64 0.21.0", "bytes", @@ -3196,7 +3356,7 @@ checksum = "ff26ed6c7c4dfc2aa9480b86a60e3c7233543a270a680e10758a507c5a4ce476" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3264,7 +3424,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -3281,9 +3441,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.9" +version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ "bitflags", "errno", @@ -3386,11 +3546,10 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "scrypt" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ - "hmac", "pbkdf2", "salsa20", "sha2", @@ -3490,9 +3649,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] @@ -3514,9 +3673,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] @@ -3553,13 +3712,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] @@ -3629,7 +3788,7 @@ checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3806,7 +3965,7 @@ dependencies = [ "quote", "serde", "serde_derive", - "syn", + "syn 1.0.109", ] [[package]] @@ -3822,7 +3981,7 @@ dependencies = [ "serde_derive", "serde_json", "sha1", - "syn", + "syn 1.0.109", ] [[package]] @@ -3856,7 +4015,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -3876,6 +4035,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8234ae35e70582bfa0f1fedffa6daa248e41dd045310b19800c4a36382c8f60" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tar" version = "0.4.38" @@ -3899,6 +4069,16 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + [[package]] name = "tempfile" version = "3.4.0" @@ -3953,9 +4133,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-generator" @@ -3973,7 +4153,7 @@ checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4006,22 +4186,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.3", ] [[package]] @@ -4096,7 +4276,7 @@ dependencies = [ "proc-macro2", "quote", "standback", - "syn", + "syn 1.0.109", ] [[package]] @@ -4165,7 +4345,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4220,15 +4400,57 @@ checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" dependencies = [ "indexmap", "toml_datetime", "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -4256,7 +4478,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4349,9 +4571,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "trybuild" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3115bddce1b5f52dd4b5e0ec8298a66ce733e4cc6759247dc2d1c11508ec38" +checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" dependencies = [ "basic-toml", "glob", @@ -4389,7 +4611,7 @@ checksum = "e60147782cc30833c05fba3bab1d9b5771b2685a2557672ac96fa5d154099c0e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4409,9 +4631,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -4491,6 +4713,21 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +[[package]] +name = "validator" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5" +dependencies = [ + "idna 0.2.3", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", +] + [[package]] name = "valuable" version = "0.1.0" @@ -4537,7 +4774,7 @@ dependencies = [ "tokio", "tracing", "typetag", - "webc", + "webc 5.0.0-rc.5", ] [[package]] @@ -4618,7 +4855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdeeb5c1170246de8425a3e123e7ef260dc05ba2b522a1d369fe2315376efea4" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.109", "wai-bindgen-gen-core", "wai-bindgen-gen-rust-wasm", ] @@ -4644,7 +4881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b3488ed88d4dd0e3bf85bad4e27dac6cb31aae5d122a5dda2424803c8dc863a" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.109", "wai-bindgen-gen-core", "wai-bindgen-gen-wasmer", ] @@ -4679,12 +4916,11 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -4698,6 +4934,65 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wapm-targz-to-pirita" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39980ef50ab15c47dbd9b26678f06ece2341178d82e25e21e4ac68310d1f2e63" +dependencies = [ + "anyhow", + "base64 0.13.1", + "flate2", + "json5", + "nuke-dir", + "rand 0.8.5", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "sha2", + "tar", + "toml", + "tracing", + "validator", + "wapm-toml 0.4.0", + "wasmer-registry 3.1.1", + "webc 4.1.1", +] + +[[package]] +name = "wapm-toml" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61b6d3b6a2fc171198e6378b3a9b38650e114298775a9e63401613abb6a10b3" +dependencies = [ + "anyhow", + "semver 1.0.17", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "serde_yaml 0.8.26", + "thiserror", + "toml", +] + +[[package]] +name = "wapm-toml" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994ef26447f3158955d2e3fca96021d1f1c47b830e2053569177673dca1447a9" +dependencies = [ + "anyhow", + "semver 1.0.17", + "serde", + "serde_cbor", + "serde_json", + "serde_yaml 0.9.19", + "thiserror", + "toml", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4746,7 +5041,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -4770,7 +5065,7 @@ checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4803,7 +5098,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4885,9 +5180,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f7d56227d910901ce12dfd19acc40c12687994dfb3f57c90690f80be946ec5" +checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" dependencies = [ "leb128", ] @@ -4992,7 +5287,7 @@ dependencies = [ "wasmer-middlewares", "wasmer-types", "wasmer-wasix", - "webc", + "webc 5.0.0-rc.5", ] [[package]] @@ -5012,7 +5307,7 @@ dependencies = [ "blake3", "criterion", "hex", - "rand", + "rand 0.8.5", "tempfile", "thiserror", "wasmer", @@ -5040,6 +5335,7 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "clap 3.2.23", + "clap-verbosity-flag", "colored 2.0.0", "dialoguer", "dirs", @@ -5052,7 +5348,6 @@ dependencies = [ "libc", "log", "minisign", - "nuke-dir", "object 0.30.3", "pathdiff", "prettytable-rs", @@ -5060,7 +5355,7 @@ dependencies = [ "reqwest", "rpassword", "rusqlite", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "sha2", @@ -5079,6 +5374,7 @@ dependencies = [ "virtual-fs", "virtual-net", "walkdir", + "wapm-targz-to-pirita", "wasm-coredump-builder", "wasmer", "wasmer-cache", @@ -5088,7 +5384,7 @@ dependencies = [ "wasmer-compiler-singlepass", "wasmer-emscripten", "wasmer-object", - "wasmer-registry", + "wasmer-registry 4.0.0", "wasmer-toml", "wasmer-types", "wasmer-vm", @@ -5097,7 +5393,8 @@ dependencies = [ "wasmer-wasm-interface", "wasmer-wast", "wasmparser 0.51.4", - "webc", + "webc 4.1.1", + "webc 5.0.0-rc.5", ] [[package]] @@ -5180,7 +5477,7 @@ dependencies = [ "rayon", "regex", "rustc_version 0.4.0", - "semver 1.0.16", + "semver 1.0.17", "smallvec", "target-lexicon 0.12.6", "wasmer-compiler", @@ -5215,7 +5512,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasmer-types", ] @@ -5239,10 +5536,10 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c4e7a2a3363ceeb2ee60371af9460748f2bf53569b58627f1f640284ab07778" dependencies = [ - "assert_cmd", + "assert_cmd 1.0.8", "cc", "lazy_static", - "predicates", + "predicates 2.1.5", "regex", "rustc_version 0.3.3", "target-lexicon 0.11.2", @@ -5266,6 +5563,7 @@ name = "wasmer-integration-tests-cli" version = "3.2.0-alpha.1" dependencies = [ "anyhow", + "assert_cmd 2.0.10", "derivative", "dirs", "flate2", @@ -5273,8 +5571,9 @@ dependencies = [ "insta", "md5", "object 0.30.3", + "predicates 2.1.5", "pretty_assertions", - "rand", + "rand 0.8.5", "reqwest", "serde", "tar", @@ -5305,6 +5604,39 @@ dependencies = [ "wasmer-types", ] +[[package]] +name = "wasmer-registry" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982286807bf43d2522a9f6139f9c2570df45d5a32148460226ccbc7edfb3ee13" +dependencies = [ + "anyhow", + "dirs", + "filetime", + "flate2", + "fs_extra", + "futures-util", + "graphql_client", + "hex", + "log", + "lzma-rs", + "regex", + "reqwest", + "semver 1.0.17", + "serde", + "serde_json", + "tar", + "tempdir", + "thiserror", + "tldextract", + "tokio", + "toml", + "url", + "wapm-toml 0.2.2", + "webc 3.0.1", + "whoami", +] + [[package]] name = "wasmer-registry" version = "4.0.0" @@ -5322,10 +5654,10 @@ dependencies = [ "lazy_static", "log", "lzma-rs", - "rand", + "rand 0.8.5", "regex", "reqwest", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "tar", @@ -5336,7 +5668,7 @@ dependencies = [ "toml", "url", "wasmer-toml", - "webc", + "webc 5.0.0-rc.5", "whoami", ] @@ -5362,7 +5694,7 @@ checksum = "4232db0aff83ed6208d541ddcf1bf72730673528be8c4fe13c6369060f6e05a7" dependencies = [ "anyhow", "indexmap", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_cbor", "serde_json", @@ -5435,7 +5767,7 @@ dependencies = [ "linked_hash_set", "once_cell", "pin-project", - "rand", + "rand 0.8.5", "reqwest", "serde", "serde_cbor", @@ -5444,10 +5776,13 @@ dependencies = [ "serde_yaml 0.8.26", "sha2", "shellexpand", + "tempfile", "term_size", "termios", "thiserror", "tokio", + "tower", + "tower-http", "tracing", "tracing-subscriber 0.2.25", "tracing-wasm", @@ -5467,7 +5802,7 @@ dependencies = [ "wasmer-wasix-types", "wcgi", "wcgi-host", - "webc", + "webc 5.0.0-rc.5", "weezl", "winapi", ] @@ -5595,9 +5930,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.101.1" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf2f22ef84ac5666544afa52f326f13e16f3d019d2e61e704fd8091c9358b130" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ "indexmap", "url", @@ -5605,12 +5940,12 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.52" +version = "0.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003f2e37b9b7caac949d388e185ecd9139f51441249a23880b0cf38e10cdf647" +checksum = "2dc17ae63836d010a2bf001c26a5fedbb9a05e5f71117fb63e0ab878bfbe1ca3" dependencies = [ "anyhow", - "wasmparser 0.101.1", + "wasmparser 0.102.0", ] [[package]] @@ -5633,23 +5968,23 @@ dependencies = [ [[package]] name = "wast" -version = "54.0.1" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d48d9d731d835f4f8dacbb8de7d47be068812cb9877f5c60d408858778d8d2a" +checksum = "4984d3e1406571f4930ba5cf79bd70f75f41d0e87e17506e0bd19b0e5d085f05" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.24.1", + "wasm-encoder 0.25.0", ] [[package]] name = "wat" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1db2e3ed05ea31243761439194bec3af6efbbaf87c4c8667fb879e4f23791a0" +checksum = "af2b53f4da14db05d32e70e9c617abdf6620c575bd5dd972b7400037b4df2091" dependencies = [ - "wast 54.0.1", + "wast 55.0.0", ] [[package]] @@ -5761,6 +6096,52 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef87e7b955d5d1feaa8697ae129f1a9ce8859e151574ad3baceae9413b48d2f0" +dependencies = [ + "anyhow", + "base64 0.13.1", + "indexmap", + "leb128", + "lexical-sort", + "memchr", + "memmap2", + "path-clean", + "rand 0.8.5", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "url", + "walkdir", +] + +[[package]] +name = "webc" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4af0f2f0b6bf2e4366375c3cd6635aef1a2eb66cd02454d79525aa7eecf0751" +dependencies = [ + "anyhow", + "base64 0.13.1", + "byteorder", + "indexmap", + "leb128", + "lexical-sort", + "memchr", + "path-clean", + "rand 0.8.5", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "url", + "walkdir", +] + [[package]] name = "webc" version = "5.0.0-rc.5" @@ -5777,7 +6158,7 @@ dependencies = [ "memmap2", "once_cell", "path-clean", - "rand", + "rand 0.8.5", "serde", "serde_cbor", "serde_json", @@ -5833,9 +6214,9 @@ checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "whoami" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" +checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" dependencies = [ "wasm-bindgen", "web-sys", @@ -5892,12 +6273,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -5911,24 +6292,24 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" @@ -5938,9 +6319,9 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" @@ -5950,9 +6331,9 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" @@ -5962,9 +6343,9 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" @@ -5974,15 +6355,15 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" @@ -5992,15 +6373,15 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "winnow" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345" +checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" dependencies = [ "memchr", ] diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 9e968ebe4d1..8d50d4ad73e 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -81,13 +81,13 @@ //! compilation-time and runtime performance, useful for development, //! * [`wasmer-compiler-llvm`] provides a deeply optimized executable //! code with the fastest runtime speed, ideal for production. -//! +//! //! * **Headless mode** — Once a WebAssembly module has been compiled, it //! is possible to serialize it in a file for example, and later execute //! it with Wasmer with headless mode turned on. Headless Wasmer has no //! compiler, which makes it more portable and faster to load. It's //! ideal for constrainted environments. -//! +//! //! * **Cross-compilation** — Most compilers support cross-compilation. It //! means it possible to pre-compile a WebAssembly module targetting a //! different architecture or platform and serialize it, to then run it diff --git a/lib/c-api/examples/assets/staticserver.webc b/lib/c-api/examples/assets/staticserver.webc new file mode 100644 index 00000000000..3cd41357ee2 Binary files /dev/null and b/lib/c-api/examples/assets/staticserver.webc differ diff --git a/lib/cache/src/cache.rs b/lib/cache/src/cache.rs index 340163db956..8d34f9cfb62 100644 --- a/lib/cache/src/cache.rs +++ b/lib/cache/src/cache.rs @@ -13,7 +13,7 @@ pub trait Cache { /// The deserialization error for the implementation type DeserializeError: Error + Send + Sync; - /// Loads a module using the provided [`Store`] and [`Hash`]. + /// Loads a module using the provided [`wasmer::Store`] and [`crate::Hash`]. /// /// # Safety /// This function is unsafe as the cache store could be tampered with. @@ -23,6 +23,6 @@ pub trait Cache { key: Hash, ) -> Result; - /// Store a [`Module`] into the cache with the given [`Hash`]. + /// Store a [`Module`] into the cache with the given [`crate::Hash`]. fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError>; } diff --git a/lib/cache/src/filesystem.rs b/lib/cache/src/filesystem.rs index 98259f94830..237390c61cc 100644 --- a/lib/cache/src/filesystem.rs +++ b/lib/cache/src/filesystem.rs @@ -31,6 +31,7 @@ use wasmer::{AsEngineRef, DeserializeError, Module, SerializeError}; /// Ok(()) /// } /// ``` +#[derive(Debug, Clone)] pub struct FileSystemCache { path: PathBuf, ext: Option, @@ -97,7 +98,7 @@ impl Cache for FileSystemCache { key: Hash, ) -> Result { let filename = if let Some(ref ext) = self.ext { - format!("{}.{}", key.to_string(), ext) + format!("{}.{}", key, ext) } else { key.to_string() }; @@ -113,7 +114,7 @@ impl Cache for FileSystemCache { fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { let filename = if let Some(ref ext) = self.ext { - format!("{}.{}", key.to_string(), ext) + format!("{}.{}", key, ext) } else { key.to_string() }; diff --git a/lib/cache/src/hash.rs b/lib/cache/src/hash.rs index 29e7fa7ce65..8c202ef6185 100644 --- a/lib/cache/src/hash.rs +++ b/lib/cache/src/hash.rs @@ -1,9 +1,12 @@ use crate::DeserializeError; -use std::str::FromStr; use std::string::ToString; +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; /// A hash used as a key when loading and storing modules in a -/// [`Cache`]. +/// [`crate::Cache`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] // Hash is made up of a 32 byte array pub struct Hash([u8; 32]); @@ -11,7 +14,7 @@ pub struct Hash([u8; 32]); impl Hash { /// Creates a new instance from 32 raw bytes. /// Does not perform any hashing. In order to create a hash from data, - /// use `Hash::generate`. + /// use [`Hash::generate()`]. pub fn new(bytes: [u8; 32]) -> Self { Self(bytes) } @@ -21,17 +24,19 @@ impl Hash { let hash = blake3::hash(bytes); Self::new(hash.into()) } - - pub(crate) fn to_array(self) -> [u8; 32] { - self.0 - } } -impl ToString for Hash { - /// Create the hexadecimal representation of the +impl Display for Hash { + /// Print the hexadecimal representation of the /// stored hash. - fn to_string(&self) -> String { - hex::encode(&self.to_array()) + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut buffer = [0_u8; 64]; + + hex::encode_to_slice(&self.0, &mut buffer) + .expect("Can never fail with a hard-coded buffer length"); + let s = std::str::from_utf8(&buffer).map_err(|_| fmt::Error)?; + + f.write_str(s) } } @@ -62,13 +67,16 @@ mod tests { use super::*; #[test] - fn hash_to_array_works() { + fn hash_is_displayed_as_hex() { let original = [ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, ]; let hash = Hash::new(original); - assert_eq!(hash.to_array(), original); + assert_eq!( + hash.to_string(), + "aabbccddeeff1265aabbccddeeff1265aabbccddeeff1265aabbccddeeff1265" + ); } } diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index bee665e1ba0..70b7eb42f88 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -26,16 +26,16 @@ required-features = ["headless"] [dependencies] wasmer = { version = "=3.2.0-alpha.1", path = "../api", default-features = false } -wasmer-compiler = { version = "=3.2.0-alpha.1", path = "../compiler", features = ["compiler", ] } +wasmer-compiler = { version = "=3.2.0-alpha.1", path = "../compiler", features = ["compiler"] } wasmer-compiler-cranelift = { version = "=3.2.0-alpha.1", path = "../compiler-cranelift", optional = true } wasmer-compiler-singlepass = { version = "=3.2.0-alpha.1", path = "../compiler-singlepass", optional = true } wasmer-compiler-llvm = { version = "=3.2.0-alpha.1", path = "../compiler-llvm", optional = true } -wasmer-emscripten = { version = "=3.2.0-alpha.1", path = "../emscripten", optional = true } +wasmer-emscripten = { version = "=3.2.0-alpha.1", path = "../emscripten" } wasmer-vm = { version = "=3.2.0-alpha.1", path = "../vm" } -wasmer-wasix = { version = "0.1.0", path = "../wasi", optional = true } +wasmer-wasix = { version = "0.1.0", path = "../wasi", features = ["logging", "webc_runner", "webc_runner_rt_wcgi", "webc_runner_rt_wasi", "webc_runner_rt_emscripten", "host-fs"] } wasmer-wasix-experimental-io-devices = { version = "0.1.0", path = "../wasi-experimental-io-devices", optional = true, features = ["link_external_libs"] } wasmer-wast = { version = "=3.2.0-alpha.1", path = "../../tests/lib/wast", optional = true } -wasmer-cache = { version = "=3.2.0-alpha.1", path = "../cache", optional = true } +wasmer-cache = { version = "=3.2.0-alpha.1", path = "../cache", features = ["blake3-pure"] } wasmer-types = { version = "=3.2.0-alpha.1", path = "../types", features = ["enable-serde"] } wasmer-registry = { version = "=4.0.0", path = "../registry" } wasmer-object = { version = "=3.2.0-alpha.1", path = "../object", optional = true } @@ -68,8 +68,10 @@ regex = "1.6.0" toml = "0.5.9" url = "2.3.1" libc = { version = "^0.2", default-features = false } -nuke-dir = { version = "0.1.0", optional = true } -webc = { version = "5.0.0-rc.5", optional = true } +webc = { version = "5.0.0-rc.5" } +# HACK(Michael-F-Bryan): Remove this once a new version of wapm-targz-to-pirita +# is published that doesn't have a public dependency on webc +webc_v4 = { version = "4", package = "webc" } isatty = "0.1.9" dialoguer = "0.10.2" tldextract = "0.6.0" @@ -88,11 +90,13 @@ pathdiff = "0.2.1" sha2 = "0.10.6" object = "0.30.0" wasm-coredump-builder = { version = "0.1.11" } -tracing = { version = "0.1", optional = true } -tracing-subscriber = { version = "0.3", features = [ "env-filter", "fmt" ], optional = true } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = [ "env-filter", "fmt" ] } +clap-verbosity-flag = "1" +wapm-targz-to-pirita = "0.1.7" [build-dependencies] -chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] } +chrono = { version = "^0.4", default-features = false, features = ["std", "clock"] } [target.'cfg(target_os = "linux")'.dependencies] unix_mode = "0.1.3" @@ -110,24 +114,10 @@ default = [ "wasmer-artifact-create", "static-artifact-create", "webc_runner", - "tracing", ] -cache = ["wasmer-cache"] -cache-blake3-pure = ["wasmer-cache/blake3-pure"] wast = ["wasmer-wast"] -wasi = ["wasmer-wasix", "host-net"] host-net = [ "virtual-net/host-net" ] -emscripten = ["wasmer-emscripten"] wat = ["wasmer/wat"] -webc_runner = [ - "wasi", - "wasmer-wasix/webc_runner", - "wasmer-wasix/webc_runner_rt_wasi", - "wasmer-wasix/webc_runner_rt_wcgi", - "wasmer-wasix/webc_runner_rt_emscripten", - "nuke-dir", - "webc" -] compiler = [ "wasmer-compiler/translator", "wasmer-compiler/compiler", @@ -155,7 +145,6 @@ static-artifact-load = ["compiler", "wasmer/static-artifact-load", "wasmer-compiler/static-artifact-load", ] - experimental-io-devices = [ "wasmer-wasix-experimental-io-devices", "wasi" @@ -172,11 +161,9 @@ llvm = [ "wasmer-compiler-llvm", "compiler", ] -debug = ["tracing", "wasmer-wasix/logging"] disable-all-logging = ["wasmer-wasix/disable-all-logging", "log/release_max_level_off"] headless = [] headless-minimal = ["headless", "disable-all-logging", "wasi"] -tracing = [ "dep:tracing", "tracing-subscriber" ] # Optional enable-serde = [ @@ -186,6 +173,14 @@ enable-serde = [ "wasmer-wasix/enable-serde", ] +# Deprecated. These feature flags no longer protect anything. +cache = [] +cache-blake3-pure = [] +debug = [] +wasi = [] +emscripten = [] +webc_runner = [] + [target.'cfg(target_os = "windows")'.dependencies] colored = "2.0.0" diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 5b65f312951..86104ec84da 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -9,7 +9,8 @@ use crate::commands::CreateExe; #[cfg(feature = "wast")] use crate::commands::Wast; use crate::commands::{ - Add, Cache, Config, Init, Inspect, List, Login, Publish, Run, SelfUpdate, Validate, Whoami, + Add, Cache, Config, Init, Inspect, List, Login, Publish, Run, RunUnstable, SelfUpdate, + Validate, Whoami, }; #[cfg(feature = "static-artifact-create")] use crate::commands::{CreateObj, GenCHeader}; @@ -160,6 +161,9 @@ enum WasmerCLIOptions { /// Add a WAPM package's bindings to your application. Add(Add), + + /// (unstable) Run a WebAssembly file or WEBC container. + RunUnstable(RunUnstable), } impl WasmerCLIOptions { @@ -189,6 +193,7 @@ impl WasmerCLIOptions { Self::Binfmt(binfmt) => binfmt.execute(), Self::Whoami(whoami) => whoami.execute(), Self::Add(install) => install.execute(), + Self::RunUnstable(run2) => run2.execute(), } } } @@ -241,8 +246,10 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { } else { match command.unwrap_or(&"".to_string()).as_ref() { "add" | "cache" | "compile" | "config" | "create-obj" | "create-exe" | "help" - | "gen-c-header" | "inspect" | "init" | "run" | "self-update" | "validate" | "wast" - | "binfmt" | "list" | "login" | "publish" => WasmerCLIOptions::parse(), + | "gen-c-header" | "inspect" | "init" | "run" | "run-unstable" | "self-update" + | "validate" | "wast" | "binfmt" | "list" | "login" | "publish" => { + WasmerCLIOptions::parse() + } _ => { WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| { match e.kind() { diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index 595ecc81530..337d825ed8c 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -18,6 +18,7 @@ mod list; mod login; mod publish; mod run; +mod run_unstable; mod self_update; mod validate; #[cfg(feature = "wast")] @@ -34,7 +35,7 @@ pub use create_exe::*; pub use wast::*; pub use { add::*, cache::*, config::*, init::*, inspect::*, list::*, login::*, publish::*, run::*, - self_update::*, validate::*, whoami::*, + run_unstable::RunUnstable, self_update::*, validate::*, whoami::*, }; #[cfg(feature = "static-artifact-create")] pub use {create_obj::*, gen_c_header::*}; diff --git a/lib/cli/src/commands/add.rs b/lib/cli/src/commands/add.rs index 885dc1f9b38..cc66722ef0d 100644 --- a/lib/cli/src/commands/add.rs +++ b/lib/cli/src/commands/add.rs @@ -111,11 +111,8 @@ fn lookup_bindings_for_package( match all_bindings.iter().find(|b| b.language == *language) { Some(b) => { - #[cfg(feature = "debug")] - { - let Bindings { url, generator, .. } = b; - log::debug!("Found {pkg} bindings generated by {generator} at {url}"); - } + let Bindings { url, generator, .. } = b; + log::debug!("Found {pkg} bindings generated by {generator} at {url}"); Ok(b.clone()) } diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 3ea8882054a..cabb2edfb38 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -2012,7 +2012,6 @@ mod http_fetch { .context("Could not lookup wasmer repository on Github.")?; if response.status_code() != StatusCode::new(200) { - #[cfg(feature = "debug")] log::warn!( "Warning: Github API replied with non-200 status code: {}. Response: {}", response.status_code(), @@ -2134,7 +2133,6 @@ mod http_fetch { let download_path = download_tempdir.path().join(&filename); let mut file = std::fs::File::create(&download_path)?; - #[cfg(feature = "debug")] log::debug!( "Downloading {} to {}", browser_download_url, diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 610637558b1..10c9fb9cfb7 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -1,6 +1,4 @@ -#[cfg(feature = "cache")] use crate::common::get_cache_dir; -#[cfg(feature = "debug")] use crate::logging; use crate::package_source::PackageSource; use crate::store::{CompilerType, StoreOptions}; @@ -11,26 +9,20 @@ use clap::Parser; use std::io::Write; use std::ops::Deref; use std::path::PathBuf; -#[cfg(feature = "cache")] use std::str::FromStr; use std::{fs::File, net::SocketAddr}; -#[cfg(feature = "emscripten")] use wasmer::FunctionEnv; use wasmer::*; -#[cfg(feature = "cache")] use wasmer_cache::{Cache, FileSystemCache, Hash}; use wasmer_types::Type as ValueType; -#[cfg(feature = "webc_runner")] use wasmer_wasix::runners::{Runner, WapmContainer}; -#[cfg(feature = "wasi")] mod wasi; -#[cfg(feature = "wasi")] -use wasi::Wasi; +pub(crate) use wasi::Wasi; /// The options for the `wasmer run` subcommand, runs either a package, URL or a file -#[derive(Debug, Parser, Clone, Default)] +#[derive(Debug, Parser, Clone)] pub struct Run { /// File to run #[clap(name = "SOURCE", parse(try_from_str))] @@ -48,7 +40,6 @@ pub struct RunWithoutFile { pub(crate) force_install: bool, /// Disable the cache - #[cfg(feature = "cache")] #[clap(long = "disable-cache")] pub(crate) disable_cache: bool, @@ -65,7 +56,6 @@ pub struct RunWithoutFile { /// A prehashed string, used to speed up start times by avoiding hashing the /// wasm module. If the specified hash is not found, Wasmer will hash the module /// as if no `cache-key` argument was passed. - #[cfg(feature = "cache")] #[clap(long = "cache-key", hide = true)] pub(crate) cache_key: Option, @@ -73,7 +63,6 @@ pub struct RunWithoutFile { pub(crate) store: StoreOptions, // TODO: refactor WASI structure to allow shared options with Emscripten - #[cfg(feature = "wasi")] #[clap(flatten)] pub(crate) wasi: Wasi, @@ -83,15 +72,12 @@ pub struct RunWithoutFile { pub(crate) enable_experimental_io_devices: bool, /// Enable debug output - #[cfg(feature = "debug")] #[clap(long = "debug", short = 'd')] pub(crate) debug: bool, - #[cfg(feature = "debug")] #[clap(long = "verbose")] pub(crate) verbose: Option, - #[cfg(feature = "webc_runner")] #[clap(flatten)] pub(crate) wcgi: WcgiOptions, @@ -131,39 +117,40 @@ impl RunWithPathBuf { self_clone.command_name.as_deref(), )?; - #[cfg(feature = "wasi")] - { - // See https://github.com/wasmerio/wasmer/issues/3492 - - // we need IndexMap to have a stable ordering for the [fs] mapping, - // otherwise overlapping filesystem mappings might not work - // since we want to control the order of mounting directories from the - // wasmer.toml file - let default = indexmap::IndexMap::default(); - let fs = manifest.fs.as_ref().unwrap_or(&default); - for (alias, real_dir) in fs.iter() { - let real_dir = self_clone.path.join(&real_dir); - if !real_dir.exists() { - #[cfg(feature = "debug")] - if self_clone.debug { - println!( - "warning: cannot map {alias:?} to {}: directory does not exist", - real_dir.display() - ); - } - continue; + // See https://github.com/wasmerio/wasmer/issues/3492 - + // we need IndexMap to have a stable ordering for the [fs] mapping, + // otherwise overlapping filesystem mappings might not work + // since we want to control the order of mounting directories from the + // wasmer.toml file + let default = indexmap::IndexMap::default(); + let fs = manifest.fs.as_ref().unwrap_or(&default); + for (alias, real_dir) in fs.iter() { + let real_dir = self_clone.path.join(&real_dir); + if !real_dir.exists() { + #[cfg(feature = "debug")] + if self_clone.debug { + println!( + "warning: cannot map {alias:?} to {}: directory does not exist", + real_dir.display() + ); } - - self_clone.options.wasi.map_dir(alias, real_dir.clone()); + continue; } + + self_clone.options.wasi.map_dir(alias, real_dir.clone()); } self_clone.path = pathbuf; } - #[cfg(feature = "debug")] if self.debug { - logging::set_up_logging(self_clone.verbose.unwrap_or(0)).unwrap(); + let level = match self_clone.verbose { + Some(1) => log::LevelFilter::Debug, + Some(_) | None => log::LevelFilter::Trace, + }; + logging::set_up_logging(level); } + let invoke_res = self_clone.inner_execute().with_context(|| { format!( "failed to run `{}`{}", @@ -250,7 +237,12 @@ impl RunWithPathBuf { .map_err(|e| anyhow!("{}", e))?; env.as_mut(&mut store).set_data( &emscripten_globals.data, - self.wasi.mapped_dirs.clone().into_iter().collect(), + self.wasi + .mapped_dirs + .clone() + .into_iter() + .map(|d| (d.guest, d.host)) + .collect(), ); let import_object = generate_emscripten_env(&mut store, &env, &mut emscripten_globals); @@ -416,8 +408,8 @@ impl RunWithPathBuf { .store(store) .addr(self.wcgi.addr) .envs(self.wasi.env_vars.clone()) - .map_directories(self.wasi.mapped_dirs.iter().map(|(g, h)| (h, g))); - if self.wcgi.forward_host_env { + .map_directories(self.wasi.mapped_dirs.clone()); + if self.wasi.forward_host_env { runner.config().forward_host_env(); } if runner.can_run_command(id, command).unwrap_or(false) { @@ -714,16 +706,12 @@ pub(crate) struct WcgiOptions { /// The address to serve on. #[clap(long, short, env, default_value_t = ([127, 0, 0, 1], 8000).into())] pub(crate) addr: SocketAddr, - /// Forward all host env variables to the wcgi task. - #[clap(long)] - pub(crate) forward_host_env: bool, } impl Default for WcgiOptions { fn default() -> Self { Self { addr: ([127, 0, 0, 1], 8000).into(), - forward_host_env: false, } } } diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 6af4b3c39dc..8f4d27b6b02 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -1,19 +1,19 @@ use crate::utils::{parse_envvar, parse_mapdir}; use anyhow::Result; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; -use std::{collections::BTreeSet, path::Path}; -use virtual_fs::FileSystem; -use virtual_fs::{DeviceFile, PassthruFileSystem, RootFileSystemBuilder}; +use std::{ + collections::{BTreeSet, HashMap}, + path::{Path, PathBuf}, + sync::Arc, +}; +use virtual_fs::{DeviceFile, FileSystem, PassthruFileSystem, RootFileSystemBuilder}; use wasmer::{AsStoreMut, Instance, Module, RuntimeError, Value}; -use wasmer_wasix::os::tty_sys::SysTty; -use wasmer_wasix::os::TtyBridge; -use wasmer_wasix::runtime::task_manager::tokio::TokioTaskManager; -use wasmer_wasix::types::__WASI_STDIN_FILENO; use wasmer_wasix::{ - default_fs_backing, get_wasi_versions, PluggableRuntime, WasiEnv, WasiError, WasiFunctionEnv, - WasiVersion, + default_fs_backing, get_wasi_versions, + os::{tty_sys::SysTty, TtyBridge}, + runners::MappedDirectory, + runtime::task_manager::tokio::TokioTaskManager, + types::__WASI_STDIN_FILENO, + PluggableRuntime, WasiEnv, WasiEnvBuilder, WasiError, WasiFunctionEnv, WasiVersion, }; use clap::Parser; @@ -31,7 +31,7 @@ pub struct Wasi { name = "GUEST_DIR:HOST_DIR", parse(try_from_str = parse_mapdir), )] - pub(crate) mapped_dirs: Vec<(String, PathBuf)>, + pub(crate) mapped_dirs: Vec, /// Pass custom environment variables #[clap( @@ -41,6 +41,10 @@ pub struct Wasi { )] pub(crate) env_vars: Vec<(String, String)>, + /// Forward all host env variables to the wcgi task. + #[clap(long, env)] + pub(crate) forward_host_env: bool, + /// List of other containers this module depends on #[clap(long = "use", name = "USE")] uses: Vec, @@ -90,7 +94,10 @@ pub struct Wasi { #[allow(dead_code)] impl Wasi { pub fn map_dir(&mut self, alias: &str, target_on_disk: PathBuf) { - self.mapped_dirs.push((alias.to_string(), target_on_disk)); + self.mapped_dirs.push(MappedDirectory { + guest: alias.to_string(), + host: target_on_disk, + }); } pub fn set_env(&mut self, key: &str, value: &str) { @@ -113,15 +120,14 @@ impl Wasi { get_wasi_versions(module, false).is_some() } - /// Helper function for instantiating a module with Wasi imports for the `Run` command. - pub fn instantiate( + pub fn prepare( &self, store: &mut impl AsStoreMut, module: &Module, program_name: String, args: Vec, - ) -> Result<(WasiFunctionEnv, Instance)> { - let args = args.iter().cloned().map(|arg| arg.into_bytes()); + ) -> Result { + let args = args.into_iter().map(|arg| arg.into_bytes()); let map_commands = self .map_commands @@ -163,12 +169,13 @@ impl Wasi { if !self.mapped_dirs.is_empty() { let fs_backing: Arc = Arc::new(PassthruFileSystem::new(default_fs_backing())); - for (src, dst) in self.mapped_dirs.clone() { - let src = match src.starts_with('/') { - true => src, - false => format!("/{}", src), + for MappedDirectory { host, guest } in self.mapped_dirs.clone() { + let host = if !host.is_absolute() { + Path::new("/").join(host) + } else { + host }; - root_fs.mount(PathBuf::from(src), &fs_backing, dst)?; + root_fs.mount(guest.into(), &fs_backing, host)?; } } @@ -182,7 +189,11 @@ impl Wasi { builder .fs(default_fs_backing()) .preopen_dirs(self.pre_opened_directories.clone())? - .map_dirs(self.mapped_dirs.clone())? + .map_dirs( + self.mapped_dirs + .iter() + .map(|d| (d.guest.clone(), d.host.clone())), + )? }; if self.http_client { @@ -198,6 +209,18 @@ impl Wasi { } } + Ok(builder) + } + + /// Helper function for instantiating a module with Wasi imports for the `Run` command. + pub fn instantiate( + &self, + store: &mut impl AsStoreMut, + module: &Module, + program_name: String, + args: Vec, + ) -> Result<(WasiFunctionEnv, Instance)> { + let builder = self.prepare(store, module, program_name, args)?; let (instance, wasi_env) = builder.instantiate(module.clone(), store)?; Ok((wasi_env, instance)) } diff --git a/lib/cli/src/commands/run_unstable.rs b/lib/cli/src/commands/run_unstable.rs new file mode 100644 index 00000000000..0bdda35bbc1 --- /dev/null +++ b/lib/cli/src/commands/run_unstable.rs @@ -0,0 +1,617 @@ +#![allow(missing_docs, unused)] + +use std::{ + collections::BTreeMap, + fmt::Display, + fs::File, + io::{ErrorKind, LineWriter, Read, Write}, + net::SocketAddr, + path::{Path, PathBuf}, + str::FromStr, + sync::Mutex, + time::{Duration, SystemTime}, +}; + +use anyhow::{Context, Error}; +use clap::Parser; +use clap_verbosity_flag::WarnLevel; +use sha2::{Digest, Sha256}; +use tempfile::NamedTempFile; +use url::Url; +use wapm_targz_to_pirita::FileMap; +use wasmer::{ + DeserializeError, Function, Imports, Instance, Module, Store, Type, TypedFunction, Value, +}; +use wasmer_cache::Cache; +use wasmer_compiler::ArtifactBuild; +use wasmer_registry::Package; +use wasmer_wasix::runners::{MappedDirectory, Runner, WapmContainer}; +use webc::metadata::Manifest; +use webc_v4::DirOrFile; + +use crate::{ + store::StoreOptions, + wasmer_home::{DownloadCached, ModuleCache, WasmerHome}, +}; + +/// The unstable `wasmer run` subcommand. +#[derive(Debug, Parser)] +pub struct RunUnstable { + #[clap(flatten)] + verbosity: clap_verbosity_flag::Verbosity, + #[clap(flatten)] + wasmer_home: WasmerHome, + #[clap(flatten)] + store: StoreOptions, + #[clap(flatten)] + wasi: crate::commands::run::Wasi, + #[clap(flatten)] + wcgi: WcgiOptions, + /// The function or command to invoke. + #[clap(short, long, aliases = &["command", "invoke"])] + entrypoint: Option, + /// Generate a coredump at this path if a WebAssembly trap occurs + #[clap(name = "COREDUMP PATH", long, parse(from_os_str))] + coredump_on_trap: Option, + /// The file, URL, or package to run. + #[clap(value_parser = PackageSource::infer)] + input: PackageSource, + /// Command-line arguments passed to the package + args: Vec, +} + +impl RunUnstable { + pub fn execute(&self) -> Result<(), Error> { + crate::logging::set_up_logging(self.verbosity.log_level_filter()); + + let target = self + .input + .resolve_target(&self.wasmer_home) + .with_context(|| format!("Unable to resolve \"{}\"", self.input))?; + + let (mut store, _) = self.store.get_store()?; + + let mut cache = self.wasmer_home.module_cache(); + let result = match target.load(&mut cache, &store)? { + ExecutableTarget::WebAssembly(wasm) => self.execute_wasm(&target, &wasm, &mut store), + ExecutableTarget::Webc(container) => self.execute_webc(&target, &container, &mut store), + }; + + if let Err(e) = &result { + if let Some(coredump) = &self.coredump_on_trap { + if let Err(e) = generate_coredump(e, target.path(), coredump) { + tracing::warn!( + error = &*e as &dyn std::error::Error, + coredump_path=%coredump.display(), + "Unable to generate a coredump", + ); + } + } + } + + result + } + + fn execute_wasm( + &self, + target: &TargetOnDisk, + module: &Module, + store: &mut Store, + ) -> Result<(), Error> { + if wasmer_emscripten::is_emscripten_module(module) { + self.execute_emscripten_module() + } else if wasmer_wasix::is_wasi_module(module) || wasmer_wasix::is_wasix_module(module) { + self.execute_wasi_module(target.path(), module, store) + } else { + self.execute_pure_wasm_module(module, store) + } + } + + #[tracing::instrument(skip_all)] + fn execute_webc( + &self, + target: &TargetOnDisk, + container: &WapmContainer, + store: &mut Store, + ) -> Result<(), Error> { + let id = match self.entrypoint.as_deref() { + Some(cmd) => cmd, + None => infer_webc_entrypoint(container.manifest())?, + }; + let command = container + .manifest() + .commands + .get(id) + .with_context(|| format!("Unable to get metadata for the \"{id}\" command"))?; + + // TODO(Michael-F-Bryan): Refactor the wasmer_wasi::runners::Runner + // trait So we can check whether a command is supported by a runner + // without needing to go through and instantiate each one. + + let (store, _compiler_type) = self.store.get_store()?; + let mut runner = wasmer_wasix::runners::wasi::WasiRunner::new(store) + .with_args(self.args.clone()) + .with_envs(self.wasi.env_vars.clone()) + .with_mapped_directories(self.wasi.mapped_dirs.clone()); + if self.wasi.forward_host_env { + runner.set_forward_host_env(); + } + if runner.can_run_command(id, command).unwrap_or(false) { + return runner.run_cmd(container, id).context("WASI runner failed"); + } + + let (store, _compiler_type) = self.store.get_store()?; + let mut runner = wasmer_wasix::runners::emscripten::EmscriptenRunner::new(store); + runner.set_args(self.args.clone()); + if runner.can_run_command(id, command).unwrap_or(false) { + return runner + .run_cmd(container, id) + .context("Emscripten runner failed"); + } + + let mut runner = wasmer_wasix::runners::wcgi::WcgiRunner::new(id); + let (store, _compiler_type) = self.store.get_store()?; + runner + .config() + .args(self.args.clone()) + .store(store) + .addr(self.wcgi.addr) + .envs(self.wasi.env_vars.clone()) + .map_directories(self.wasi.mapped_dirs.clone()) + .callbacks(Callbacks::default()); + if self.wasi.forward_host_env { + runner.config().forward_host_env(); + } + if runner.can_run_command(id, command).unwrap_or(false) { + return runner.run_cmd(container, id).context("WCGI runner failed"); + } + + anyhow::bail!( + "Unable to find a runner that supports \"{}\"", + command.runner + ); + } + + #[tracing::instrument(skip_all)] + fn execute_pure_wasm_module(&self, module: &Module, store: &mut Store) -> Result<(), Error> { + let imports = Imports::default(); + let instance = Instance::new(store, module, &imports) + .context("Unable to instantiate the WebAssembly module")?; + + let entrypoint = match &self.entrypoint { + Some(entry) => { + instance.exports + .get_function(entry) + .with_context(|| format!("The module doesn't contain a \"{entry}\" function"))? + }, + None => { + instance.exports.get_function("_start") + .context("The module doesn't contain a \"_start\" function. Either implement it or specify an entrypoint function.")? + } + }; + + let return_values = invoke_function(&instance, store, entrypoint, &self.args)?; + + println!( + "{}", + return_values + .iter() + .map(|val| val.to_string()) + .collect::>() + .join(" ") + ); + + Ok(()) + } + + #[tracing::instrument(skip_all)] + fn execute_wasi_module( + &self, + wasm_path: &Path, + module: &Module, + store: &mut Store, + ) -> Result<(), Error> { + let program_name = wasm_path.display().to_string(); + let builder = self + .wasi + .prepare(store, module, program_name, self.args.clone())?; + + builder.run_with_store(module.clone(), store)?; + Ok(()) + } + + #[tracing::instrument(skip_all)] + fn execute_emscripten_module(&self) -> Result<(), Error> { + todo!() + } +} + +fn invoke_function( + instance: &Instance, + store: &mut Store, + func: &Function, + args: &[String], +) -> Result, Error> { + let func_ty = func.ty(store); + let required_arguments = func_ty.params().len(); + let provided_arguments = args.len(); + + anyhow::ensure!( + required_arguments == provided_arguments, + "Function expected {} arguments, but received {}", + required_arguments, + provided_arguments, + ); + + let invoke_args = args + .iter() + .zip(func_ty.params().iter()) + .map(|(arg, param_type)| { + parse_value(arg, *param_type) + .with_context(|| format!("Unable to convert {arg:?} to {param_type:?}")) + }) + .collect::, _>>()?; + + let return_values = func.call(store, &invoke_args)?; + + Ok(return_values) +} + +fn parse_value(s: &str, ty: wasmer_types::Type) -> Result { + let value = match ty { + Type::I32 => Value::I32(s.parse()?), + Type::I64 => Value::I64(s.parse()?), + Type::F32 => Value::F32(s.parse()?), + Type::F64 => Value::F64(s.parse()?), + Type::V128 => Value::V128(s.parse()?), + _ => anyhow::bail!("There is no known conversion from {s:?} to {ty:?}"), + }; + Ok(value) +} + +fn infer_webc_entrypoint(manifest: &Manifest) -> Result<&str, Error> { + if let Some(entrypoint) = manifest.entrypoint.as_deref() { + return Ok(entrypoint); + } + + let commands: Vec<_> = manifest.commands.keys().collect(); + + match commands.as_slice() { + [] => anyhow::bail!("The WEBC file doesn't contain any executable commands"), + [one] => Ok(one.as_str()), + [..] => { + anyhow::bail!( + "Unable to determine the WEBC file's entrypoint. Please choose one of {commands:?}" + ); + } + } +} + +fn compile_directory_to_webc(dir: &Path) -> Result, Error> { + let mut files = BTreeMap::new(); + load_files_from_disk(&mut files, dir, dir)?; + + let wasmer_toml = webc_v4::DirOrFile::File("wasmer.toml".into()); + if let Some(toml_data) = files.remove(&wasmer_toml) { + // HACK(Michael-F-Bryan): The version of wapm-targz-to-pirita we are + // using doesn't know we renamed "wapm.toml" to "wasmer.toml", so we + // manually patch things up if people have already migrated their + // projects. + files + .entry(DirOrFile::File("wapm.toml".into())) + .or_insert(toml_data); + } + + let functions = wapm_targz_to_pirita::TransformManifestFunctions::default(); + wapm_targz_to_pirita::generate_webc_file(files, &dir.to_path_buf(), None, &functions) +} + +fn load_files_from_disk(files: &mut FileMap, dir: &Path, base: &Path) -> Result<(), Error> { + let entries = dir + .read_dir() + .with_context(|| format!("Unable to read the contents of \"{}\"", dir.display()))?; + + for entry in entries { + let path = entry?.path(); + let relative_path = path.strip_prefix(base)?.to_path_buf(); + + if path.is_dir() { + load_files_from_disk(files, &path, base)?; + files.insert(webc_v4::DirOrFile::Dir(relative_path), Vec::new()); + } else if path.is_file() { + let data = std::fs::read(&path) + .with_context(|| format!("Unable to read \"{}\"", path.display()))?; + files.insert(webc_v4::DirOrFile::File(relative_path), data); + } + } + Ok(()) +} + +#[derive(Debug, Clone, PartialEq)] +enum PackageSource { + File(PathBuf), + Dir(PathBuf), + Package(Package), + Url(Url), +} + +impl PackageSource { + fn infer(s: &str) -> Result { + let path = Path::new(s); + if path.is_file() { + return Ok(PackageSource::File(path.to_path_buf())); + } else if path.is_dir() { + return Ok(PackageSource::Dir(path.to_path_buf())); + } + + if let Ok(url) = Url::parse(s) { + return Ok(PackageSource::Url(url)); + } + + if let Ok(pkg) = Package::from_str(s) { + return Ok(PackageSource::Package(pkg)); + } + + Err(anyhow::anyhow!( + "Unable to resolve \"{s}\" as a URL, package name, or file on disk" + )) + } + + /// Try to resolve the [`PackageSource`] to an artifact on disk. + /// + /// This will try to automatically download and cache any resources from the + /// internet. + fn resolve_target(&self, home: &impl DownloadCached) -> Result { + match self { + PackageSource::File(path) => TargetOnDisk::from_file(path.clone()), + PackageSource::Dir(d) => Ok(TargetOnDisk::Directory(d.clone())), + PackageSource::Package(pkg) => { + let cached = home.download_package(pkg)?; + Ok(TargetOnDisk::Webc(cached)) + } + PackageSource::Url(url) => { + let cached = home.download_url(url)?; + Ok(TargetOnDisk::Webc(cached)) + } + } + } +} + +impl Display for PackageSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PackageSource::File(path) | PackageSource::Dir(path) => write!(f, "{}", path.display()), + PackageSource::Package(p) => write!(f, "{p}"), + PackageSource::Url(u) => write!(f, "{u}"), + } + } +} + +/// A file/directory on disk that will be executed. +/// +/// Depending on the type of target and the command-line arguments, this might +/// be something the user passed in manually or something that was automatically +/// saved to `$WASMER_HOME` for caching purposes. +#[derive(Debug, Clone)] +enum TargetOnDisk { + WebAssemblyBinary(PathBuf), + Wat(PathBuf), + Webc(PathBuf), + Directory(PathBuf), + Artifact(PathBuf), +} + +impl TargetOnDisk { + fn from_file(path: PathBuf) -> Result { + // Normally the first couple hundred bytes is enough to figure + // out what type of file this is. + let mut buffer = [0_u8; 512]; + + let mut f = File::open(&path) + .with_context(|| format!("Unable to open \"{}\" for reading", path.display(),))?; + let bytes_read = f.read(&mut buffer)?; + + let leading_bytes = &buffer[..bytes_read]; + + if wasmer::is_wasm(leading_bytes) { + Ok(TargetOnDisk::WebAssemblyBinary(path)) + } else if webc::detect(leading_bytes).is_ok() { + Ok(TargetOnDisk::Webc(path)) + } else if ArtifactBuild::is_deserializable(leading_bytes) { + Ok(TargetOnDisk::Artifact(path)) + } else if path.extension() == Some("wat".as_ref()) { + Ok(TargetOnDisk::Wat(path)) + } else { + anyhow::bail!("Unable to determine how to execute \"{}\"", path.display()); + } + } + + fn path(&self) -> &Path { + match self { + TargetOnDisk::WebAssemblyBinary(p) + | TargetOnDisk::Webc(p) + | TargetOnDisk::Wat(p) + | TargetOnDisk::Directory(p) + | TargetOnDisk::Artifact(p) => p, + } + } + + fn load(&self, cache: &mut ModuleCache, store: &Store) -> Result { + match self { + TargetOnDisk::Webc(webc) => { + // As an optimisation, try to use the mmapped version first. + if let Ok(container) = WapmContainer::from_path(webc.clone()) { + return Ok(ExecutableTarget::Webc(container)); + } + + // Otherwise, fall back to the version that reads everything + // into memory. + let bytes = std::fs::read(webc) + .with_context(|| format!("Unable to read \"{}\"", webc.display()))?; + let container = WapmContainer::from_bytes(bytes.into())?; + + Ok(ExecutableTarget::Webc(container)) + } + TargetOnDisk::Directory(dir) => { + // FIXME: Runners should be able to load directories directly + // instead of needing to compile to a WEBC file. + let webc = compile_directory_to_webc(dir).with_context(|| { + format!("Unable to bundle \"{}\" as a WEBC package", dir.display()) + })?; + let container = WapmContainer::from_bytes(webc.into()) + .context("Unable to parse the generated WEBC file")?; + + Ok(ExecutableTarget::Webc(container)) + } + TargetOnDisk::WebAssemblyBinary(path) => { + let wasm = std::fs::read(&path) + .with_context(|| format!("Unable to read \"{}\"", path.display()))?; + let module = compile_wasm_cached(path, &wasm, cache, store)?; + Ok(ExecutableTarget::WebAssembly(module)) + } + TargetOnDisk::Wat(path) => { + let wat = std::fs::read(path) + .with_context(|| format!("Unable to read \"{}\"", path.display()))?; + let wasm = + wasmer::wat2wasm(&wat).context("Unable to convert the WAT to WebAssembly")?; + + let module = compile_wasm_cached(path, &wasm, cache, store)?; + Ok(ExecutableTarget::WebAssembly(module)) + } + TargetOnDisk::Artifact(artifact) => { + let module = unsafe { + Module::deserialize_from_file(store, artifact) + .context("Unable to deserialize the pre-compiled module")? + }; + Ok(ExecutableTarget::WebAssembly(module)) + } + } + } +} + +fn compile_wasm_cached( + path: &Path, + wasm: &[u8], + cache: &mut ModuleCache, + store: &Store, +) -> Result { + let hash = wasmer_cache::Hash::generate(wasm); + + unsafe { + match cache.load(store, hash) { + Ok(m) => { + tracing::debug!(%hash, "Reusing a cached module"); + return Ok(m); + } + Err(DeserializeError::Io(e)) if e.kind() == ErrorKind::NotFound => {} + Err(error) => { + tracing::warn!( + %hash, + error=&error as &dyn std::error::Error, + wasm_path=%path.display(), + "Unable to deserialize the cached module", + ); + } + } + } + + let mut module = Module::new(store, wasm).context("Unable to load the module from a file")?; + module.set_name(path.display().to_string().as_str()); + + if let Err(e) = cache.store(hash, &module) { + tracing::warn!( + error=&e as &dyn std::error::Error, + wat=%path.display(), + key=%hash, + "Unable to cache the compiled module", + ); + } + + Ok(module) +} + +#[derive(Debug, Clone)] +enum ExecutableTarget { + WebAssembly(Module), + Webc(WapmContainer), +} + +fn generate_coredump(err: &Error, source: &Path, coredump_path: &Path) -> Result<(), Error> { + let err: &wasmer::RuntimeError = match err.downcast_ref() { + Some(e) => e, + None => { + log::warn!("no runtime error found to generate coredump with"); + return Ok(()); + } + }; + + let source_name = source.display().to_string(); + let mut coredump_builder = + wasm_coredump_builder::CoredumpBuilder::new().executable_name(&source_name); + + let mut thread_builder = wasm_coredump_builder::ThreadBuilder::new().thread_name("main"); + + for frame in err.trace() { + let coredump_frame = wasm_coredump_builder::FrameBuilder::new() + .codeoffset(frame.func_offset() as u32) + .funcidx(frame.func_index()) + .build(); + thread_builder.add_frame(coredump_frame); + } + + coredump_builder.add_thread(thread_builder.build()); + + let coredump = coredump_builder + .serialize() + .map_err(Error::msg) + .context("Coredump serializing failed")?; + + std::fs::write(&coredump_path, &coredump).with_context(|| { + format!( + "Unable to save the coredump to \"{}\"", + coredump_path.display() + ) + })?; + + Ok(()) +} + +#[derive(Debug, Clone, Parser)] +pub(crate) struct WcgiOptions { + /// The address to serve on. + #[clap(long, short, env, default_value_t = ([127, 0, 0, 1], 8000).into())] + pub(crate) addr: SocketAddr, +} + +impl Default for WcgiOptions { + fn default() -> Self { + Self { + addr: ([127, 0, 0, 1], 8000).into(), + } + } +} + +#[derive(Debug)] +struct Callbacks { + stderr: Mutex>, +} + +impl Default for Callbacks { + fn default() -> Self { + Self { + stderr: Mutex::new(LineWriter::new(std::io::stderr())), + } + } +} + +impl wasmer_wasix::runners::wcgi::Callbacks for Callbacks { + fn on_stderr(&self, raw_message: &[u8]) { + if let Ok(mut stderr) = self.stderr.lock() { + // If the WCGI runner printed any log messages we want to make sure + // they get propagated to the user. Line buffering is important here + // because it helps prevent the output from becoming a complete + // mess. + let _ = stderr.write_all(raw_message); + } + } +} diff --git a/lib/cli/src/lib.rs b/lib/cli/src/lib.rs index 5129d48be5e..12c0f8f95e8 100644 --- a/lib/cli/src/lib.rs +++ b/lib/cli/src/lib.rs @@ -22,12 +22,12 @@ pub mod common; pub mod error; pub mod c_gen; pub mod cli; -#[cfg(feature = "debug")] pub mod logging; pub mod package_source; pub mod store; pub mod suggestions; pub mod utils; +pub mod wasmer_home; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/cli/src/logging.rs b/lib/cli/src/logging.rs index bfa6148bcfa..54ce76c6275 100644 --- a/lib/cli/src/logging.rs +++ b/lib/cli/src/logging.rs @@ -1,28 +1,48 @@ //! Logging functions for the debug feature. -/// Subroutine to instantiate the loggers -#[cfg(any(feature = "tracing", feature = "debug"))] -pub fn set_up_logging(verbose: u8) -> Result<(), String> { - use tracing_subscriber::prelude::*; - use tracing_subscriber::{fmt, EnvFilter}; +use tracing_subscriber::{ + filter::Directive, fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, +}; +/// Subroutine to instantiate the loggers +pub fn set_up_logging(level: log::LevelFilter) { let fmt_layer = fmt::layer() - .with_target(false) + .with_target(true) .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_ansi(should_emit_colors()) .with_thread_ids(true) + .with_writer(std::io::stderr) .compact(); - let filter_layer = EnvFilter::try_from_default_env() - .or_else(|_| match verbose { - 1 => EnvFilter::try_new("debug"), - _ => EnvFilter::try_new("trace"), - }) - .unwrap(); + let filter_layer = EnvFilter::builder() + .with_default_directive(log_directive(level)) + .from_env_lossy(); tracing_subscriber::registry() .with(filter_layer) .with(fmt_layer) .init(); +} + +/// Check whether we should emit ANSI escape codes for log formatting. +/// +/// The `tracing-subscriber` crate doesn't have native support for +/// "--color=always|never|auto", so we implement a poor man's version. +/// +/// For more, see https://github.com/tokio-rs/tracing/issues/2388 +fn should_emit_colors() -> bool { + isatty::stderr_isatty() && std::env::var_os("NO_COLOR").is_none() +} + +fn log_directive(level: log::LevelFilter) -> Directive { + let tracing_level = match level { + log::LevelFilter::Off => tracing::level_filters::LevelFilter::OFF, + log::LevelFilter::Error => tracing::level_filters::LevelFilter::ERROR, + log::LevelFilter::Warn => tracing::level_filters::LevelFilter::WARN, + log::LevelFilter::Info => tracing::level_filters::LevelFilter::INFO, + log::LevelFilter::Debug => tracing::level_filters::LevelFilter::DEBUG, + log::LevelFilter::Trace => tracing::level_filters::LevelFilter::TRACE, + }; - Ok(()) + tracing_level.into() } diff --git a/lib/cli/src/utils.rs b/lib/cli/src/utils.rs index 93e8fda8c81..fc5fa9cc14f 100644 --- a/lib/cli/src/utils.rs +++ b/lib/cli/src/utils.rs @@ -2,6 +2,7 @@ use anyhow::{bail, Result}; use std::env; use std::path::PathBuf; +use wasmer_wasix::runners::MappedDirectory; /// Whether or not Wasmer should print with color pub fn wasmer_should_print_color() -> bool { @@ -11,7 +12,7 @@ pub fn wasmer_should_print_color() -> bool { .unwrap_or_else(|| atty::is(atty::Stream::Stdout)) } -fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result<(String, PathBuf)> { +fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result { let pb = PathBuf::from(&real_dir); if let Ok(pb_metadata) = pb.metadata() { if !pb_metadata.is_dir() { @@ -20,11 +21,14 @@ fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result<(String, PathBu } else { bail!("Directory \"{}\" does not exist", &real_dir); } - Ok((alias.to_string(), pb)) + Ok(MappedDirectory { + guest: alias.to_string(), + host: pb, + }) } /// Parses a mapdir from a string -pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> { +pub fn parse_mapdir(entry: &str) -> Result { // We try first splitting by `::` if let [alias, real_dir] = entry.split("::").collect::>()[..] { retrieve_alias_pathbuf(alias, real_dir) diff --git a/lib/cli/src/wasmer_home.rs b/lib/cli/src/wasmer_home.rs new file mode 100644 index 00000000000..786b1d2343d --- /dev/null +++ b/lib/cli/src/wasmer_home.rs @@ -0,0 +1,399 @@ +#![allow(missing_docs)] + +use std::{ + io::Write, + path::{Path, PathBuf}, + time::{Duration, SystemTime}, +}; + +use anyhow::{Context, Error}; +use reqwest::{blocking::Client, Url}; +use tempfile::NamedTempFile; +use wasmer::{AsEngineRef, DeserializeError, Module, SerializeError}; +use wasmer_cache::Hash; +use wasmer_registry::Package; + +const DEFAULT_REGISTRY: &str = "https://wapm.io/"; +const CACHE_INVALIDATION_THRESHOLD: Duration = Duration::from_secs(5 * 60); + +/// Something which can fetch resources from the internet and will cache them +/// locally. +pub trait DownloadCached { + fn download_url(&self, url: &Url) -> Result; + fn download_package(&self, pkg: &Package) -> Result; +} + +#[derive(Debug, clap::Parser)] +pub struct WasmerHome { + /// The Wasmer home directory. + #[clap(long = "wasmer-dir", env = "WASMER_DIR")] + pub home: Option, + /// Override the registry packages are downloaded from. + #[clap(long, env = "WASMER_REGISTRY")] + registry: Option, + /// Skip all caching. + #[clap(long)] + pub disable_cache: bool, +} + +impl WasmerHome { + pub fn wasmer_home(&self) -> Result { + if let Some(wasmer_home) = &self.home { + return Ok(wasmer_home.clone()); + } + + if let Some(user_home) = dirs::home_dir() { + return Ok(user_home.join(".wasmer")); + } + + anyhow::bail!("Unable to determine the Wasmer directory"); + } + + pub fn module_cache(&self) -> ModuleCache { + if self.disable_cache { + return ModuleCache::Disabled; + }; + + self.wasmer_home() + .ok() + .and_then(|home| wasmer_cache::FileSystemCache::new(home.join("cache")).ok()) + .map(ModuleCache::Enabled) + .unwrap_or(ModuleCache::Disabled) + } +} + +impl DownloadCached for WasmerHome { + #[tracing::instrument(skip_all)] + fn download_url(&self, url: &Url) -> Result { + tracing::debug!(%url, "Downloading"); + + let home = self.wasmer_home()?; + let checkouts = wasmer_registry::get_checkouts_dir(&home); + + // This function is a bit tricky because we go to great lengths to avoid + // unnecessary downloads. + + let cache_key = Hash::generate(url.to_string().as_bytes()); + + // First, we figure out some basic information about the item + let cache_info = CacheInfo::for_url(&cache_key, &checkouts, self.disable_cache); + + // Next we check if we definitely got a cache hit + let state = match classify_cache_using_mtime(cache_info) { + Ok(path) => { + tracing::debug!(path=%path.display(), "Cache hit"); + return Ok(path); + } + Err(s) => s, + }; + + // Okay, looks like we're going to have to download the item + tracing::debug!(%url, "Sending a GET request"); + + let client = Client::new(); + + let request = client.get(url.clone()).header("Accept", "application/webc"); + + let mut response = match request.send() { + Ok(r) => r + .error_for_status() + .with_context(|| format!("The GET request to \"{url}\" was unsuccessful"))?, + Err(e) => { + // Something went wrong. If it was a connection issue and we've + // got a cached file, let's use that and emit a warning. + if e.is_connect() { + if let Some(path) = state.take_path() { + tracing::warn!( + path=%path.display(), + error=&e as &dyn std::error::Error, + "An error occurred while connecting to {}. Falling back to a cached version.", + url.host_str().unwrap_or(url.as_str()), + ); + return Ok(path); + } + } + + // Oh well, we tried. + let msg = format!("Unable to send a GET request to \"{url}\""); + return Err(Error::from(e).context(msg)); + } + }; + + tracing::debug!( + status_code=%response.status(), + url=%response.url(), + content_length=response.content_length(), + "Download started", + ); + tracing::trace!(headers=?response.headers()); + + // Now there is one last chance to avoid downloading the full file. If + // it has an ETag header, we can use that to see whether the (possibly) + // cached file is outdated. + let etag = response + .headers() + .get("Etag") + .and_then(|v| v.to_str().ok()) + .map(|etag| etag.trim().to_string()); + + if let Some(cached) = state.use_etag_to_resolve_cached_file(etag.as_deref()) { + tracing::debug!( + path=%cached.display(), + "Reusing the cached file because the ETag header is still valid", + ); + return Ok(cached); + } + + std::fs::create_dir_all(&checkouts) + .with_context(|| format!("Unable to make sure \"{}\" exists", checkouts.display()))?; + + // Note: we want to copy directly into a file so we don't hold + // everything in memory. + let (mut f, path) = if self.disable_cache { + // Leave the temporary file where it is. The OS will clean it up + // for us later, and hopefully the caller will open it before the + // temp file cleaner comes along. + let temp = NamedTempFile::new().context("Unable to create a temporary file")?; + temp.keep() + .context("Unable to persist the temporary file")? + } else { + let cached_path = checkouts.join(cache_key.to_string()); + let f = std::fs::File::create(&cached_path).with_context(|| { + format!("Unable to open \"{}\" for writing", cached_path.display()) + })?; + + (f, cached_path) + }; + + let bytes_read = std::io::copy(&mut response, &mut f) + .and_then(|bytes_read| f.flush().map(|_| bytes_read)) + .with_context(|| format!("Unable to save the response to \"{}\"", path.display()))?; + tracing::debug!(bytes_read, path=%path.display(), "Saved to disk"); + + if !self.disable_cache { + if let Some(etag) = etag { + let etag_path = path.with_extension("etag"); + tracing::debug!( + path=%etag_path.display(), + %etag, + "Saving the ETag to disk", + ); + + if let Err(e) = std::fs::write(&etag_path, etag.as_bytes()) { + tracing::warn!( + error=&e as &dyn std::error::Error, + path=%etag_path.display(), + %etag, + "Unable to save the ETag to disk", + ); + } + } + } + + Ok(path) + } + + fn download_package(&self, pkg: &Package) -> Result { + let registry = self.registry.as_deref().unwrap_or(DEFAULT_REGISTRY); + let url = package_url(registry, pkg)?; + + self.download_url(&url) + } +} + +#[derive(Debug, Clone, PartialEq)] +enum CacheInfo { + /// Caching has been disabled. + Disabled, + /// An item isn't in the cache, but could be cached later on. + Miss, + /// An item in the cache. + Hit { + path: PathBuf, + etag: Option, + last_modified: Option, + }, +} + +impl CacheInfo { + fn for_url(key: &Hash, checkout_dir: &Path, disabled: bool) -> CacheInfo { + if disabled { + return CacheInfo::Disabled; + } + + let path = checkout_dir.join(key.to_string()); + + if !path.exists() { + return CacheInfo::Miss; + } + + let etag = std::fs::read_to_string(path.with_extension("etag")).ok(); + let last_modified = path.metadata().and_then(|m| m.modified()).ok(); + + CacheInfo::Hit { + etag, + last_modified, + path, + } + } +} + +fn classify_cache_using_mtime(info: CacheInfo) -> Result { + let (path, last_modified, etag) = match info { + CacheInfo::Hit { + path, + last_modified: Some(last_modified), + etag, + .. + } => (path, last_modified, etag), + CacheInfo::Hit { + path, + last_modified: None, + etag: Some(etag), + .. + } => return Err(CacheState::PossiblyDirty { etag, path }), + CacheInfo::Hit { + etag: None, + last_modified: None, + path, + .. + } => { + return Err(CacheState::UnableToVerify { path }); + } + CacheInfo::Disabled | CacheInfo::Miss { .. } => return Err(CacheState::Miss), + }; + + if let Ok(time_since_last_modified) = last_modified.elapsed() { + if time_since_last_modified <= CACHE_INVALIDATION_THRESHOLD { + return Ok(path); + } + } + + match etag { + Some(etag) => Err(CacheState::PossiblyDirty { etag, path }), + None => Err(CacheState::UnableToVerify { path }), + } +} + +/// Classification of how valid an item is based on filesystem metadata. +#[derive(Debug)] +enum CacheState { + /// The item isn't in the cache. + Miss, + /// The cached item might be invalid, but it has an ETag we can use for + /// further validation. + PossiblyDirty { etag: String, path: PathBuf }, + /// The cached item exists on disk, but we weren't able to tell whether it is still + /// valid, and there aren't any other ways to validate it further. You can + /// probably reuse this if you are having internet issues. + UnableToVerify { path: PathBuf }, +} + +impl CacheState { + fn take_path(self) -> Option { + match self { + CacheState::PossiblyDirty { path, .. } | CacheState::UnableToVerify { path } => { + Some(path) + } + _ => None, + } + } + + fn use_etag_to_resolve_cached_file(self, new_etag: Option<&str>) -> Option { + match (new_etag, self) { + ( + Some(new_etag), + CacheState::PossiblyDirty { + etag: cached_etag, + path, + }, + ) if cached_etag == new_etag => Some(path), + _ => None, + } + } +} + +fn package_url(registry: &str, pkg: &Package) -> Result { + let registry: Url = registry + .parse() + .with_context(|| format!("Unable to parse \"{registry}\" as a URL"))?; + + let Package { + name, + namespace, + version, + } = pkg; + + let mut path = format!("{namespace}/{name}"); + if let Some(version) = version { + path.push('@'); + path.push_str(version); + } + + let url = registry + .join(&path) + .context("Unable to construct the package URL")?; + Ok(url) +} + +#[derive(Debug, Clone)] +pub enum ModuleCache { + Enabled(wasmer_cache::FileSystemCache), + Disabled, +} + +impl wasmer_cache::Cache for ModuleCache { + type SerializeError = SerializeError; + type DeserializeError = DeserializeError; + + unsafe fn load( + &self, + engine: &impl AsEngineRef, + key: Hash, + ) -> Result { + match self { + ModuleCache::Enabled(f) => f.load(engine, key), + ModuleCache::Disabled => Err(DeserializeError::Io(std::io::ErrorKind::NotFound.into())), + } + } + + fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { + match self { + ModuleCache::Enabled(f) => f.store(key, module), + ModuleCache::Disabled => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construct_package_urls() { + let inputs = [ + ( + "https://wapm.io/", + "syrusakbary/python", + "https://wapm.io/syrusakbary/python", + ), + ( + "https://wapm.dev", + "syrusakbary/python@1.2.3", + "https://wapm.dev/syrusakbary/python@1.2.3", + ), + ( + "https://localhost:8000/path/to/nested/dir/", + "syrusakbary/python", + "https://localhost:8000/path/to/nested/dir/syrusakbary/python", + ), + ]; + + for (registry, package, expected) in inputs { + let package: Package = package.parse().unwrap(); + + let got = package_url(registry, &package).unwrap(); + assert_eq!(got.to_string(), expected); + } + } +} diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index 2cd89312488..5317cb6a918 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -20,7 +20,7 @@ lazy_static = "1.4" fs_extra = { version = "1.2.0", optional = true } filetime = { version = "0.2.18", optional = true } bytes = "1" -tokio = { version = "1", features = [ "io-util", "sync", "macros" ], default_features = false } +tokio = { version = "1", features = ["io-util", "sync", "macros"], default_features = false } pin-project-lite = "0.2.9" indexmap = "1.9.2" @@ -33,7 +33,7 @@ getrandom = { version = "0.2", features = [ "js" ] } [dev-dependencies] pretty_assertions = "1.3.0" tempfile = "3.4.0" -tokio = { version = "1", features = [ "io-util", "rt" ], default_features = false } +tokio = { version = "1", features = ["io-util", "rt"], default_features = false } [features] default = ["host-fs", "webc-fs", "static-fs"] diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 28d43f6fb99..e20f42b4580 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -6,6 +6,7 @@ use std::any::Any; use std::ffi::OsString; use std::fmt; use std::io; +use std::ops::Deref; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::task::Context; @@ -94,6 +95,40 @@ impl dyn FileSystem + 'static { } } +impl FileSystem for D +where + D: Deref + std::fmt::Debug + Send + Sync + 'static, + F: FileSystem + ?Sized, +{ + fn read_dir(&self, path: &Path) -> Result { + (**self).read_dir(path) + } + + fn create_dir(&self, path: &Path) -> Result<()> { + (**self).create_dir(path) + } + + fn remove_dir(&self, path: &Path) -> Result<()> { + (**self).remove_dir(path) + } + + fn rename(&self, from: &Path, to: &Path) -> Result<()> { + (**self).rename(from, to) + } + + fn metadata(&self, path: &Path) -> Result { + (**self).metadata(path) + } + + fn remove_file(&self, path: &Path) -> Result<()> { + (**self).remove_file(path) + } + + fn new_open_options(&self) -> OpenOptions { + (**self).new_open_options() + } +} + pub trait FileOpener { fn open( &self, diff --git a/lib/vfs/src/webc_fs.rs b/lib/vfs/src/webc_fs.rs index 11db43c7eff..0bb1970cc81 100644 --- a/lib/vfs/src/webc_fs.rs +++ b/lib/vfs/src/webc_fs.rs @@ -3,7 +3,7 @@ use crate::{ FileOpener, FileSystem, FsError, Metadata, OpenOptions, OpenOptionsConfig, ReadDir, VirtualFile, }; use anyhow::anyhow; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::io::{self, Error as IoError, ErrorKind as IoErrorKind, SeekFrom}; use std::ops::Deref; use std::path::Path; @@ -177,7 +177,7 @@ where T: Deref>, { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, ) -> Poll> { @@ -194,16 +194,14 @@ where .get_file_bytes(&self.entry) .map_err(|e| IoError::new(IoErrorKind::NotFound, e))?; - let cursor: usize = self.cursor.try_into().unwrap_or(u32::MAX as usize); - let _start = cursor.min(bytes.len()); - let bytes = &bytes[cursor..]; + let start: usize = self.cursor.try_into().unwrap(); + let remaining = &bytes[start..]; + let bytes_read = remaining.len().min(buf.remaining()); + let bytes = &remaining[..bytes_read]; + + buf.put_slice(bytes); + self.cursor += u64::try_from(bytes_read).unwrap(); - if bytes.len() > buf.remaining() { - let remaining = buf.remaining(); - buf.put_slice(&bytes[..remaining]); - } else { - buf.put_slice(bytes); - } Poll::Ready(Ok(())) } } @@ -446,3 +444,33 @@ fn translate_file_type(f: FsEntryType) -> crate::FileType { fifo: false, } } + +#[cfg(test)] +mod tests { + use tokio::io::AsyncReadExt; + use webc::v1::{ParseOptions, WebCOwned}; + + use super::*; + + #[tokio::test] + async fn read_a_file_from_the_webc_fs() { + let webc: &[u8] = include_bytes!("../../c-api/examples/assets/python-0.1.0.wasmer"); + let options = ParseOptions::default(); + let webc = WebCOwned::parse(webc.to_vec(), &options).unwrap(); + + let fs = WebcFileSystem::init_all(Arc::new(webc)); + + let mut f = fs + .new_open_options() + .read(true) + .open(Path::new("/lib/python3.6/collections/abc.py")) + .unwrap(); + + let mut abc_py = String::new(); + f.read_to_string(&mut abc_py).await.unwrap(); + assert_eq!( + abc_py, + "from _collections_abc import *\nfrom _collections_abc import __all__\n" + ); + } +} diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 4c7719b6ab7..19a261b84b3 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -60,6 +60,8 @@ pin-project = "1.0.12" hyper = { version = "0.14", features = ["server", "stream"], optional = true } wcgi = { version = "0.1.1", optional = true } wcgi-host = { version = "0.1.0", optional = true } +tower-http = { version = "0.4.0", features = ["trace", "util", "catch-panic", "cors"], optional = true } +tower = { version = "0.4.13", features = ["make", "util"], optional = true } [dependencies.reqwest] version = "0.11" @@ -80,6 +82,7 @@ wasm-bindgen = "0.2.74" [dev-dependencies] wasmer = { path = "../api", version = "=3.2.0-alpha.1", default-features = false, features = ["wat", "js-serializable-module"] } tokio = { version = "1", features = [ "sync", "macros", "rt" ], default_features = false } +tempfile = "3.4.0" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.0" @@ -96,7 +99,7 @@ time = ["tokio/time"] webc_runner = ["serde_cbor", "wasmer/compiler"] webc_runner_rt_wasi = [] -webc_runner_rt_wcgi = ["hyper", "wcgi", "wcgi-host"] +webc_runner_rt_wcgi = ["hyper", "wcgi", "wcgi-host", "tower", "tower-http"] webc_runner_rt_emscripten = ["wasmer-emscripten"] sys = ["wasmer/sys", "wasmer-wasix-types/sys", "webc/mmap", "wasmer-vm", "time"] diff --git a/lib/wasi/src/runners/container.rs b/lib/wasi/src/runners/container.rs index 66de75a1bca..810f5b6c0ca 100644 --- a/lib/wasi/src/runners/container.rs +++ b/lib/wasi/src/runners/container.rs @@ -192,22 +192,13 @@ impl Bindings for WitBindings { } /// Error that ocurred while parsing the .webc file -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum WebcParseError { /// Parse error - Parse(webc::v1::Error), - Detect(webc::DetectError), + #[error("Unable to parse the WEBC file")] + Parse(#[from] webc::v1::Error), + #[error("Unable to determine the WEBC version")] + Detect(#[from] webc::DetectError), + #[error("Unsupported WEBC version, {_0}")] UnsupportedVersion(Version), } - -impl From for WebcParseError { - fn from(e: webc::v1::Error) -> Self { - WebcParseError::Parse(e) - } -} - -impl From for WebcParseError { - fn from(e: webc::DetectError) -> Self { - WebcParseError::Detect(e) - } -} diff --git a/lib/wasi/src/runners/mod.rs b/lib/wasi/src/runners/mod.rs index d156753fe93..95206dd097e 100644 --- a/lib/wasi/src/runners/mod.rs +++ b/lib/wasi/src/runners/mod.rs @@ -12,3 +12,11 @@ pub use self::{ container::{Bindings, WapmContainer, WebcParseError, WitBindings}, runner::Runner, }; + +use std::path::PathBuf; + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct MappedDirectory { + pub host: PathBuf, + pub guest: String, +} diff --git a/lib/wasi/src/runners/wasi.rs b/lib/wasi/src/runners/wasi.rs index f4b76a0067a..d17b72382ac 100644 --- a/lib/wasi/src/runners/wasi.rs +++ b/lib/wasi/src/runners/wasi.rs @@ -1,8 +1,11 @@ //! WebC container support for running WASI modules -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; -use crate::{runners::WapmContainer, PluggableRuntime, VirtualTaskManager}; +use crate::{ + runners::{MappedDirectory, WapmContainer}, + PluggableRuntime, VirtualTaskManager, +}; use crate::{WasiEnv, WasiEnvBuilder}; use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; @@ -12,6 +15,9 @@ use webc::metadata::{annotations::Wasi, Command}; #[derive(Debug, Serialize, Deserialize)] pub struct WasiRunner { args: Vec, + env: HashMap, + forward_host_env: bool, + mapped_dirs: Vec, #[serde(skip, default)] store: Store, #[serde(skip, default)] @@ -23,7 +29,10 @@ impl WasiRunner { pub fn new(store: Store) -> Self { Self { args: Vec::new(), + env: HashMap::new(), store, + mapped_dirs: Vec::new(), + forward_host_env: false, tasks: None, } } @@ -52,6 +61,56 @@ impl WasiRunner { self.args = args.into_iter().map(|s| s.into()).collect(); } + /// Builder method to provide environment variables to the runner. + pub fn with_env(mut self, key: impl Into, value: impl Into) -> Self { + self.set_env(key, value); + self + } + + /// Provide environment variables to the runner. + pub fn set_env(&mut self, key: impl Into, value: impl Into) { + self.env.insert(key.into(), value.into()); + } + + pub fn with_envs(mut self, envs: I) -> Self + where + I: IntoIterator, + K: Into, + V: Into, + { + self.set_envs(envs); + self + } + + pub fn set_envs(&mut self, envs: I) + where + I: IntoIterator, + K: Into, + V: Into, + { + for (key, value) in envs { + self.env.insert(key.into(), value.into()); + } + } + + pub fn with_forward_host_env(mut self) -> Self { + self.set_forward_host_env(); + self + } + + pub fn set_forward_host_env(&mut self) { + self.forward_host_env = true; + } + + pub fn with_mapped_directories(mut self, dirs: I) -> Self + where + I: IntoIterator, + D: Into, + { + self.mapped_dirs.extend(dirs.into_iter().map(|d| d.into())); + self + } + pub fn with_task_manager(mut self, tasks: impl VirtualTaskManager) -> Self { self.set_task_manager(tasks); self @@ -60,6 +119,39 @@ impl WasiRunner { pub fn set_task_manager(&mut self, tasks: impl VirtualTaskManager) { self.tasks = Some(Arc::new(tasks)); } + + fn prepare_webc_env( + &self, + container: &WapmContainer, + command: &str, + ) -> Result { + let (fs, preopen_dirs) = container.container_fs(); + + let mut builder = WasiEnv::builder(command).args(&self.args); + + for dir in preopen_dirs { + builder.add_preopen_dir(dir)?; + } + + builder.set_fs(Box::new(fs)); + + if self.forward_host_env { + for (k, v) in std::env::vars() { + builder.add_env(k, v); + } + } + + for (k, v) in &self.env { + builder.add_env(k, v); + } + + if let Some(tasks) = &self.tasks { + let rt = PluggableRuntime::new(Arc::clone(tasks)); + builder.set_runtime(Arc::new(rt)); + } + + Ok(builder) + } } impl crate::runners::Runner for WasiRunner { @@ -88,38 +180,8 @@ impl crate::runners::Runner for WasiRunner { let mut module = Module::new(&self.store, atom)?; module.set_name(&atom_name); - let mut builder = prepare_webc_env(container, &atom_name, &self.args)?; - - if let Some(tasks) = &self.tasks { - let rt = PluggableRuntime::new(Arc::clone(tasks)); - builder.set_runtime(Arc::new(rt)); - } - - let res = builder.run(module); - match res { - Ok(()) => Ok(()), - Err(crate::WasiRuntimeError::Wasi(crate::WasiError::Exit(_))) => Ok(()), - Err(e) => Err(e), - }?; + self.prepare_webc_env(container, &atom_name)?.run(module)?; Ok(()) } } - -// https://github.com/tokera-com/ate/blob/42c4ce5a0c0aef47aeb4420cc6dc788ef6ee8804/term-lib/src/eval/exec.rs#L444 -fn prepare_webc_env( - container: &WapmContainer, - command: &str, - args: &[String], -) -> Result { - let (filesystem, preopen_dirs) = container.container_fs(); - let mut builder = WasiEnv::builder(command).args(args); - - for entry in preopen_dirs { - builder.add_preopen_build(|p| p.directory(&entry).read(true).write(true).create(true))?; - } - - builder.set_fs(filesystem); - - Ok(builder) -} diff --git a/lib/wasi/src/runners/wcgi/handler.rs b/lib/wasi/src/runners/wcgi/handler.rs index 58469c66d0e..565f3be8b28 100644 --- a/lib/wasi/src/runners/wcgi/handler.rs +++ b/lib/wasi/src/runners/wcgi/handler.rs @@ -7,7 +7,7 @@ use std::{ task::Poll, }; -use anyhow::Error; +use anyhow::{Context, Error}; use futures::{Future, FutureExt, StreamExt, TryFutureExt}; use http::{Request, Response}; use hyper::{service::Service, Body}; @@ -15,6 +15,7 @@ use tokio::{ io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt}, runtime::Handle, }; +use tracing::Instrument; use virtual_fs::{FileSystem, PassthruFileSystem, RootFileSystemBuilder, TmpFileSystem}; use wasmer::Module; use wcgi_host::CgiDialect; @@ -22,7 +23,7 @@ use wcgi_host::CgiDialect; use crate::{ capabilities::Capabilities, http::HttpClientCapabilityV1, - runners::wcgi::{Callbacks, MappedDirectory}, + runners::{wcgi::Callbacks, MappedDirectory, WapmContainer}, Pipe, PluggableRuntime, VirtualTaskManager, WasiEnv, }; @@ -37,12 +38,16 @@ impl Handler { } pub(crate) async fn handle(&self, req: Request) -> Result, Error> { + tracing::debug!(headers=?req.headers()); + let (parts, body) = req.into_parts(); let (req_body_sender, req_body_receiver) = Pipe::channel(); let (res_body_sender, res_body_receiver) = Pipe::channel(); let (stderr_sender, stderr_receiver) = Pipe::channel(); + tracing::debug!("Creating the WebAssembly instance"); + let builder = WasiEnv::builder(self.program.to_string()); let mut request_specific_env = HashMap::new(); @@ -50,7 +55,7 @@ impl Handler { .prepare_environment_variables(parts, &mut request_specific_env); let rt = PluggableRuntime::new(Arc::clone(&self.task_manager)); - let builder = builder + let mut builder = builder .envs(self.env.iter()) .envs(request_specific_env) .args(self.args.iter()) @@ -63,11 +68,19 @@ impl Handler { threading: Default::default(), }) .runtime(Arc::new(rt)) - .sandbox_fs(self.fs()?) + .fs(Box::new(self.fs()?)) .preopen_dir(Path::new("/"))?; + for mapping in &self.mapped_dirs { + builder + .add_preopen_dir(&mapping.guest) + .with_context(|| format!("Unable to preopen \"{}\"", mapping.guest))?; + } + let module = self.module.clone(); + tracing::debug!("Calling into the WCGI executable"); + let done = self .task_manager .runtime() @@ -78,19 +91,26 @@ impl Handler { let handle = self.task_manager.runtime().clone(); let callbacks = Arc::clone(&self.callbacks); - handle.spawn(async move { - consume_stderr(stderr_receiver, callbacks).await; - }); - - self.task_manager.runtime().spawn(async move { - if let Err(e) = drive_request_to_completion(&handle, done, body, req_body_sender).await - { - tracing::error!( - error = &*e as &dyn std::error::Error, - "Unable to drive the request to completion" - ); + handle.spawn( + async move { + consume_stderr(stderr_receiver, callbacks).await; } - }); + .in_current_span(), + ); + + self.task_manager.runtime().spawn( + async move { + if let Err(e) = + drive_request_to_completion(&handle, done, body, req_body_sender).await + { + tracing::error!( + error = &*e as &dyn std::error::Error, + "Unable to drive the request to completion" + ); + } + } + .in_current_span(), + ); let mut res_body_receiver = tokio::io::BufReader::new(res_body_receiver); @@ -128,23 +148,30 @@ impl Handler { true => PathBuf::from(guest), false => Path::new("/").join(guest), }; - tracing::trace!( + tracing::debug!( host=%host.display(), guest=%guest.display(), - "mounting directory to instance fs", + "mounting host directory", ); + if let Some(parent) = guest.parent() { + create_dir_all(&root_fs, parent) + .with_context(|| format!("Unable to create \"{}\"", parent.display()))?; + } + root_fs - .mount(host.clone(), &fs_backing, guest.clone()) - .map_err(|error| { - anyhow::anyhow!( - "Unable to mount \"{}\" to \"{}\": {error}", + .mount(guest.clone(), &fs_backing, host.clone()) + .with_context(|| { + format!( + "Unable to mount \"{}\" to \"{}\"", host.display(), guest.display() ) - })?; + }) + .map_err(|e| dbg!(e))?; } } + Ok(root_fs) } } @@ -157,6 +184,20 @@ impl Deref for Handler { } } +fn create_dir_all(fs: &dyn FileSystem, path: &Path) -> Result<(), Error> { + if fs.metadata(path).is_ok() { + return Ok(()); + } + + if let Some(parent) = path.parent() { + create_dir_all(fs, parent)?; + } + + fs.create_dir(path)?; + + Ok(()) +} + /// Drive the request to completion by streaming the request body to the /// instance and waiting for it to exit. async fn drive_request_to_completion( @@ -166,21 +207,30 @@ async fn drive_request_to_completion( mut instance_stdin: impl AsyncWrite + Send + Unpin + 'static, ) -> Result<(), Error> { let request_body_send = handle - .spawn(async move { - // Copy the request into our instance, chunk-by-chunk. If the instance - // dies before we finish writing the body, the instance's side of the - // pipe will be automatically closed and we'll error out. - while let Some(res) = request_body.next().await { - // FIXME(theduke): figure out how to propagate a body error to the - // CGI instance. - let chunk = res?; - instance_stdin.write_all(chunk.as_ref()).await?; - } + .spawn( + async move { + // Copy the request into our instance, chunk-by-chunk. If the instance + // dies before we finish writing the body, the instance's side of the + // pipe will be automatically closed and we'll error out. + let mut request_size = 0; + while let Some(res) = request_body.next().await { + // FIXME(theduke): figure out how to propagate a body error to the + // CGI instance. + let chunk = res?; + request_size += chunk.len(); + instance_stdin.write_all(chunk.as_ref()).await?; + } - instance_stdin.shutdown().await?; + instance_stdin.shutdown().await?; + tracing::debug!( + request_size, + "Finished forwarding the request to the WCGI server" + ); - Ok::<(), Error>(()) - }) + Ok::<(), Error>(()) + } + .in_current_span(), + ) .map_err(Error::from) .and_then(|r| async { r }); @@ -227,6 +277,7 @@ pub(crate) struct SharedState { pub(crate) env: HashMap, pub(crate) args: Vec, pub(crate) mapped_dirs: Vec, + pub(crate) container: WapmContainer, pub(crate) module: Module, pub(crate) dialect: CgiDialect, pub(crate) task_manager: Arc, diff --git a/lib/wasi/src/runners/wcgi/mod.rs b/lib/wasi/src/runners/wcgi/mod.rs index 1fe6f485c12..ddfe6d783a2 100644 --- a/lib/wasi/src/runners/wcgi/mod.rs +++ b/lib/wasi/src/runners/wcgi/mod.rs @@ -1,12 +1,4 @@ mod handler; mod runner; -use std::path::PathBuf; - pub use self::runner::{Callbacks, Config, WcgiRunner}; - -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct MappedDirectory { - pub host: PathBuf, - pub guest: String, -} diff --git a/lib/wasi/src/runners/wcgi/runner.rs b/lib/wasi/src/runners/wcgi/runner.rs index 14e67859a3a..ed6dd3bfde8 100644 --- a/lib/wasi/src/runners/wcgi/runner.rs +++ b/lib/wasi/src/runners/wcgi/runner.rs @@ -1,8 +1,12 @@ -use std::{collections::HashMap, convert::Infallible, net::SocketAddr, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; use anyhow::{Context, Error}; use futures::future::AbortHandle; -use virtual_fs::FileSystem; +use http::{Request, Response}; +use hyper::Body; +use tower::{make::Shared, ServiceBuilder}; +use tower_http::{catch_panic::CatchPanicLayer, cors::CorsLayer, trace::TraceLayer}; +use tracing::Span; use wasmer::{Engine, Module, Store}; use wcgi_host::CgiDialect; use webc::metadata::{ @@ -12,11 +16,8 @@ use webc::metadata::{ use crate::{ runners::{ - wcgi::{ - handler::{Handler, SharedState}, - MappedDirectory, - }, - WapmContainer, + wcgi::handler::{Handler, SharedState}, + MappedDirectory, WapmContainer, }, runtime::task_manager::tokio::TokioTaskManager, VirtualTaskManager, @@ -37,7 +38,7 @@ impl WcgiRunner { } #[tracing::instrument(skip(self, ctx))] - fn run_(&mut self, command_name: &str, ctx: &RunnerContext<'_>) -> Result<(), Error> { + fn run(&mut self, command_name: &str, ctx: &RunnerContext<'_>) -> Result<(), Error> { let wasi: Wasi = ctx .command() .get_annotation("wasi") @@ -50,17 +51,31 @@ impl WcgiRunner { let handler = self.create_handler(module, &wasi, ctx)?; let task_manager = Arc::clone(&handler.task_manager); + let callbacks = Arc::clone(&self.config.callbacks); - let make_service = hyper::service::make_service_fn(move |_| { - let handler = handler.clone(); - async { Ok::<_, Infallible>(handler) } - }); + let service = ServiceBuilder::new() + .layer( + TraceLayer::new_for_http() + .make_span_with(|request: &Request| { + tracing::info_span!( + "request", + method = %request.method(), + uri = %request.uri(), + status_code = tracing::field::Empty, + ) + }) + .on_response(|response: &Response<_>, _latency: Duration, span: &Span| { + span.record("status_code", &tracing::field::display(response.status())); + tracing::info!("response generated") + }), + ) + .layer(CatchPanicLayer::new()) + .layer(CorsLayer::permissive()) + .service(handler); let address = self.config.addr; tracing::info!(%address, "Starting the server"); - let callbacks = Arc::clone(&self.config.callbacks); - task_manager .block_on(async { let (shutdown, abort_handle) = @@ -69,7 +84,7 @@ impl WcgiRunner { callbacks.started(abort_handle); hyper::Server::bind(&address) - .serve(make_service) + .serve(Shared::new(service)) .with_graceful_shutdown(async { let _ = shutdown.await; tracing::info!("Shutting down gracefully"); @@ -132,6 +147,7 @@ impl WcgiRunner { .clone() .unwrap_or_else(|| Arc::new(TokioTaskManager::default())), module, + container: ctx.container.clone(), dialect, callbacks: Arc::clone(&self.config.callbacks), }; @@ -209,10 +225,6 @@ impl RunnerContext<'_> { &self.store } - fn volume(&self, _name: &str) -> Option> { - todo!("Implement a read-only filesystem backed by a volume"); - } - fn get_atom(&self, name: &str) -> Option<&[u8]> { self.container.get_atom(name) } @@ -245,7 +257,7 @@ impl crate::runners::Runner for WcgiRunner { store, }; - self.run_(command_name, &ctx) + WcgiRunner::run(self, command_name, &ctx) } } @@ -314,29 +326,16 @@ impl Config { self } - pub fn map_directory( - &mut self, - host: impl Into, - guest: impl Into, - ) -> &mut Self { - self.mapped_dirs.push(MappedDirectory { - host: host.into(), - guest: guest.into(), - }); + pub fn map_directory(&mut self, dir: MappedDirectory) -> &mut Self { + self.mapped_dirs.push(dir); self } - pub fn map_directories(&mut self, mappings: I) -> &mut Self - where - I: IntoIterator, - H: Into, - G: Into, - { - let mappings = mappings.into_iter().map(|(h, g)| MappedDirectory { - host: h.into(), - guest: g.into(), - }); - self.mapped_dirs.extend(mappings); + pub fn map_directories( + &mut self, + mappings: impl IntoIterator, + ) -> &mut Self { + self.mapped_dirs.extend(mappings.into_iter()); self } @@ -373,8 +372,10 @@ impl Default for Config { pub trait Callbacks: Send + Sync + 'static { /// A callback that is called whenever the server starts. fn started(&self, _abort: AbortHandle) {} + /// Data was written to stderr by an instance. fn on_stderr(&self, _stderr: &[u8]) {} + /// Reading from stderr failed. fn on_stderr_error(&self, _error: std::io::Error) {} } diff --git a/lib/wasi/tests/runners.rs b/lib/wasi/tests/runners.rs index 3bf033442a8..8792fe14e8f 100644 --- a/lib/wasi/tests/runners.rs +++ b/lib/wasi/tests/runners.rs @@ -39,6 +39,7 @@ mod wasi { let handle = std::thread::spawn(move || { WasiRunner::new(store) .with_task_manager(tasks) + .with_args(["--version"]) .run_cmd(&container, "wat2wasm") }); let err = handle.join().unwrap().unwrap_err(); diff --git a/tests/integration/cli/Cargo.toml b/tests/integration/cli/Cargo.toml index cb76f0293fc..4c6cd6aecd0 100644 --- a/tests/integration/cli/Cargo.toml +++ b/tests/integration/cli/Cargo.toml @@ -16,8 +16,10 @@ md5 = "0.7.0" hex = "0.4.3" pretty_assertions = "1.3.0" object = "0.30.0" -reqwest = { version = "0.11.14", features = ["json"] } +reqwest = { version = "0.11.14", features = ["json", "blocking"] } tokio = { version = "1", features = [ "rt", "rt-multi-thread", "macros" ] } +assert_cmd = "2.0.8" +predicates = "2.1.5" [dependencies] anyhow = "1" diff --git a/tests/integration/cli/tests/run_unstable.rs b/tests/integration/cli/tests/run_unstable.rs new file mode 100644 index 00000000000..8877c3fe44a --- /dev/null +++ b/tests/integration/cli/tests/run_unstable.rs @@ -0,0 +1,433 @@ +//! Integration tests for `wasmer run2`. +//! +//! Note that you will need to manually compile the `wasmer` CLI in release mode +//! before running any of these tests. +use std::{ + io::Read, + process::Stdio, + time::{Duration, Instant}, +}; + +use assert_cmd::{assert::Assert, prelude::OutputAssertExt, Command}; +use predicates::str::contains; +use reqwest::{blocking::Client, IntoUrl}; +use tempfile::TempDir; +use wasmer_integration_tests_cli::get_wasmer_path; + +const RUST_LOG: &str = "info,wasmer_wasi::runners=debug"; + +mod webc_on_disk { + use super::*; + use rand::Rng; + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wasi_runner() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::qjs()) + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wasi_runner_with_mounted_directories() { + let temp = TempDir::new().unwrap(); + std::fs::write(temp.path().join("index.js"), "console.log('Hello, World!')").unwrap(); + + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::qjs()) + .arg(format!("--mapdir=/app:{}", temp.path().display())) + .arg("--") + .arg("/app/index.js") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[ignore = "WASI runners only give you access to the webc fs for now"] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wasi_runner_with_mounted_directories_and_webc_volumes() { + let temp = TempDir::new().unwrap(); + std::fs::write(temp.path().join("main.py"), "print('Hello, World!')").unwrap(); + + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::python()) + .arg(format!("--mapdir=/app:{}", temp.path().display())) + .arg("--") + .arg("/app/main.py") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn webc_files_with_multiple_commands_require_an_entrypoint_flag() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::wabt()) + .assert(); + + let msg = r#"Unable to determine the WEBC file's entrypoint. Please choose one of ["wat2wasm", "wast2json", "wasm2wat", "wasm-interp", "wasm-validate", "wasm-strip"]"#; + assert.failure().stderr(contains(msg)); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wasi_runner_with_env_vars() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::python()) + .arg("--env=SOME_VAR=Hello, World!") + .arg("--") + .arg("-c") + .arg("import os; print(os.environ['SOME_VAR'])") + .env("RUST_LOG", RUST_LOG) + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wcgi_runner() { + // Start the WCGI server in the background + let port = rand::thread_rng().gen_range(10_000_u16..u16::MAX); + let mut cmd = std::process::Command::new(get_wasmer_path()); + cmd.arg("run-unstable") + .arg(format!("--addr=127.0.0.1:{port}")) + .arg(fixtures::static_server()) + .env("RUST_LOG", RUST_LOG); + let child = JoinableChild::spawn(cmd); + + // make the request + let body = http_get(format!("http://127.0.0.1:{port}/")).unwrap(); + assert!(body.contains("Index of /"), "{body}"); + + // Let's make sure 404s work too + let err = + http_get(format!("http://127.0.0.1:{port}/this/does/not/exist.html")).unwrap_err(); + assert_eq!(err.status().unwrap(), reqwest::StatusCode::NOT_FOUND); + + // And kill the server, making sure it generated the expected logs + let assert = child.join(); + + assert + .stderr(contains("Starting the server")) + .stderr(contains("response generated method=GET uri=/ status_code=200 OK")) + .stderr(contains("response generated method=GET uri=/this/does/not/exist.html status_code=404 Not Found")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wcgi_runner_with_mounted_directories() { + let temp = TempDir::new().unwrap(); + std::fs::write(temp.path().join("file.txt"), "Hello, World!").unwrap(); + // Start the WCGI server in the background + let port = rand::thread_rng().gen_range(10_000_u16..u16::MAX); + let mut cmd = std::process::Command::new(get_wasmer_path()); + cmd.arg("run-unstable") + .arg(format!("--addr=127.0.0.1:{port}")) + .arg(format!("--mapdir=/path/to:{}", temp.path().display())) + .arg(fixtures::static_server()) + .env("RUST_LOG", RUST_LOG); + let child = JoinableChild::spawn(cmd); + + let body = http_get(format!("http://127.0.0.1:{port}/path/to/file.txt")).unwrap(); + assert!(body.contains("Hello, World!"), "{body}"); + + // And kill the server, making sure it generated the expected logs + let assert = child.join(); + + assert + .stderr(contains("Starting the server")) + .stderr(contains( + "response generated method=GET uri=/path/to/file.txt status_code=200 OK", + )); + } +} + +mod wasm_on_disk { + use super::*; + use predicates::str::contains; + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn wasi_executable() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::qjs()) + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn no_abi() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::fib()) + .assert(); + + assert.success(); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn error_if_no_start_function_found() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(fixtures::wat_no_start()) + .assert(); + + assert + .failure() + .stderr(contains("The module doesn't contain a \"_start\" function")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn pre_compiled() { + let temp = TempDir::new().unwrap(); + let dest = temp.path().join("qjs.wasmu"); + let qjs = fixtures::qjs(); + // Make sure it is compiled + Command::new(get_wasmer_path()) + .arg("compile") + .arg("-o") + .arg(&dest) + .arg(&qjs) + .assert() + .success(); + assert!(dest.exists()); + + // Now we can try to run the compiled artifact + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(&dest) + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } +} + +#[test] +#[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" +)] +fn wasmer_package_directory() { + let temp = TempDir::new().unwrap(); + std::fs::copy(fixtures::qjs(), temp.path().join("qjs.wasm")).unwrap(); + std::fs::copy(fixtures::qjs_wasmer_toml(), temp.path().join("wasmer.toml")).unwrap(); + + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg(temp.path()) + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); +} + +mod remote_webc { + use super::*; + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn quickjs_as_package_name() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg("saghul/quickjs") + .arg("--entrypoint=quickjs") + .arg("--registry=https://wapm.io/") + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } + + #[test] + #[cfg_attr( + all(target_env = "musl", target_os = "linux"), + ignore = "wasmer run-unstable segfaults on musl" + )] + fn quickjs_as_url() { + let assert = Command::new(get_wasmer_path()) + .arg("run-unstable") + .arg("https://wapm.io/saghul/quickjs") + .arg("--entrypoint=quickjs") + .arg("--") + .arg("--eval") + .arg("console.log('Hello, World!')") + .assert(); + + assert.success().stdout(contains("Hello, World!")); + } +} + +mod fixtures { + use std::path::{Path, PathBuf}; + + use wasmer_integration_tests_cli::{ASSET_PATH, C_ASSET_PATH}; + + /// A WEBC file containing the Python interpreter, compiled to WASI. + pub fn python() -> PathBuf { + Path::new(C_ASSET_PATH).join("python-0.1.0.wasmer") + } + + /// A WEBC file containing `wat2wasm`, `wasm-validate`, and other helpful + /// WebAssembly-related commands. + pub fn wabt() -> PathBuf { + Path::new(C_ASSET_PATH).join("wabt-1.0.37.wasmer") + } + + /// A WEBC file containing the WCGI static server. + pub fn static_server() -> PathBuf { + Path::new(C_ASSET_PATH).join("staticserver.webc") + } + + /// The QuickJS interpreter, compiled to a WASI module. + pub fn qjs() -> PathBuf { + Path::new(C_ASSET_PATH).join("qjs.wasm") + } + + /// The `wasmer.toml` file for QuickJS. + pub fn qjs_wasmer_toml() -> PathBuf { + Path::new(C_ASSET_PATH).join("qjs-wasmer.toml") + } + + /// An executable which calculates fib(40) and exits with no output. + pub fn fib() -> PathBuf { + Path::new(ASSET_PATH).join("fib.wat") + } + + pub fn wat_no_start() -> PathBuf { + Path::new(ASSET_PATH).join("no_start.wat") + } +} + +/// A helper that wraps [`std::process::Child`] to make sure it gets terminated +/// when it is no longer needed. +struct JoinableChild(Option); + +impl JoinableChild { + fn spawn(mut cmd: std::process::Command) -> Self { + let child = cmd + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + JoinableChild(Some(child)) + } + + /// Kill the underlying [`std::process::Child`] and get an [`Assert`] we + /// can use to check it. + fn join(mut self) -> Assert { + let mut child = self.0.take().unwrap(); + child.kill().unwrap(); + child.wait_with_output().unwrap().assert() + } +} + +impl Drop for JoinableChild { + fn drop(&mut self) { + if let Some(mut child) = self.0.take() { + eprintln!("==== WARNING: Child was dropped before being joined ===="); + + let _ = child.kill(); + + if let Some(mut stderr) = child.stderr.take() { + let mut buffer = String::new(); + if stderr.read_to_string(&mut buffer).is_ok() { + eprintln!("---- STDERR ----"); + eprintln!("{buffer}"); + } + } + + if !std::thread::panicking() { + panic!("Child was dropped before being joined"); + } + } + } +} + +/// Send a GET request to a particular URL, automatically retrying (with +/// a timeout) if there are any connection errors. +fn http_get(url: impl IntoUrl) -> Result { + let start = Instant::now(); + let timeout = Duration::from_secs(5); + let url = url.into_url().unwrap(); + + let client = Client::new(); + + while start.elapsed() < timeout { + match client.get(url.clone()).send() { + Ok(response) => { + return response.error_for_status()?.text(); + } + Err(e) if e.is_connect() => continue, + Err(other) => return Err(other), + } + } + + panic!("Didn't receive a response from \"{url}\" within the allocated time"); +}