diff --git a/Cargo.lock b/Cargo.lock index f6236856..ba57308d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -70,6 +70,16 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.8", +] + [[package]] name = "cc" version = "1.2.24" @@ -96,9 +106,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -106,9 +116,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -118,9 +128,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -151,9 +161,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.47" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265" +checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" dependencies = [ "curl-sys", "libc", @@ -161,14 +171,14 @@ dependencies = [ "openssl-sys", "schannel", "socket2", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "curl-sys" -version = "0.4.80+curl-8.12.1" +version = "0.4.83+curl-8.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f7df2eac63200c3ab25bde3b2268ef2ee56af3d238e76d61f01c3c49bff734" +checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483" dependencies = [ "cc", "libc", @@ -176,7 +186,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -197,7 +207,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -235,9 +245,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -247,9 +257,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -269,9 +279,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libredox" @@ -297,15 +307,16 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "midenup" version = "0.1.0" dependencies = [ "anyhow", + "cargo_toml", "chrono", "clap", "colored", @@ -317,7 +328,7 @@ dependencies = [ "serde_json", "tempdir", "thiserror", - "toml", + "toml 0.8.23", "upon", ] @@ -467,10 +478,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -485,11 +497,20 @@ dependencies = [ "typeid", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -498,14 +519,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -517,6 +539,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "shlex" version = "1.3.0" @@ -525,12 +556,12 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -562,18 +593,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -588,11 +619,26 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -602,6 +648,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -610,18 +665,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "toml_write" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "typeid" version = "1.0.3" @@ -691,22 +761,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.5", ] [[package]] @@ -715,14 +791,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -731,53 +824,101 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index c5fda130..71ea4784 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ upon = { version = "0.9.0", default-features = false, features = [ ] } colored = "3.0.0" +[build-dependencies] +cargo_toml = "0.22.3" + [dev-dependencies] tempdir = "0.3.7" diff --git a/build.rs b/build.rs index c598f80e..b91fe602 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,34 @@ use std::process::Command; +use cargo_toml::Manifest; + fn main() { + let toml_contents = std::fs::read("Cargo.toml").expect("Failed to find Cargo.toml"); + let manifest = + Manifest::from_slice(&toml_contents).expect("Failed to parse Cargo.toml as Manifest"); + let curl_version = { + let curl = manifest + .dependencies + .get("curl") + .expect("Couldn't find curl in parsed manifest"); + match curl { + cargo_toml::Dependency::Simple(ver) => ver, + cargo_toml::Dependency::Detailed(detail) => &detail.version.as_ref().unwrap().clone(), + // This case should not happen since we are reading the root Cargo.toml + cargo_toml::Dependency::Inherited(_) => { + panic!("Couldn't determine curl version since it appears as inherited.") + }, + } + }; + // Pass the $CURL_VERSION to the crate at compile time. + println!("cargo:rustc-env=CURL_VERSION={}", curl_version); + + // Pass the $TARGET to the crate at compile time. + println!( + "cargo:rustc-env=TARGET={}", + std::env::var("TARGET").expect("Failed to obtain $TARGET triple constant.") + ); + // Generated by cargo at runtime when the build script is run: // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts let build_script = std::env::var("OUT_DIR").unwrap(); diff --git a/manifest/channel-manifest.json b/manifest/channel-manifest.json index 2f650142..96d6fe12 100644 --- a/manifest/channel-manifest.json +++ b/manifest/channel-manifest.json @@ -269,6 +269,15 @@ "aliases": { "start-node": ["executable", "bundled", "start", "--data-directory", "var_path", "data", "--rpc.url", "http://0.0.0.0:57291"] } + }, + { + "name": "debug", + "package": "miden-debug", + "version": "0.4.4", + "installed_executable": "miden-debug", + "artifacts": [ + "https://github.com/0xMiden/miden-debug/releases/download/v0.4.4/miden-debug-aarch64-apple-darwin" + ] } ] } diff --git a/src/artifact.rs b/src/artifact.rs new file mode 100644 index 00000000..eca675e7 --- /dev/null +++ b/src/artifact.rs @@ -0,0 +1,66 @@ +use serde::{Deserialize, Serialize}; + +/// All the artifacts that the [[Component]] contains. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Artifacts { + artifacts: Vec, +} + +impl Artifacts { + /// Get a URI to download an artifact that's valid for [target]. + pub fn get_uri_for(&self, target: &TargetTriple) -> Option { + self.artifacts.iter().find_map(|artifact| artifact.get_uri_for(target)) + } +} + +/// Holds a URI used to fetch an artifact. These URIs have the following format: +/// (https://|file://)/(-|.masp) +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Artifact(String); + +#[derive(Debug, PartialEq)] +pub enum TargetTriple { + /// Custom triplet used by cargo. Since we use the same triplets as cargo, we + /// simply copy them as-is, without any type of parsing. + Custom(String), + /// Used for .masp Libraries that are used in the MidenVM. Components that + /// have these libraries as artifacts only have one entry in + /// [[Artifacts::artifacts]]. + MidenVM, +} + +impl TargetTriple { + fn get_uri_extension(&self) -> String { + match &self { + Self::MidenVM => String::from(".masp"), + Self::Custom(triplet) => triplet.to_string(), + } + } +} + +impl Artifact { + /// Returns the URI for the specified component + triplet if it has it. + /// + /// NOTE: The component name is required to separate the triplet from the + /// filename in the URI. + fn get_uri_for(&self, target: &TargetTriple) -> Option { + let path = if let Some(file_path) = self.0.strip_prefix("file://") { + file_path + } else if let Some(url_path) = self.0.strip_prefix("https://") { + url_path + } else { + return None; + }; + + // (-|.masp) + let uri_extension = path.split("/").last()?; + + let wanted_uri_extension = target.get_uri_extension(); + + if uri_extension.contains(&wanted_uri_extension) { + Some(self.0.clone()) + } else { + None + } + } +} diff --git a/src/channel.rs b/src/channel.rs index 92fe79f2..bb625f55 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -3,7 +3,7 @@ use std::{ collections::HashMap, fmt::{self, Display}, hash::{Hash, Hasher}, - path::PathBuf, + path::{Path, PathBuf}, }; use anyhow::{Context, bail}; @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Config, + artifact::{Artifacts, TargetTriple}, toolchain::{Toolchain, ToolchainJustification}, utils, version::{Authority, GitTarget}, @@ -349,6 +350,14 @@ impl InstalledFile { InstalledFile::Library { library_struct, .. } => Some(library_struct), } } + pub fn get_path_from(&self, toolchain_dir: &Path) -> PathBuf { + match &self { + exe @ InstalledFile::Executable { .. } => { + toolchain_dir.join("bin").join(exe.to_string()) + }, + lib @ InstalledFile::Library { .. } => toolchain_dir.join("lib").join(lib.to_string()), + } + } } impl Display for InstalledFile { @@ -521,6 +530,12 @@ pub struct Component { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] symlink_name: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub initialization: Vec, + /// Pre-built artifact. + #[serde(flatten)] + artifacts: Option, } impl Component { @@ -535,6 +550,8 @@ impl Component { installed_file: None, aliases: HashMap::new(), symlink_name: None, + initialization: Vec::new(), + artifacts: None, } } @@ -698,6 +715,11 @@ impl Component { self.call_format.clone() } } + + /// Returns the URI for a given [target] (if available). + pub fn get_artifact_uri(&self, target: &TargetTriple) -> Option { + self.artifacts.as_ref().and_then(|artifacts| artifacts.get_uri_for(target)) + } } /// User-facing channel reference. The main difference with this and [Channel] diff --git a/src/commands/install.rs b/src/commands/install.rs index 7d2dedd3..2a749116 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -1,9 +1,10 @@ -use std::{io::Write, time::SystemTime}; +use std::{io::Write, path::Path, time::SystemTime}; use anyhow::{Context, bail}; use crate::{ Config, InstallationOptions, + artifact::TargetTriple, channel::{Channel, ChannelAlias, InstalledFile}, commands, manifest::Manifest, @@ -44,7 +45,31 @@ pub fn install( })?; } - // We create the opt/ directory where the aliases are going to be stored. + // bin/ directory which holds binaries. + let bin_dir = toolchain_dir.join("bin"); + if !bin_dir.exists() { + std::fs::create_dir_all(&bin_dir).with_context(|| { + format!("failed to create toolchain directory: '{}'", bin_dir.display()) + })?; + } + + // lib/ directory which holds MASP libraries. + let lib_dir = toolchain_dir.join("lib"); + if !lib_dir.exists() { + std::fs::create_dir_all(&lib_dir).with_context(|| { + format!("failed to create toolchain directory: '{}'", lib_dir.display()) + })?; + } + + // opt/ directory which holds symlinks to binaries in bin/. + // These are used in order to preserve a "midenup" compatible + // interface. This relies on the fact that clap uses argv[0] in order to + // display executable names names. These symlinks have the following format: + // `miden ` + // Then, when `miden` is invoked, it uses these symlinks to execute the + // underlying binary. With this setup, `clap` displays the name as: `miden + // ` instead of just `binary_name` when displaying help + // messages. let opt_dir = toolchain_dir.join("opt"); if !opt_dir.exists() { std::fs::create_dir_all(&opt_dir).with_context(|| { @@ -60,7 +85,7 @@ pub fn install( format!("failed to create file for install script at '{}'", install_file_path.display()) })?; - let install_script_contents = generate_install_script(config, channel, options); + let install_script_contents = generate_install_script(config, channel, options, &toolchain_dir); install_file.write_all(&install_script_contents.into_bytes()).with_context(|| { format!("failed to write install script at '{}'", install_file_path.display()) })?; @@ -186,6 +211,7 @@ fn generate_install_script( config: &Config, channel: &Channel, options: &InstallationOptions, + toolchain_directory: &Path, ) -> String { // Prepare install script template let engine = upon::Engine::new(); @@ -200,14 +226,16 @@ fn generate_install_script( {%- else if dep.path %}, path = "{{ dep.path }}" {%- endif %} } {%- endfor %} +curl = "{{ curl_version }}" --- // NOTE: This file was generated by midenup. Do not edit by hand -use std::process::Command; use std::io::{Write}; use std::fs::{OpenOptions, rename}; +{{ install_artifact.function }} + // Utility functions mod utility { #[cfg(unix)] @@ -265,10 +293,47 @@ fn main() { // NOTE: If the file already exists, then we are running an update and we // don't need to update this element if !std::fs::exists(&lib_path).expect("Can't check existence of file") { - lib.as_ref() - .write_to_file(&lib_path) - .expect("failed to install {{ dep.name }} library component"); - println!("{} Installed!", padding); + let do_fetch_artifact: bool; + let mut do_install_from_source: bool; + let mut successfully_installed = false; + let initial_message: String; + + if !"{{ dep.artifact.0 }}".is_empty() { + do_fetch_artifact = true; + do_install_from_source = false; + initial_message = format!("{} Fetching artifact", padding); + } else { + do_fetch_artifact = false; + do_install_from_source = true; + initial_message = format!("{} No artifact found. Proceeding to install from source", padding); + } + + println!("{initial_message}"); + if do_fetch_artifact { + if let Err(err) = install_artifact("{{ dep.artifact.0 }}", std::path::Path::new("{{ dep.artifact.1 }}")) { + println!("{} {err}.", padding); + println!("{} Proceeding to install from source.", padding); + do_install_from_source = true; + } else { + successfully_installed = true; + } + } + + if do_install_from_source { + if let Err(err) = lib.as_ref().write_to_file(&lib_path) { + if {{ keep_going }} { + println!("{} Failed to install '{{ dep.name }}' from source because of {err}. Skipping.", padding); + } else { + panic!("Failed to install '{{ dep.name }}' from source because of {err}."); + } + } else { + successfully_installed = true; + } + } + + if successfully_installed { + println!("{} Installed!", padding); + } } else { println!("{} Already installed", padding); } @@ -285,44 +350,64 @@ fn main() { println!("Installing: {{ component.name }}"); let bin_path = bin_dir.join("{{ component.installed_file }}"); if !std::fs::exists(&bin_path).unwrap_or(false) { - let mut child = Command::new("cargo") - .arg( - "{{ component.required_toolchain_flag }}", - ) - .arg("install") - .arg("--locked") - .args([ - {%- for arg in chosen_profile %} - "{{ arg }}", - {%- endfor %} - ]) - {%- if verbosity.quiet_flag %} - .arg("{{ verbosity.quiet_flag }}") - {%- endif %} - .args([ - {%- for arg in component.args %} - "{{ arg }}", - {%- endfor %} - ]) - // Force the install target directory to be $MIDEN_SYSROOT/bin - .arg("--root") - .arg(&miden_sysroot_dir) - // Spawn command - .stderr(std::process::Stdio::inherit()) - .stdout(std::process::Stdio::inherit()) - .spawn() - .expect("failed to install component '{{ component.name }}'"); - - // Await results - let status = child.wait().expect("Error occurred while waiting to install component '{{ component.name }}'"); - - - if !status.success() { - panic!( - "midenup failed to install '{{ component.name }}'" - ); + let do_fetch_artifact: bool; + let mut do_install_from_source: bool; + let mut successfully_installed = false; + let initial_message: String; + + if !"{{ component.artifact.0 }}".is_empty() { + do_fetch_artifact = true; + do_install_from_source = false; + initial_message = format!("{} Fetching artifact", padding); + } else { + do_fetch_artifact = false; + do_install_from_source = true; + initial_message = format!("{} No artifact found. Proceeding to install from source", padding); + } + + println!("{initial_message}"); + if do_fetch_artifact { + if let Err(err) = install_artifact("{{ component.artifact.0 }}", std::path::Path::new("{{ component.artifact.1 }}")) { + println!("{} {err}.", padding); + println!("{} Proceeding to install from source.", padding); + do_install_from_source = true; + } else { + successfully_installed = true; + } + } + + if do_install_from_source { + if let Err(err) = install_from_source( + "{{ component.name }}", + "{{ component.required_toolchain_flag }}", + &[ + {%- for arg in chosen_profile %} + "{{ arg }}", + {%- endfor %} + ], + "{{ verbosity.quiet_flag }}", + &[ + {%- for arg in component.args %} + "{{ arg }}", + {%- endfor %} + ], + miden_sysroot_dir, + ) { + + if {{ keep_going }} { + println!("{} Failed to install '{{ component.name }}' from source because of {err}. Skipping.", padding); + } else { + panic!("Failed to install '{{ component.name }}' from source because of {err}."); + } + + } else { + successfully_installed = true; + } + } + + if successfully_installed { + println!("{} Installed!", padding); } - println!("{} Installed!", padding); } else { println!("{} Already installed", padding); } @@ -363,8 +448,28 @@ fn main() { let mut installable_components = Vec::new(); for component in channel.components.iter() { match component.get_installed_file() { - InstalledFile::Executable { .. } => installable_components.push(component), - InstalledFile::Library { .. } => dependencies.push(component), + InstalledFile::Executable { .. } => { + let artifact_destination = { + component.get_artifact_uri(&config.target).map(|uri| { + let destination = + component.get_installed_file().get_path_from(toolchain_directory); + (uri, destination) + }) + }; + installable_components.push((component, artifact_destination)) + }, + InstalledFile::Library { .. } => { + let artifact_destination = { + component.get_artifact_uri(&TargetTriple::MidenVM).map(|uri| { + let destination = + component.get_installed_file().get_path_from(toolchain_directory); + + (uri, destination) + }) + }; + + dependencies.push((component, artifact_destination)) + }, } } @@ -398,7 +503,7 @@ fn main() { // The set of cargo dependencies needed for the install script let dependencies = dependencies .into_iter() - .map(|component| { + .map(|(component, artifact)| { let installed_file = component .get_installed_file(); let library_struct = installed_file @@ -410,6 +515,7 @@ fn main() { library_struct: \"miden_stdlib::MidenStdLib::default()\"" , component.name)).unwrap(); let exposing_function = format!("{library_struct}::default()"); + let artifact = artifact.unwrap_or_default(); match &component.version { Authority::Cargo { package, version } => { let package = package.as_deref().unwrap_or(component.name.as_ref()).to_string(); @@ -420,6 +526,7 @@ fn main() { git_uri: "", path: "", exposing_function: exposing_function, + artifact: artifact, } }, Authority::Git { repository_url, crate_name, target } => { @@ -430,6 +537,7 @@ fn main() { git_uri: format!("{}\", {target}", repository_url.clone()), path: "", exposing_function: exposing_function, + artifact: artifact, } }, Authority::Path { crate_name, path, .. } => { @@ -440,6 +548,7 @@ fn main() { git_uri: "", path: path.display().to_string(), exposing_function: exposing_function, + artifact: artifact, } }, } @@ -449,7 +558,7 @@ fn main() { // The set of components to be installed with `cargo install` let installable_components = installable_components .into_iter() - .map(|component| { + .map(|(component, artifact)| { let mut args = vec![]; match &component.version { Authority::Cargo { package, version } => { @@ -490,6 +599,7 @@ fn main() { installed_file: installed_file, required_toolchain_flag: required_toolchain_flag, args: args, + artifact: artifact.unwrap_or_default(), } }) .collect::>(); @@ -512,6 +622,28 @@ fn main() { } }; + let install_artifact_function = { + upon::value! { + function: include_str!("../external.rs") + } + }; + + let curl_version = env!("CURL_VERSION"); + + // This determines whether to panic if a component fails to be install. + // In release builds, we want midenup to keep going; but on debug builds we + // want to catch those errors. + let install_keep_going = { + #[cfg(debug_assertions)] + { + false + } + #[cfg(not(debug_assertions))] + { + true + } + }; + // Render the install script template .render( @@ -523,6 +655,9 @@ fn main() { symlinks: symlinks, chosen_profile: chosen_profile, verbosity: verbosity, + install_artifact: install_artifact_function, + curl_version: curl_version, + keep_going: install_keep_going, }, ) .to_string() diff --git a/src/config.rs b/src/config.rs index 873449cf..d858d1a7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,9 @@ use std::{ffi::OsString, path::PathBuf}; use anyhow::{Context, bail}; -use crate::{channel::Channel, manifest::Manifest, toolchain::Toolchain, utils}; +use crate::{ + artifact::TargetTriple, channel::Channel, manifest::Manifest, toolchain::Toolchain, utils, +}; #[derive(Debug)] /// This struct holds contextual information about the environment in which @@ -39,6 +41,13 @@ pub struct Config { /// tests. At the time of writing, this is mostly done to install debug /// builds of the various miden components to speed tests up. pub debug: bool, + /// The machine's triplet (e.g. x86_64-unknown-linux-gnu, + /// aarch64-apple-darwin, etc). This is used to determine which + /// [[artifact::Artifact]] to download. + /// If, for whatever reason (which should be rare), we fail to obtain the + /// system's TargetTriple, then we leave it as None. In those cases, we will + /// simply install everything from source. + pub target: TargetTriple, } impl Config { @@ -51,11 +60,17 @@ impl Config { let working_directory = std::env::current_dir().context("Could not obtain present working directory")?; + let target = { + let target = env!("TARGET"); + TargetTriple::Custom(target.to_string()) + }; + let config = Config { working_directory, midenup_home, manifest, debug, + target, }; Ok(config) diff --git a/src/external.rs b/src/external.rs new file mode 100644 index 00000000..872cde9f --- /dev/null +++ b/src/external.rs @@ -0,0 +1,109 @@ +// This file holds functions which are used in the cargo install script, after +// being imported via include_str. +// +// Since these functions are intended to be used in the install script, they +// should *not* import utilities from any crate besides the standard library and +// they should also prioritize qualifying over importing, in order to avoid +// duplicate "use" declarations. + +const HTTP_ERROR_CODES: std::ops::Range = 400..500; + +#[allow(dead_code)] +pub fn install_artifact(uri: &str, to: &std::path::Path) -> Result<(), String> { + if let Some(binary_path) = uri.strip_prefix("file://") { + std::fs::copy(binary_path, to).map_err(|err| { + format!("Failed to copy binary file to {} because of {}", to.display(), err) + })?; + } else if uri.starts_with("https://") { + let mut data = Vec::new(); + let mut handle = curl::easy::Easy::new(); + handle + .follow_location(true) + .map_err(|_| String::from("Failed to set curl up"))?; + handle.url(uri).map_err(|error| { + format!("Error while trying to fetch binary: {}", error.description()) + })?; + { + let response_code = handle.response_code().map_err(|_| { + String::from("Failed to get response code from webpage; despite HTTP protocol supporting it.") + })?; + if HTTP_ERROR_CODES.contains(&response_code) { + return Err(format!("Webpage returned error. Does {} exist?", uri)); + } + + let mut transfer = handle.transfer(); + transfer + .write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }) + .unwrap(); + transfer.perform().map_err(|error| { + format!("Error while trying to fetch binary: {}", error.description()) + })? + } + if data.is_empty() { + return Err(format!("Found webpage {} to be empty.", uri)); + } + let tmp = to.with_extension("tmp"); + let mut file = std::fs::File::create(&tmp).map_err(|error| { + format!("Failed to create download file in {} because of {}", to.display(), error) + })?; + // We set the same flags that cargo uses when producing an executable. + file.set_permissions( + ::from_mode(0o755), + ) + .map_err(|error| { + format!("Failed to set permissions in {} because of {}", to.display(), error) + })?; + std::io::Write::write_all(&mut file, &data).map_err(|error| { + format!("Failed to write download file to {} because of {}", to.display(), error) + })?; + std::fs::rename(&tmp, to) + .expect("Couldn't rename .installation-in-progress to installation-successful"); + } else { + return Err(format!( + "Unrecognized URI type: {}. Supported URI's are 'https://' and 'file//'", + uri + )); + } + + Ok(()) +} + +#[allow(dead_code)] +pub fn install_from_source( + component_name: &str, + toolchain_flag: &str, + chosen_profile: &[&str], + verbosity_flag: &str, + args: &[&str], + root_directory: &std::path::Path, +) -> Result<(), String> { + let mut child = std::process::Command::new("cargo") + .arg(toolchain_flag) + .arg("install") + .arg("--locked") + .args(chosen_profile) + .arg(verbosity_flag) + .args(args) + // Force the install target directory to be $MIDEN_SYSROOT/bin + .arg("--root") + .arg(root_directory) + // Spawn command + .stderr(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .spawn() + .map_err(|error|format!("Failed to install {component_name} because of {error}"))?; + + // Await results + let status = child.wait().map_err(|error| { + format!("Error occurred while waiting to install {component_name} because of {error}") + })?; + + if !status.success() { + return Err(format!("midenup failed to install '{component_name}'")); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index f485f93e..b1e9e00f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ +mod artifact; mod channel; mod commands; mod config; +mod external; mod manifest; mod miden_wrapper; mod toolchain;