diff --git a/.vscode/settings.json b/.vscode/settings.json index e7f36e0bb4..1078f85852 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,4 +16,7 @@ // "rust-analyzer.check.allTargets": true, // we don't want the formatter to kick in while we're working on dioxus itself "dioxus.formatOnSave": "disabled", + "cSpell.words": [ + "subsecond" + ], } diff --git a/Cargo.lock b/Cargo.lock index bb482827a2..f37a848eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,13 +4,19 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.31.1", + "gimli 0.28.1", ] +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler2" version = "2.0.0" @@ -75,7 +81,7 @@ dependencies = [ "once_cell", "serde", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -286,7 +292,7 @@ dependencies = [ "aws-smithy-types", "base64 0.21.7", "bcder", - "bitflags 2.6.0", + "bitflags 2.9.0", "bytes", "chrono", "clap", @@ -1498,17 +1504,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", + "cc", "cfg-if", "libc", - "miniz_oxide", - "object 0.36.5", + "miniz_oxide 0.7.4", + "object 0.32.2", "rustc-demangle", - "windows-targets 0.52.6", ] [[package]] @@ -1596,13 +1602,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1654,9 +1669,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" dependencies = [ "serde", ] @@ -1966,7 +1981,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cairo-sys-rs", "glib", "libc", @@ -2081,6 +2096,20 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 2.0.6", +] + [[package]] name = "cargo_toml" version = "0.20.5" @@ -2278,9 +2307,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -2288,9 +2317,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -2301,9 +2330,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2359,7 +2388,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block", "cocoa-foundation 0.2.0", "core-foundation 0.10.0", @@ -2389,7 +2418,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block", "core-foundation 0.10.0", "core-graphics-types 0.2.0", @@ -2413,6 +2442,33 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors 3.5.0", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors 3.5.0", + "tracing-core", + "tracing-error", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -2582,7 +2638,7 @@ version = "0.6.3" dependencies = [ "const-serialize", "const-serialize-macro", - "rand 0.8.5", + "rand 0.9.0", "serde", ] @@ -2725,7 +2781,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -2749,7 +2805,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "libc", ] @@ -2935,7 +2991,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "crossterm_winapi", "futures-core", "mio 1.0.3", @@ -3193,7 +3249,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libloading 0.8.6", "winapi", ] @@ -3467,7 +3523,7 @@ name = "dioxus-check" version = "0.6.3" dependencies = [ "indoc", - "owo-colors", + "owo-colors 4.1.0", "pretty_assertions", "proc-macro2", "quote", @@ -3488,7 +3544,7 @@ dependencies = [ "built", "cargo-config2", "cargo-generate", - "cargo_metadata", + "cargo_metadata 0.18.1", "cargo_toml", "chrono", "clap", @@ -3527,14 +3583,15 @@ dependencies = [ "hyper-util", "ignore", "include_dir", - "itertools 0.13.0", + "itertools 0.14.0", "krates", "local-ip-address", "log", "manganis", "manganis-core", + "memmap", "memoize", - "notify", + "notify 6.1.1", "object 0.36.5", "once_cell", "open", @@ -3549,7 +3606,9 @@ dependencies = [ "rustls 0.23.19", "serde", "serde_json", + "shell-words", "strum 0.26.3", + "subsecond-cli-support", "syn 2.0.90", "tar", "tauri-bundler", @@ -3666,14 +3725,15 @@ dependencies = [ "generational-box", "longest-increasing-subsequence", "pretty_assertions", - "rand 0.8.5", + "rand 0.9.0", "reqwest 0.12.9", "rustc-hash 1.1.0", "rustversion", "serde", "slab", "slotmap", - "sysinfo", + "subsecond", + "sysinfo 0.33.1", "tokio", "tracing", "tracing-fluent-assertions", @@ -3768,8 +3828,11 @@ dependencies = [ "dioxus-core", "dioxus-devtools-types", "dioxus-signals", + "libc", + "libloading 0.8.6", "serde", "serde_json", + "subsecond", "tokio", "tracing", "tungstenite 0.23.0", @@ -3782,6 +3845,7 @@ version = "0.6.3" dependencies = [ "dioxus-core", "serde", + "subsecond-types", ] [[package]] @@ -3806,7 +3870,7 @@ dependencies = [ name = "dioxus-dx-wire-format" version = "0.6.3" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.18.1", "serde", "serde_json", ] @@ -3826,7 +3890,7 @@ dependencies = [ "http-range", "openssl", "ouroboros", - "rand 0.8.5", + "rand 0.9.0", "reqwest 0.12.9", "separator", "serde", @@ -4197,7 +4261,7 @@ dependencies = [ "generational-box", "once_cell", "parking_lot", - "rand 0.8.5", + "rand 0.9.0", "reqwest 0.12.9", "rustc-hash 1.1.0", "serde", @@ -4819,18 +4883,34 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide", + "miniz_oxide 0.8.0", "rayon-core", "smallvec", "zune-inflate", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "faster-hex" version = "0.9.0" @@ -4935,7 +5015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -5394,7 +5474,7 @@ version = "0.6.3" dependencies = [ "criterion", "parking_lot", - "rand 0.8.5", + "rand 0.9.0", "tracing", ] @@ -5442,6 +5522,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "ghash" version = "0.5.1" @@ -5468,16 +5560,27 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ - "fallible-iterator", + "fallible-iterator 0.2.0", "indexmap 1.9.3", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator 0.3.0", + "indexmap 2.7.0", + "stable_deref_trait", +] [[package]] name = "gio" @@ -5517,7 +5620,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", "libgit2-sys", "log", @@ -5567,7 +5670,7 @@ version = "0.14.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49aaeef5d98390a3bcf9dbc6440b520b793d1bf3ed99317dc407b02be995b28e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "bstr", "gix-path", "libc", @@ -5618,7 +5721,7 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "bstr", "gix-features", "gix-path", @@ -5705,7 +5808,7 @@ version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8b876ef997a955397809a2ec398d6a45b7a55b4918f2446344330f778d14fd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "gix-path", "libc", "windows-sys 0.52.0", @@ -5767,7 +5870,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "futures-channel", "futures-core", "futures-executor", @@ -5820,7 +5923,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b436093d1598b05e3b7fddc097b2bad32763f53a1beb25ab6f9718c6a60acd09" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cocoa 0.25.0", "crossbeam-channel", "keyboard-types", @@ -5959,7 +6062,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "gpu-alloc-types", ] @@ -5969,7 +6072,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -5991,7 +6094,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "gpu-descriptor-types", "hashbrown 0.14.5", ] @@ -6002,7 +6105,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -6223,7 +6326,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "com", "libc", "libloading 0.8.6", @@ -6882,6 +6985,12 @@ dependencies = [ "quote", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -6958,6 +7067,17 @@ dependencies = [ "libc", ] +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.9.0", + "inotify-sys", + "libc", +] + [[package]] name = "inotify-sys" version = "0.1.5" @@ -7331,7 +7451,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "serde", "unicode-segmentation", ] @@ -7505,9 +7625,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libfuzzer-sys" @@ -7565,7 +7685,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", "redox_syscall", ] @@ -7633,7 +7753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20c9e1f991b3861d25bf872ecca2eb6a73f7a9fe671da047cd1f9b49c65cbc40" dependencies = [ "ahash 0.8.11", - "bitflags 2.6.0", + "bitflags 2.9.0", "browserslist-rs", "const-str", "cssparser 0.33.0", @@ -7851,6 +7971,36 @@ dependencies = [ "winapi", ] +[[package]] +name = "macext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e7c68cadc41a5342a826858a31b0bd656447b4306873628e93ffb807009599" +dependencies = [ + "mach2", + "process-memory", + "sudo", + "sysinfo 0.30.13", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -7960,6 +8110,16 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "memmap2" version = "0.9.5" @@ -8007,7 +8167,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block", "core-graphics-types 0.1.3", "foreign-types 0.5.0", @@ -8068,6 +8228,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -8189,7 +8358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" dependencies = [ "bit-set", - "bitflags 2.6.0", + "bitflags 2.9.0", "codespan-reporting", "hexf-parse", "indexmap 2.7.0", @@ -8243,7 +8412,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -8322,7 +8491,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -8335,7 +8504,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -8394,11 +8563,11 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "crossbeam-channel", "filetime", "fsevent-sys", - "inotify", + "inotify 0.9.6", "kqueue", "libc", "log", @@ -8408,6 +8577,31 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "notify" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +dependencies = [ + "bitflags 2.9.0", + "filetime", + "fsevent-sys", + "inotify 0.11.0", + "kqueue", + "libc", + "log", + "mio 1.0.3", + "notify-types", + "walkdir", + "windows-sys 0.59.0", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "ntapi" version = "0.4.1" @@ -8641,7 +8835,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block2", "libc", "objc2", @@ -8657,7 +8851,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block2", "objc2", "objc2-foundation", @@ -8687,7 +8881,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block2", "libc", "objc2", @@ -8699,7 +8893,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block2", "objc2", "objc2-foundation", @@ -8711,7 +8905,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "block2", "objc2", "objc2-foundation", @@ -8756,7 +8950,10 @@ version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ + "crc32fast", "flate2", + "hashbrown 0.15.2", + "indexmap 2.7.0", "memchr", "ruzstd 0.7.3", "wasmparser 0.218.0", @@ -8828,7 +9025,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -8904,9 +9101,9 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" dependencies = [ "aliasable", "ouroboros_macro", @@ -8915,12 +9112,11 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" dependencies = [ "heck 0.4.1", - "itertools 0.12.1", "proc-macro2", "proc-macro2-diagnostics", "quote", @@ -8945,6 +9141,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "owo-colors" version = "4.1.0" @@ -9021,6 +9223,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "pango" version = "0.18.3" @@ -9052,7 +9264,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7645c578d3a5c4cdf667af1ad39765f5f751c4883d251e050d5e1204b5cad0a9" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cssparser 0.33.0", "log", "phf 0.11.2", @@ -9601,7 +9813,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -9655,7 +9867,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -9680,6 +9892,12 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "pretty-hex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc83ee4a840062f368f9096d80077a9841ec117e17e7f700df81958f1451254" + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -9789,6 +10007,17 @@ dependencies = [ "yansi", ] +[[package]] +name = "process-memory" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9599c34fcc8067c3105dc746c0ce85e3ea61784568b8234179fad490b1dcc1" +dependencies = [ + "libc", + "mach", + "winapi", +] + [[package]] name = "prodash" version = "28.0.0" @@ -10017,6 +10246,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy 0.8.23", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -10037,6 +10277,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -10055,6 +10305,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -10123,7 +10382,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cassowary", "compact_str", "crossterm", @@ -10235,7 +10494,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -10488,7 +10747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702" dependencies = [ "ahash 0.8.11", - "bitflags 2.6.0", + "bitflags 2.9.0", "instant", "num-traits", "once_cell", @@ -10568,7 +10827,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95c49b6baaa0e8fa864b2069d1e94e7a132471da3ac26a132f3fa7e71416772c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "bzip2", "chrono", "cpio", @@ -10679,7 +10938,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -10968,7 +11227,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -10981,7 +11240,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -11642,7 +11901,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -11787,7 +12046,7 @@ dependencies = [ "atoi", "base64 0.21.7", "bigdecimal", - "bitflags 2.6.0", + "bitflags 2.9.0", "byteorder", "bytes", "chrono", @@ -11835,7 +12094,7 @@ dependencies = [ "base64 0.21.7", "bigdecimal", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.9.0", "byteorder", "chrono", "crc 3.2.1", @@ -12040,12 +12299,120 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "subsecond" +version = "0.6.3" +dependencies = [ + "bincode", + "libc", + "libloading 0.8.6", + "serde", + "subsecond-macro", + "subsecond-types", +] + +[[package]] +name = "subsecond-cli" +version = "0.6.3" +dependencies = [ + "anyhow", + "bincode", + "cargo_metadata 0.19.2", + "clap", + "crossterm", + "futures", + "futures-channel", + "futures-util", + "gimli 0.31.1", + "include_dir", + "itertools 0.14.0", + "libc", + "libloading 0.8.6", + "macext", + "memmap", + "notify 8.0.0", + "object 0.36.5", + "ouroboros", + "page_size", + "pretty-hex", + "pretty_assertions", + "rustc-demangle", + "serde", + "serde_json", + "shell-words", + "subsecond-cli-support", + "sysinfo 0.33.1", + "tokio", + "tracing", + "urlencoding", + "walkdir", +] + +[[package]] +name = "subsecond-cli-support" +version = "0.6.3" +dependencies = [ + "anyhow", + "bincode", + "itertools 0.14.0", + "libc", + "memmap", + "object 0.36.5", + "pretty-hex", + "pretty_assertions", + "rustc-demangle", + "serde", + "subsecond-types", + "tokio", +] + +[[package]] +name = "subsecond-harness" +version = "0.6.3" +dependencies = [ + "anyhow", + "color-eyre", + "dioxus", + "rand 0.9.0", + "ratatui", + "subsecond", +] + +[[package]] +name = "subsecond-macro" +version = "0.6.3" +dependencies = [ + "base16", + "digest", + "proc-macro2", + "quote", + "sha2", + "syn 2.0.90", +] + +[[package]] +name = "subsecond-types" +version = "0.6.3" +dependencies = [ + "serde", +] + [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "sudo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bd84d4c082e18e37fef52c0088e4407dabcef19d23a607fb4b5ee03b7d5b83" +dependencies = [ + "libc", + "log", +] + [[package]] name = "supports-color" version = "2.1.0" @@ -12203,7 +12570,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82f448db2d1c52ffd2bd3788d89cafd8b5a75b97f0dc8aae00874dda2647f6b6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "is-macro", "num-bigint", "phf 0.11.2", @@ -12334,7 +12701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fdc36d220bcd51f70b1d78bdd8c1e1a172b4e594c385bdd9614b84a7c0e112" dependencies = [ "better_scoped_tls", - "bitflags 2.6.0", + "bitflags 2.9.0", "indexmap 2.7.0", "once_cell", "phf 0.11.2", @@ -12587,6 +12954,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows 0.52.0", +] + [[package]] name = "sysinfo" version = "0.33.1" @@ -12618,7 +13000,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -12662,7 +13044,7 @@ version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cocoa 0.26.0", "core-foundation 0.10.0", "core-graphics 0.24.0", @@ -13057,9 +13439,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", @@ -13086,9 +13468,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -13309,7 +13691,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "async-compression", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.9.0", "bytes", "futures-core", "futures-util", @@ -13377,6 +13759,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-fluent-assertions" version = "0.3.0" @@ -14026,6 +14418,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -14259,7 +14660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6" dependencies = [ "ahash 0.8.11", - "bitflags 2.6.0", + "bitflags 2.9.0", "hashbrown 0.14.5", "indexmap 2.7.0", "semver 1.0.23", @@ -14272,7 +14673,7 @@ version = "0.218.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -14281,7 +14682,7 @@ version = "0.225.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36e5456165f81e64cb9908a0fe9b9d852c2c74582aa3fe2be3c2da57f937d3ae" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "hashbrown 0.15.2", "indexmap 2.7.0", "semver 1.0.23", @@ -14459,7 +14860,7 @@ checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg_aliases 0.1.1", "codespan-reporting", "indexmap 2.7.0", @@ -14487,7 +14888,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.6.0", + "bitflags 2.9.0", "block", "cfg_aliases 0.1.1", "core-graphics-types 0.1.3", @@ -14528,7 +14929,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "js-sys", "web-sys", ] @@ -15050,6 +15451,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -15321,7 +15731,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -15335,6 +15754,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zerofrom" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index a3aed4b2e6..58daae2127 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,14 @@ members = [ "packages/wasm-split/wasm-split-cli", "packages/wasm-split/wasm-used", + # subsecond + "packages/subsecond/subsecond", + "packages/subsecond/subsecond-macro", + "packages/subsecond/subsecond-cli-support", + "packages/subsecond/subsecond-types", + "packages/subsecond/subsecond-cli", + "packages/subsecond/subsecond-harness", + # Full project examples "example-projects/fullstack-hackernews", "example-projects/ecommerce-site", @@ -102,6 +110,8 @@ members = [ "packages/playwright-tests/nested-suspense", "packages/playwright-tests/cli-optimization", "packages/playwright-tests/wasm-split-harness", + + ] [workspace.package] @@ -149,11 +159,18 @@ const-serialize-macro = { path = "packages/const-serialize-macro", version = "0. generational-box = { path = "packages/generational-box", version = "0.6.2" } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.2" } +# subsecond +subsecond-cli-support = { path = "packages/subsecond/subsecond-cli-support", version = "0.6.3" } +subsecond-types = { path = "packages/subsecond/subsecond-types", version = "0.6.3" } +subsecond-macro = { path = "packages/subsecond/subsecond-macro", version = "0.6.3" } +subsecond = { path = "packages/subsecond/subsecond", version = "0.6.3" } +# manganis manganis = { path = "packages/manganis/manganis", version = "0.6.2" } manganis-core = { path = "packages/manganis/manganis-core", version = "0.6.2" } manganis-macro = { path = "packages/manganis/manganis-macro", version = "0.6.2" } +# wasm-split wasm-split = { path = "packages/wasm-split/wasm-split", version = "0.1.0" } wasm-split-macro = { path = "packages/wasm-split/wasm-split-macro", version = "0.1.0" } wasm-split-cli = { path = "packages/wasm-split/wasm-split-cli", version = "0.1.0" } @@ -224,7 +241,7 @@ chrono = { version = "0.4.34" } gloo = { version = "0.8.0" } gloo-utils = { version = "0.1.6" } rustversion = "1.0.17" -rand = "0.8.5" +rand = "0.9.0" longest-increasing-subsequence = "0.1.0" trybuild = "1.0" dirs = "5.0.1" @@ -245,6 +262,7 @@ async-once-cell = { version = "0.5.3" } rayon = "1.2.0" wasmparser = "0.225.0" itertools = "0.14.0" +object = { version = "0.36.0" } # desktop wry = { version = "0.45.0", default-features = false } @@ -262,6 +280,15 @@ objc = { version = "0.2.7", features = ["exception"] } objc_id = "0.1.1" tray-icon = "0.19" +# tui stuff +ansi-to-tui = "6.0" +ansi-to-html = "0.2.1" +path-absolutize = "3.1" +crossterm = { version = "0.28.0" } +ratatui = { version = "0.28.0" } +shell-words = "1.1.0" +color-eyre = "0.6.3" + # disable debug symbols in dev builds - shouldn't matter for downstream crates but makes our binaries (examples, cli, etc) build faster [profile.dev] debug = 0 @@ -279,6 +306,11 @@ opt-level = 'z' lto = true debug=true +[profile.subsecond-dev] +inherits = "dev" +debug = 0 +strip = "debuginfo" + # a profile for running the CLI that's also incremental [profile.cli-release-dev] inherits = "release" @@ -338,11 +370,11 @@ wasm-split = { workspace = true } [dev-dependencies] dioxus = { workspace = true, features = ["router"] } dioxus-ssr = { workspace = true } -futures-util = "0.3.31" +futures-util = { workspace = true } separator = "0.4.1" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -rand = { version = "0.8.4", features = ["small_rng"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +rand = { workspace = true, features = ["small_rng"] } form_urlencoded = "1.2.0" async-std = "1.12.0" web-time = "1.1.0" diff --git a/packages/cli-opt/Cargo.toml b/packages/cli-opt/Cargo.toml index 3587e3c6ee..1d650b56fd 100644 --- a/packages/cli-opt/Cargo.toml +++ b/packages/cli-opt/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react"] anyhow = { workspace = true } manganis = { workspace = true } manganis-core = { workspace = true } -object = {version="0.36.0", features=["wasm"]} +object = {workspace = true, features = ["wasm"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } const-serialize = { workspace = true, features = ["serde"] } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 5549e53066..c03e838081 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -23,6 +23,7 @@ dioxus-cli-config = { workspace = true } dioxus-cli-opt = { workspace = true } dioxus-fullstack = { workspace = true } dioxus-dx-wire-format = { workspace = true } +subsecond-cli-support = { workspace = true } wasm-split-cli = { workspace = true } clap = { workspace = true, features = ["derive", "cargo"] } @@ -96,11 +97,12 @@ tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter", "jso console-subscriber = { version = "0.3.0", optional = true } tracing = { workspace = true } wasm-opt = { version = "0.116.1", optional = true } -crossterm = { version = "0.28.0", features = ["event-stream"] } -ansi-to-tui = "6.0" -ansi-to-html = "0.2.1" -path-absolutize = "3.1" -ratatui = { version = "0.28.0", features = ["crossterm", "unstable"] } +ansi-to-tui = { workspace = true } +ansi-to-html = { workspace = true } +path-absolutize = { workspace = true } +crossterm = { workspace = true, features = ["event-stream"] } +ratatui = { workspace = true, features = ["crossterm", "unstable"] } +shell-words = { workspace = true } # disable `log` entirely since `walrus` uses it and is *much* slower with it enableda log = { version = "0.4", features = ["max_level_off", "release_max_level_off"] } @@ -111,9 +113,9 @@ manganis = { workspace = true } manganis-core = { workspace = true } # Extracting data from an executable -object = {version="0.36.0", features=["wasm"]} +object = { workspace = true, features = ["all"] } tokio-util = { version = "0.7.11", features = ["full"] } -itertools = "0.13.0" +itertools = "0.14.0" throbber-widgets-tui = "=0.7.0" unicode-segmentation = "1.12.0" handlebars = "6.1.0" @@ -129,6 +131,8 @@ dircpy = "0.3.19" plist = "1.7.0" memoize = "0.5.1" +memmap = "0.7.0" + [build-dependencies] built = { version = "=0.7.4", features = ["git2"] } diff --git a/packages/cli/src/build/builder.rs b/packages/cli/src/build/builder.rs index 68e90c9548..78bed7de4b 100644 --- a/packages/cli/src/build/builder.rs +++ b/packages/cli/src/build/builder.rs @@ -4,6 +4,8 @@ use crate::{ }; use std::time::{Duration, Instant}; +use super::BuildMode; + /// The component of the serve engine that watches ongoing builds and manages their state, handle, /// and progress. /// @@ -39,17 +41,15 @@ impl Builder { /// Create a new builder and immediately start a build pub(crate) fn start(krate: &DioxusCrate, args: BuildArgs) -> Result { let (tx, rx) = futures_channel::mpsc::unbounded(); - let request = BuildRequest::new(krate.clone(), args, tx.clone()); + let request = BuildRequest::new(krate.clone(), args, tx.clone(), BuildMode::Fat); Ok(Self { krate: krate.clone(), request: request.clone(), stage: BuildStage::Initializing, build: tokio::spawn(async move { - // On the first build, we want to verify the tooling - // We wont bother verifying on subsequent builds + // On the first build, we want to verify the tooling... don't bother on subsequent builds request.verify_tooling().await?; - request.build_all().await }), tx, @@ -101,6 +101,7 @@ impl Builder { BuildStage::Starting { crate_count, is_server, + .. } => { if *is_server { self.expected_crates_server = *crate_count; @@ -177,13 +178,31 @@ impl Builder { update } + pub(crate) fn patch_rebuild(&mut self, args: BuildArgs, direct_rustc: Vec) { + // Abort all the ongoing builds, cleaning up any loose artifacts and waiting to cleanly exit + self.abort_all(); + + // And then start a new build, resetting our progress/stage to the beginning and replacing the old tokio task + let request = BuildRequest::new( + self.krate.clone(), + args, + self.tx.clone(), + BuildMode::Thin { direct_rustc }, + ); + self.request = request.clone(); + self.stage = BuildStage::Restarting; + + // This build doesn't have any extra special logging - rebuilds would get pretty noisy + self.build = tokio::spawn(async move { request.build_all().await }); + } + /// Restart this builder with new build arguments. pub(crate) fn rebuild(&mut self, args: BuildArgs) { // Abort all the ongoing builds, cleaning up any loose artifacts and waiting to cleanly exit self.abort_all(); // And then start a new build, resetting our progress/stage to the beginning and replacing the old tokio task - let request = BuildRequest::new(self.krate.clone(), args, self.tx.clone()); + let request = BuildRequest::new(self.krate.clone(), args, self.tx.clone(), BuildMode::Fat); self.request = request.clone(); self.stage = BuildStage::Restarting; diff --git a/packages/cli/src/build/bundle.rs b/packages/cli/src/build/bundle.rs index e2b769f9d2..c83b024fd5 100644 --- a/packages/cli/src/build/bundle.rs +++ b/packages/cli/src/build/bundle.rs @@ -1,16 +1,19 @@ use super::prerender::pre_render_static_routes; use super::templates::InfoPlistData; -use crate::{BuildRequest, Platform, WasmOptConfig}; +use crate::{BuildMode, BuildRequest, Platform, WasmOptConfig}; use crate::{Result, TraceSrc}; use anyhow::Context; use dioxus_cli_opt::{process_file_to, AssetManifest}; use manganis::{AssetOptions, JsAssetOptions}; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; -use std::future::Future; -use std::path::{Path, PathBuf}; -use std::pin::Pin; use std::sync::atomic::Ordering; use std::{collections::HashSet, io::Write}; +use std::{future::Future, time::Instant}; +use std::{ + path::{Path, PathBuf}, + time::UNIX_EPOCH, +}; +use std::{pin::Pin, time::SystemTime}; use std::{sync::atomic::AtomicUsize, time::Duration}; use tokio::process::Command; @@ -90,14 +93,25 @@ use tokio::process::Command; pub(crate) struct AppBundle { pub(crate) build: BuildRequest, pub(crate) app: BuildArtifacts, + pub(crate) assets: AssetManifest, pub(crate) server: Option, + pub(crate) server_assets: Option, } +#[derive(Debug)] +pub struct UnbundledApp { + pub(crate) request: BuildRequest, + pub(crate) app: BuildArtifacts, + pub(crate) server: Option, +} + +/// The result of the `cargo rustc` including any additional metadata #[derive(Debug)] pub struct BuildArtifacts { pub(crate) exe: PathBuf, - pub(crate) assets: AssetManifest, - pub(crate) time_taken: Duration, + pub(crate) direct_rustc: Vec>, + pub(crate) time_start: SystemTime, + pub(crate) time_end: SystemTime, } impl AppBundle { @@ -264,35 +278,65 @@ impl AppBundle { app: BuildArtifacts, server: Option, ) -> Result { - let mut bundle = Self { app, server, build }; + let mut bundle = Self { + app, + server, + build, + assets: Default::default(), + server_assets: Default::default(), + }; - tracing::debug!("Assembling app bundle"); + match bundle.build.mode { + BuildMode::Base | BuildMode::Fat => { + tracing::debug!("Assembling app bundle"); + + bundle.build.status_start_bundle(); + bundle + .write_main_executable() + .await + .context("Failed to write main executable")?; + bundle.write_server_executable().await?; + bundle + .write_assets() + .await + .context("Failed to write assets")?; + bundle.write_metadata().await?; + bundle.optimize().await?; + bundle.pre_render_ssg_routes().await?; + bundle + .assemble() + .await + .context("Failed to assemble app bundle")?; + + tracing::debug!("Bundle created at {}", bundle.build.root_dir().display()); + } + BuildMode::Thin { .. } => { + tracing::debug!("Patching existing bundle"); + bundle.write_patch().await?; + } + } - bundle.build.status_start_bundle(); - /* - assume the build dir is already created by BuildRequest - todo(jon): maybe refactor this a bit to force AppBundle to be created before it can be filled in - */ - bundle - .write_main_executable() - .await - .context("Failed to write main executable")?; - bundle.write_server_executable().await?; - bundle - .write_assets() - .await - .context("Failed to write assets")?; - bundle.write_metadata().await?; - bundle.optimize().await?; - bundle.pre_render_ssg_routes().await?; - bundle - .assemble() - .await - .context("Failed to assemble app bundle")?; + Ok(bundle) + } - tracing::debug!("Bundle created at {}", bundle.build.root_dir().display()); + /// Traverse the target directory and collect all assets from the incremental cache + /// + /// This uses "known paths" that have stayed relatively stable during cargo's lifetime. + /// One day this system might break and we might need to go back to using the linker approach. + pub(crate) async fn collect_assets(&self, exe: &Path) -> Result { + tracing::debug!("Collecting assets ..."); - Ok(bundle) + if self.build.build.skip_assets { + return Ok(AssetManifest::default()); + } + + // walk every file in the incremental cache dir, reading and inserting items into the manifest. + let mut manifest = AssetManifest::default(); + + // And then add from the exe directly, just in case it's LTO compiled and has no incremental cache + _ = manifest.add_from_object_path(exe); + + Ok(manifest) } /// Take the output of rustc and make it into the main exe of the bundle @@ -347,6 +391,8 @@ impl AppBundle { | Platform::Ios | Platform::Liveview | Platform::Server => { + _ = std::fs::remove_dir_all(self.build.exe_dir()); + std::fs::create_dir_all(self.build.exe_dir())?; std::fs::copy(&self.app.exe, self.main_exe())?; } } @@ -369,7 +415,6 @@ impl AppBundle { _ = tokio::fs::create_dir_all(&asset_dir).await; // Create a set of all the paths that new files will be bundled to let mut keep_bundled_output_paths: HashSet<_> = self - .app .assets .assets .values() @@ -429,7 +474,7 @@ impl AppBundle { let mut assets_to_transfer = vec![]; // Queue the bundled assets - for (asset, bundled) in &self.app.assets.assets { + for (asset, bundled) in &self.assets.assets { let from = asset.clone(); let to = asset_dir.join(bundled.bundled_path()); @@ -502,6 +547,36 @@ impl AppBundle { Ok(()) } + /// patch-{time}.(so/dll/dylib) (next to the main exe) + pub fn patch_exe(&self) -> PathBuf { + let path = self.main_exe().with_file_name(format!( + "patch-{}", + self.app + .time_start + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(), + )); + + let extension = match self.build.build.platform() { + Platform::Web => "wasm", + Platform::MacOS => "dylib", + Platform::Windows => "dll", + Platform::Linux => "so", + Platform::Ios => "dylib", + Platform::Android => "so", + Platform::Server => todo!(), + Platform::Liveview => todo!(), + }; + + path.with_extension("") + } + + async fn write_patch(&self) -> Result<()> { + std::fs::copy(&self.app.exe, self.patch_exe())?; + Ok(()) + } + /// The item that we'll try to run directly if we need to. /// /// todo(jon): we should name the app properly instead of making up the exe name. It's kinda okay for dev mode, but def not okay for prod @@ -723,7 +798,6 @@ impl AppBundle { writeln!( glue, "export const __wasm_split_load_chunk_{idx} = makeLoad(\"/assets/{url}\", [], fusedImports);", url = self - .app .assets .register_asset(&path, AssetOptions::Unknown)?.bundled_path(), )?; @@ -750,7 +824,6 @@ impl AppBundle { // Again, register this wasm with the asset system url = self - .app .assets .register_asset(&path, AssetOptions::Unknown)?.bundled_path(), @@ -792,12 +865,11 @@ impl AppBundle { } // Make sure to register the main wasm file with the asset system - self.app - .assets + self.assets .register_asset(&post_bindgen_wasm, AssetOptions::Unknown)?; // Register the main.js with the asset system so it bundles in the snippets and optimizes - self.app.assets.register_asset( + self.assets.register_asset( &self.build.wasm_bindgen_js_output_file(), AssetOptions::Js(JsAssetOptions::new().with_minify(true).with_preload(true)), )?; @@ -952,4 +1024,6 @@ impl AppBundle { std::fs::copy(source, destination)?; Ok(()) } + + async fn binary_patch(&self) {} } diff --git a/packages/cli/src/build/mod.rs b/packages/cli/src/build/mod.rs index 56d9eb40b6..e3b05e8479 100644 --- a/packages/cli/src/build/mod.rs +++ b/packages/cli/src/build/mod.rs @@ -7,6 +7,7 @@ mod builder; mod bundle; +mod patch; mod prerender; mod progress; mod request; @@ -16,5 +17,6 @@ mod web; pub(crate) use builder::*; pub(crate) use bundle::*; +pub(crate) use patch::*; pub(crate) use progress::*; pub(crate) use request::*; diff --git a/packages/cli/src/build/patch.rs b/packages/cli/src/build/patch.rs new file mode 100644 index 0000000000..97f15b9893 --- /dev/null +++ b/packages/cli/src/build/patch.rs @@ -0,0 +1,763 @@ +use anyhow::{Context, Result}; +use itertools::Itertools; +use memmap::{Mmap, MmapOptions}; +use object::{ + read::File, Architecture, BinaryFormat, Endianness, Object, ObjectSection, ObjectSymbol, + Relocation, RelocationTarget, SectionIndex, +}; +use std::{cmp::Ordering, ffi::OsStr, fs, ops::Deref, path::PathBuf}; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + path::Path, +}; +use tokio::process::Command; + +use crate::Platform; + +pub enum ReloadKind { + /// An RSX-only patch + Rsx, + + /// A patch that includes both RSX and binary assets + Binary, + + /// A full rebuild + Full, +} + +#[derive(Debug, Clone)] +pub struct PatchData { + pub direct_rustc: Vec, +} + +pub async fn attempt_partial_link( + linker: PathBuf, + work_dir: PathBuf, + old_cache: PathBuf, + new_cache: PathBuf, + proc_main_addr: u64, + patch_target: PathBuf, + out_path: PathBuf, +) { + let mut object = ObjectDiff::new(old_cache, new_cache).unwrap(); + object.load().unwrap(); + + let all_exports = object + .new + .iter() + .flat_map(|(_, f)| f.file.exports().unwrap()) + .map(|e| e.name()) + .collect::>(); + + let mut adrp_imports = HashSet::new(); + + let mut satisfied_exports = HashSet::new(); + + let modified_symbols = object + .modified_symbols + .iter() + .map(|f| f.as_str()) + .collect::>(); + + if modified_symbols.is_empty() { + println!("No modified symbols"); + } + + let mut modified_log = String::new(); + for m in modified_symbols.iter() { + // if m.starts_with("l") { + // continue; + // } + + let path = object.find_path_to_main(m); + println!("m: {m}"); + println!("path: {path:#?}\n"); + modified_log.push_str(&format!("{m}\n")); + modified_log.push_str(&format!("{path:#?}\n")); + } + + let modified = object + .modified_files + .iter() + .sorted_by(|a, b| a.0.cmp(&b.0)) + .collect::>(); + + // Figure out which symbols are required from *existing* code + // We're going to create a stub `.o` file that satisfies these by jumping into the original code via a dynamic lookup / and or literally just manually doing it + for fil in modified.iter() { + let f = object + .new + .get(fil.0.file_name().unwrap().to_str().unwrap()) + .unwrap(); + + for i in f.file.imports().unwrap() { + if all_exports.contains(i.name()) { + adrp_imports.insert(i.name()); + } + } + + for e in f.file.exports().unwrap() { + satisfied_exports.insert(e.name()); + } + } + + // Remove any imports that are indeed satisifed + for s in satisfied_exports.iter() { + adrp_imports.remove(s); + } + + // // Assemble the stub + // let stub_data = make_stub_file(proc_main_addr, patch_target, adrp_imports); + // let stub_file = work_dir.join("stub.o"); + // std::fs::write(&stub_file, stub_data).unwrap(); + + // let out = Command::new("cc") + // .args(modified.iter().map(|(f, _)| f)) + // .arg(stub_file) + // .arg("-dylib") + // .arg("-Wl,-undefined,dynamic_lookup") + // .arg("-Wl,-unexported_symbol,_main") + // .arg("-arch") + // .arg("arm64") + // .arg("-dead_strip") + // .arg("-o") + // .arg(out_path) + // .output() + // .await + // .unwrap(); + + // -O0 ? supposedly faster + // -reproducible - even better? + // -exported_symbol and friends - could help with dead-code stripping + // -e symbol_name - for setting the entrypoint + // -keep_relocs ? + + // run the linker, but unexport the `_main` symbol + let out = Command::new("cc") + .args(modified.iter().map(|(f, _)| f)) + .arg("-dylib") + .arg("-undefined") + .arg("dynamic_lookup") + .arg("-Wl,-unexported_symbol,_main") + // .arg("-Wl,-export-dynamic") + .arg("-arch") + .arg("arm64") + .arg("-dead_strip") // maybe? + .arg("-o") + .arg(&out_path) + // .stdout(Stdio::piped()) + // .stderr(Stdio::piped()) + .output() + .await + .unwrap(); + + let err = String::from_utf8_lossy(&out.stderr); + println!("err: {err}"); + std::fs::write(work_dir.join("link_errs_partial.txt"), &*err).unwrap(); +} + +/// todo: detect if the user specified a custom linker +fn system_linker(platform: Platform) -> &'static str { + match platform { + // mac + linux use just CC unless the user is trying to use something like mold / lld + Platform::MacOS => "cc", + Platform::Linux => "cc", + Platform::Windows => "cc", + Platform::Ios => "cc", + Platform::Android => "cc", + Platform::Server => "cc", + Platform::Liveview => "cc", + Platform::Web => "wasm-ld", + } +} + +struct ObjectDiff { + old: BTreeMap, + new: BTreeMap, + modified_files: HashMap>, + modified_symbols: HashSet, + parents: HashMap>, +} + +impl ObjectDiff { + fn new(old_cache: PathBuf, new_cache: PathBuf) -> Result { + Ok(Self { + old: LoadedFile::from_dir(&old_cache)?, + new: LoadedFile::from_dir(&new_cache)?, + modified_files: Default::default(), + modified_symbols: Default::default(), + parents: Default::default(), + }) + } + + fn load(&mut self) -> Result<()> { + let num_right = self.new.len(); + + let keys = self.new.keys().cloned().collect::>(); + for (idx, f) in keys.iter().enumerate() { + println!("----- {:?} {}/{} -----", f, idx, num_right); + + let changed_before = self.modified_symbols.len(); + self.load_file(f)?; + let changed_after = self.modified_symbols.len(); + + if changed_after > changed_before { + println!("❌ -> {}", changed_after - changed_before); + } + } + + Ok(()) + } + + /// Walk the call to find the path to the main function + fn find_path_to_main(&self, name: &str) -> Vec { + let mut path = Vec::new(); + let mut visited = std::collections::HashSet::new(); + + // Helper function for DFS with backtracking + fn dfs( + current: &str, + path: &mut Vec, + visited: &mut std::collections::HashSet, + parents: &std::collections::HashMap>, + ) -> bool { + // If we've found main, we're done + if current.ends_with("_main") { + path.push(current.to_string()); + return true; + } + + // Mark current node as visited + visited.insert(current.to_string()); + path.push(current.to_string()); + + // Check all parents of the current node + if let Some(parent_nodes) = parents.get(current) { + for parent in parent_nodes { + if !visited.contains(parent) { + if dfs(parent, path, visited, parents) { + return true; + } + } + } + } + + // If no path is found through this node, backtrack + path.pop(); + + false + } + + // Start DFS from the given name + dfs(name, &mut path, &mut visited, &self.parents); + + path + } + + fn load_file(&mut self, name: &str) -> Result<()> { + let new = &self.new[name]; + let Some(old) = self.old.get(name) else { + self.modified_files.entry(new.path.clone()).or_default(); + return Ok(()); + }; + + let mut changed_list = HashSet::new(); + for section in new.file.sections() { + let n = section.name().unwrap(); + if n == "__text" + || n == "__const" + || n.starts_with("__literal") + || n == "__eh_frame" + || n == "__compact_unwind" + || n == "__gcc_except_tab" + || n == "__common" + || n == "__bss" + { + changed_list.extend(self.accumulate_changed(&old, &new, section.index())); + } else { + println!("Skipping section: {n}"); + } + } + + for c in changed_list.iter() { + if !c.starts_with("l") && !c.starts_with("ltmp") { + self.modified_symbols.insert(c.to_string()); + } else { + let mod_name = format!("{c}_{name}"); + self.modified_symbols.insert(mod_name); + } + } + + for (child, parents) in new.parents.iter() { + let child_name = match child.starts_with("l") { + true => format!("{child}_{name}"), + false => child.to_string(), + }; + + for parent in parents { + let p_name = match parent.starts_with("l") { + true => format!("{parent}_{name}"), + false => parent.to_string(), + }; + + self.parents + .entry(child_name.clone()) + .or_default() + .insert(p_name); + } + } + + Ok(()) + } + + fn accumulate_changed( + &self, + old: &LoadedFile, + new: &LoadedFile, + section_idx: SectionIndex, + ) -> HashSet<&'static str> { + let mut local_modified = HashSet::new(); + + // Accumulate modified symbols using masking in functions + let relocated_new = acc_symbols(&new.file, section_idx); + let mut relocated_old = acc_symbols(&old.file, section_idx) + .into_iter() + .map(|f| (f.name, f)) + .collect::>(); + + for right in relocated_new { + let Some(left) = relocated_old.remove(right.name) else { + local_modified.insert(right.name); + continue; + }; + + // If the contents of the assembly changed, track it + if !compare_masked(old.file, new.file, &left, &right) { + local_modified.insert(left.name); + local_modified.insert(right.name); + } + } + + local_modified + } +} + +/// A file loaded into memory with its analysis +/// +/// We leak the module to make it easier to deal with its contents +struct LoadedFile { + path: PathBuf, + open_file: std::fs::File, + mmap: &'static Mmap, + + file: &'static File<'static>, + + // symbol -> symbols + parents: HashMap<&'static str, HashSet<&'static str>>, +} + +impl LoadedFile { + fn from_dir(dir: &Path) -> anyhow::Result> { + std::fs::read_dir(dir)? + .into_iter() + .flatten() + .filter(|e| e.path().extension() == Some(OsStr::new("o"))) + .map(|e| { + Ok(( + e.path().file_name().unwrap().to_string_lossy().to_string(), + Self::new(e.path())?, + )) + }) + .collect() + } + + fn new(path: PathBuf) -> anyhow::Result { + let open_file = std::fs::File::open(&path)?; + let mmap = unsafe { MmapOptions::new().map(&open_file).unwrap() }; + let mmap: &'static Mmap = Box::leak(Box::new(mmap)); + let f = File::parse(mmap.deref() as &[u8])?; + let file: &'static File<'static> = Box::leak(Box::new(f)); + + // Set up the data structures + let mut sym_tab = HashMap::<&'static str, RelocatedSymbol<'static>>::new(); + let mut parents = HashMap::<&'static str, HashSet<&'static str>>::new(); + + // Build the symbol table + for sect in file.sections() { + for r in acc_symbols(&file, sect.index()) { + sym_tab.insert(r.name, r); + } + } + + // Create a map of address -> symbol so we can resolve the section of a symbol + let local_defs = file + .symbols() + .filter(|s| s.is_definition()) + .map(|s| (s.address(), s.name().unwrap())) + .collect::>(); + + // Build the call graph by walking the relocations + // We keep track of what calls whata + for (sym_name, sym) in sym_tab.iter() { + let sym_section = file.section_by_index(sym.section).unwrap(); + let sym_data = sym_section.data().unwrap(); + + for (addr, reloc) in sym.relocations.iter() { + let target = match symbol_name_of_relo(file, reloc.target()) { + Some(name) => name, + None => { + let addend = u64::from_le_bytes( + sym_data[*addr as usize..(*addr + 8) as usize] + .try_into() + .unwrap(), + ); + local_defs.get(&addend).unwrap() + } + }; + + parents.entry(target).or_default().insert(sym_name); + } + } + + Ok(Self { + path, + open_file, + mmap, + file, + parents, + }) + } +} + +/// A function with its relevant relocations to be used for masked comparisons +struct RelocatedSymbol<'a> { + name: &'a str, + /// offset within the section + offset: usize, + data: &'a [u8], + relocations: &'a [(u64, Relocation)], + sym: object::Symbol<'a, 'a>, + section: SectionIndex, +} + +fn acc_symbols<'a>(new: &'a File<'a>, section_idx: SectionIndex) -> Vec> { + let mut syms = vec![]; + + let section = new.section_by_index(section_idx).unwrap(); + + let sorted = new + .symbols() + .filter(|s| s.section_index() == Some(section_idx)) + .sorted_by(|a, b| { + let addr = a.address().cmp(&b.address()); + if addr == Ordering::Equal { + a.index().0.cmp(&b.index().0) + } else { + addr + } + }) + .collect::>(); + + // todo!!!!!! jon: don't leak this lol + let relocations = section + .relocations() + .sorted_by(|a, b| a.0.cmp(&b.0).reverse()) + .collect::>() + .leak(); + + let data = section.data().unwrap(); + + // No symbols, no symbols, + if sorted.is_empty() { + return vec![]; + } + + // The end of the currently analyzed function + let mut func_end = section.size() as usize; + + // The idx into the relocation list that applies to this function. We'll march these + let mut reloc_idx = 0; + + // Walk in reverse so we can use the text_length as the initial backstop and to match relocation order + for sym in sorted.into_iter().rev() { + let sym_offset = sym.address() - section.address(); + + // Move the head/tail to include the sub-slice of the relocations that apply to this symbol + let mut reloc_start = None; + loop { + // If we've reached the end of the relocations then we're done + if reloc_idx == relocations.len() { + break; + } + + // relocations behind the symbol start don't apply + if relocations[reloc_idx].0 < sym_offset { + break; + } + + // Set the head to the first relocation that applies + if reloc_start.is_none() { + reloc_start = Some(reloc_idx); + } + + reloc_idx += 1; + } + + // Identify the instructions that apply to this symbol + let data = match reloc_start { + Some(_start) => &data[sym_offset as usize..func_end], + _ => &[], + }; + + // Identify the relocations that apply to this symbol + let relocations = match reloc_start { + Some(start) => &relocations[start..reloc_idx], + None => &[], + }; + + syms.push(RelocatedSymbol { + name: sym.name().unwrap(), + sym, + offset: sym_offset as usize, + data, + relocations, + section: section_idx, + }); + + func_end = sym_offset as usize; + } + + assert_eq!(reloc_idx, relocations.len()); + + syms +} + +/// Compare two sets of bytes, masking out the bytes that are not part of the symbol +/// This is so we can compare functions with different relocations +fn compare_masked<'a>( + old: &impl Object<'a>, + new: &impl Object<'a>, + left: &RelocatedSymbol, + right: &RelocatedSymbol, +) -> bool { + // Make sure the relocations are the same length + if left.relocations.len() != right.relocations.len() { + return false; + } + + // Make sure the data is the same length + // If the size changed then the instructions are different (well, not necessarily, but enough) + if left.data.len() != right.data.len() { + return false; + } + + // Make sure the names match + if left.name != right.name { + return false; + } + + // We're going to walk from relocation target to target, but since there's no implicit target + // to start with, we simply use the end of the data + let mut last = left.data.len(); + + // Ensure the relocations point to the same symbol + // Data symbols are special ... todo + // + // relocations are in reverse order, so we can also compare the data as we go + for x in 0..left.relocations.len() { + // Grab the reloc + let (l_addr, left_reloc): &(u64, Relocation) = &left.relocations[x]; + let (_r_addr, right_reloc): &(u64, Relocation) = &right.relocations[x]; + + // The targets might not be same by index but should resolve to the same *name* + let left_target: RelocationTarget = left_reloc.target(); + let right_target: RelocationTarget = right_reloc.target(); + + // Use the name of the symbol to compare + // todo: decide if it's internal vs external + let left_name = symbol_name_of_relo(old, left_target); + let right_name = symbol_name_of_relo(new, right_target); + let (Some(left_name), Some(right_name)) = (left_name, right_name) else { + continue; + }; + + // Make sure the names match + // if the target is a locally defined symbol, then it might be the same + // todo(jon): hash the masked contents + if left_name != right_name { + return false; + } + + // Check the data + // the slice is the end of the relocation to the start of the previous relocation + let reloc_byte_size = (left_reloc.size() as usize) / 8; + let start = *l_addr as usize - left.offset as usize + reloc_byte_size; + + // Some relocations target the same location + // In these cases, we just continue since we just masked and checked them already + if (*l_addr as usize - left.offset as usize) == last { + continue; + } + + debug_assert!(start <= last); + debug_assert!(start <= left.data.len()); + + if &left.data[start..last] != &right.data[start..last] { + return false; + } + + if left_reloc.flags() != right_reloc.flags() { + return false; + } + + // todo: more checking... the symbols might be local + last = start - reloc_byte_size; + } + + // And a final check to make sure the data is the same + if left.data[..last] != right.data[..last] { + return false; + } + + true +} + +fn symbol_name_of_relo<'a>(obj: &impl Object<'a>, target: RelocationTarget) -> Option<&'a str> { + match target { + RelocationTarget::Symbol(symbol_index) => obj + .symbol_by_index(symbol_index) + .unwrap() + .name_bytes() + .ok() + .and_then(|s| std::str::from_utf8(s).ok()), + RelocationTarget::Section(_) => None, + RelocationTarget::Absolute => None, + _ => None, + } +} + +fn make_stub_file( + proc_main_addr: u64, + patch_target: PathBuf, + adrp_imports: HashSet<&[u8]>, +) -> Vec { + let data = fs::read(&patch_target).unwrap(); + let old = File::parse(&data as &[u8]).unwrap(); + let main_sym = old.symbol_by_name_bytes(b"_main").unwrap(); + let aslr_offset = proc_main_addr - main_sym.address(); + let addressed = old + .symbols() + .filter_map(|sym| { + adrp_imports + .get(sym.name_bytes().ok()?) + .copied() + .map(|o| (o, sym.address() + aslr_offset)) + }) + .collect::>(); + + build_stub( + old.format(), + old.architecture(), + old.endianness(), + addressed, + ) + .unwrap() +} + +/// Builds an object file that satisfies the imports +/// +/// Creates stub functions that jump to known addresses in a target process. +/// +/// .section __TEXT,__text +/// .globl __ZN4core3fmt3num52_$LT$impl$u20$core..fmt..Debug$u20$for$u20$usize$GT$3fmt17h4e710f94be547818E +/// .p2align 2 +/// __ZN4core3fmt3num52_$LT$impl$u20$core..fmt..Debug$u20$for$u20$usize$GT$3fmt17h4e710f94be547818E: +/// // Load 64-bit address using immediate values +/// movz x9, #0xCDEF // Bottom 16 bits +/// movk x9, #0x89AB, lsl #16 // Next 16 bits +/// movk x9, #0x4567, lsl #32 // Next 16 bits +/// movk x9, #0x0123, lsl #48 // Top 16 bits +/// +/// // Branch to the loaded address +/// br x9 +fn build_stub( + format: BinaryFormat, + architecture: Architecture, + endian: Endianness, + adrp_imports: HashMap<&[u8], u64>, +) -> Result> { + use object::{ + write::{Object, Symbol, SymbolSection}, + SectionKind, SymbolFlags, SymbolKind, SymbolScope, + }; + + // Create a new ARM64 object file + let mut obj = Object::new(format, architecture, endian); + + // Add a text section for our trampolines + let text_section = obj.add_section(Vec::new(), ".text".into(), SectionKind::Text); + + // For each symbol, create a trampoline that loads the immediate address and jumps to it + for (name, addr) in adrp_imports { + let mut trampoline = Vec::new(); + + // todo: writing these bytes are only good for arm64 + // + // + // Break down the 64-bit address into 16-bit chunks + let addr0 = (addr & 0xFFFF) as u16; // Bits 0-15 + let addr1 = ((addr >> 16) & 0xFFFF) as u16; // Bits 16-31 + let addr2 = ((addr >> 32) & 0xFFFF) as u16; // Bits 32-47 + let addr3 = ((addr >> 48) & 0xFFFF) as u16; // Bits 48-63 + + // MOVZ x9, #addr0 + let movz = 0xD2800009 | ((addr0 as u32) << 5); + trampoline.extend_from_slice(&movz.to_le_bytes()); + + // MOVK x9, #addr1, LSL #16 + let movk1 = 0xF2A00009 | ((addr1 as u32) << 5); + trampoline.extend_from_slice(&movk1.to_le_bytes()); + + // MOVK x9, #addr2, LSL #32 + let movk2 = 0xF2C00009 | ((addr2 as u32) << 5); + trampoline.extend_from_slice(&movk2.to_le_bytes()); + + // MOVK x9, #addr3, LSL #48 + let movk3 = 0xF2E00009 | ((addr3 as u32) << 5); + trampoline.extend_from_slice(&movk3.to_le_bytes()); + + // BR x9 - Branch to the address in x9 + let br: u32 = 0xD61F0120; + trampoline.extend_from_slice(&br.to_le_bytes()); + + // Add the trampoline to the text section + let symbol_offset = obj.append_section_data(text_section, &trampoline, 4); + + // we are writing this: + // __ZN93_$LT$generational_box..references..GenerationalRef$LT$R$GT$$u20$as$u20$core..fmt..Display$GT$3fmt17h455abb35572b9c11E + // + // but we should be writing this: + // _$LT$generational_box..references..GenerationalRef$LT$R$GT$$u20$as$u20$core..fmt..Display$GT$::fmt::h455abb35572b9c11 + // let name = strip_mangled(name); + + let name = if name.starts_with(b"_") { + &name[1..] + } else { + name + }; + + // Add the symbol + obj.add_symbol(Symbol { + name: name.into(), + value: symbol_offset, + size: trampoline.len() as u64, + kind: SymbolKind::Text, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(text_section), + flags: SymbolFlags::None, + }); + } + + obj.write().context("Failed to write object file") +} diff --git a/packages/cli/src/build/progress.rs b/packages/cli/src/build/progress.rs index e0efa41023..ea0d9d3661 100644 --- a/packages/cli/src/build/progress.rs +++ b/packages/cli/src/build/progress.rs @@ -70,6 +70,7 @@ impl BuildRequest { _ = self.progress.unbounded_send(BuildUpdate::Progress { stage: BuildStage::Starting { is_server: self.build.platform() == Platform::Server, + patch: self.is_patch(), crate_count, }, }); diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index c4e288ea1f..3fef5363a9 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -1,15 +1,16 @@ -use super::{progress::ProgressTx, BuildArtifacts}; +use super::{progress::ProgressTx, BuildArtifacts, PatchData}; use crate::dioxus_crate::DioxusCrate; use crate::{link::LinkAction, BuildArgs}; use crate::{AppBundle, Platform, Result, TraceSrc}; use anyhow::Context; use dioxus_cli_config::{APP_TITLE_ENV, ASSET_ROOT_ENV}; use dioxus_cli_opt::AssetManifest; +use krates::Utf8PathBuf; use serde::Deserialize; use std::{ path::{Path, PathBuf}, process::Stdio, - time::Instant, + time::{Instant, SystemTime}, }; use tokio::{io::AsyncBufReadExt, process::Command}; @@ -26,14 +27,42 @@ pub(crate) struct BuildRequest { /// The target directory for the build pub(crate) custom_target_dir: Option, + + /// How we'll go about building + pub(crate) mode: BuildMode, +} + +/// dx can produce different "modes" of a build. A "regular" build is a "base" build. The Fat and Thin +/// modes are used together to achieve binary patching and linking. +#[derive(Clone, Debug)] +pub enum BuildMode { + /// A normal build generated using `cargo rustc` + Base, + + /// A "Fat" build generated with cargo rustc and dx as a custom linker without -Wl,-dead-strip + Fat, + + /// A "thin" build generated with `rustc` directly and dx as a custom linker + Thin { direct_rustc: Vec }, +} + +pub struct CargoBuildResult { + rustc_args: Vec>, + exe: PathBuf, } impl BuildRequest { - pub fn new(krate: DioxusCrate, build: BuildArgs, progress: ProgressTx) -> Self { + pub fn new( + krate: DioxusCrate, + build: BuildArgs, + progress: ProgressTx, + mode: BuildMode, + ) -> Self { Self { build, krate, progress, + mode, custom_target_dir: None, } } @@ -53,42 +82,13 @@ impl BuildRequest { ); let (app, server) = match self.build.force_sequential { - true => self.build_sequential().await?, - false => self.build_concurrent().await?, + true => futures_util::future::try_join(self.cargo_build(), self.build_server()).await?, + false => (self.cargo_build().await?, self.build_server().await?), }; AppBundle::new(self, app, server).await } - /// Run the build command with a pretty loader, returning the executable output location - async fn build_concurrent(&self) -> Result<(BuildArtifacts, Option)> { - let (app, server) = - futures_util::future::try_join(self.build_app(), self.build_server()).await?; - - Ok((app, server)) - } - - async fn build_sequential(&self) -> Result<(BuildArtifacts, Option)> { - let app = self.build_app().await?; - let server = self.build_server().await?; - Ok((app, server)) - } - - pub(crate) async fn build_app(&self) -> Result { - tracing::debug!("Building app..."); - - let start = Instant::now(); - self.prepare_build_dir()?; - let exe = self.build_cargo().await?; - let assets = self.collect_assets(&exe).await?; - - Ok(BuildArtifacts { - exe, - assets, - time_taken: start.elapsed(), - }) - } - pub(crate) async fn build_server(&self) -> Result> { tracing::debug!("Building server..."); @@ -98,59 +98,30 @@ impl BuildRequest { let mut cloned = self.clone(); cloned.build.platform = Some(Platform::Server); - Ok(Some(cloned.build_app().await?)) + + Ok(Some(cloned.cargo_build().await?)) } - /// Run `cargo`, returning the location of the final executable - /// - /// todo: add some stats here, like timing reports, crate-graph optimizations, etc - pub(crate) async fn build_cargo(&self) -> Result { + pub(crate) async fn cargo_build(&self) -> Result { + let start = SystemTime::now(); + self.prepare_build_dir()?; + tracing::debug!("Executing cargo..."); + let mut cmd = self.build_command()?; + + tracing::trace!(dx_src = ?TraceSrc::Build, "Rust cargo args: {:#?}", cmd); + // Extract the unit count of the crate graph so build_cargo has more accurate data - let crate_count = self.get_unit_count_estimate().await; + // "Thin" builds only build the final exe, so we only need to build one crate + let crate_count = match self.mode { + BuildMode::Thin { .. } => 1, + _ => self.get_unit_count_estimate().await, + }; // Update the status to show that we're starting the build and how many crates we expect to build self.status_starting_build(crate_count); - let mut cmd = Command::new("cargo"); - - cmd.arg("rustc") - .current_dir(self.krate.crate_dir()) - .arg("--message-format") - .arg("json-diagnostic-rendered-ansi") - .args(self.build_arguments()) - .envs(self.env_vars()?); - - if let Some(target_dir) = self.custom_target_dir.as_ref() { - cmd.env("CARGO_TARGET_DIR", target_dir); - } - - // Android needs a special linker since the linker is actually tied to the android toolchain. - // For the sake of simplicity, we're going to pass the linker here using ourselves as the linker, - // but in reality we could simply use the android toolchain's linker as the path. - // - // We don't want to overwrite the user's .cargo/config.toml since that gets committed to git - // and we want everyone's install to be the same. - if self.build.platform() == Platform::Android { - let ndk = self - .krate - .android_ndk() - .context("Could not autodetect android linker")?; - let arch = self.build.target_args.arch(); - let linker = arch.android_linker(&ndk); - - let link_action = LinkAction::LinkAndroid { - linker, - extra_flags: vec![], - } - .to_json(); - - cmd.env(LinkAction::ENV_VAR_NAME, link_action); - } - - tracing::trace!(dx_src = ?TraceSrc::Build, "Rust cargo args: {:#?}", cmd); - let mut child = cmd .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -159,11 +130,12 @@ impl BuildRequest { let stdout = tokio::io::BufReader::new(child.stdout.take().unwrap()); let stderr = tokio::io::BufReader::new(child.stderr.take().unwrap()); - let mut output_location = None; + let mut output_location: Option = None; let mut stdout = stdout.lines(); let mut stderr = stderr.lines(); let mut units_compiled = 0; let mut emitting_error = false; + let mut direct_rustc = Vec::new(); loop { use cargo_metadata::Message; @@ -181,6 +153,32 @@ impl BuildRequest { match message { Message::BuildScriptExecuted(_) => units_compiled += 1, Message::TextLine(line) => { + // Try to extract the direct rustc args from the output + if line.trim().starts_with("Running ") { + // trim everyting but the contents between the quotes + let args = line + .trim() + .trim_start_matches("Running `") + .trim_end_matches('`'); + + // Parse these as shell words so we can get the direct rustc args + if let Ok(split) = shell_words::split(args) { + direct_rustc.push(split); + } + } + + #[derive(Debug, Deserialize)] + struct RustcArtifact { + artifact: PathBuf, + emit: String, + } + + if let Ok(artifact) = serde_json::from_str::(&line) { + if artifact.emit == "link" { + output_location = Some(artifact.artifact); + } + } + // For whatever reason, if there's an error while building, we still receive the TextLine // instead of an "error" message. However, the following messages *also* tend to // be the error message, and don't start with "error:". So we'll check if we've already @@ -223,40 +221,42 @@ impl BuildRequest { tracing::error!("Cargo build failed - no output location. Toggle tracing mode (press `t`) for more information."); } - let out_location = output_location.context("Build did not return an executable")?; + let exe = output_location.context("Build did not return an executable")?; - tracing::debug!( - "Build completed successfully - output location: {:?}", - out_location - ); + tracing::debug!("Build completed successfully - output location: {:?}", exe); - Ok(out_location) + Ok(BuildArtifacts { + exe, + direct_rustc, + time_start: start, + time_end: SystemTime::now(), + }) } - /// Traverse the target directory and collect all assets from the incremental cache - /// - /// This uses "known paths" that have stayed relatively stable during cargo's lifetime. - /// One day this system might break and we might need to go back to using the linker approach. - pub(crate) async fn collect_assets(&self, exe: &Path) -> Result { - tracing::debug!("Collecting assets ..."); + #[tracing::instrument( + skip(self), + level = "trace", + fields(dx_src = ?TraceSrc::Build) + )] + fn build_command(&self) -> Result { + // if let BuildMode::Thin { direct_rustc } = &self.mode { + // if !direct_rustc.is_empty() { + // let mut cmd = Command::new(direct_rustc[0].clone()); + // cmd.args(direct_rustc[1..].iter()).envs(self.env_vars()?); + // return Ok(cmd); + // } + // } - if self.build.skip_assets { - return Ok(AssetManifest::default()); - } - - // Experimental feature for testing - if the env var is set, we'll use the deeplinker - if std::env::var("DEEPLINK").is_ok() { - tracing::debug!("Using deeplinker instead of incremental cache"); - return self.deep_linker_asset_extract().await; - } - - // walk every file in the incremental cache dir, reading and inserting items into the manifest. - let mut manifest = AssetManifest::default(); + let mut cmd = Command::new("cargo"); - // And then add from the exe directly, just in case it's LTO compiled and has no incremental cache - _ = manifest.add_from_object_path(exe); + cmd.arg("rustc") + .current_dir(self.krate.crate_dir()) + .arg("--message-format") + .arg("json-diagnostic-rendered-ansi") + .args(self.build_arguments()) + .envs(self.env_vars()?); - Ok(manifest) + Ok(cmd) } /// Create a list of arguments for cargo builds @@ -340,14 +340,25 @@ impl BuildRequest { cargo_args.push(self.krate.executable_name().to_string()); + cargo_args.push("--".to_string()); + // the bundle splitter needs relocation data // we'll trim these out if we don't need them during the bundling process // todo(jon): for wasm binary patching we might want to leave these on all the time. if self.build.platform() == Platform::Web && self.build.experimental_wasm_split { - cargo_args.push("--".to_string()); cargo_args.push("-Clink-args=--emit-relocs".to_string()); } + match self.mode { + BuildMode::Fat | BuildMode::Thin { .. } => cargo_args.push(format!( + "-Clinker={}", + dunce::canonicalize(std::env::current_exe().unwrap()) + .unwrap() + .display() + )), + _ => {} + } + tracing::debug!(dx_src = ?TraceSrc::Build, "cargo args: {:?}", cargo_args); cargo_args @@ -451,59 +462,6 @@ impl BuildRequest { }) } - /// We used to require traversing incremental artifacts for assets that were included but not - /// directly exposed to the final binary. Now, however, we force APIs to carry items created - /// from asset calls into top-level items such that they *do* get included in the final binary. - /// - /// There's a chance that's not actually true, so this function is kept around in case we do - /// need to revert to "deep extraction". - #[allow(unused)] - async fn deep_linker_asset_extract(&self) -> Result { - // Create a temp file to put the output of the args - // We need to do this since rustc won't actually print the link args to stdout, so we need to - // give `dx` a file to dump its env::args into - let tmp_file = tempfile::NamedTempFile::new()?; - - // Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force - // `dx` to act as a linker - // - // This will force `dx` to look through the incremental cache and find the assets from the previous build - Command::new("cargo") - .arg("rustc") - .args(self.build_arguments()) - .envs(self.env_vars()?) - .arg("--offline") /* don't use the network, should already be resolved */ - .arg("--") - .arg(format!( - "-Clinker={}", - std::env::current_exe() - .unwrap() - .canonicalize() - .unwrap() - .display() - )) - .env( - LinkAction::ENV_VAR_NAME, - LinkAction::BuildAssetManifest { - destination: tmp_file.path().to_path_buf().clone(), - } - .to_json(), - ) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await?; - - // The linker wrote the manifest to the temp file, let's load it! - let manifest = AssetManifest::load_from_file(tmp_file.path())?; - - if let Ok(path) = std::env::var("DEEPLINK").map(|s| s.parse::().unwrap()) { - _ = tmp_file.persist(path); - } - - Ok(manifest) - } - fn env_vars(&self) -> Result> { let mut env_vars = vec![]; @@ -577,6 +535,59 @@ impl BuildRequest { // env_vars.push(("PATH", extended_path)); }; + let custom_linker = if self.build.platform() == Platform::Android { + let ndk = self + .krate + .android_ndk() + .context("Could not autodetect android linker")?; + + Some(self.build.target_args.arch().android_linker(&ndk)) + } else { + None + }; + + match &self.mode { + BuildMode::Base => { + if let Some(linker) = custom_linker { + env_vars.push(( + LinkAction::ENV_VAR_NAME, + LinkAction::BaseLink { + platform: self.build.platform(), + linker, + incremental_dir: self.incremental_cache_dir(), + strip: false, + } + .to_json(), + )); + } + } + BuildMode::Fat => env_vars.push(( + LinkAction::ENV_VAR_NAME, + LinkAction::BaseLink { + platform: self.build.platform(), + linker: custom_linker.unwrap_or_else(|| "cc".into()), + incremental_dir: self.incremental_cache_dir(), + strip: matches!(self.mode, BuildMode::Base), + } + .to_json(), + )), + BuildMode::Thin { .. } => env_vars.push(( + LinkAction::ENV_VAR_NAME, + LinkAction::ThinLink { + platform: self.build.platform(), + linker: custom_linker.unwrap_or_else(|| "cc".into()), + incremental_dir: self.incremental_cache_dir(), + main_ptr: 0, + patch_target: Default::default(), + } + .to_json(), + )), + } + + if let Some(target_dir) = self.custom_target_dir.as_ref() { + env_vars.push(("CARGO_TARGET_DIR", target_dir.display().to_string())); + } + // If this is a release build, bake the base path and title // into the binary with env vars if self.build.release { @@ -631,6 +642,10 @@ impl BuildRequest { Ok(()) } + pub fn incremental_cache_dir(&self) -> PathBuf { + self.platform_dir().join("incremental-cache") + } + /// The directory in which we'll put the main exe /// /// Mac, Android, Web are a little weird @@ -968,4 +983,8 @@ impl BuildRequest { kotlin_dir } + + pub(crate) fn is_patch(&self) -> bool { + matches!(&self.mode, BuildMode::Thin { .. }) + } } diff --git a/packages/cli/src/build/web.rs b/packages/cli/src/build/web.rs index 52b2e8e914..a809754566 100644 --- a/packages/cli/src/build/web.rs +++ b/packages/cli/src/build/web.rs @@ -85,7 +85,7 @@ impl AppBundle { } // Inject any resources from manganis into the head - for asset in self.app.assets.assets.values() { + for asset in self.assets.assets.values() { let asset_path = asset.bundled_path(); match asset.options() { AssetOptions::Css(css_options) => { @@ -115,7 +115,6 @@ impl AppBundle { // Manually inject the wasm file for preloading. WASM currently doesn't support preloading in the manganis asset system let wasm_source_path = self.build.wasm_bindgen_wasm_output_file(); let wasm_path = self - .app .assets .assets .get(&wasm_source_path) @@ -175,7 +174,6 @@ r#"