diff --git a/Cargo.lock b/Cargo.lock index 9da9821f..d7300fd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,7 +186,7 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ - "bzip2", + "bzip2 0.5.2", "flate2", "futures-core", "memchr", @@ -418,9 +418,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.83.0" +version = "1.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51384750334005f40e1a334b0d54eca822a77eacdcf3c50fdf38f583c5eee7a2" +checksum = "d5c82dae9304e7ced2ff6cca43dceb2d6de534c95a506ff0f168a7463c9a885d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -918,6 +918,31 @@ dependencies = [ "piper", ] +[[package]] +name = "bon" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61138465baf186c63e8d9b6b613b508cd832cba4ce93cf37ce5f096f91ac1a6" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d1dad34aa19bf02295382f08d9bc40651585bd497266831d40ee6296fb49ca" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "boxcar" version = "0.2.11" @@ -943,12 +968,6 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" -[[package]] -name = "bytecount" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" - [[package]] name = "byteorder" version = "1.5.0" @@ -960,6 +979,9 @@ name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] [[package]] name = "bytes-utils" @@ -980,6 +1002,15 @@ dependencies = [ "bzip2-sys", ] +[[package]] +name = "bzip2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +dependencies = [ + "libbz2-rs-sys", +] + [[package]] name = "bzip2-sys" version = "0.1.13+1.0.8" @@ -1061,9 +1092,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -1082,9 +1113,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -1094,18 +1125,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.48" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8c97f3a6f02b9e24cadc12aaba75201d18754b53ea0a9d99642f806ccdb4c9" +checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" dependencies = [ "clap", ] [[package]] name = "clap_complete_nushell" -version = "4.5.5" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a8b1593457dfc2fe539002b795710d022dc62a65bf15023f039f9760c7b18a" +checksum = "cdb8335b398d197fb3176efe9400c6c053a41733c26794316c73423d212b2f3d" dependencies = [ "clap", "clap_complete", @@ -1113,9 +1144,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -1480,6 +1511,16 @@ dependencies = [ "syn", ] +[[package]] +name = "diffy" +version = "0.4.2" +source = "git+https://github.com/prefix-dev/diffy.git?branch=master#da81bf4ceb4a5b9e6f9c43616a4a74aabf50a29b" +dependencies = [ + "nu-ansi-term 0.50.1", + "strsim", + "thiserror 2.0.12", +] + [[package]] name = "digest" version = "0.10.7" @@ -1510,7 +1551,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1545,6 +1586,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "ecdsa" version = "0.14.8" @@ -1714,7 +1761,7 @@ dependencies = [ [[package]] name = "fancy_display" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "console", ] @@ -1737,9 +1784,9 @@ dependencies = [ [[package]] name = "file_url" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46ca1565923e8d9bcf962889abeeb2f15d4b73356689ce26ae7dd1f54b928498" +checksum = "b6e6bbcc80ea90b1924156b36e41b1ac75170580fff3b26fb6724c306538312b" dependencies = [ "itertools 0.14.0", "percent-encoding", @@ -1768,11 +1815,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -1808,9 +1856,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" +checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683" dependencies = [ "autocfg", "tokio", @@ -1990,9 +2038,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2008,17 +2056,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "gitpatch" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce212119eb3801015ea7e8bfb78f029ca16805de0d808358c080ff55ecbe675" -dependencies = [ - "chrono", - "nom", - "nom_locate", -] - [[package]] name = "glob" version = "0.3.2" @@ -2052,9 +2089,9 @@ dependencies = [ [[package]] name = "goblin" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745" +checksum = "0e961b33649994dcf69303af6b3a332c1228549e604d455d61ec5d2ab5e68d3a" dependencies = [ "log", "plain", @@ -2063,44 +2100,70 @@ dependencies = [ [[package]] name = "google-cloud-auth" -version = "0.17.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57a13fbacc5e9c41ded3ad8d0373175a6b7a6ad430d99e89d314ac121b7ab06" +checksum = "0bd58406d918ffb345f0482c1e9a9794a5836169581ecc4dcb6c02330fefdc72" dependencies = [ "async-trait", - "base64 0.21.7", - "google-cloud-metadata", - "google-cloud-token", - "home", - "jsonwebtoken", + "base64 0.22.1", + "bon", + "google-cloud-gax", + "http 1.3.1", "reqwest", + "rustls 0.23.26", + "rustls-pemfile 2.2.0", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "time", "tokio", - "tracing", - "urlencoding", ] [[package]] -name = "google-cloud-metadata" -version = "0.5.1" +name = "google-cloud-gax" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d901aeb453fd80e51d64df4ee005014f6cf39f2d736dd64f7239c132d9d39a6a" +checksum = "d2dad82102be3d1052c43950c1ded1641edd08d8ec066af306012e4f71826991" dependencies = [ - "reqwest", - "thiserror 1.0.69", + "base64 0.22.1", + "bytes", + "futures", + "google-cloud-rpc", + "google-cloud-wkt", + "http 1.3.1", + "pin-project", + "rand 0.9.1", + "serde", + "serde_json", "tokio", ] [[package]] -name = "google-cloud-token" -version = "0.1.2" +name = "google-cloud-rpc" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c12ba8b21d128a2ce8585955246977fbce4415f680ebf9199b6f9d6d725f" +checksum = "f42736ae792ea662facb2841a573e281f32a3a161a99fc342c6aef9738f2dfb0" dependencies = [ - "async-trait", + "bytes", + "google-cloud-wkt", + "serde", + "serde_json", + "serde_with", +] + +[[package]] +name = "google-cloud-wkt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd0127a9a4b436e7f2f72db309e8199b4eba248bc602dda9a82ae9e339742c" +dependencies = [ + "base64 0.22.1", + "bytes", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.12", + "time", ] [[package]] @@ -2126,7 +2189,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util 0.7.15", @@ -2145,7 +2208,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util 0.7.15", @@ -2154,11 +2217,11 @@ dependencies = [ [[package]] name = "halfbrown" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" +checksum = "aa2c385c6df70fd180bbb673d93039dbd2cd34e41d782600bdf6e1ca7bce39aa" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.3", "serde", ] @@ -2173,10 +2236,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashbrown" @@ -2189,15 +2248,6 @@ dependencies = [ "foldhash", ] -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "hashlink" version = "0.10.0" @@ -2215,15 +2265,15 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2330,6 +2380,19 @@ dependencies = [ "time", ] +[[package]] +name = "http-range-client" +version = "0.9.0" +source = "git+https://github.com/pka/http-range-client.git#b3ab3efd54b657f728686189495b1afa75d38fb7" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "read-logger", + "reqwest", + "thiserror 1.0.69", +] + [[package]] name = "http-serde" version = "2.1.1" @@ -2670,9 +2733,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.3", @@ -2709,12 +2772,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" dependencies = [ "console", + "globset", "once_cell", "pest", "pest_derive", "regex", "serde", "similar", + "walkdir", ] [[package]] @@ -2796,7 +2861,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -2812,9 +2877,9 @@ dependencies = [ [[package]] name = "json-patch" -version = "3.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4" dependencies = [ "jsonptr", "serde", @@ -2824,9 +2889,9 @@ dependencies = [ [[package]] name = "jsonptr" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" dependencies = [ "serde", "serde_json", @@ -2894,21 +2959,6 @@ dependencies = [ "tokio-util 0.6.10", ] -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "keyring" version = "3.6.2" @@ -2963,6 +3013,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libbz2-rs-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb" + [[package]] name = "libc" version = "0.2.172" @@ -2980,12 +3036,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.6", + "windows-targets 0.53.2", ] [[package]] @@ -3005,6 +3061,21 @@ dependencies = [ "redox_syscall 0.5.11", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "line-ending" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834e592123b39b7b3ba3fdc4b7e4822fad3ced449010f8229f843fe6dd1a33f1" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3061,6 +3132,12 @@ dependencies = [ "hashbrown 0.15.3", ] +[[package]] +name = "lzma-rust2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecaa2bc9f1bd284be221d0770bbc9b92a438fdf422ba45211f6c6bf157ebb932" + [[package]] name = "lzma-sys" version = "0.1.20" @@ -3072,17 +3149,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "marked-yaml" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2eb25a7ab146f4058d67a74dfea52e25c133c575f08ce5851da97d224e3ad8d" -dependencies = [ - "doc-comment", - "hashlink 0.9.1", - "yaml-rust2 0.9.0", -] - [[package]] name = "marked-yaml" version = "0.8.0" @@ -3090,8 +3156,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a76cf4e66a8ffccfce983161b0faafe61a5ef03fe875ef2e3deb897e4e915fa" dependencies = [ "doc-comment", - "hashlink 0.10.0", - "yaml-rust2 0.10.3", + "hashlink", + "yaml-rust2", ] [[package]] @@ -3115,9 +3181,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" @@ -3194,12 +3260,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.8" @@ -3250,24 +3310,33 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.0", + "cfg-if 1.0.0", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" -version = "7.1.3" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" dependencies = [ "memchr", - "minimal-lexical", ] [[package]] -name = "nom_locate" -version = "4.2.0" +name = "nom-language" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +checksum = "2de2bc5b451bfedaef92c90b8939a8fff5770bdcc1fafd6239d086aab8fa6b29" dependencies = [ - "bytecount", - "memchr", "nom", ] @@ -3290,6 +3359,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "num" version = "0.4.3" @@ -3371,11 +3449,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.5.2", "libc", ] @@ -3385,6 +3463,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -3402,38 +3499,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opendal" -version = "0.52.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c840b5a6ad96106d6c0612fabb8f35a5ace826e0474fc55ebda33042b8d33" -dependencies = [ - "anyhow", - "async-trait", - "backon", - "base64 0.22.1", - "bytes", - "chrono", - "crc32c", - "futures", - "getrandom 0.2.16", - "http 1.3.1", - "log", - "md-5", - "once_cell", - "percent-encoding", - "quick-xml 0.36.2", - "reqsign", - "reqwest", - "serde", - "serde_json", - "tokio", - "uuid", -] - -[[package]] -name = "opendal" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f407ca2797ca6c010720aa3cedfa0187430e4f70b729c75c33142d44021f59" +checksum = "f947c4efbca344c1a125753366033c8107f552b2e3f8251815ed1908f116ca3e" dependencies = [ "anyhow", "async-trait", @@ -3499,6 +3567,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "ordermap" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6bff06e4a5dc6416bead102d3e63c480dd852ffbb278bf8cfeb4966b329609" +dependencies = [ + "indexmap 2.10.0", + "serde", +] + [[package]] name = "outref" version = "0.5.2" @@ -3594,16 +3672,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" -[[package]] -name = "pem" -version = "3.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" -dependencies = [ - "base64 0.22.1", - "serde", -] - [[package]] name = "pep440_rs" version = "0.7.3" @@ -3624,7 +3692,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faee7227064121fcadcd2ff788ea26f0d8f2bd23a0574da11eca23bc935bcc05" dependencies = [ "boxcar", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.13.0", "once_cell", "pep440_rs", @@ -3697,18 +3765,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.9.0", + "indexmap 2.10.0", ] [[package]] name = "petgraph" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset", "hashbrown 0.15.3", - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", ] @@ -3809,7 +3877,7 @@ dependencies = [ "clap-verbosity-flag", "dirs", "fs-err", - "indexmap 2.9.0", + "indexmap 2.10.0", "insta", "itertools 0.14.0", "jsonrpc-core", @@ -3818,6 +3886,7 @@ dependencies = [ "log", "miette", "minijinja", + "ordermap", "parking_lot 0.12.4", "pathdiff", "pixi_build_type_conversions", @@ -3837,9 +3906,10 @@ dependencies = [ "serde_json", "serde_yaml", "tempfile", + "thiserror 2.0.12", "tokio", "toml 0.7.8", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing-subscriber", "url", ] @@ -3848,23 +3918,19 @@ dependencies = [ name = "pixi-build-cmake" version = "0.2.0" dependencies = [ - "async-trait", - "chrono", - "indexmap 2.9.0", + "indexmap 2.10.0", "insta", "miette", "minijinja", "pixi-build-backend", - "pixi_build_type_conversions", "pixi_build_types", - "pixi_manifest", "rattler-build", "rattler_conda_types", - "rattler_package_streaming", "recipe-stage0", + "rstest", "serde", "serde_json", - "tempfile", + "strum", "tokio", ] @@ -3878,7 +3944,7 @@ dependencies = [ "clap-verbosity-flag", "dirs", "fs-err", - "indexmap 2.9.0", + "indexmap 2.10.0", "insta", "itertools 0.14.0", "jsonrpc-core", @@ -3908,7 +3974,7 @@ dependencies = [ "serde_yaml", "tempfile", "tokio", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing-subscriber", "url", ] @@ -3918,8 +3984,10 @@ name = "pixi-build-rattler-build" version = "0.2.0" dependencies = [ "async-trait", + "chrono", "fs-err", - "indexmap 2.9.0", + "indexmap 2.10.0", + "insta", "itertools 0.14.0", "miette", "pathdiff", @@ -3941,9 +4009,9 @@ version = "0.2.0" dependencies = [ "async-trait", "chrono", - "indexmap 2.9.0", + "indexmap 2.10.0", "insta", - "marked-yaml 0.8.0", + "marked-yaml", "miette", "minijinja", "pixi-build-backend", @@ -3966,22 +4034,23 @@ dependencies = [ [[package]] name = "pixi_build_type_conversions" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ - "indexmap 2.9.0", "itertools 0.14.0", + "ordermap", "pixi_build_types", "pixi_manifest", "pixi_spec", "rattler_conda_types", + "xxhash-rust", ] [[package]] name = "pixi_build_types" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ - "indexmap 2.9.0", + "ordermap", "rattler_conda_types", "rattler_digest", "serde", @@ -3993,7 +4062,7 @@ dependencies = [ [[package]] name = "pixi_config" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "clap", "console", @@ -4005,14 +4074,13 @@ dependencies = [ "rattler", "rattler_conda_types", "rattler_networking", - "rattler_package_streaming", "rattler_repodata_gateway", "reqwest", "serde", "serde_ignored", "serde_json", "thiserror 2.0.12", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing", "url", ] @@ -4020,7 +4088,7 @@ dependencies = [ [[package]] name = "pixi_consts" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "console", "rattler_cache", @@ -4031,7 +4099,7 @@ dependencies = [ [[package]] name = "pixi_git" version = "0.0.1" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "dashmap", "dunce", @@ -4051,14 +4119,14 @@ dependencies = [ [[package]] name = "pixi_manifest" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "chrono", "console", "dunce", "fancy_display", "fs-err", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "miette", "minijinja", @@ -4083,7 +4151,7 @@ dependencies = [ "strum", "thiserror 2.0.12", "toml-span", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing", "url", ] @@ -4091,7 +4159,7 @@ dependencies = [ [[package]] name = "pixi_pypi_spec" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "itertools 0.14.0", "pep440_rs", @@ -4103,7 +4171,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "toml-span", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing", "url", ] @@ -4111,7 +4179,7 @@ dependencies = [ [[package]] name = "pixi_spec" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "dirs", "file_url", @@ -4125,7 +4193,7 @@ dependencies = [ "serde_with", "thiserror 2.0.12", "toml-span", - "toml_edit 0.22.26", + "toml_edit 0.22.27", "tracing", "typed-path", "url", @@ -4134,10 +4202,11 @@ dependencies = [ [[package]] name = "pixi_spec_containers" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", + "pixi_spec", "rattler_conda_types", "serde", ] @@ -4145,11 +4214,11 @@ dependencies = [ [[package]] name = "pixi_toml" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "digest", "hex", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "miette", "strsim", @@ -4160,7 +4229,7 @@ dependencies = [ [[package]] name = "pixi_utils" version = "0.1.0" -source = "git+https://github.com/prefix-dev/pixi?branch=main#1ca9578b0ad73dbd5a889849f3bff47ddb91a81f" +source = "git+https://github.com/prefix-dev/pixi?branch=main#d5e6c4513e99bec71e807e00f8afd1ba4358c749" dependencies = [ "async-fd-lock", "fs-err", @@ -4215,7 +4284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ "base64 0.22.1", - "indexmap 2.9.0", + "indexmap 2.10.0", "quick-xml 0.32.0", "serde", "time", @@ -4248,6 +4317,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -4257,13 +4332,23 @@ dependencies = [ "zerocopy 0.8.25", ] +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] @@ -4296,12 +4381,12 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643af57c3f36ba90a8b53e972727d8092f7408a9ebfbaf4c3d2c17b07c58d835" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "pep440_rs", "pep508_rs", "serde", "thiserror 1.0.69", - "toml 0.8.22", + "toml 0.8.23", ] [[package]] @@ -4313,16 +4398,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "quick-xml" -version = "0.36.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quick-xml" version = "0.37.5" @@ -4360,7 +4435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", - "getrandom 0.3.2", + "getrandom 0.3.3", "rand 0.9.1", "ring", "rustc-hash", @@ -4464,14 +4539,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] name = "rattler" -version = "0.33.5" +version = "0.34.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e7bfacc5f5ff3517d984b5e2f2df3eea46e8309685cb7cdff39d62f7b2bee2" +checksum = "69f1ee665e700bdbb7c36ee61d929c7be85321af543c5d25744aa6ea30887d39" dependencies = [ "anyhow", "clap", @@ -4481,7 +4556,7 @@ dependencies = [ "fs-err", "futures", "humantime", - "indexmap 2.9.0", + "indexmap 2.10.0", "indicatif", "itertools 0.14.0", "memchr", @@ -4513,14 +4588,14 @@ dependencies = [ [[package]] name = "rattler-build" -version = "0.41.0" -source = "git+https://github.com/prefix-dev/rattler-build?branch=main#ea03f64d243260dfd06e7ec75e015233ae9e2bd2" +version = "0.44.0" +source = "git+https://github.com/prefix-dev/rattler-build?branch=main#6c3234456e800ed09c64441382a7722904702793" dependencies = [ "anyhow", "async-once-cell", "async-recursion", "base64 0.22.1", - "bzip2", + "bzip2 0.6.0", "chrono", "clap", "clap-verbosity-flag", @@ -4529,32 +4604,34 @@ dependencies = [ "comfy-table", "console", "content_inspector", + "diffy", "dunce", "flate2", "fs-err", "futures", - "gitpatch", "globset", "goblin", "hex", + "http-range-client", "ignore", - "indexmap 2.9.0", + "indexmap 2.10.0", "indicatif", "itertools 0.14.0", "lazy_static", - "marked-yaml 0.7.2", + "line-ending", + "marked-yaml", "memchr", "memmap2", "miette", "minijinja", "num_cpus", - "opendal 0.53.1", + "opendal", "pathdiff", - "petgraph 0.8.1", - "pixi_config", + "petgraph 0.8.2", "rattler", "rattler_cache", "rattler_conda_types", + "rattler_config", "rattler_digest", "rattler_index", "rattler_menuinst", @@ -4572,13 +4649,14 @@ dependencies = [ "reqwest", "reqwest-middleware", "reqwest-retry", - "retry-policies", + "retry-policies 0.5.1", "scroll", "serde", "serde-untagged", "serde_json", "serde_with", "serde_yaml", + "sevenz-rust2", "sha1", "sha2", "spdx", @@ -4589,23 +4667,24 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tokio-util 0.7.15", - "toml 0.8.22", + "toml 0.8.23", "tracing", "tracing-core", "tracing-subscriber", + "unicode-normalization", "url", "walkdir", "which", "xz2", - "zip", + "zip 4.2.0", "zstd", ] [[package]] name = "rattler_cache" -version = "0.3.17" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2ad44802bc15f7196f0898ff52729aab52338203ddbdff5212146f45aa2a67" +checksum = "cbea9d0e81a922ea7d657c320ca4f02a22c8a54c16bdccb0ce98998b0f3a9d2f" dependencies = [ "anyhow", "dashmap", @@ -4635,9 +4714,9 @@ dependencies = [ [[package]] name = "rattler_conda_types" -version = "0.32.0" +version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c2ed99f2fc8044499c63c65db265f6ec3e0333cdf15bb442d5063187449d51" +checksum = "c274929e5f1a067691a20c8861343799581edaaea8df6ee8d2ba1e9311d2bafa" dependencies = [ "chrono", "core-foundation 0.10.0", @@ -4647,10 +4726,11 @@ dependencies = [ "fxhash", "glob", "hex", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "lazy-regex", "nom", + "nom-language", "purl", "rattler_digest", "rattler_macros", @@ -4660,24 +4740,42 @@ dependencies = [ "serde", "serde-untagged", "serde_json", - "serde_repr", - "serde_with", - "serde_yaml", - "simd-json", - "smallvec", - "strum", - "tempfile", + "serde_repr", + "serde_with", + "serde_yaml", + "simd-json", + "smallvec", + "strum", + "tempfile", + "thiserror 2.0.12", + "tracing", + "typed-path", + "url", +] + +[[package]] +name = "rattler_config" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4681a6af0439dee6a8d23ca523dfba3d3119ffdf47bc91936d46244f993f2bc" +dependencies = [ + "console", + "fs-err", + "indexmap 2.10.0", + "rattler_conda_types", + "serde", + "serde_json", "thiserror 2.0.12", + "toml 0.8.23", "tracing", - "typed-path", "url", ] [[package]] name = "rattler_digest" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34751b3e9c9755379281d1b49b9335648a4686c434ce77b5376323980d38e1b8" +checksum = "51af113b5325cd8f6d07132da839f59d7e5e629585543259cc8a1fcac1d09ca4" dependencies = [ "blake2", "digest", @@ -4693,12 +4791,13 @@ dependencies = [ [[package]] name = "rattler_index" -version = "0.22.5" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3674b10a38a3033d0124978f6c251ad0926d4a621c7330bf03dfd61c40a0078a" +checksum = "728ef7b43c9a10b257fdb3ad870ba20e2d6bdd5bcf3c1d955e21005fae27ecc8" dependencies = [ "anyhow", "bytes", + "chrono", "clap", "clap-verbosity-flag", "console", @@ -4706,24 +4805,29 @@ dependencies = [ "futures", "fxhash", "indicatif", - "opendal 0.52.0", + "opendal", "rattler_conda_types", "rattler_digest", "rattler_networking", "rattler_package_streaming", "reqwest", + "rmp-serde", + "serde", "serde_json", + "sha2", + "tar", "tokio", "tracing", "tracing-subscriber", "url", + "zstd", ] [[package]] name = "rattler_macros" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9b25725cedeef8dee22c41dee138daf76ebd70da50fe210aa5e50288036816" +checksum = "80fd4cd77090cb4f5f42e990fdcf65f37a3ea9d9763fcb699fa7a04fbcfa82e4" dependencies = [ "quote", "syn", @@ -4731,9 +4835,9 @@ dependencies = [ [[package]] name = "rattler_menuinst" -version = "0.2.7" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3eedb82b5366136093acb1bafc4839f3f122c64889a358cbe6e50fc8c573b0" +checksum = "2d7eb978175752fc1db6eca68bf6fb1eafc9c87516cdf243db708a614a937ec8" dependencies = [ "chrono", "configparser", @@ -4755,15 +4859,15 @@ dependencies = [ "tracing", "unicode-normalization", "which", - "windows 0.60.0", - "windows-registry 0.5.1", + "windows 0.61.1", + "windows-registry 0.5.2", ] [[package]] name = "rattler_networking" -version = "0.22.12" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe0171ee4c22e335ade0cb926e83cdf08a5dda5eaf26fc21fa9948beb65c764" +checksum = "49e70fdc3304daa6b38c89233c3627329730fb84659fd9b36300c51dc62bf5e8" dependencies = [ "anyhow", "async-trait", @@ -4772,16 +4876,16 @@ dependencies = [ "base64 0.22.1", "dirs", "fs-err", - "getrandom 0.2.16", + "getrandom 0.3.3", "google-cloud-auth", - "google-cloud-token", "http 1.3.1", "itertools 0.14.0", "keyring", "netrc-rs", + "rattler_config", "reqwest", "reqwest-middleware", - "retry-policies", + "retry-policies 0.4.0", "serde", "serde_json", "tempfile", @@ -4792,11 +4896,11 @@ dependencies = [ [[package]] name = "rattler_package_streaming" -version = "0.22.36" +version = "0.22.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11879ff84195638459a15f0bb4c0d167f58ecd497d2df00df46c6cf81c0f2bb7" +checksum = "bd7b8dbfe695b5037e6ae40befcfdb1e00ba96fbba26dd06914a0bfe5f24b7d3" dependencies = [ - "bzip2", + "bzip2 0.6.0", "chrono", "fs-err", "futures-util", @@ -4816,26 +4920,26 @@ dependencies = [ "tokio-util 0.7.15", "tracing", "url", - "zip", + "zip 3.0.0", "zstd", ] [[package]] name = "rattler_pty" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1ba454a7e23bfe50c09abdb3164e26f98b326a04c104389c41e300918b219f" +checksum = "77c9380cb2a6519ecb334ddd1fadb59b686d29443f671c30b95b64e06ce41b9e" dependencies = [ "libc", - "nix", + "nix 0.30.1", "signal-hook", ] [[package]] name = "rattler_redaction" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d46116e48405681feb0c0c9938ecb6751825eb9bde353069e516784ca983e9" +checksum = "fd4bf2c07af84b2db2849fe7d6c8605eaa830e4a1b90ffb900d81847ba35eccc" dependencies = [ "reqwest", "reqwest-middleware", @@ -4844,9 +4948,9 @@ dependencies = [ [[package]] name = "rattler_repodata_gateway" -version = "0.22.5" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e25d367f6ff1d83cf925ccacfcf7805f94030f96de6858992db37edb3c5a638" +checksum = "654ced7b036b919ae43773d5718a3b47790b586a5baefdc4364e2a594ec84efa" dependencies = [ "anyhow", "async-compression", @@ -4880,13 +4984,14 @@ dependencies = [ "rattler_redaction", "reqwest", "reqwest-middleware", - "retry-policies", + "retry-policies 0.4.0", "rmp-serde", "self_cell", "serde", "serde_json", "serde_with", "simple_spawn_blocking", + "strum", "superslice", "tempfile", "thiserror 2.0.12", @@ -4895,15 +5000,15 @@ dependencies = [ "tracing", "url", "wasmtimer", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "zstd", ] [[package]] name = "rattler_sandbox" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b645c39a3822a3ae3ccc5d02e3ea830576e98178c0c47dcb2d19b227fa96c" +checksum = "815cae23697b8354a97760ffbf1aae9348ad3c357b70e1b871cfc67510632f8f" dependencies = [ "birdcage", "clap", @@ -4913,14 +5018,14 @@ dependencies = [ [[package]] name = "rattler_shell" -version = "0.22.26" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf546ea648c95cfeb27d8eedb6d592c8dec050d2dc9fb9af5be4ffe13759d7f" +checksum = "fb74c43a011badc5a97867288beff5ea13521dbeefb52cacb089584fa6aed761" dependencies = [ "anyhow", "enum_dispatch", "fs-err", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "rattler_conda_types", "rattler_pty", @@ -4934,9 +5039,9 @@ dependencies = [ [[package]] name = "rattler_solve" -version = "1.4.5" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b632fe7366b6b17e5ba96b14af992258aca04e2d36cf467a19f7980fa2298d7" +checksum = "bbdbaf1e3a0f25226b4b1acea7dcbe76eab2445e2a8a2ba6796061f8e249d547" dependencies = [ "chrono", "futures", @@ -4952,9 +5057,9 @@ dependencies = [ [[package]] name = "rattler_virtual_packages" -version = "2.0.10" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f8be154a9e0be3bfab3a68263ac568207eab9fb0e0820316374e805b340033" +checksum = "ea46af324d794aa9084ca923266b21a9467e3e1adb6a0e3f5f158480b4c79d3e" dependencies = [ "archspec", "libloading", @@ -4989,16 +5094,23 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "read-logger" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7f715a23c7db804b71eb9162a9cf210b89e99db9c3649a2a038d13b7594a99" +dependencies = [ + "log", +] + [[package]] name = "recipe-stage0" version = "0.1.0" dependencies = [ - "either", - "hashlink 0.10.0", - "indexmap 2.9.0", + "hashlink", + "indexmap 2.10.0", "insta", - "marked-yaml 0.8.0", - "miette", + "marked-yaml", "pixi_build_types", "rattler-build", "rattler_conda_types", @@ -5155,9 +5267,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "async-compression", "base64 0.22.1", @@ -5173,12 +5285,10 @@ dependencies = [ "hyper 1.6.0", "hyper-rustls 0.27.5", "hyper-util", - "ipnet", "js-sys", "log", "mime", "mime_guess", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", @@ -5233,7 +5343,7 @@ dependencies = [ "parking_lot 0.11.2", "reqwest", "reqwest-middleware", - "retry-policies", + "retry-policies 0.4.0", "thiserror 1.0.69", "tokio", "tracing", @@ -5242,16 +5352,16 @@ dependencies = [ [[package]] name = "resolvo" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "996e40b356864ad72942a9ef6815df7f219b7b42ccc791906d3e0a17df5d66ab" +checksum = "dba027c8e5dd4b5e5a690cfcfb3900d5ffe6985adb048cbd111d5aa596a6c0c8" dependencies = [ "ahash", "bitvec", "elsa", "event-listener", "futures", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "petgraph 0.7.1", "tracing", @@ -5266,6 +5376,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "retry-policies" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a4bd6027df676bcb752d3724db0ea3c0c5fc1dd0376fec51ac7dcaf9cc69be" +dependencies = [ + "rand 0.9.1", +] + [[package]] name = "rfc6979" version = "0.3.1" @@ -5419,6 +5538,7 @@ version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -5434,7 +5554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "schannel", "security-framework 2.11.1", ] @@ -5460,6 +5580,15 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.11.0" @@ -5520,6 +5649,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -5534,18 +5687,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scroll" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" dependencies = [ "scroll_derive", ] [[package]] name = "scroll_derive" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +checksum = "22fc4f90c27b57691bbaf11d8ecc7cfbfe98a4da6dbe60226115d322aa80c06e" dependencies = [ "proc-macro2", "quote", @@ -5708,7 +5861,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "memchr", "ryu", @@ -5728,9 +5881,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -5749,15 +5902,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.3", "serde", "serde_derive", "serde_json", @@ -5767,9 +5922,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling", "proc-macro2", @@ -5783,13 +5938,32 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "sevenz-rust2" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c12bd2c6a1f413f516836e80c1f541116f9b17ccbab2540f655ceeb58f5592c" +dependencies = [ + "aes", + "byteorder", + "bzip2 0.6.0", + "cbc", + "crc32fast", + "getrandom 0.3.3", + "js-sys", + "lzma-rust2", + "ppmd-rust", + "sha2", + "wasm-bindgen", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5839,9 +6013,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -5874,11 +6048,11 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "simd-json" -version = "0.14.3" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" +checksum = "c962f626b54771990066e5435ec8331d1462576cd2d1e62f24076ae014f92112" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.3.3", "halfbrown", "ref-cast", "serde", @@ -5899,18 +6073,6 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.12", - "time", -] - [[package]] name = "simple_spawn_blocking" version = "1.1.0" @@ -6004,9 +6166,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] @@ -6104,16 +6266,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.33.1" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" +checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" dependencies = [ - "core-foundation-sys", "libc", "memchr", "ntapi", - "rayon", - "windows 0.57.0", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.1", ] [[package]] @@ -6170,7 +6332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix 1.0.7", "windows-sys 0.59.0", @@ -6413,21 +6575,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] name = "toml-span" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757f36f490e7b3a25ed9fb692d7a0beb1424eabec3f7e8f40f576bece9a8cdc5" +checksum = "2d36acfca70d66f9b5f9c4786fec60096c3594169bf77b8d4207174dc862e6a4" dependencies = [ "serde", "smallvec", @@ -6435,9 +6597,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] @@ -6448,7 +6610,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -6457,23 +6619,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", "toml_write", - "winnow 0.7.8", + "winnow 0.7.11", ] [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tower" @@ -6544,9 +6706,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -6580,7 +6742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "regex", "serde", @@ -6608,9 +6770,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typed-path" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" +checksum = "c462d18470a2857aa657d338af5fa67170bb48bcc80a296710ce3b0802a32566" [[package]] name = "typeid" @@ -6742,11 +6904,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "js-sys", "rand 0.9.1", "serde", @@ -6761,9 +6923,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-trait" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" +checksum = "0508fce11ad19e0aab49ce20b6bec7f8f82902ded31df1c9fc61b90f0eb396b8" dependencies = [ "float-cmp", "halfbrown", @@ -6979,11 +7141,10 @@ dependencies = [ [[package]] name = "which" -version = "7.0.3" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ - "either", "env_home", "rustix 1.0.7", "winsafe", @@ -7029,16 +7190,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -7049,39 +7200,17 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" -dependencies = [ - "windows-collections 0.1.1", - "windows-core 0.60.1", - "windows-future 0.1.1", - "windows-link", - "windows-numerics 0.1.1", -] - [[package]] name = "windows" version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-collections 0.2.0", + "windows-collections", "windows-core 0.61.0", - "windows-future 0.2.0", + "windows-future", "windows-link", - "windows-numerics 0.2.0", -] - -[[package]] -name = "windows-collections" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" -dependencies = [ - "windows-core 0.60.1", + "windows-numerics", ] [[package]] @@ -7093,18 +7222,6 @@ dependencies = [ "windows-core 0.61.0", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -7118,19 +7235,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.60.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.3.1", -] - [[package]] name = "windows-core" version = "0.61.0" @@ -7140,18 +7244,8 @@ dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", -] - -[[package]] -name = "windows-future" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" -dependencies = [ - "windows-core 0.60.1", - "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -7164,17 +7258,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.58.0" @@ -7186,17 +7269,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -7208,17 +7280,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.58.0" @@ -7247,16 +7308,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" -[[package]] -name = "windows-numerics" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" -dependencies = [ - "windows-core 0.60.1", - "windows-link", -] - [[package]] name = "windows-numerics" version = "0.2.0" @@ -7273,29 +7324,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result 0.3.2", + "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-targets 0.53.2", ] [[package]] name = "windows-registry" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1da3e436dc7653dfdf3da67332e22bff09bb0e28b0239e1624499c7830842e" +checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" dependencies = [ "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -7309,9 +7351,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] @@ -7337,9 +7379,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -7362,6 +7404,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -7395,9 +7446,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -7558,9 +7609,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -7636,6 +7687,12 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "xz2" version = "0.1.7" @@ -7645,17 +7702,6 @@ dependencies = [ "lzma-sys", ] -[[package]] -name = "yaml-rust2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink 0.9.1", -] - [[package]] name = "yaml-rust2" version = "0.10.3" @@ -7664,7 +7710,7 @@ checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" dependencies = [ "arraydeque", "encoding_rs", - "hashlink 0.10.0", + "hashlink", ] [[package]] @@ -7713,7 +7759,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.29.0", "ordered-stream", "rand 0.8.5", "serde", @@ -7844,20 +7890,39 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" dependencies = [ "arbitrary", "crc32fast", - "crossbeam-utils", "flate2", - "indexmap 2.9.0", + "indexmap 2.10.0", "memchr", "time", "zopfli", ] +[[package]] +name = "zip" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap 2.10.0", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + [[package]] name = "zopfli" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 2416451a..e8e2c3b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,13 @@ clap = "4.5.29" clap-verbosity-flag = "3.0.2" either = "1.15.0" fs-err = "3.1.0" +hashlink = "0.10.0" +insta = "1.43.1" indexmap = "2.7.1" +ordermap = "0.5.7" itertools = "0.14.0" log = "0.4.25" +marked-yaml = "0.8.0" miette = "7.5.0" minijinja = "2.7.0" parking_lot = "0.12.3" @@ -35,19 +39,20 @@ url = "2.5.4" pyproject-toml = "0.13.4" dirs = "6.0.0" pathdiff = "0.2.3" +thiserror = "2.0.12" +strum = "0.27.2" jsonrpc-stdio-server = "18.0.0" jsonrpc-http-server = "18.0.0" jsonrpc-core = "18.0.0" -rattler-build = { version = "*", default-features = false, features = [ +rattler-build = { git = "https://github.com/prefix-dev/rattler-build", branch = "main", default-features = false, features = [ "rustls-tls", ] } - -rattler_conda_types = { version = "0.32.0", default-features = false } -rattler_package_streaming = { version = "0.22.35", default-features = false } -rattler_virtual_packages = { version = "2.0.9", default-features = false } +rattler_conda_types = { version = "0.35.3", default-features = false } +rattler_package_streaming = { version = "0.22.42", default-features = false } +rattler_virtual_packages = { version = "2.0.16", default-features = false } pixi_build_types = { version = "*" } pixi_consts = { version = "*" } @@ -80,6 +85,3 @@ pixi_build_type_conversions = { git = "https://github.com/prefix-dev/pixi", bran #rattler_virtual_packages = { path = "../rattler/crates/rattler_virtual_packages" } #rattler_repodata_gateway = { path = "../rattler/crates/rattler_repodata_gateway" } #simple_spawn_blocking = { path = "../rattler/crates/simple_spawn_blocking" } - -rattler-build = { git = "https://github.com/prefix-dev/rattler-build", branch = "main" } -#rattler-build = { path = "../rattler-build" } diff --git a/crates/pixi-build-backend/Cargo.toml b/crates/pixi-build-backend/Cargo.toml index d89de808..71c3082b 100644 --- a/crates/pixi-build-backend/Cargo.toml +++ b/crates/pixi-build-backend/Cargo.toml @@ -10,6 +10,7 @@ clap = { workspace = true, features = ["derive", "env"] } clap-verbosity-flag = { workspace = true } fs-err = { workspace = true } indexmap = { workspace = true } +ordermap = { workspace = true } itertools = { workspace = true } log = { workspace = true } miette = { workspace = true } @@ -32,6 +33,7 @@ url = { workspace = true } pyproject-toml = { workspace = true } dirs = { workspace = true } pathdiff = { workspace = true } +thiserror = { workspace = true } pixi_build_types = { workspace = true } pixi_consts = { workspace = true } diff --git a/crates/pixi-build-backend/src/cli.rs b/crates/pixi-build-backend/src/cli.rs index 15212330..fb425131 100644 --- a/crates/pixi-build-backend/src/cli.rs +++ b/crates/pixi-build-backend/src/cli.rs @@ -6,7 +6,7 @@ use miette::{Context, IntoDiagnostic}; use pixi_build_types::{ BackendCapabilities, ChannelConfiguration, FrontendCapabilities, PlatformAndVirtualPackages, procedures::{ - conda_build::CondaBuildParams, + conda_build_v0::CondaBuildParams, conda_metadata::{CondaMetadataParams, CondaMetadataResult}, initialize::InitializeParams, negotiate_capabilities::NegotiateCapabilitiesParams, @@ -93,17 +93,33 @@ pub async fn main T> Some(Commands::Capabilities) => { let backend_capabilities = capabilities::().await?; eprintln!( - "Supports conda metadata: {}", + "Supports {}: {}", + pixi_build_types::procedures::conda_metadata::METHOD_NAME, backend_capabilities .provides_conda_metadata .unwrap_or_default() ); eprintln!( - "Supports conda build: {}", + "Supports {}: {}", + pixi_build_types::procedures::conda_outputs::METHOD_NAME, + backend_capabilities + .provides_conda_outputs + .unwrap_or_default() + ); + eprintln!( + "Supports {}: {}", + pixi_build_types::procedures::conda_build_v0::METHOD_NAME, backend_capabilities .provides_conda_build .unwrap_or_default() ); + eprintln!( + "Supports {}: {}", + pixi_build_types::procedures::conda_build_v1::METHOD_NAME, + backend_capabilities + .provides_conda_build_v1 + .unwrap_or_default() + ); eprintln!( "Highest project model: {}", backend_capabilities @@ -156,6 +172,7 @@ async fn initialize( // Initialize the backend let (protocol, _initialize_result) = factory .initialize(InitializeParams { + source_dir: None, manifest_path: manifest_path.to_path_buf(), project_model, cache_directory: None, @@ -232,7 +249,7 @@ async fn build(factory: T, manifest_path: &Path) -> mie .context("failed to create a temporary directory in the current directory")?; let result = protocol - .conda_build(CondaBuildParams { + .conda_build_v0(CondaBuildParams { host_platform: None, build_platform_virtual_packages: None, channel_base_urls: None, diff --git a/crates/pixi-build-backend/src/dependencies.rs b/crates/pixi-build-backend/src/dependencies.rs index 1de716f7..ec0ff85f 100644 --- a/crates/pixi-build-backend/src/dependencies.rs +++ b/crates/pixi-build-backend/src/dependencies.rs @@ -3,15 +3,27 @@ use std::{ str::FromStr, }; -use miette::{Context, IntoDiagnostic}; +use miette::{Context, Diagnostic, IntoDiagnostic}; use pixi_build_types as pbt; +use pixi_build_types::{BinaryPackageSpecV1, NamedSpecV1}; use rattler_build::{ NormalizedKey, + metadata::PackageIdentifier, recipe::{parser::Dependency, variable::Variable}, + render::{ + pin::PinError, + resolved_dependencies::{ + DependencyInfo, PinCompatibleDependency, PinSubpackageDependency, ResolveError, + SourceDependency, VariantDependency, + }, + }, }; -use rattler_conda_types::{MatchSpec, NamelessMatchSpec, PackageName, ParseStrictness::Strict}; +use rattler_conda_types::{ + MatchSpec, NamelessMatchSpec, PackageName, PackageRecord, ParseStrictness::Strict, +}; +use thiserror::Error; -use crate::traits::PackageSpec; +use crate::{specs_conversion::from_source_url_to_source_package, traits::PackageSpec}; /// A helper struct to extract match specs from a manifest. #[derive(Default)] @@ -111,3 +123,308 @@ impl ExtractedDependencies { }) } } + +/// Converts the input variant configuration passed from pixi to something that +/// rattler build can deal with. +pub fn convert_input_variant_configuration( + variants: Option>>, +) -> Option>> { + variants.map(|v| { + v.into_iter() + .map(|(k, v)| { + ( + k.into(), + v.into_iter().map(|v| Variable::from_string(&v)).collect(), + ) + }) + .collect() + }) +} + +#[derive(Debug, Error, Diagnostic)] +pub enum ConvertDependencyError { + #[error("only matchspecs with defined package names are supported")] + MissingName, + + #[error("could not parse version spec for variant key {0}: {1}")] + VariantSpecParseError(String, rattler_conda_types::ParseMatchSpecError), + + #[error("could not apply pin. The following subpackage is not available: {0:?}")] + SubpackageNotFound(PackageName), + + #[error("could not apply pin: {0}")] + PinApplyError(PinError), +} + +fn convert_nameless_matchspec(spec: NamelessMatchSpec) -> pbt::BinaryPackageSpecV1 { + pbt::BinaryPackageSpecV1 { + version: spec.version, + build: spec.build, + build_number: spec.build_number, + file_name: spec.file_name, + channel: spec.channel.map(|c| c.base_url.clone().into()), + subdir: spec.subdir, + md5: spec.md5, + sha256: spec.sha256, + url: spec.url, + license: spec.license, + } +} + +/// Checks if it is applicable to apply a variant on the specified match spec. A +/// variant can be applied if it has a name and no other fields set. Returns the +/// name of the variant that should be used. +fn can_apply_variant(spec: &MatchSpec) -> Option<&PackageName> { + match &spec { + MatchSpec { + name: Some(name), + version: None, + build: None, + build_number: None, + file_name: None, + extras: None, + channel: None, + subdir: None, + namespace: None, + md5: None, + sha256: None, + license: None, + url: None, + } => Some(name), + _ => None, + } +} + +fn apply_variant_and_convert( + spec: &MatchSpec, + variant: &BTreeMap, +) -> Result>, ConvertDependencyError> { + let Some(name) = can_apply_variant(spec) else { + return Ok(None); + }; + let Some(version) = variant.get(&name.into()).map(Variable::to_string) else { + return Ok(None); + }; + + // if the variant starts with an alphanumeric character, + // we have to add a '=' to the version spec + let mut spec = version.to_string(); + + // check if all characters are alphanumeric or ., in that case add + // a '=' to get "startswith" behavior + if spec.chars().all(|c| c.is_alphanumeric() || c == '.') { + spec = format!("={spec}"); + } + + let variant = name.as_normalized().to_string(); + let spec: NamelessMatchSpec = spec + .parse() + .map_err(|e| ConvertDependencyError::VariantSpecParseError(variant.clone(), e))?; + + Ok(Some(pbt::NamedSpecV1 { + name: name.as_source().to_owned(), + spec: convert_nameless_matchspec(spec), + })) +} + +fn convert_dependency( + dependency: Dependency, + variant: &BTreeMap, + subpackages: &HashMap, + sources: &HashMap, +) -> Result, ConvertDependencyError> { + let match_spec = match dependency { + Dependency::Spec(spec) => { + // Convert back to source spec if it is a source spec. + if let Some(source_package) = + spec.url.clone().and_then(from_source_url_to_source_package) + { + let Some(name) = spec.name else { + return Err(ConvertDependencyError::MissingName); + }; + return Ok(pbt::NamedSpecV1 { + name: name.as_source().into(), + spec: pbt::PackageSpecV1::Source(source_package), + }); + } + + // Apply a variant if it is applicable. + if let Some(NamedSpecV1 { name, spec }) = apply_variant_and_convert(&spec, variant)? { + return Ok(pbt::NamedSpecV1 { + name, + spec: pbt::PackageSpecV1::Binary(Box::new(spec)), + }); + } + spec + } + Dependency::PinSubpackage(pin) => { + let name = &pin.pin_value().name; + let subpackage = subpackages + .get(name) + .ok_or(ConvertDependencyError::SubpackageNotFound(name.to_owned()))?; + pin.pin_value() + .apply(&subpackage.version, &subpackage.build_string) + .map_err(ConvertDependencyError::PinApplyError)? + } + _ => todo!("Handle other dependency types"), + }; + + let (Some(name), spec) = match_spec.into_nameless() else { + return Err(ConvertDependencyError::MissingName); + }; + + if let Some(source) = sources + .get(name.as_source()) + .or_else(|| sources.get(name.as_normalized())) + { + Ok(pbt::NamedSpecV1 { + name: name.as_source().to_owned(), + spec: pbt::PackageSpecV1::Source(source.clone()), + }) + } else { + Ok(pbt::NamedSpecV1 { + name: name.as_source().to_owned(), + spec: pbt::PackageSpecV1::Binary(Box::new(convert_nameless_matchspec(spec))), + }) + } +} + +fn convert_binary_dependency( + dependency: Dependency, + variant: &BTreeMap, + subpackages: &HashMap, +) -> Result, ConvertDependencyError> { + let match_spec = match dependency { + Dependency::Spec(spec) => { + // Apply a variant if it is applicable. + if let Some(spec) = apply_variant_and_convert(&spec, variant)? { + return Ok(spec); + } + spec + } + Dependency::PinSubpackage(pin) => { + let name = &pin.pin_value().name; + let subpackage = subpackages + .get(name) + .ok_or(ConvertDependencyError::SubpackageNotFound(name.to_owned()))?; + pin.pin_value() + .apply(&subpackage.version, &subpackage.build_string) + .map_err(ConvertDependencyError::PinApplyError)? + } + _ => todo!("Handle other dependency types"), + }; + + // Apply a variant if it is applicable. + if let Some(spec) = apply_variant_and_convert(&match_spec, variant)? { + return Ok(spec); + } + + let (Some(name), spec) = match_spec.into_nameless() else { + return Err(ConvertDependencyError::MissingName); + }; + + Ok(pbt::NamedSpecV1 { + name: name.as_source().to_owned(), + spec: convert_nameless_matchspec(spec), + }) +} + +pub fn convert_dependencies( + dependencies: Vec, + variant: &BTreeMap, + subpackages: &HashMap, + sources: &HashMap, +) -> Result>, ConvertDependencyError> { + dependencies + .into_iter() + .map(|spec| convert_dependency(spec, variant, subpackages, sources)) + .collect() +} + +pub fn convert_binary_dependencies( + dependencies: Vec, + variant: &BTreeMap, + subpackages: &HashMap, +) -> Result>, ConvertDependencyError> { + dependencies + .into_iter() + .map(|spec| convert_binary_dependency(spec, variant, subpackages)) + .collect() +} + +/// Apply a variant to a dependency list and resolve all pin_subpackage and +/// compiler dependencies +pub fn apply_variant( + raw_specs: &[Dependency], + variant: &BTreeMap, + subpackages: &HashMap, + compatibility_specs: &HashMap, + build_time: bool, +) -> Result, ResolveError> { + raw_specs + .iter() + .map(|s| { + match s { + Dependency::Spec(m) => { + let m = m.clone(); + if build_time && m.version.is_none() && m.build.is_none() { + if let Some(name) = &m.name { + if let Some(version) = variant.get(&name.into()) { + // if the variant starts with an alphanumeric character, + // we have to add a '=' to the version spec + let mut spec = version.to_string(); + + // check if all characters are alphanumeric or ., in that case add + // a '=' to get "startswith" behavior + if spec.chars().all(|c| c.is_alphanumeric() || c == '.') { + spec = format!("={spec}"); + } + + let variant = name.as_normalized().to_string(); + let spec: NamelessMatchSpec = spec.parse().map_err(|e| { + ResolveError::VariantSpecParseError(variant.clone(), e) + })?; + + let spec = MatchSpec::from_nameless(spec, Some(name.clone())); + + return Ok(VariantDependency { spec, variant }.into()); + } + } + } + Ok(SourceDependency { spec: m }.into()) + } + Dependency::PinSubpackage(pin) => { + let name = &pin.pin_value().name; + let subpackage = subpackages + .get(name) + .ok_or(ResolveError::SubpackageNotFound(name.to_owned()))?; + let pinned = pin + .pin_value() + .apply(&subpackage.version, &subpackage.build_string)?; + Ok(PinSubpackageDependency { + spec: pinned, + name: name.as_normalized().to_string(), + args: pin.pin_value().args.clone(), + } + .into()) + } + Dependency::PinCompatible(pin) => { + let name = &pin.pin_value().name; + let pin_package = compatibility_specs + .get(name) + .ok_or(ResolveError::SubpackageNotFound(name.to_owned()))?; + + let pinned = pin + .pin_value() + .apply(&pin_package.version, &pin_package.build)?; + Ok(PinCompatibleDependency { + spec: pinned, + name: name.as_normalized().to_string(), + args: pin.pin_value().args.clone(), + } + .into()) + } + } + }) + .collect() +} diff --git a/crates/pixi-build-backend/src/generated_recipe.rs b/crates/pixi-build-backend/src/generated_recipe.rs index bad2a597..a726cc9b 100644 --- a/crates/pixi-build-backend/src/generated_recipe.rs +++ b/crates/pixi-build-backend/src/generated_recipe.rs @@ -1,5 +1,5 @@ use std::{ - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}, }; @@ -18,8 +18,8 @@ pub struct PythonParams { pub editable: bool, } -/// The trait is responsible of converting a certain [`ProjectModelV1`] (or others in the future) -/// into an [`IntermediateRecipe`]. +/// The trait is responsible of converting a certain [`ProjectModelV1`] (or +/// others in the future) into an [`IntermediateRecipe`]. /// By implementing this trait, you can create a new backend for `pixi-build`. /// /// It also uses a [`BackendConfig`] to provide additional configuration @@ -56,8 +56,8 @@ pub trait GenerateRecipe { _config: &Self::Config, _workdir: impl AsRef, _editable: bool, - ) -> Vec { - vec![] + ) -> BTreeSet { + BTreeSet::new() } /// Returns "default" variants for the given host platform. This allows @@ -80,8 +80,8 @@ pub trait BackendConfig: DeserializeOwned + Default { #[derive(Default)] pub struct GeneratedRecipe { pub recipe: IntermediateRecipe, - pub metadata_input_globs: Vec, - pub build_input_globs: Vec, + pub metadata_input_globs: BTreeSet, + pub build_input_globs: BTreeSet, } impl GeneratedRecipe { @@ -98,9 +98,12 @@ impl GeneratedRecipe { ), }; - let source = ConditionalList::from([Item::Value(Value::Concrete(Source::path( - manifest_root.display().to_string(), - )))]); + let manifest_path = match manifest_root.display().to_string() { + path if path.is_empty() => String::from("."), + path => path, + }; + let source = + ConditionalList::from([Item::Value(Value::Concrete(Source::path(manifest_path)))]); let requirements = from_targets_v1_to_conditional_requirements(&model.targets.unwrap_or_default()); diff --git a/crates/pixi-build-backend/src/intermediate_backend.rs b/crates/pixi-build-backend/src/intermediate_backend.rs index 28371bce..c7c49238 100644 --- a/crates/pixi-build-backend/src/intermediate_backend.rs +++ b/crates/pixi-build-backend/src/intermediate_backend.rs @@ -4,16 +4,21 @@ use std::{ sync::Arc, }; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use miette::{Context, IntoDiagnostic}; use pixi_build_types::{ - BackendCapabilities, CondaPackageMetadata, ProjectModelV1, + BackendCapabilities, CondaPackageMetadata, PathSpecV1, ProjectModelV1, SourcePackageSpecV1, procedures::{ - conda_build::{ + conda_build_v0::{ CondaBuildParams, CondaBuildResult, CondaBuiltPackage, CondaOutputIdentifier, }, + conda_build_v1::{CondaBuildV1Output, CondaBuildV1Params, CondaBuildV1Result}, conda_metadata::{CondaMetadataParams, CondaMetadataResult}, + conda_outputs::{ + CondaOutput, CondaOutputDependencies, CondaOutputIgnoreRunExports, CondaOutputMetadata, + CondaOutputRunExports, CondaOutputsParams, CondaOutputsResult, + }, initialize::{InitializeParams, InitializeResult}, negotiate_capabilities::{NegotiateCapabilitiesParams, NegotiateCapabilitiesResult}, }, @@ -23,7 +28,7 @@ use rattler_build::{ console_utils::LoggingOutputHandler, hash::HashInfo, metadata::{ - BuildConfiguration, Debug, Output, PackageIdentifier, PackagingSettings, + BuildConfiguration, Debug, Directories, Output, PackageIdentifier, PackagingSettings, PlatformWithVirtualPackages, }, recipe::{ @@ -31,19 +36,25 @@ use rattler_build::{ parser::{BuildString, find_outputs_from_src}, variable::Variable, }, - render::resolved_dependencies::DependencyInfo, + render::resolved_dependencies::{ + DependencyInfo, FinalizedDependencies, FinalizedRunDependencies, ResolvedDependencies, + }, selectors::SelectorConfig, source_code::Source, system_tools::SystemTools, tool_configuration::Configuration, - variant_config::{ParseErrors, VariantConfig}, + variant_config::{DiscoveredOutput, ParseErrors, VariantConfig}, +}; +use rattler_conda_types::{ + ChannelConfig, MatchSpec, Platform, compression_level::CompressionLevel, package::ArchiveType, }; -use rattler_conda_types::{ChannelConfig, MatchSpec, Platform, package::ArchiveType}; -use rattler_package_streaming::write::CompressionLevel; use recipe_stage0::matchspec::{PackageDependency, SerializableMatchSpec}; use serde::Deserialize; use crate::{ + dependencies::{ + convert_binary_dependencies, convert_dependencies, convert_input_variant_configuration, + }, generated_recipe::{BackendConfig, GenerateRecipe, PythonParams}, protocol::{Protocol, ProtocolInstantiator}, specs_conversion::from_source_matchspec_into_package_spec, @@ -89,6 +100,7 @@ pub struct IntermediateBackend { impl IntermediateBackend { pub fn new( manifest_path: PathBuf, + source_dir: Option, project_model: ProjectModelV1, generate_recipe: T, config: serde_json::Value, @@ -96,19 +108,37 @@ impl IntermediateBackend { cache_dir: Option, ) -> miette::Result { // Determine the root directory of the manifest - let manifest_root = manifest_path - .parent() - .ok_or_else(|| miette::miette!("the project manifest must reside in a directory"))? - .to_path_buf(); + let (source_dir, manifest_rel_path) = match source_dir { + None => { + let source_dir = manifest_path + .parent() + .ok_or_else(|| { + miette::miette!("the project manifest must reside in a directory") + })? + .to_path_buf(); + let manifest_rel_path = manifest_path + .file_name() + .map(Path::new) + .expect("we already validated that the manifest path is a file") + .to_path_buf(); + (source_dir, manifest_rel_path) + } + Some(source_dir) => { + let manifest_rel_path = pathdiff::diff_paths(manifest_path, &source_dir) + .ok_or_else(|| { + miette::miette!("the manifest is not relative to the source directory") + })?; + (source_dir, manifest_rel_path) + } + }; let config = serde_json::from_value::(config) .into_diagnostic() .context("failed to parse configuration")?; Ok(Self { - manifest_rel_path: pathdiff::diff_paths(manifest_path, &manifest_root) - .expect("must be relative"), - source_dir: manifest_root, + source_dir, + manifest_rel_path, project_model, generate_recipe, config, @@ -122,7 +152,7 @@ impl IntermediateBackend { impl ProtocolInstantiator for IntermediateBackendInstantiator where T: GenerateRecipe + Clone + Send + Sync + 'static, - T::Config: BackendConfig + Send + Sync + 'static, + T::Config: Send + Sync + 'static, { fn debug_dir(configuration: Option) -> Option { let config = configuration @@ -152,6 +182,7 @@ where let instance = IntermediateBackend::::new( params.manifest_path, + params.source_dir, project_model, self.generator.clone(), config, @@ -274,6 +305,7 @@ where variant: Default::default(), experimental: false, allow_undefined: false, + recipe_path: Some(self.source_dir.join(&self.manifest_rel_path)), }; let outputs = find_outputs_from_src(named_source.clone())?; let discovered_outputs = variant_config.find_variants( @@ -385,6 +417,7 @@ where sandbox_config: None, debug: Debug::default(), solve_strategy: Default::default(), + exclude_newer: None, }, finalized_dependencies: None, finalized_sources: None, @@ -490,7 +523,7 @@ where }) } - async fn conda_build(&self, params: CondaBuildParams) -> miette::Result { + async fn conda_build_v0(&self, params: CondaBuildParams) -> miette::Result { let channel_config = ChannelConfig { channel_alias: params.channel_configuration.base_url, root_dir: self.source_dir.to_path_buf(), @@ -505,7 +538,7 @@ where let build_platform = Platform::current(); // Construct the intermediate recipe - let generated_recipe = self.generate_recipe.generate_recipe( + let mut generated_recipe = self.generate_recipe.generate_recipe( &self.project_model, &self.config, self.source_dir.clone(), @@ -542,19 +575,9 @@ where // from the generator and the user supplied parameters. The parameters // from the user take precedence over the default variants. let recipe_variants = self.generate_recipe.default_variants(host_platform); - let mut param_variant_configuration = params - .variant_configuration - .unwrap_or_default() - .into_iter() - .map(|(k, v)| { - ( - k.into(), - v.into_iter().map(|v| Variable::from_string(&v)).collect(), - ) - }) - .collect(); - let mut variants = recipe_variants; - variants.append(&mut param_variant_configuration); + let param_variants = + convert_input_variant_configuration(params.variant_configuration).unwrap_or_default(); + let variants = BTreeMap::from_iter(itertools::chain!(recipe_variants, param_variants)); let variant_config = VariantConfig { variants, pin_run_as_build: None, @@ -576,6 +599,7 @@ where variant: Default::default(), experimental: false, allow_undefined: false, + recipe_path: Some(self.source_dir.join(&self.manifest_rel_path)), }; let outputs = find_outputs_from_src(named_source.clone())?; let mut discovered_outputs = variant_config.find_variants( @@ -716,6 +740,7 @@ where sandbox_config: None, debug: Debug::default(), solve_strategy: Default::default(), + exclude_newer: None, }, finalized_dependencies: None, finalized_sources: None, @@ -734,28 +759,18 @@ where .within_context_async(move || async move { run_build(output, &tool_config).await }) .await?; - let input_globs = T::extract_input_globs_from_build( + // Extract the input globs from the build and recipe + let mut input_globs = T::extract_input_globs_from_build( &self.config, ¶ms.work_directory, params.editable, ); - - // join it with the files that were read during the recipe generation - let input_globs = input_globs - .into_iter() - .chain( - generated_recipe - .build_input_globs - .iter() - .map(|f| f.to_string()), - ) - .collect::>(); + input_globs.append(&mut generated_recipe.build_input_globs); let built_package = CondaBuiltPackage { output_file: package, - // TODO: we should handle input globs properly input_globs, - name: output.name().as_normalized().to_string(), + name: output.name().clone(), version: output.version().to_string(), build: output.build_string().into_owned(), subdir: output.target_platform().to_string(), @@ -765,6 +780,464 @@ where Ok(CondaBuildResult { packages }) } + + async fn conda_outputs( + &self, + params: CondaOutputsParams, + ) -> miette::Result { + let build_platform = params.host_platform; + + // Construct the intermediate recipe + let recipe = self.generate_recipe.generate_recipe( + &self.project_model, + &self.config, + self.source_dir.clone(), + params.host_platform, + Some(PythonParams { editable: false }), + )?; + + // Convert the recipe to source code. + // TODO(baszalmstra): In the future it would be great if we could just + // immediately use the intermediate recipe for some of this rattler-build + // functions. + let recipe_path = self.source_dir.join(&self.manifest_rel_path); + let named_source = Source { + name: self.manifest_rel_path.display().to_string(), + code: Arc::from(recipe.recipe.to_yaml_pretty().into_diagnostic()?.as_str()), + path: recipe_path.clone(), + }; + + // Construct a `VariantConfig` based on the input parameters. + // + // rattler-build recipes would also load variant.yaml (or + // conda-build-config.yaml) files here, but we only respect the variant + // configuration passed in. + // + // Determine the variant configuration to use. This is a combination of defaults + // from the generator and the user supplied parameters. The parameters + // from the user take precedence over the default variants. + let recipe_variants = self.generate_recipe.default_variants(params.host_platform); + let param_variants = + convert_input_variant_configuration(params.variant_configuration).unwrap_or_default(); + let variants = BTreeMap::from_iter(itertools::chain!(recipe_variants, param_variants)); + let variant_config = VariantConfig { + variants, + pin_run_as_build: None, + zip_keys: None, + }; + + // Determine the different outputs that are supported by the recipe by expanding + // all the different variant combinations. + // + // TODO(baszalmstra): The selector config we pass in here doesnt have all values + // filled in. This is on prupose because at this point we dont yet know all + // values like the variant. We should introduce a new type of selector config + // for this particular case. + let selector_config_for_variants = SelectorConfig { + target_platform: params.host_platform, + host_platform: params.host_platform, + build_platform, + hash: None, + variant: Default::default(), + experimental: false, + allow_undefined: false, + recipe_path: Some(self.source_dir.join(&self.manifest_rel_path)), + }; + let outputs = find_outputs_from_src(named_source.clone())?; + let discovered_outputs = variant_config.find_variants( + &outputs, + named_source.clone(), + &selector_config_for_variants, + )?; + + // Construct a mapping that for packages that we want from source. + // + // By default, this includes all the outputs in the recipe. These should all be + // build from source, in particular from the current source. + let local_source_packages: HashMap = discovered_outputs + .iter() + .map(|output| { + ( + output.name.clone(), + SourcePackageSpecV1::Path(PathSpecV1 { path: ".".into() }), + ) + }) + .collect(); + + let mut subpackages = HashMap::new(); + let mut outputs = Vec::new(); + for discovered_output in discovered_outputs { + let variant = discovered_output.used_vars; + let hash = HashInfo::from_variant(&variant, &discovered_output.noarch_type); + + // Construct the selector config for this particular output. We base this on the + // selector config that was used to determine the variants. + let selector_config = SelectorConfig { + variant: variant.clone(), + hash: Some(hash.clone()), + target_platform: discovered_output.target_platform, + ..selector_config_for_variants.clone() + }; + + // Convert this discovered output into a recipe. + let recipe = Recipe::from_node(&discovered_output.node, selector_config.clone()) + .map_err(|err| { + let errs: ParseErrors<_> = err + .into_iter() + .map(|err| ParsingError::from_partial(named_source.clone(), err)) + .collect::>() + .into(); + errs + })?; + + // Skip this output if the recipe is marked as skipped + if recipe.build().skip() { + continue; + } + + let build_number = recipe.build().number; + + subpackages.insert( + recipe.package().name().clone(), + PackageIdentifier { + name: recipe.package().name().clone(), + version: recipe.package().version().clone(), + build_string: discovered_output.build_string.clone(), + }, + ); + + outputs.push(CondaOutput { + metadata: CondaOutputMetadata { + name: recipe.package().name().clone(), + version: recipe.package.version().clone(), + build: discovered_output.build_string.clone(), + build_number, + subdir: discovered_output.target_platform, + license: recipe.about.license.map(|l| l.to_string()), + license_family: recipe.about.license_family, + noarch: recipe.build.noarch, + purls: None, + python_site_packages_path: None, + variant: variant + .iter() + .map(|(key, value)| (key.0.clone(), value.to_string())) + .collect(), + }, + build_dependencies: Some(CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.build, + &variant, + &subpackages, + &local_source_packages, + )?, + constraints: Vec::new(), + }), + host_dependencies: Some(CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.host, + &variant, + &subpackages, + &local_source_packages, + )?, + constraints: Vec::new(), + }), + run_dependencies: CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.run, + &BTreeMap::default(), // Variants are not applied to run dependencies + &subpackages, + &local_source_packages, + )?, + constraints: convert_binary_dependencies( + recipe.requirements.run_constraints, + &BTreeMap::default(), // Variants are not applied to run constraints + &subpackages, + )?, + }, + ignore_run_exports: CondaOutputIgnoreRunExports { + by_name: recipe + .requirements + .ignore_run_exports + .by_name + .into_iter() + .collect(), + from_package: recipe + .requirements + .ignore_run_exports + .from_package + .into_iter() + .collect(), + }, + run_exports: CondaOutputRunExports { + weak: convert_dependencies( + recipe.requirements.run_exports.weak, + &variant, + &subpackages, + &local_source_packages, + )?, + strong: convert_dependencies( + recipe.requirements.run_exports.strong, + &variant, + &subpackages, + &local_source_packages, + )?, + noarch: convert_dependencies( + recipe.requirements.run_exports.noarch, + &variant, + &subpackages, + &local_source_packages, + )?, + weak_constrains: convert_binary_dependencies( + recipe.requirements.run_exports.weak_constraints, + &variant, + &subpackages, + )?, + strong_constrains: convert_binary_dependencies( + recipe.requirements.run_exports.strong_constraints, + &variant, + &subpackages, + )?, + }, + + // The input globs are the same for all outputs + input_globs: None, + // TODO: Implement caching + }); + } + + Ok(CondaOutputsResult { + outputs, + input_globs: recipe.metadata_input_globs, + }) + } + + async fn conda_build_v1( + &self, + params: CondaBuildV1Params, + ) -> miette::Result { + let host_platform = params + .host_prefix + .as_ref() + .map_or_else(Platform::current, |prefix| prefix.platform); + let build_platform = params + .build_prefix + .as_ref() + .map_or_else(Platform::current, |prefix| prefix.platform); + + // Construct the intermediate recipe + let mut recipe = self.generate_recipe.generate_recipe( + &self.project_model, + &self.config, + self.source_dir.clone(), + host_platform, + Some(PythonParams { + editable: params.editable.unwrap_or_default(), + }), + )?; + + // Convert the recipe to source code. + // TODO(baszalmstra): In the future it would be great if we could just + // immediately use the intermediate recipe for some of this rattler-build + // functions. + let recipe_path = self.source_dir.join(&self.manifest_rel_path); + let named_source = Source { + name: self.manifest_rel_path.display().to_string(), + code: Arc::from(recipe.recipe.to_yaml_pretty().into_diagnostic()?.as_str()), + path: recipe_path.clone(), + }; + + // Construct a `VariantConfig` based on the input parameters. We only + // have a single variant here so we can just use the variant from the + // parameters. + let variant_config = VariantConfig { + variants: params + .output + .variant + .iter() + .map(|(k, v)| (k.as_str().into(), vec![Variable::from_string(v)])) + .collect(), + pin_run_as_build: None, + zip_keys: None, + }; + + // Determine the different outputs that are supported by the recipe. + let selector_config_for_variants = SelectorConfig { + target_platform: host_platform, + host_platform, + build_platform, + hash: None, + variant: Default::default(), + experimental: false, + allow_undefined: false, + recipe_path: Some(self.source_dir.join(&self.manifest_rel_path)), + }; + let outputs = find_outputs_from_src(named_source.clone())?; + let discovered_outputs = variant_config.find_variants( + &outputs, + named_source.clone(), + &selector_config_for_variants, + )?; + let discovered_output = find_matching_output(¶ms.output, discovered_outputs)?; + + // Set up the proper directories for the build. + let directories = conda_build_v1_directories( + params.host_prefix.as_ref().map(|p| p.prefix.as_path()), + params.build_prefix.as_ref().map(|p| p.prefix.as_path()), + params.work_directory.clone(), + self.cache_dir.as_deref(), + self.source_dir.clone(), + params.output_directory.as_deref(), + recipe_path, + ); + + let tool_config = Configuration::builder() + .with_opt_cache_dir(self.cache_dir.clone()) + .with_logging_output_handler(self.logging_output_handler.clone()) + .with_testing(false) + // Pixi is incremental so keep the build + .with_keep_build(true) + // This indicates that the environments are externally managed, e.g. they are already + // prepared. + .with_environments_externally_managed(true) + .finish(); + + let output = Output { + recipe: discovered_output.recipe, + build_configuration: BuildConfiguration { + target_platform: discovered_output.target_platform, + host_platform: PlatformWithVirtualPackages { + platform: host_platform, + virtual_packages: vec![], + }, + build_platform: PlatformWithVirtualPackages { + platform: build_platform, + virtual_packages: vec![], + }, + hash: discovered_output.hash, + variant: discovered_output.used_vars.clone(), + directories, + channels: vec![], + channel_priority: Default::default(), + solve_strategy: Default::default(), + timestamp: chrono::Utc::now(), + subpackages: BTreeMap::new(), + packaging_settings: PackagingSettings::from_args( + ArchiveType::Conda, + CompressionLevel::default(), + ), + store_recipe: false, + force_colors: true, + sandbox_config: None, + debug: Debug::new(false), + exclude_newer: None, + }, + // TODO: We should pass these values to the build backend from pixi + finalized_dependencies: Some(FinalizedDependencies { + build: Some(ResolvedDependencies { + specs: vec![], + resolved: vec![], + }), + host: Some(ResolvedDependencies { + specs: vec![], + resolved: vec![], + }), + run: FinalizedRunDependencies { + depends: vec![], + constraints: vec![], + run_exports: Default::default(), + }, + }), + finalized_sources: None, + finalized_cache_dependencies: None, + finalized_cache_sources: None, + build_summary: Arc::default(), + system_tools: Default::default(), + extra_meta: None, + }; + + let (output, output_path) = run_build(output, &tool_config).await?; + + // Extract the input globs from the build and recipe + let mut input_globs = T::extract_input_globs_from_build( + &self.config, + ¶ms.work_directory, + params.editable.unwrap_or_default(), + ); + input_globs.append(&mut recipe.build_input_globs); + + Ok(CondaBuildV1Result { + output_file: output_path, + input_globs, + name: output.name().as_normalized().to_string(), + version: output.version().clone(), + build: output.build_string().into_owned(), + subdir: *output.target_platform(), + }) + } +} + +pub fn find_matching_output( + expected_output: &CondaBuildV1Output, + discovered_outputs: IndexSet, +) -> miette::Result { + // Find the only output that matches the request. + let discovered_output = discovered_outputs + .into_iter() + .find(|output| { + expected_output.name.as_normalized() == output.name + && expected_output + .build + .as_ref() + .is_none_or(|build_string| build_string == &output.build_string) + && expected_output + .version + .as_ref() + .is_none_or(|version| version == &output.recipe.package.version) + && expected_output.subdir == output.target_platform + && !output.recipe.build.skip() + }) + .ok_or_else(|| { + miette::miette!( + "the requested output {}/{}={}@{} was not found in the recipe", + expected_output.name.as_source(), + expected_output + .version + .as_ref() + .map_or_else(|| String::from("??"), |v| v.as_str().into_owned()), + expected_output.build.as_deref().unwrap_or("??"), + expected_output.subdir + ) + })?; + Ok(discovered_output) +} + +pub fn conda_build_v1_directories( + host_prefix: Option<&Path>, + build_prefix: Option<&Path>, + work_directory: PathBuf, + cache_dir: Option<&Path>, + source_dir: PathBuf, + output_dir: Option<&Path>, + recipe_path: PathBuf, +) -> Directories { + Directories { + recipe_dir: source_dir, + recipe_path, + cache_dir: cache_dir + .map(Path::to_path_buf) + .unwrap_or_else(|| work_directory.join("cache")), + host_prefix: host_prefix + .map(Path::to_path_buf) + .unwrap_or_else(|| work_directory.join("host")), + build_prefix: build_prefix + .map(Path::to_path_buf) + .unwrap_or_else(|| work_directory.join("build")), + work_dir: work_directory.join("work"), + output_dir: output_dir + .map(Path::to_path_buf) + .unwrap_or_else(|| work_directory.join("output")), + build_dir: work_directory, + } } /// Returns the capabilities for this backend @@ -772,6 +1245,8 @@ fn default_capabilities() -> BackendCapabilities { BackendCapabilities { provides_conda_metadata: Some(true), provides_conda_build: Some(true), + provides_conda_outputs: Some(true), + provides_conda_build_v1: Some(true), highest_supported_project_model: Some( pixi_build_types::VersionedProjectModel::highest_version(), ), diff --git a/crates/pixi-build-backend/src/protocol.rs b/crates/pixi-build-backend/src/protocol.rs index b9c406c9..65e28831 100644 --- a/crates/pixi-build-backend/src/protocol.rs +++ b/crates/pixi-build-backend/src/protocol.rs @@ -1,7 +1,9 @@ use std::path::{Path, PathBuf}; +use pixi_build_types::procedures::conda_build_v1::{CondaBuildV1Params, CondaBuildV1Result}; +use pixi_build_types::procedures::conda_outputs::{CondaOutputsParams, CondaOutputsResult}; use pixi_build_types::procedures::{ - conda_build::{CondaBuildParams, CondaBuildResult}, + conda_build_v0::{CondaBuildParams, CondaBuildResult}, conda_metadata::{CondaMetadataParams, CondaMetadataResult}, initialize::{InitializeParams, InitializeResult}, negotiate_capabilities::{NegotiateCapabilitiesParams, NegotiateCapabilitiesResult}, @@ -47,7 +49,23 @@ pub trait Protocol { } /// Called when the client requests to build a Conda package. - async fn conda_build(&self, _params: CondaBuildParams) -> miette::Result { + async fn conda_build_v0(&self, _params: CondaBuildParams) -> miette::Result { unimplemented!("conda_build not implemented"); } + + /// Called when the client requests outputs for a Conda package. + async fn conda_outputs( + &self, + _params: CondaOutputsParams, + ) -> miette::Result { + unimplemented!("conda_outputs not implemented"); + } + + /// Called when the client calls `conda/build_v2`. + async fn conda_build_v1( + &self, + _params: CondaBuildV1Params, + ) -> miette::Result { + unimplemented!("conda_build_v1 not implemented"); + } } diff --git a/crates/pixi-build-backend/src/rattler_build_integration.rs b/crates/pixi-build-backend/src/rattler_build_integration.rs index 42b6d19b..404deaa5 100644 --- a/crates/pixi-build-backend/src/rattler_build_integration.rs +++ b/crates/pixi-build-backend/src/rattler_build_integration.rs @@ -14,8 +14,8 @@ use rattler_build::{ tool_configuration, variant_config::VariantConfig, }; +use rattler_conda_types::compression_level::CompressionLevel; use rattler_conda_types::{GenericVirtualPackage, NamedChannelOrUrl, package::ArchiveType}; -use rattler_package_streaming::write::CompressionLevel; use url::Url; use crate::{generated_recipe::GeneratedRecipe, utils::TemporaryRenderedRecipe}; @@ -140,6 +140,7 @@ pub async fn get_build_output( &output_dir, true, ×tamp, + recipe.build().merge_build_and_host_envs, ) .into_diagnostic()?, channels, @@ -155,6 +156,7 @@ pub async fn get_build_output( sandbox_config: None, debug: Debug::default(), solve_strategy: Default::default(), + exclude_newer: None, }, finalized_dependencies: None, finalized_sources: None, diff --git a/crates/pixi-build-backend/src/server.rs b/crates/pixi-build-backend/src/server.rs index 71ba5da1..070619b6 100644 --- a/crates/pixi-build-backend/src/server.rs +++ b/crates/pixi-build-backend/src/server.rs @@ -3,12 +3,14 @@ use std::{net::SocketAddr, path::Path, sync::Arc}; use fs_err::tokio as tokio_fs; use jsonrpc_core::{Error, IoHandler, Params, serde_json, to_value}; use miette::{Context, IntoDiagnostic, JSONReportHandler}; -use pixi_build_types::VersionedProjectModel; -use pixi_build_types::procedures::{ - self, conda_build::CondaBuildParams, conda_metadata::CondaMetadataParams, - initialize::InitializeParams, negotiate_capabilities::NegotiateCapabilitiesParams, +use pixi_build_types::{ + VersionedProjectModel, + procedures::{ + self, conda_build_v0::CondaBuildParams, conda_build_v1::CondaBuildV1Params, + conda_metadata::CondaMetadataParams, conda_outputs::CondaOutputsParams, + initialize::InitializeParams, negotiate_capabilities::NegotiateCapabilitiesParams, + }, }; - use tokio::sync::RwLock; use crate::protocol::{Protocol, ProtocolInstantiator}; @@ -128,9 +130,34 @@ impl Server { }, ); + let conda_outputs = state.clone(); + io.add_method( + procedures::conda_outputs::METHOD_NAME, + move |params: Params| { + let state = conda_outputs.clone(); + + async move { + let params: CondaOutputsParams = params.parse()?; + let state = state.read().await; + let endpoint = state.as_endpoint()?; + + let debug_dir = endpoint.debug_dir(); + log_conda_outputs(debug_dir, ¶ms) + .await + .map_err(convert_error)?; + + endpoint + .conda_outputs(params) + .await + .map(|value| to_value(value).expect("failed to convert to json")) + .map_err(convert_error) + } + }, + ); + let conda_build = state.clone(); io.add_method( - procedures::conda_build::METHOD_NAME, + procedures::conda_build_v0::METHOD_NAME, move |params: Params| { let state = conda_build.clone(); @@ -145,7 +172,32 @@ impl Server { .map_err(convert_error)?; endpoint - .conda_build(params) + .conda_build_v0(params) + .await + .map(|value| to_value(value).expect("failed to convert to json")) + .map_err(convert_error) + } + }, + ); + + let conda_build_v1 = state.clone(); + io.add_method( + procedures::conda_build_v1::METHOD_NAME, + move |params: Params| { + let state = conda_build_v1.clone(); + + async move { + let params: CondaBuildV1Params = params.parse()?; + let state = state.read().await; + let endpoint = state.as_endpoint()?; + + let debug_dir = endpoint.debug_dir(); + log_conda_build_v1(debug_dir, ¶ms) + .await + .map_err(convert_error)?; + + endpoint + .conda_build_v1(params) .await .map(|value| to_value(value).expect("failed to convert to json")) .map_err(convert_error) @@ -221,6 +273,31 @@ async fn log_conda_get_metadata( Ok(()) } +async fn log_conda_outputs( + debug_dir: Option<&Path>, + params: &CondaOutputsParams, +) -> miette::Result<()> { + let Some(debug_dir) = debug_dir else { + return Ok(()); + }; + + let json = serde_json::to_string_pretty(¶ms) + .into_diagnostic() + .context("failed to serialize parameters to JSON")?; + + tokio_fs::create_dir_all(&debug_dir) + .await + .into_diagnostic() + .context("failed to create data directory")?; + + let path = debug_dir.join("conda_outputs_params.json"); + tokio_fs::write(&path, json) + .await + .into_diagnostic() + .context("failed to write JSON to file")?; + Ok(()) +} + async fn log_conda_build( debug_dir: Option<&Path>, params: &CondaBuildParams, @@ -245,3 +322,28 @@ async fn log_conda_build( .context("failed to write JSON to file")?; Ok(()) } + +async fn log_conda_build_v1( + debug_dir: Option<&Path>, + params: &CondaBuildV1Params, +) -> miette::Result<()> { + let Some(debug_dir) = debug_dir else { + return Ok(()); + }; + + let json = serde_json::to_string_pretty(¶ms) + .into_diagnostic() + .context("failed to serialize parameters to JSON")?; + + tokio_fs::create_dir_all(&debug_dir) + .await + .into_diagnostic() + .context("failed to create data directory")?; + + let path = debug_dir.join("conda_build_v1_params.json"); + tokio_fs::write(&path, json) + .await + .into_diagnostic() + .context("failed to write JSON to file")?; + Ok(()) +} diff --git a/crates/pixi-build-backend/src/specs_conversion.rs b/crates/pixi-build-backend/src/specs_conversion.rs index 43eb7598..2811761e 100644 --- a/crates/pixi-build-backend/src/specs_conversion.rs +++ b/crates/pixi-build-backend/src/specs_conversion.rs @@ -1,7 +1,7 @@ use std::{path::Path, str::FromStr}; -use indexmap::IndexMap; use miette::IntoDiagnostic; +use ordermap::OrderMap; use pixi_build_types::{ GitSpecV1, PackageSpecV1, SourcePackageSpecV1, TargetV1, TargetsV1, UrlSpecV1, }; @@ -13,17 +13,14 @@ use recipe_stage0::{ }; use url::Url; -pub fn from_source_matchspec_into_package_spec( - source_matchspec: SourceMatchSpec, -) -> miette::Result { - let source_url = source_matchspec.location; +pub fn from_source_url_to_source_package(source_url: Url) -> Option { match source_url.scheme() { - "source" => Ok(SourcePackageSpecV1::Path(pixi_build_types::PathSpecV1 { + "source" => Some(SourcePackageSpecV1::Path(pixi_build_types::PathSpecV1 { path: SafeRelativePathUrl::from(source_url).to_path(), })), "http" | "https" => { // For now, we only support URL sources with no checksums. - Ok(SourcePackageSpecV1::Url(UrlSpecV1 { + Some(SourcePackageSpecV1::Url(UrlSpecV1 { url: source_url, md5: None, sha256: None, @@ -32,16 +29,23 @@ pub fn from_source_matchspec_into_package_spec( "git" => { // For git URLs, we can only support the URL without any additional metadata. // This is a limitation of the current implementation. - Ok(SourcePackageSpecV1::Git(GitSpecV1 { + Some(SourcePackageSpecV1::Git(GitSpecV1 { git: source_url, rev: None, subdirectory: None, })) } - _ => unimplemented!("Only file, http/https and git are supported for now"), + _ => None, } } +pub fn from_source_matchspec_into_package_spec( + source_matchspec: SourceMatchSpec, +) -> miette::Result { + from_source_url_to_source_package(source_matchspec.location) + .ok_or_else(|| miette::miette!("Only file, http/https and git are supported for now")) +} + pub fn from_targets_v1_to_conditional_requirements(targets: &TargetsV1) -> ConditionalRequirements { let mut build_items = ConditionalList::new(); let mut host_items = ConditionalList::new(); @@ -198,7 +202,7 @@ pub(crate) fn source_package_spec_to_package_dependency( } pub(crate) fn package_specs_to_package_dependency( - specs: IndexMap, + specs: OrderMap, ) -> miette::Result> { specs .into_iter() diff --git a/crates/pixi-build-backend/src/tools.rs b/crates/pixi-build-backend/src/tools.rs index 8022e8ac..5e78dcfe 100644 --- a/crates/pixi-build-backend/src/tools.rs +++ b/crates/pixi-build-backend/src/tools.rs @@ -1,6 +1,7 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}, + sync::Arc, }; use indexmap::IndexSet; @@ -20,10 +21,11 @@ use rattler_build::{ }, selectors::SelectorConfig, system_tools::SystemTools, - variant_config::{DiscoveredOutput, ParseErrors, VariantConfig}, + variant_config::{DiscoveredOutput, ParseErrors, VariantConfig, VariantConfigError}, +}; +use rattler_conda_types::{ + GenericVirtualPackage, Platform, compression_level::CompressionLevel, package::ArchiveType, }; -use rattler_conda_types::{GenericVirtualPackage, Platform, package::ArchiveType}; -use rattler_package_streaming::write::CompressionLevel; use rattler_virtual_packages::VirtualPackageOverrides; use url::Url; @@ -47,6 +49,67 @@ pub struct RattlerBuild { pub work_directory: PathBuf, } +/// Variant configuration that was loaded from the recipe. +#[derive(Debug)] +pub struct LoadedVariantConfig { + /// The variant configuration that was loaded. + pub variant_config: VariantConfig, + + /// Input globs that identity the files that were loaded. + pub input_globs: BTreeSet, +} + +impl LoadedVariantConfig { + /// Load variant configuration from a recipe path. This checks if there is a + /// `variants.yaml` and loads it alongside the recipe. + pub fn from_recipe_path( + source_dir: &Path, + recipe_path: &Path, + selector_config: &SelectorConfig, + ) -> Result>> { + let mut variant_files = Vec::new(); + let mut input_globs = BTreeSet::new(); + + // Check if there is a `variants.yaml` file next to the recipe that we should + // potentially use. + if let Some(variant_path) = recipe_path + .parent() + .map(|parent| parent.join(VARIANTS_CONFIG_FILE)) + { + if let Some(path) = pathdiff::diff_paths(&variant_path, source_dir) { + // Normalize paths on windows + let normalized_path = if cfg!(target_os = "windows") { + path.to_string_lossy().replace("\\", "/") + } else { + path.to_string_lossy().to_string() + }; + input_globs.insert(normalized_path); + } + if variant_path.is_file() { + variant_files.push(variant_path); + } + }; + + Ok(Self { + variant_config: VariantConfig::from_files(&variant_files, selector_config)?, + input_globs, + }) + } + + pub fn extend_with_input_variants( + mut self, + input_variant_configuration: &BTreeMap>, + ) -> Self { + for (k, v) in input_variant_configuration { + let variables = v.iter().map(|v| Variable::from_string(v)).collect(); + self.variant_config + .variants + .insert(k.as_str().into(), variables); + } + self + } +} + pub enum OneOrMultipleOutputs { Single(String), OneOfMany(String), @@ -84,13 +147,14 @@ impl RattlerBuild { variant: Default::default(), experimental: true, allow_undefined: false, + recipe_path: None, } } /// Discover the outputs from the recipe. pub fn discover_outputs( &self, - variant_config_input: &Option>>, + variant_config_input: &Option>>, ) -> miette::Result> { // First find all outputs from the recipe let outputs = find_outputs_from_src(self.recipe_source.clone())?; @@ -157,6 +221,7 @@ impl RattlerBuild { build_platform: self.selector_config.build_platform, experimental: true, allow_undefined: false, + recipe_path: Some(self.recipe_source.path.clone()), }; let mut recipe = Recipe::from_node(&discovered_output.node, selector_config.clone()) @@ -252,6 +317,7 @@ impl RattlerBuild { force_colors: true, sandbox_config: None, debug: Debug::new(false), + exclude_newer: None, }, finalized_dependencies: None, finalized_cache_dependencies: None, diff --git a/crates/pixi-build-backend/src/traits/package_spec.rs b/crates/pixi-build-backend/src/traits/package_spec.rs index 817db7f3..ea116d04 100644 --- a/crates/pixi-build-backend/src/traits/package_spec.rs +++ b/crates/pixi-build-backend/src/traits/package_spec.rs @@ -8,8 +8,7 @@ //! * [`BinarySpecExt`] - Extension for converting binary specs to nameless //! match specs -use std::fmt::Debug; -use std::sync::Arc; +use std::{fmt::Debug, sync::Arc}; use miette::IntoDiagnostic; use pixi_build_types::{self as pbt}; @@ -63,6 +62,8 @@ impl PackageSpec for pbt::PackageSpecV1 { subdir, md5, sha256, + url, + license, } = &**boxed_spec; version == &Some(rattler_conda_types::VersionSpec::Any) @@ -73,6 +74,8 @@ impl PackageSpec for pbt::PackageSpecV1 { && subdir.is_none() && md5.is_none() && sha256.is_none() + && url.is_none() + && license.is_none() } _ => false, } @@ -124,12 +127,12 @@ impl BinarySpecExt for pbt::BinaryPackageSpecV1 { .as_ref() .map(|url| Arc::new(Channel::from_url(url.clone()))), subdir: self.subdir.clone(), - md5: self.md5.as_ref().map(|m| m.0), - sha256: self.sha256.as_ref().map(|s| s.0), - namespace: None, - url: None, + md5: self.md5, + sha256: self.sha256, + url: self.url.clone(), + license: self.license.clone(), extras: None, - license: None, + namespace: None, } } } diff --git a/crates/pixi-build-backend/src/utils/mod.rs b/crates/pixi-build-backend/src/utils/mod.rs index 9253d113..dce8a7c8 100644 --- a/crates/pixi-build-backend/src/utils/mod.rs +++ b/crates/pixi-build-backend/src/utils/mod.rs @@ -1,3 +1,4 @@ mod temporary_recipe; +pub mod test; pub use temporary_recipe::TemporaryRenderedRecipe; diff --git a/crates/pixi-build-backend/src/utils/test.rs b/crates/pixi-build-backend/src/utils/test.rs new file mode 100644 index 00000000..2370ade3 --- /dev/null +++ b/crates/pixi-build-backend/src/utils/test.rs @@ -0,0 +1,100 @@ +use std::{collections::BTreeMap, path::PathBuf}; + +use pixi_build_types::procedures::{ + conda_outputs::{CondaOutputsParams, CondaOutputsResult}, + initialize::InitializeParams, +}; +use rattler_build::console_utils::LoggingOutputHandler; +use rattler_conda_types::Platform; +use serde_json::Value; + +use crate::{ + generated_recipe::GenerateRecipe, intermediate_backend::IntermediateBackendInstantiator, + protocol::ProtocolInstantiator, +}; + +/// A utility function to remove empty values from a JSON object. +pub(crate) fn remove_empty_values(value: &mut Value) { + fn keep_value(value: &Value) -> bool { + match value { + Value::Object(map) => !map.is_empty(), + Value::Array(arr) => !arr.is_empty(), + Value::Null => false, + _ => true, + } + } + + match value { + Value::Object(map) => { + map.retain(|_, v| { + remove_empty_values(v); + keep_value(v) + }); + } + Value::Array(arr) => { + arr.retain_mut(|v| { + remove_empty_values(v); + keep_value(v) + }); + } + _ => {} + } +} + +/// Calls the `conda/outputs` procedure of the `IntermediateBackend` for the +/// given recipe general and with the given project model. +/// +/// Returns a pretty-printed JSON string of the outputs that can be used for +/// snapshots +pub fn intermediate_conda_outputs_snapshot( + project_model: Option, + source_dir: Option, + host_platform: Platform, + variant_configuration: Option>>, +) -> String +where + T: GenerateRecipe + Default + Clone + Send + Sync + 'static, + ::Config: Send + Sync + 'static, +{ + let manifest_path = match &source_dir { + Some(dir) => dir.join("pixi.toml"), + None => PathBuf::from("pixi.toml"), + }; + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + runtime.block_on(async move { + let (protocol, _result) = + IntermediateBackendInstantiator::::new(LoggingOutputHandler::default()) + .initialize(InitializeParams { + source_dir, + manifest_path, + project_model: project_model.map(Into::into), + configuration: None, + cache_directory: None, + }) + .await + .unwrap(); + + let current_dir = std::env::current_dir().unwrap(); + let result = protocol + .conda_outputs(CondaOutputsParams { + host_platform, + build_platform: host_platform, + variant_configuration, + work_directory: current_dir, + }) + .await + .unwrap(); + + conda_outputs_snapshot(result) + }) +} + +/// A function to convert a `CondaOutputsResult` into a pretty-printed JSON +/// string. +pub fn conda_outputs_snapshot(result: CondaOutputsResult) -> String { + let mut value = serde_json::to_value(result).unwrap(); + remove_empty_values(&mut value); + serde_json::to_string_pretty(&value).unwrap() +} diff --git a/crates/pixi-build-backend/src/variants.rs b/crates/pixi-build-backend/src/variants.rs index 8d4448ee..429782b1 100644 --- a/crates/pixi-build-backend/src/variants.rs +++ b/crates/pixi-build-backend/src/variants.rs @@ -1,7 +1,8 @@ use pixi_build_types as pbt; use rattler_conda_types::VersionSpec; -/// Returns true if the specified [`pbt::PackageSpecV1`] is a valid variant spec. +/// Returns true if the specified [`pbt::PackageSpecV1`] is a valid variant +/// spec. /// /// At the moment, a spec that allows any version is considered a variant spec. pub fn can_be_used_as_variant(spec: &pbt::PackageSpecV1) -> bool { @@ -16,6 +17,8 @@ pub fn can_be_used_as_variant(spec: &pbt::PackageSpecV1) -> bool { subdir, md5, sha256, + url, + license, } = &**boxed_spec; version == &Some(VersionSpec::Any) @@ -26,6 +29,8 @@ pub fn can_be_used_as_variant(spec: &pbt::PackageSpecV1) -> bool { && subdir.is_none() && md5.is_none() && sha256.is_none() + && url.is_none() + && license.is_none() } _ => false, } diff --git a/crates/pixi-build-backend/tests/integration/common/model.rs b/crates/pixi-build-backend/tests/integration/common/model.rs index 1814031f..8b9aee4b 100644 --- a/crates/pixi-build-backend/tests/integration/common/model.rs +++ b/crates/pixi-build-backend/tests/integration/common/model.rs @@ -190,6 +190,8 @@ fn convert_package_spec_to_v1(spec: &PackageSpec) -> PackageSpecV1 { subdir: None, md5: None, sha256: None, + url: None, + license: None, })) } PackageSpec::Source(source_spec) => { diff --git a/crates/pixi-build-backend/tests/integration/protocol.rs b/crates/pixi-build-backend/tests/integration/protocol.rs index 7128df68..98a5c0ca 100644 --- a/crates/pixi-build-backend/tests/integration/protocol.rs +++ b/crates/pixi-build-backend/tests/integration/protocol.rs @@ -3,7 +3,7 @@ use imp::TestGenerateRecipe; use pixi_build_backend::{intermediate_backend::IntermediateBackend, protocol::Protocol}; use pixi_build_types::{ ChannelConfiguration, PlatformAndVirtualPackages, - procedures::{conda_build::CondaBuildParams, conda_metadata::CondaMetadataParams}, + procedures::{conda_build_v0::CondaBuildParams, conda_metadata::CondaMetadataParams}, }; use rattler_build::console_utils::LoggingOutputHandler; use rattler_conda_types::Platform; @@ -98,6 +98,7 @@ async fn test_conda_get_metadata() { let intermediate_backend = IntermediateBackend::::new( pixi_manifest.clone(), + Some(tmp_dir_path.clone()), project_model_v1, TestGenerateRecipe::default(), some_config, @@ -157,6 +158,7 @@ async fn test_conda_build() { let intermediate_backend = IntermediateBackend::new( pixi_manifest.clone(), + Some(tmp_dir_path.clone()), project_model_v1, TestGenerateRecipe::default(), some_config, @@ -166,7 +168,7 @@ async fn test_conda_build() { .unwrap(); let conda_build_result = intermediate_backend - .conda_build(build_params) + .conda_build_v0(build_params) .await .unwrap(); diff --git a/crates/pixi-build-cmake/Cargo.toml b/crates/pixi-build-cmake/Cargo.toml index 89445486..c5a5b684 100644 --- a/crates/pixi-build-cmake/Cargo.toml +++ b/crates/pixi-build-cmake/Cargo.toml @@ -4,26 +4,19 @@ version = "0.2.0" edition.workspace = true [dependencies] -async-trait = { workspace = true } -chrono = { workspace = true } indexmap = { workspace = true } miette = { workspace = true } minijinja = { workspace = true } -rattler_conda_types = { workspace = true } -rattler_package_streaming = { workspace = true } +pixi-build-backend = { workspace = true } +pixi_build_types = { workspace = true } rattler-build = { workspace = true } +rattler_conda_types = { workspace = true } +recipe-stage0 = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -tempfile = { workspace = true } tokio = { workspace = true, features = ["macros"] } -pixi-build-backend = { workspace = true } - -pixi_build_types = { workspace = true } -pixi_manifest = { workspace = true } -pixi_build_type_conversions = { workspace = true } - -recipe-stage0 = { workspace = true } - [dev-dependencies] -insta = { version = "1.42.1", features = ["yaml", "redactions", "filters"] } +insta = { workspace = true, features = ["yaml", "redactions", "filters"] } +rstest = { workspace = true } +strum = { workspace = true } diff --git a/crates/pixi-build-cmake/src/build_script.j2 b/crates/pixi-build-cmake/src/build_script.j2 index 83d3a487..e90de958 100644 --- a/crates/pixi-build-cmake/src/build_script.j2 +++ b/crates/pixi-build-cmake/src/build_script.j2 @@ -1,45 +1,59 @@ +{%- set is_cmd_exe = build_platform == "windows" -%} +{%- macro env(key) -%} +{%- if is_cmd_exe %}{{ "%" ~ key ~ "%" }}{% else %}{{ "$" ~key }}{% endif -%} +{% endmacro -%} + +{# - Set up common variables -#} +{%- set build_dir = "build" -%} +{%- set source_dir = env("SRC_DIR") -%} +{%- set library_prefix = "%LIBRARY_PREFIX%" if build_platform == "windows" else "$PREFIX" -%} + +{# Set up default CMake arguments -#} +{%- set cmake_args = [ + env("CMAKE_ARGS"), + "-GNinja", + "-S \"" ~ source_dir ~ "\"", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=" ~ library_prefix, + "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", + "-DBUILD_SHARED_LIBS=ON", +] +-%} + +{# Add Python executable if available -#} +{%- if has_host_python -%} +{%- set cmake_args = cmake_args + [ + "-DPython_EXECUTABLE=" ~ env("PYTHON") +] -%} +{% endif -%} + +{#- Output version information -#} ninja --version cmake --version -{# Windows #} -{% if build_platform == "windows" -%} -if not exist %SRC_DIR%\..\build\build.ninja ( - cmake %CMAKE_ARGS% ^ - -GNinja ^ - -DCMAKE_BUILD_TYPE=Release ^ - -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ^ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ^ - -DBUILD_SHARED_LIBS=ON ^ - {% if has_host_python -%} - -DPython_EXECUTABLE="%PYTHON%" ^ - {% endif -%} - {{ extra_args | join(" ") }} ^ - -B %SRC_DIR%\..\build ^ - -S "{{ source_dir }}" +{# Set up the build directory -#} +{% if is_cmd_exe -%} +if not exist {{ build_dir }} mkdir {{ build_dir }} +{% else -%} +mkdir -p build +{% endif -%} +pushd build + +{# Windows -#} +{% if is_cmd_exe -%} +if not exist build.ninja ( + cmake {{ cmake_args | join(" ^\n ") }} @if errorlevel 1 exit 1 ) -cmake --build %SRC_DIR%\..\build --target install + +cmake --build . --target install @if errorlevel 1 exit 1 {# Non Windows #} {% else -%} -if [ ! -f "$SRC_DIR/../build/build.ninja" ]; then - cmake $CMAKE_ARGS \ - -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$PREFIX \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - {% if has_host_python -%} - -DPython_EXECUTABLE=$PYTHON \ - {% endif -%} - -DBUILD_SHARED_LIBS=ON \ - {{ extra_args | join(" ") }} \ - -B $SRC_DIR/../build \ - -S "{{ source_dir }}" +if [ ! -f "build.ninja" ]; then + cmake {{ cmake_args | join(" \\\n ") }} fi -cmake --build $SRC_DIR/../build --target install -{% endif -%} -{% if build_platform == "windows" -%} -@if errorlevel 1 exit 1 -{% endif %} +cmake --build . --target install +{% endif -%} diff --git a/crates/pixi-build-cmake/src/build_script.rs b/crates/pixi-build-cmake/src/build_script.rs index d96f78c3..819f83e4 100644 --- a/crates/pixi-build-cmake/src/build_script.rs +++ b/crates/pixi-build-cmake/src/build_script.rs @@ -12,8 +12,10 @@ pub struct BuildScriptContext { pub has_host_python: bool, } -#[derive(Serialize)] +#[derive(Copy, Clone, Serialize)] #[serde(rename_all = "kebab-case")] +#[cfg_attr(test, derive(strum::Display))] +#[cfg_attr(test, strum(serialize_all = "snake_case"))] pub enum BuildPlatform { Windows, Unix, @@ -29,3 +31,38 @@ impl BuildScriptContext { rendered.lines().map(|s| s.to_string()).collect() } } + +#[cfg(test)] +mod test { + use rstest::*; + + use super::*; + + #[rstest] + fn test_build_script( + #[values(BuildPlatform::Windows, BuildPlatform::Unix)] build_platform: BuildPlatform, + #[values(true, false)] has_host_python: bool, + ) { + let context = BuildScriptContext { + build_platform, + source_dir: String::from("my-prefix-dir"), + extra_args: vec![], + has_host_python, + }; + let script = context.render(); + + let mut settings = insta::Settings::clone_current(); + settings.set_snapshot_suffix(format!( + "{}-{}", + build_platform, + if has_host_python { + "python" + } else { + "nopython" + } + )); + settings.bind(|| { + insta::assert_snapshot!(script.join("\n")); + }); + } +} diff --git a/crates/pixi-build-cmake/src/main.rs b/crates/pixi-build-cmake/src/main.rs index 552876cc..b163669c 100644 --- a/crates/pixi-build-cmake/src/main.rs +++ b/crates/pixi-build-cmake/src/main.rs @@ -1,7 +1,10 @@ mod build_script; mod config; -use std::{collections::BTreeMap, path::Path}; +use std::{ + collections::{BTreeMap, BTreeSet}, + path::Path, +}; use build_script::{BuildPlatform, BuildScriptContext}; use config::CMakeBackendConfig; @@ -36,7 +39,7 @@ impl GenerateRecipe for CMakeGenerator { let requirements = &mut generated_recipe.recipe.requirements; - let resolved_requirements = requirements.resolve(Some(&host_platform)); + let resolved_requirements = requirements.resolve(Some(host_platform)); // Ensure the compiler function is added to the build requirements // only if a specific compiler is not already present. @@ -93,7 +96,7 @@ impl GenerateRecipe for CMakeGenerator { config: &Self::Config, _workdir: impl AsRef, _editable: bool, - ) -> Vec { + ) -> BTreeSet { [ // Source files "**/*.{c,cc,cxx,cpp,h,hpp,hxx}", @@ -137,7 +140,12 @@ mod tests { use std::path::PathBuf; use indexmap::IndexMap; - use pixi_build_types::ProjectModelV1; + use pixi_build_backend::protocol::ProtocolInstantiator; + use pixi_build_types::{ + ProjectModelV1, + procedures::{conda_outputs::CondaOutputsParams, initialize::InitializeParams}, + }; + use rattler_build::console_utils::LoggingOutputHandler; use super::*; @@ -329,4 +337,46 @@ mod tests { ".build.script" => "[ ... script ... ]", }); } + + #[tokio::test] + async fn test_windows_default_compiler() { + let project_model = project_fixture!({ + "name": "foobar", + "version": "0.1.0", + }); + + let factory = + IntermediateBackendInstantiator::::new(LoggingOutputHandler::default()) + .initialize(InitializeParams { + source_dir: None, + manifest_path: PathBuf::from("pixi.toml"), + project_model: Some(project_model.into()), + configuration: None, + cache_directory: None, + }) + .await + .unwrap(); + + let current_dir = std::env::current_dir().unwrap(); + let outputs = factory + .0 + .conda_outputs(CondaOutputsParams { + host_platform: Platform::Win64, + build_platform: Platform::Win64, + variant_configuration: None, + work_directory: current_dir, + }) + .await + .unwrap(); + + assert_eq!( + outputs.outputs[0] + .metadata + .variant + .get("cxx_compiler") + .map(String::as_str), + Some("vs2019"), + "On windows the default cxx_compiler variant should be vs2019" + ); + } } diff --git a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-nopython.snap b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-nopython.snap new file mode 100644 index 00000000..cc3d9b05 --- /dev/null +++ b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-nopython.snap @@ -0,0 +1,21 @@ +--- +source: crates/pixi-build-cmake/src/build_script.rs +expression: "script.join(\"\\n\")" +--- +ninja --version +cmake --version + +mkdir -p build +pushd build + +if [ ! -f "build.ninja" ]; then + cmake $CMAKE_ARGS \ + -GNinja \ + -S "$SRC_DIR" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DBUILD_SHARED_LIBS=ON +fi + +cmake --build . --target install diff --git a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-python.snap b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-python.snap new file mode 100644 index 00000000..9ee43d61 --- /dev/null +++ b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@unix-python.snap @@ -0,0 +1,22 @@ +--- +source: crates/pixi-build-cmake/src/build_script.rs +expression: "script.join(\"\\n\")" +--- +ninja --version +cmake --version + +mkdir -p build +pushd build + +if [ ! -f "build.ninja" ]; then + cmake $CMAKE_ARGS \ + -GNinja \ + -S "$SRC_DIR" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DBUILD_SHARED_LIBS=ON \ + -DPython_EXECUTABLE=$PYTHON +fi + +cmake --build . --target install diff --git a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-nopython.snap b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-nopython.snap new file mode 100644 index 00000000..81bb36f6 --- /dev/null +++ b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-nopython.snap @@ -0,0 +1,23 @@ +--- +source: crates/pixi-build-cmake/src/build_script.rs +expression: "script.join(\"\\n\")" +--- +ninja --version +cmake --version + +if not exist build mkdir build +pushd build + +if not exist build.ninja ( + cmake %CMAKE_ARGS% ^ + -GNinja ^ + -S "%SRC_DIR%" ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ^ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ^ + -DBUILD_SHARED_LIBS=ON + @if errorlevel 1 exit 1 +) + +cmake --build . --target install +@if errorlevel 1 exit 1 diff --git a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-python.snap b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-python.snap new file mode 100644 index 00000000..fafe1923 --- /dev/null +++ b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__build_script__test__build_script@windows-python.snap @@ -0,0 +1,24 @@ +--- +source: crates/pixi-build-cmake/src/build_script.rs +expression: "script.join(\"\\n\")" +--- +ninja --version +cmake --version + +if not exist build mkdir build +pushd build + +if not exist build.ninja ( + cmake %CMAKE_ARGS% ^ + -GNinja ^ + -S "%SRC_DIR%" ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ^ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ^ + -DBUILD_SHARED_LIBS=ON ^ + -DPython_EXECUTABLE=%PYTHON% + @if errorlevel 1 exit 1 +) + +cmake --build . --target install +@if errorlevel 1 exit 1 diff --git a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__tests__input_globs_includes_extra_globs.snap b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__tests__input_globs_includes_extra_globs.snap index fb13d267..09ffb231 100644 --- a/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__tests__input_globs_includes_extra_globs.snap +++ b/crates/pixi-build-cmake/src/snapshots/pixi_build_cmake__tests__input_globs_includes_extra_globs.snap @@ -2,9 +2,9 @@ source: crates/pixi-build-cmake/src/main.rs expression: result --- -[ +{ "**/*.{c,cc,cxx,cpp,h,hpp,hxx}", "**/*.{cmake,cmake.in}", "**/CMakeFiles.txt", "custom/*.c", -] +} diff --git a/crates/pixi-build-python/src/main.rs b/crates/pixi-build-python/src/main.rs index 125f9929..c9f133ac 100644 --- a/crates/pixi-build-python/src/main.rs +++ b/crates/pixi-build-python/src/main.rs @@ -1,11 +1,6 @@ mod build_script; mod config; -use std::{ - path::{Path, PathBuf}, - str::FromStr, -}; - use build_script::{BuildPlatform, BuildScriptContext, Installer}; use config::PythonBackendConfig; use miette::IntoDiagnostic; @@ -17,6 +12,11 @@ use pixi_build_types::ProjectModelV1; use pyproject_toml::PyProjectToml; use rattler_conda_types::{PackageName, Platform, package::EntryPoint}; use recipe_stage0::recipe::{NoArchKind, Python, Script}; +use std::collections::BTreeSet; +use std::{ + path::{Path, PathBuf}, + str::FromStr, +}; #[derive(Default, Clone)] pub struct PythonGenerator {} @@ -59,7 +59,7 @@ impl GenerateRecipe for PythonGenerator { let requirements = &mut generated_recipe.recipe.requirements; - let resolved_requirements = requirements.resolve(Some(&host_platform)); + let resolved_requirements = requirements.resolve(Some(host_platform)); // Ensure the python build tools are added to the `host` requirements. // Please note: this is a subtle difference for python, where the build tools are @@ -124,7 +124,7 @@ impl GenerateRecipe for PythonGenerator { let pyproject_manifest = if pyproject_manifest_path.exists() { let contents = std::fs::read_to_string(&pyproject_manifest_path).into_diagnostic()?; generated_recipe.build_input_globs = - vec![pyproject_manifest_path.to_string_lossy().to_string()]; + BTreeSet::from([pyproject_manifest_path.to_string_lossy().to_string()]); Some(toml_edit::de::from_str(&contents).into_diagnostic()?) } else { None @@ -156,7 +156,7 @@ impl GenerateRecipe for PythonGenerator { config: &Self::Config, _workdir: impl AsRef, editable: bool, - ) -> Vec { + ) -> BTreeSet { let base_globs = Vec::from([ // Source files "**/*.c", diff --git a/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs.snap b/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs.snap index 0184f94d..bb0492e5 100644 --- a/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs.snap +++ b/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs.snap @@ -2,31 +2,31 @@ source: crates/pixi-build-python/src/main.rs expression: result --- -[ +{ "**/*.c", "**/*.cpp", + "**/*.json", + "**/*.py", + "**/*.pyx", "**/*.rs", "**/*.sh", - "**/*.json", + "**/*.txt", "**/*.yaml", "**/*.yml", - "**/*.txt", - "setup.py", - "setup.cfg", - "pyproject.toml", - "requirements*.txt", + "MANIFEST.in", + "Makefile", "Pipfile", "Pipfile.lock", + "VERSION", + "custom/*.py", + "docs/**/*.md", + "docs/**/*.rst", "poetry.lock", - "tox.ini", - "Makefile", - "MANIFEST.in", + "pyproject.toml", + "requirements*.txt", + "setup.cfg", + "setup.py", "tests/**/*.py", - "docs/**/*.rst", - "docs/**/*.md", - "VERSION", + "tox.ini", "version.py", - "**/*.py", - "**/*.pyx", - "custom/*.py", -] +} diff --git a/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs_editable.snap b/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs_editable.snap index c30c86d3..9d96710a 100644 --- a/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs_editable.snap +++ b/crates/pixi-build-python/src/snapshots/pixi_build_python__tests__input_globs_includes_extra_globs_editable.snap @@ -2,29 +2,29 @@ source: crates/pixi-build-python/src/main.rs expression: result --- -[ +{ "**/*.c", "**/*.cpp", + "**/*.json", "**/*.rs", "**/*.sh", - "**/*.json", + "**/*.txt", "**/*.yaml", "**/*.yml", - "**/*.txt", - "setup.py", - "setup.cfg", - "pyproject.toml", - "requirements*.txt", + "MANIFEST.in", + "Makefile", "Pipfile", "Pipfile.lock", + "VERSION", + "custom/*.py", + "docs/**/*.md", + "docs/**/*.rst", "poetry.lock", - "tox.ini", - "Makefile", - "MANIFEST.in", + "pyproject.toml", + "requirements*.txt", + "setup.cfg", + "setup.py", "tests/**/*.py", - "docs/**/*.rst", - "docs/**/*.md", - "VERSION", + "tox.ini", "version.py", - "custom/*.py", -] +} diff --git a/crates/pixi-build-rattler-build/Cargo.toml b/crates/pixi-build-rattler-build/Cargo.toml index 958dabaf..f00b8275 100644 --- a/crates/pixi-build-rattler-build/Cargo.toml +++ b/crates/pixi-build-rattler-build/Cargo.toml @@ -22,5 +22,7 @@ itertools = { workspace = true } pixi-build-backend = { workspace = true } pixi_build_types = { workspace = true } +chrono = "0.4.41" [dev-dependencies] +insta = { workspace = true, features = ["json", "glob"] } diff --git a/crates/pixi-build-rattler-build/src/protocol.rs b/crates/pixi-build-rattler-build/src/protocol.rs index 89b0be18..632c8f0a 100644 --- a/crates/pixi-build-rattler-build/src/protocol.rs +++ b/crates/pixi-build-rattler-build/src/protocol.rs @@ -1,24 +1,32 @@ use std::{ - collections::HashMap, + collections::{BTreeMap, BTreeSet, HashMap}, path::{Path, PathBuf}, str::FromStr, + sync::Arc, }; use fs_err::tokio as tokio_fs; use itertools::Itertools; use miette::{Context, IntoDiagnostic}; use pixi_build_backend::{ + dependencies::{convert_binary_dependencies, convert_dependencies}, + intermediate_backend::{conda_build_v1_directories, find_matching_output}, protocol::{Protocol, ProtocolInstantiator}, - tools::RattlerBuild, + tools::{LoadedVariantConfig, RattlerBuild}, utils::TemporaryRenderedRecipe, }; use pixi_build_types::{ - BackendCapabilities, CondaPackageMetadata, SourcePackageSpecV1, TargetV1, + BackendCapabilities, CondaPackageMetadata, PathSpecV1, SourcePackageSpecV1, TargetV1, procedures::{ - conda_build::{ + conda_build_v0::{ CondaBuildParams, CondaBuildResult, CondaBuiltPackage, CondaOutputIdentifier, }, + conda_build_v1::{CondaBuildV1Params, CondaBuildV1Result}, conda_metadata::{CondaMetadataParams, CondaMetadataResult}, + conda_outputs::{ + CondaOutput, CondaOutputDependencies, CondaOutputIgnoreRunExports, CondaOutputMetadata, + CondaOutputRunExports, CondaOutputsParams, CondaOutputsResult, + }, initialize::{InitializeParams, InitializeResult}, negotiate_capabilities::{NegotiateCapabilitiesParams, NegotiateCapabilitiesResult}, }, @@ -27,13 +35,26 @@ use rattler_build::{ build::run_build, console_utils::LoggingOutputHandler, hash::HashInfo, - metadata::PlatformWithVirtualPackages, - recipe::{Jinja, parser::BuildString}, - render::resolved_dependencies::DependencyInfo, + metadata::{ + BuildConfiguration, Debug, Output, PackageIdentifier, PackagingSettings, + PlatformWithVirtualPackages, + }, + recipe::{ + Jinja, ParsingError, Recipe, + parser::{BuildString, find_outputs_from_src}, + variable::Variable, + }, + render::resolved_dependencies::{ + DependencyInfo, FinalizedDependencies, FinalizedRunDependencies, ResolvedDependencies, + }, selectors::SelectorConfig, tool_configuration::{BaseClient, Configuration}, + variant_config::{ParseErrors, VariantConfig}, +}; +use rattler_conda_types::{ + ChannelConfig, MatchSpec, PackageName, Platform, compression_level::CompressionLevel, + package::ArchiveType, }; -use rattler_conda_types::{ChannelConfig, MatchSpec, PackageName, Platform}; use rattler_virtual_packages::VirtualPackageOverrides; use url::Url; @@ -228,7 +249,208 @@ impl Protocol for RattlerBuildBackend { }) } - async fn conda_build(&self, params: CondaBuildParams) -> miette::Result { + async fn conda_outputs( + &self, + params: CondaOutputsParams, + ) -> miette::Result { + let build_platform = params.host_platform; + + // Determine the variant configuration to use. This loads the variant + // configuration from disk as well as including the variants from the input + // parameters. + let selector_config_for_variants = SelectorConfig { + target_platform: params.host_platform, + host_platform: params.host_platform, + build_platform, + hash: None, + variant: Default::default(), + experimental: false, + allow_undefined: false, + recipe_path: Some(self.recipe_source.path.clone()), + }; + let variant_config = LoadedVariantConfig::from_recipe_path( + &self.source_dir, + &self.recipe_source.path, + &selector_config_for_variants, + ) + .into_diagnostic()? + .extend_with_input_variants(¶ms.variant_configuration.unwrap_or_default()); + + // Find all outputs from the recipe + let output_nodes = find_outputs_from_src(self.recipe_source.clone())?; + let discovered_outputs = variant_config + .variant_config + .find_variants( + &output_nodes, + self.recipe_source.clone(), + &selector_config_for_variants, + ) + .into_diagnostic()?; + + // Construct a mapping that for packages that we want from source. + // + // By default, this includes all the outputs in the recipe. These should all be + // build from source, in particular from the current source. + let local_source_packages = discovered_outputs + .iter() + .map(|output| { + ( + output.name.clone(), + SourcePackageSpecV1::Path(PathSpecV1 { path: ".".into() }), + ) + }) + .collect(); + + let mut subpackages = HashMap::new(); + let mut outputs = Vec::new(); + for discovered_output in discovered_outputs { + let variant = discovered_output.used_vars; + let hash = HashInfo::from_variant(&variant, &discovered_output.noarch_type); + + // Construct the selector config for this particular output. We base this on the + // selector config that was used to determine the variants. + let selector_config = SelectorConfig { + variant: variant.clone(), + hash: Some(hash.clone()), + target_platform: discovered_output.target_platform, + ..selector_config_for_variants.clone() + }; + + // Convert this discovered output into a recipe. + let recipe = Recipe::from_node(&discovered_output.node, selector_config.clone()) + .map_err(|err| { + let errs: ParseErrors<_> = err + .into_iter() + .map(|err| ParsingError::from_partial(self.recipe_source.clone(), err)) + .collect::>() + .into(); + errs + })?; + + // Skip this output if the recipe is marked as skipped + if recipe.build().skip() { + continue; + } + + let jinja = Jinja::new(selector_config); + let build_number = recipe.build().number; + let build_string = recipe.build().string().resolve(&hash, build_number, &jinja); + + subpackages.insert( + recipe.package().name().clone(), + PackageIdentifier { + name: recipe.package().name().clone(), + version: recipe.package().version().version().clone().into(), + build_string: build_string.to_string(), + }, + ); + + outputs.push(CondaOutput { + metadata: CondaOutputMetadata { + name: recipe.package().name().clone(), + version: recipe.package.version().clone(), + build: build_string.to_string(), + build_number, + subdir: discovered_output.target_platform, + license: recipe.about.license.map(|l| l.to_string()), + license_family: recipe.about.license_family, + noarch: recipe.build.noarch, + purls: None, + python_site_packages_path: None, + variant: variant + .iter() + .map(|(k, v)| (k.0.clone(), v.to_string())) + .collect(), + }, + build_dependencies: Some(CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.build, + &variant, + &subpackages, + &local_source_packages, + )?, + constraints: Vec::new(), + }), + host_dependencies: Some(CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.host, + &variant, + &subpackages, + &local_source_packages, + )?, + constraints: Vec::new(), + }), + run_dependencies: CondaOutputDependencies { + depends: convert_dependencies( + recipe.requirements.run, + &BTreeMap::default(), // Variants are not applied to run dependencies + &subpackages, + &local_source_packages, + )?, + constraints: convert_binary_dependencies( + recipe.requirements.run_constraints, + &BTreeMap::default(), // Variants are not applied to run constraints + &subpackages, + )?, + }, + ignore_run_exports: CondaOutputIgnoreRunExports { + by_name: recipe + .requirements + .ignore_run_exports + .by_name + .into_iter() + .collect(), + from_package: recipe + .requirements + .ignore_run_exports + .from_package + .into_iter() + .collect(), + }, + run_exports: CondaOutputRunExports { + weak: convert_dependencies( + recipe.requirements.run_exports.weak, + &variant, + &subpackages, + &local_source_packages, + )?, + strong: convert_dependencies( + recipe.requirements.run_exports.strong, + &variant, + &subpackages, + &local_source_packages, + )?, + noarch: convert_dependencies( + recipe.requirements.run_exports.noarch, + &variant, + &subpackages, + &local_source_packages, + )?, + weak_constrains: convert_binary_dependencies( + recipe.requirements.run_exports.weak_constraints, + &variant, + &subpackages, + )?, + strong_constrains: convert_binary_dependencies( + recipe.requirements.run_exports.strong_constraints, + &variant, + &subpackages, + )?, + }, + + // The input globs are the same for all outputs + input_globs: None, + // TODO: Implement caching + }); + } + + Ok(CondaOutputsResult { + outputs, + input_globs: variant_config.input_globs, + }) + } + + async fn conda_build_v0(&self, params: CondaBuildParams) -> miette::Result { // Create the work directory if it does not exist tokio_fs::create_dir_all(¶ms.work_directory) .await @@ -250,6 +472,7 @@ impl Protocol for RattlerBuildBackend { variant: Default::default(), experimental: true, allow_undefined: false, + recipe_path: Some(self.recipe_source.path.clone()), }; let host_vpkgs = params @@ -358,28 +581,15 @@ impl Protocol for RattlerBuildBackend { }) .await?; - let package_sources = output.finalized_sources.as_ref().map(|package_sources| { - package_sources - .iter() - .filter_map(|source| { - if let rattler_build::recipe::parser::Source::Path(path_source) = source { - Some(path_source.path.clone()) - } else { - None - } - }) - .collect() - }); - built.push(CondaBuiltPackage { output_file: build_path, input_globs: build_input_globs( &self.manifest_root, &self.recipe_source.path, - package_sources, + extract_mutable_package_sources(&output), self.config.extra_input_globs.clone(), )?, - name: output.name().as_normalized().to_string(), + name: output.name().clone(), version: output.version().to_string(), build: build_string.to_string(), subdir: output.target_platform().to_string(), @@ -387,6 +597,165 @@ impl Protocol for RattlerBuildBackend { } Ok(CondaBuildResult { packages: built }) } + + async fn conda_build_v1( + &self, + params: CondaBuildV1Params, + ) -> miette::Result { + let host_platform = params + .host_prefix + .as_ref() + .map_or_else(Platform::current, |prefix| prefix.platform); + let build_platform = params + .build_prefix + .as_ref() + .map_or_else(Platform::current, |prefix| prefix.platform); + + // Construct a `VariantConfig` based on the input parameters. We only + // have a single variant here so we can just use the variant from the + // parameters. + let variant_config = VariantConfig { + variants: params + .output + .variant + .iter() + .map(|(k, v)| (k.as_str().into(), vec![Variable::from_string(v)])) + .collect(), + pin_run_as_build: None, + zip_keys: None, + }; + + // Determine the variant configuration to use. This loads the variant + // configuration from disk as well as including the variants from the input + // parameters. + let selector_config_for_variants = SelectorConfig { + target_platform: host_platform, + host_platform, + build_platform, + hash: None, + variant: Default::default(), + experimental: false, + allow_undefined: false, + recipe_path: Some(self.recipe_source.path.clone()), + }; + let outputs = find_outputs_from_src(self.recipe_source.clone())?; + let discovered_outputs = variant_config.find_variants( + &outputs, + self.recipe_source.clone(), + &selector_config_for_variants, + )?; + let discovered_output = find_matching_output(¶ms.output, discovered_outputs)?; + + // Set up the proper directories for the build. + let directories = conda_build_v1_directories( + params.host_prefix.as_ref().map(|p| p.prefix.as_path()), + params.build_prefix.as_ref().map(|p| p.prefix.as_path()), + params.work_directory, + self.cache_dir.as_deref(), + self.source_dir.clone(), + params.output_directory.as_deref(), + self.recipe_source.path.clone(), + ); + + let tool_config = Configuration::builder() + .with_opt_cache_dir(self.cache_dir.clone()) + .with_logging_output_handler(self.logging_output_handler.clone()) + .with_testing(false) + // Pixi is incremental so keep the build + .with_keep_build(true) + // This indicates that the environments are externally managed, e.g. they are already + // prepared. + .with_environments_externally_managed(true) + .finish(); + + let output = Output { + recipe: discovered_output.recipe, + build_configuration: BuildConfiguration { + target_platform: discovered_output.target_platform, + host_platform: PlatformWithVirtualPackages { + platform: host_platform, + virtual_packages: vec![], + }, + build_platform: PlatformWithVirtualPackages { + platform: build_platform, + virtual_packages: vec![], + }, + hash: discovered_output.hash, + variant: discovered_output.used_vars.clone(), + directories, + channels: vec![], + channel_priority: Default::default(), + solve_strategy: Default::default(), + timestamp: chrono::Utc::now(), + subpackages: BTreeMap::new(), + packaging_settings: PackagingSettings::from_args( + ArchiveType::Conda, + CompressionLevel::default(), + ), + store_recipe: false, + force_colors: true, + sandbox_config: None, + debug: Debug::new(false), + exclude_newer: None, + }, + // TODO: We should pass these values to the build backend from pixi + finalized_dependencies: Some(FinalizedDependencies { + build: Some(ResolvedDependencies { + specs: vec![], + resolved: vec![], + }), + host: Some(ResolvedDependencies { + specs: vec![], + resolved: vec![], + }), + run: FinalizedRunDependencies { + depends: vec![], + constraints: vec![], + run_exports: Default::default(), + }, + }), + finalized_sources: None, + finalized_cache_dependencies: None, + finalized_cache_sources: None, + build_summary: Arc::default(), + system_tools: Default::default(), + extra_meta: None, + }; + + let (output, output_path) = run_build(output, &tool_config).await?; + + Ok(CondaBuildV1Result { + output_file: output_path, + input_globs: build_input_globs( + &self.manifest_root, + &self.recipe_source.path, + extract_mutable_package_sources(&output), + self.config.extra_input_globs.clone(), + )?, + name: output.name().as_normalized().to_string(), + version: output.version().clone(), + build: output.build_string().into_owned(), + subdir: *output.target_platform(), + }) + } +} + +/// Extracts the package sources from an `Output` object that are mutable and +/// should be watched for changes. +fn extract_mutable_package_sources(output: &Output) -> Option> { + let package_sources = output.finalized_sources.as_ref().map(|package_sources| { + package_sources + .iter() + .filter_map(|source| { + if let rattler_build::recipe::parser::Source::Path(path_source) = source { + Some(path_source.path.clone()) + } else { + None + } + }) + .collect() + }); + package_sources } /// Returns the relative path from `base` to `input`, joined by "/". @@ -421,7 +790,7 @@ fn build_input_globs( source: &Path, package_sources: Option>, extra_globs: Vec, -) -> miette::Result> { +) -> miette::Result> { // Get parent directory path let parent = if source.is_file() { // use the parent path as glob @@ -432,7 +801,7 @@ fn build_input_globs( }; // Always add the current directory of the package to the globs - let mut input_globs = Vec::from([build_relative_glob(manifest_root, &parent)?]); + let mut input_globs = BTreeSet::from([build_relative_glob(manifest_root, &parent)?]); // If there are sources add them to the globs as well if let Some(package_sources) = package_sources { @@ -442,7 +811,7 @@ fn build_input_globs( } else { parent.join(source) }; - input_globs.push(build_relative_glob(manifest_root, &source)?); + input_globs.insert(build_relative_glob(manifest_root, &source)?); } } @@ -457,10 +826,10 @@ fn build_input_globs( fn get_metadata_input_globs( manifest_root: &Path, recipe_source_path: &Path, -) -> miette::Result> { +) -> miette::Result> { match build_relative_glob(manifest_root, recipe_source_path) { - Ok(rel) if !rel.is_empty() => Ok(vec![rel]), - Ok(_) => Ok(Vec::new()), + Ok(rel) if !rel.is_empty() => Ok(BTreeSet::from_iter([rel])), + Ok(_) => Ok(Default::default()), Err(e) => Err(e), } } @@ -521,6 +890,7 @@ impl ProtocolInstantiator for RattlerBuildBackendInstantiator { } let instance = RattlerBuildBackend::new( + params.source_dir, params.manifest_path.as_path(), self.logging_output_handler.clone(), params.cache_directory, @@ -543,6 +913,8 @@ pub(crate) fn default_capabilities() -> BackendCapabilities { BackendCapabilities { provides_conda_metadata: Some(true), provides_conda_build: Some(true), + provides_conda_outputs: Some(true), + provides_conda_build_v1: Some(true), highest_supported_project_model: Some( pixi_build_types::VersionedProjectModel::highest_version(), ), @@ -556,10 +928,11 @@ mod tests { str::FromStr, }; + use pixi_build_backend::utils::test::conda_outputs_snapshot; use pixi_build_types::{ ChannelConfiguration, procedures::{ - conda_build::CondaBuildParams, conda_metadata::CondaMetadataParams, + conda_build_v0::CondaBuildParams, conda_metadata::CondaMetadataParams, initialize::InitializeParams, }, }; @@ -577,6 +950,7 @@ mod tests { let factory = RattlerBuildBackendInstantiator::new(LoggingOutputHandler::default()) .initialize(InitializeParams { + source_dir: None, manifest_path: recipe, project_model: None, configuration: None, @@ -605,6 +979,42 @@ mod tests { assert_eq!(result.packages.len(), 1); } + #[test] + fn test_conda_outputs() { + insta::glob!("../../../tests/recipe", "*/recipe.yaml", |recipe_path| { + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + runtime.block_on(async move { + let factory = RattlerBuildBackendInstantiator::new(LoggingOutputHandler::default()) + .initialize(InitializeParams { + source_dir: None, + manifest_path: recipe_path.to_path_buf(), + project_model: None, + configuration: None, + cache_directory: None, + }) + .await + .unwrap(); + + let current_dir = std::env::current_dir().unwrap(); + + let result = factory + .0 + .conda_outputs(CondaOutputsParams { + host_platform: Platform::Linux64, + build_platform: Platform::Linux64, + variant_configuration: None, + work_directory: current_dir, + }) + .await + .unwrap(); + + insta::assert_snapshot!(conda_outputs_snapshot(result)); + }); + }); + } + #[tokio::test] async fn test_conda_build() { // get cargo manifest dir @@ -613,6 +1023,7 @@ mod tests { let factory = RattlerBuildBackendInstantiator::new(LoggingOutputHandler::default()) .initialize(InitializeParams { + source_dir: None, manifest_path: recipe, project_model: None, configuration: None, @@ -625,7 +1036,7 @@ mod tests { let result = factory .0 - .conda_build(CondaBuildParams { + .conda_build_v0(CondaBuildParams { build_platform_virtual_packages: None, host_platform: None, channel_base_urls: None, @@ -640,7 +1051,10 @@ mod tests { .await .unwrap(); - assert_eq!(result.packages[0].name, "boltons-with-extra"); + assert_eq!( + result.packages[0].name.as_normalized(), + "boltons-with-extra" + ); } const FAKE_RECIPE: &str = r#" @@ -653,6 +1067,7 @@ mod tests { manifest_path: impl AsRef, ) -> miette::Result { RattlerBuildBackend::new( + None, manifest_path.as_ref(), LoggingOutputHandler::default(), None, @@ -779,7 +1194,7 @@ mod tests { let recipe_path = base_path.join("recipe.yaml"); fs::write(&recipe_path, "fake").unwrap(); let globs = super::build_input_globs(base_path, &recipe_path, None, Vec::new()).unwrap(); - assert_eq!(globs, vec!["*/**"]); + assert_eq!(globs, BTreeSet::from([String::from("*/**")])); // Case 2: source is a directory, with a file and a dir as package sources let pkg_dir = base_path.join("pkg"); @@ -794,7 +1209,14 @@ mod tests { Vec::new(), ) .unwrap(); - assert_eq!(globs, vec!["*/**", "pkg/file.txt", "pkg/dir/**"]); + assert_eq!( + globs, + BTreeSet::from([ + String::from("*/**"), + String::from("pkg/file.txt"), + String::from("pkg/dir/**") + ]) + ); } #[test] @@ -822,7 +1244,10 @@ mod tests { Vec::new(), ) .unwrap(); - assert_eq!(globs, vec!["source/**", "pkgsrc/**"]); + assert_eq!( + globs, + BTreeSet::from([String::from("source/**"), String::from("pkgsrc/**")]) + ); } #[test] @@ -850,7 +1275,14 @@ mod tests { ) .unwrap(); // The relative path from base_path to rel_dir should be "rel_folder/**" - assert_eq!(globs, vec!["*/**", "rel_folder/**"]); + assert_eq!( + globs, + BTreeSet::from_iter( + ["*/**", "rel_folder/**"] + .into_iter() + .map(ToString::to_string) + ) + ); } #[test] @@ -860,27 +1292,28 @@ mod tests { let manifest_root = PathBuf::from("/foo/bar"); let path = PathBuf::from("/foo/bar/recipe.yaml"); let globs = super::get_metadata_input_globs(&manifest_root, &path).unwrap(); - assert_eq!(globs, vec!["recipe.yaml"]); + assert_eq!(globs, BTreeSet::from([String::from("recipe.yaml")])); // Case: file with no name (root) let manifest_root = PathBuf::from("/"); let path = PathBuf::from("/"); let globs = super::get_metadata_input_globs(&manifest_root, &path).unwrap(); - assert_eq!(globs, vec!["*/**".to_string()]); + assert_eq!(globs, BTreeSet::from([String::from("*/**")])); // Case: file with .yml extension let manifest_root = PathBuf::from("/foo/bar"); let path = PathBuf::from("/foo/bar/recipe.yml"); let globs = super::get_metadata_input_globs(&manifest_root, &path).unwrap(); - assert_eq!(globs, vec!["recipe.yml"]); + assert_eq!(globs, BTreeSet::from([String::from("recipe.yml")])); // Case: file in subdir let manifest_root = PathBuf::from("/foo"); let path = PathBuf::from("/foo/bar/recipe.yaml"); let globs = super::get_metadata_input_globs(&manifest_root, &path).unwrap(); - assert_eq!(globs, vec!["bar/recipe.yaml"]); + assert_eq!(globs, BTreeSet::from([String::from("bar/recipe.yaml")])); } #[test] fn test_build_input_globs_includes_extra_globs() { use std::fs; + use tempfile::tempdir; // Create a temp directory to act as the base @@ -906,6 +1339,6 @@ mod tests { } // Verify that the basic manifest glob is still present - assert!(globs.contains(&"*/**".to_string())); + assert!(globs.contains("*/**")); } } diff --git a/crates/pixi-build-rattler-build/src/rattler_build.rs b/crates/pixi-build-rattler-build/src/rattler_build.rs index 68997a0d..8bfab053 100644 --- a/crates/pixi-build-rattler-build/src/rattler_build.rs +++ b/crates/pixi-build-rattler-build/src/rattler_build.rs @@ -11,6 +11,7 @@ use crate::config::RattlerBuildBackendConfig; pub struct RattlerBuildBackend { pub(crate) logging_output_handler: LoggingOutputHandler, + pub(crate) source_dir: PathBuf, /// In case of rattler-build, manifest is the raw recipe /// We need to apply later the selectors to get the final recipe pub(crate) recipe_source: Source, @@ -23,6 +24,7 @@ impl RattlerBuildBackend { /// Returns a new instance of [`RattlerBuildBackend`] by reading the /// manifest at the given path. pub fn new( + source_dir: Option, manifest_path: &Path, logging_output_handler: LoggingOutputHandler, cache_dir: Option, @@ -30,10 +32,24 @@ impl RattlerBuildBackend { ) -> miette::Result { // Locate the recipe let manifest_file_name = manifest_path.file_name().and_then(OsStr::to_str); - let recipe_path = match manifest_file_name { - Some("recipe.yaml") | Some("recipe.yml") => manifest_path.to_path_buf(), + let (recipe_path, source_dir) = match manifest_file_name { + Some("recipe.yaml") | Some("recipe.yml") => { + let source_dir = source_dir.unwrap_or_else(|| { + manifest_path + .parent() + .expect("file always has parent") + .to_path_buf() + }); + (manifest_path.to_path_buf(), source_dir) + } _ => { // The manifest is not a recipe, so we need to find the recipe.yaml file. + let source_dir = source_dir.unwrap_or_else(|| { + manifest_path + .parent() + .unwrap_or(manifest_path) + .to_path_buf() + }); let recipe_path = manifest_path.parent().and_then(|manifest_dir| { [ "recipe.yaml", @@ -48,7 +64,7 @@ impl RattlerBuildBackend { }) }); - recipe_path.ok_or_else(|| miette::miette!("Could not find a recipe.yaml in the source directory to use as the recipe manifest."))? + (recipe_path.ok_or_else(|| miette::miette!("Could not find a recipe.yaml in the source directory to use as the recipe manifest."))?, source_dir) } }; @@ -62,6 +78,7 @@ impl RattlerBuildBackend { Ok(Self { logging_output_handler, + source_dir, recipe_source, manifest_root, cache_dir, diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs.snap new file mode 100644 index 00000000..63bcca87 --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs.snap @@ -0,0 +1,106 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: result.outputs +--- +[ + { + "identifier": { + "name": "boltons-with-extra", + "version": "23.0.0", + "build": "pyh4616a5c_0", + "buildNumber": 0, + "subdir": "noarch", + "license": "BSD-3-Clause", + "licenseFamily": null, + "noarch": "python", + "purls": null + }, + "buildDependencies": { + "depends": [], + "constraints": [] + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": null, + "build": null, + "buildNumber": null, + "fileName": null, + "channel": null, + "subdir": null, + "md5": null, + "sha256": null, + "url": null, + "license": null + } + }, + { + "name": "pip", + "binary": { + "version": null, + "build": null, + "buildNumber": null, + "fileName": null, + "channel": null, + "subdir": null, + "md5": null, + "sha256": null, + "url": null, + "license": null + } + }, + { + "name": "setuptools", + "binary": { + "version": null, + "build": null, + "buildNumber": null, + "fileName": null, + "channel": null, + "subdir": null, + "md5": null, + "sha256": null, + "url": null, + "license": null + } + } + ], + "constraints": [] + }, + "runDependencies": { + "depends": [ + { + "name": "pip", + "binary": { + "version": null, + "build": null, + "buildNumber": null, + "fileName": null, + "channel": null, + "subdir": null, + "md5": null, + "sha256": null, + "url": null, + "license": null + } + } + ], + "constraints": [] + }, + "ignoreRunExports": { + "byName": [], + "fromPackage": [] + }, + "runExports": { + "weak": [], + "strong": [], + "noarch": [], + "weakConstrains": [], + "strongConstrains": [] + }, + "cache": null, + "inputGlobs": null + } +] diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@boltons__recipe.yaml.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@boltons__recipe.yaml.snap new file mode 100644 index 00000000..5eb952c0 --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@boltons__recipe.yaml.snap @@ -0,0 +1,46 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: conda_outputs_snapshot(result) +input_file: tests/recipe/boltons/recipe.yaml +--- +{ + "outputs": [ + { + "metadata": { + "name": "boltons-with-extra", + "version": "23.0.0", + "build": "pyh4616a5c_0", + "buildNumber": 0, + "subdir": "noarch", + "license": "BSD-3-Clause", + "noarch": "python", + "variant": { + "target_platform": "noarch" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python" + }, + { + "name": "pip" + }, + { + "name": "setuptools" + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "pip" + } + ] + } + } + ], + "inputGlobs": [ + "variants.yaml" + ] +} diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage-variants__recipe.yaml.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage-variants__recipe.yaml.snap new file mode 100644 index 00000000..2e65c262 --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage-variants__recipe.yaml.snap @@ -0,0 +1,162 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: conda_outputs_snapshot(result) +input_file: tests/recipe/pin-subpackage-variants/recipe.yaml +--- +{ + "outputs": [ + { + "metadata": { + "name": "my-package", + "version": "0.1.0", + "build": "py38hb368f13_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "python": "3.8", + "target_platform": "noarch" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.8.*" + } + } + ] + } + }, + { + "metadata": { + "name": "my-package-pinned", + "version": "0.1.0", + "build": "h1670b25_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "my-package": "0.1.0 py38hb368f13_0", + "target_platform": "noarch" + } + }, + "runDependencies": { + "depends": [ + { + "name": "my-package", + "source": { + "Path": { + "path": "." + } + } + } + ] + } + }, + { + "metadata": { + "name": "my-package", + "version": "0.1.0", + "build": "py39h6a44458_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "python": "3.9", + "target_platform": "noarch" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.9.*" + } + } + ] + } + }, + { + "metadata": { + "name": "my-package-pinned", + "version": "0.1.0", + "build": "h11d4084_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "my-package": "0.1.0 py39h6a44458_0", + "target_platform": "noarch" + } + }, + "runDependencies": { + "depends": [ + { + "name": "my-package", + "source": { + "Path": { + "path": "." + } + } + } + ] + } + }, + { + "metadata": { + "name": "my-package", + "version": "0.1.0", + "build": "py310hbede2c2_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "python": "3.10", + "target_platform": "noarch" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.10.*" + } + } + ] + } + }, + { + "metadata": { + "name": "my-package-pinned", + "version": "0.1.0", + "build": "hba4f637_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "my-package": "0.1.0 py310hbede2c2_0", + "target_platform": "noarch" + } + }, + "runDependencies": { + "depends": [ + { + "name": "my-package", + "source": { + "Path": { + "path": "." + } + } + } + ] + } + } + ], + "inputGlobs": [ + "variants.yaml" + ] +} diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage__recipe.yaml.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage__recipe.yaml.snap new file mode 100644 index 00000000..383f0272 --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@pin-subpackage__recipe.yaml.snap @@ -0,0 +1,51 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: conda_outputs_snapshot(result) +input_file: tests/recipe/pin-subpackage/recipe.yaml +--- +{ + "outputs": [ + { + "metadata": { + "name": "my-package", + "version": "0.1.0", + "build": "h4616a5c_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "target_platform": "noarch" + } + } + }, + { + "metadata": { + "name": "my-package-a", + "version": "0.1.0", + "build": "hc811278_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic", + "variant": { + "my-package": "0.1.0 h4616a5c_0", + "target_platform": "noarch" + } + }, + "runDependencies": { + "depends": [ + { + "name": "my-package", + "source": { + "Path": { + "path": "." + } + } + } + ] + } + } + ], + "inputGlobs": [ + "variants.yaml" + ] +} diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@sub_packages__recipe.yaml.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@sub_packages__recipe.yaml.snap new file mode 100644 index 00000000..702ab3ae --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@sub_packages__recipe.yaml.snap @@ -0,0 +1,44 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: "serde_json::to_string_pretty(&value).unwrap()" +input_file: tests/recipe/sub_packages/recipe.yaml +--- +{ + "outputs": [ + { + "identifier": { + "name": "my-package", + "version": "0.1.0", + "build": "h4616a5c_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic" + } + }, + { + "identifier": { + "name": "my-package-a", + "version": "0.1.0", + "build": "hc811278_0", + "buildNumber": 0, + "subdir": "noarch", + "noarch": "generic" + }, + "runDependencies": { + "depends": [ + { + "name": "my-package", + "source": { + "Path": { + "path": "." + } + } + } + ] + } + } + ], + "inputGlobs": [ + "variants.yaml" + ] +} diff --git a/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@variants__recipe.yaml.snap b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@variants__recipe.yaml.snap new file mode 100644 index 00000000..e7b0d21b --- /dev/null +++ b/crates/pixi-build-rattler-build/src/snapshots/pixi_build_rattler_build__protocol__tests__conda_outputs@variants__recipe.yaml.snap @@ -0,0 +1,105 @@ +--- +source: crates/pixi-build-rattler-build/src/protocol.rs +expression: conda_outputs_snapshot(result) +input_file: tests/recipe/variants/recipe.yaml +--- +{ + "outputs": [ + { + "metadata": { + "name": "foobar", + "version": "1.0.0", + "build": "py38h0891c58_0", + "buildNumber": 0, + "subdir": "linux-64", + "noarch": false, + "variant": { + "python": "3.8", + "target_platform": "linux-64" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.8.*" + } + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "python" + } + ] + } + }, + { + "metadata": { + "name": "foobar", + "version": "1.0.0", + "build": "py39h2e92cda_0", + "buildNumber": 0, + "subdir": "linux-64", + "noarch": false, + "variant": { + "python": "3.9", + "target_platform": "linux-64" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.9.*" + } + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "python" + } + ] + } + }, + { + "metadata": { + "name": "foobar", + "version": "1.0.0", + "build": "py310h8e7c8fa_0", + "buildNumber": 0, + "subdir": "linux-64", + "noarch": false, + "variant": { + "python": "3.10", + "target_platform": "linux-64" + } + }, + "hostDependencies": { + "depends": [ + { + "name": "python", + "binary": { + "version": "3.10.*" + } + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "python" + } + ] + } + } + ], + "inputGlobs": [ + "variants.yaml" + ] +} diff --git a/crates/pixi-build-rust/src/main.rs b/crates/pixi-build-rust/src/main.rs index e579cce0..aeddf26a 100644 --- a/crates/pixi-build-rust/src/main.rs +++ b/crates/pixi-build-rust/src/main.rs @@ -1,11 +1,6 @@ mod build_script; mod config; -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; - use build_script::BuildScriptContext; use config::RustBackendConfig; use miette::IntoDiagnostic; @@ -21,6 +16,11 @@ use recipe_stage0::{ matchspec::PackageDependency, recipe::{Item, Script}, }; +use std::collections::BTreeSet; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; #[derive(Default, Clone)] pub struct RustGenerator {} @@ -44,7 +44,7 @@ impl GenerateRecipe for RustGenerator { let requirements = &mut generated_recipe.recipe.requirements; - let resolved_requirements = requirements.resolve(Some(&host_platform)); + let resolved_requirements = requirements.resolve(Some(host_platform)); // Ensure the compiler function is added to the build requirements // only if it is not already present. @@ -132,7 +132,7 @@ impl GenerateRecipe for RustGenerator { config: &Self::Config, _workdir: impl AsRef, _editable: bool, - ) -> Vec { + ) -> BTreeSet { [ "**/*.rs", // Cargo configuration files @@ -183,10 +183,10 @@ mod tests { } // Verify that default globs are still present - assert!(result.contains(&"**/*.rs".to_string())); - assert!(result.contains(&"Cargo.toml".to_string())); - assert!(result.contains(&"Cargo.lock".to_string())); - assert!(result.contains(&"build.rs".to_string())); + assert!(result.contains("**/*.rs")); + assert!(result.contains("Cargo.toml")); + assert!(result.contains("Cargo.lock")); + assert!(result.contains("build.rs")); } #[macro_export] diff --git a/crates/pixi-build-rust/src/snapshots/pixi_build_rust__tests__conda_outputs_with_variants.snap b/crates/pixi-build-rust/src/snapshots/pixi_build_rust__tests__conda_outputs_with_variants.snap new file mode 100644 index 00000000..b982f8e5 --- /dev/null +++ b/crates/pixi-build-rust/src/snapshots/pixi_build_rust__tests__conda_outputs_with_variants.snap @@ -0,0 +1,62 @@ +--- +source: crates/pixi-build-rust/src/main.rs +expression: "intermediate_conda_outputs_snapshot::(Some(project_model),\nNone, Platform::Linux64, Some(variants))" +--- +{ + "outputs": [ + { + "metadata": { + "name": "foobar", + "version": "0.1.0", + "build": "hc325156_0", + "buildNumber": 0, + "subdir": "linux-64", + "noarch": false + }, + "buildDependencies": { + "depends": [ + { + "name": "rust_linux-64", + "binary": { + "version": "1.86.*" + } + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "boltons" + } + ] + } + }, + { + "metadata": { + "name": "foobar", + "version": "0.1.0", + "build": "hce365a6_0", + "buildNumber": 0, + "subdir": "linux-64", + "noarch": false + }, + "buildDependencies": { + "depends": [ + { + "name": "rust_linux-64", + "binary": { + "version": "1.87.*" + } + } + ] + }, + "runDependencies": { + "depends": [ + { + "name": "boltons" + } + ] + } + } + ] +} diff --git a/crates/recipe-stage0/Cargo.toml b/crates/recipe-stage0/Cargo.toml index 23130bce..187375c3 100644 --- a/crates/recipe-stage0/Cargo.toml +++ b/crates/recipe-stage0/Cargo.toml @@ -4,20 +4,17 @@ version = "0.1.0" edition.workspace = true [dependencies] -hashlink = "0.10.0" -marked-yaml = "0.8.0" +hashlink = { workspace = true } +marked-yaml = { workspace = true } rattler_conda_types = { workspace = true } -serde = { version = "1.0.219", features = ["derive"] } -serde_yaml = "0.9.34" +serde = { workspace = true, features = ["derive"] } +serde_yaml = { workspace = true } rattler-build = { workspace = true } indexmap = { workspace = true } url = { workspace = true } -either = { workspace = true } -pixi_build_types = { workspace = true } -miette = { workspace = true } [dev-dependencies] -insta = { version = "1.42.1", features = ["yaml", "redactions", "filters"] } -serde_json = "1.0" +insta = { workspace = true, features = ["yaml", "redactions", "filters"] } +serde_json = { workspace = true } pixi_build_types = { workspace = true } diff --git a/crates/recipe-stage0/src/recipe.rs b/crates/recipe-stage0/src/recipe.rs index 84fea0a3..e43208fa 100644 --- a/crates/recipe-stage0/src/recipe.rs +++ b/crates/recipe-stage0/src/recipe.rs @@ -495,7 +495,7 @@ impl ConditionalRequirements { /// Resolves the conditional requirements for a given platform. pub fn resolve( &self, - platform: Option<&Platform>, + platform: Option, ) -> PackageSpecDependencies { PackageSpecDependencies { build: self.resolve_list(&self.build, platform), @@ -508,7 +508,7 @@ impl ConditionalRequirements { pub(crate) fn resolve_list( &self, list: &ConditionalList, - platform: Option<&Platform>, + platform: Option, ) -> IndexMap { list.iter() .flat_map(|item| Self::resolve_item(item, platform)) @@ -517,7 +517,7 @@ impl ConditionalRequirements { pub(crate) fn resolve_item( item: &Item, - platform: Option<&Platform>, + platform: Option, ) -> IndexMap { match item { Item::Value(v) => { diff --git a/recipe/pixi-build-api-version.yaml b/recipe/pixi-build-api-version.yaml index 8f4b7eaf..09d77364 100644 --- a/recipe/pixi-build-api-version.yaml +++ b/recipe/pixi-build-api-version.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json context: name: pixi-build-api-version - version: "0" + version: "1" package: name: ${{ name }} diff --git a/recipe/pixi-build-cmake.yaml b/recipe/pixi-build-cmake.yaml index 4ddc3dba..25719884 100644 --- a/recipe/pixi-build-cmake.yaml +++ b/recipe/pixi-build-cmake.yaml @@ -22,7 +22,7 @@ requirements: - if: unix then: openssl run: - - pixi-build-api-version >=0,<1 + - pixi-build-api-version >=0,<2 build: script: diff --git a/recipe/pixi-build-python.yaml b/recipe/pixi-build-python.yaml index b4c3b242..9e30555d 100644 --- a/recipe/pixi-build-python.yaml +++ b/recipe/pixi-build-python.yaml @@ -22,7 +22,7 @@ requirements: - if: unix then: openssl run: - - pixi-build-api-version >=0,<1 + - pixi-build-api-version >=0,<2 build: script: diff --git a/recipe/pixi-build-rattler-build.yaml b/recipe/pixi-build-rattler-build.yaml index 9855a0bd..f027c345 100644 --- a/recipe/pixi-build-rattler-build.yaml +++ b/recipe/pixi-build-rattler-build.yaml @@ -23,7 +23,7 @@ requirements: - if: unix then: openssl run: - - pixi-build-api-version >=0,<1 + - pixi-build-api-version >=0,<2 build: script: diff --git a/recipe/pixi-build-rust.yaml b/recipe/pixi-build-rust.yaml index 103c1a86..dbba8231 100644 --- a/recipe/pixi-build-rust.yaml +++ b/recipe/pixi-build-rust.yaml @@ -22,7 +22,7 @@ requirements: - if: unix then: openssl run: - - pixi-build-api-version >=0,<1 + - pixi-build-api-version >=0,<2 build: script: diff --git a/tests/recipe/pin-subpackage-variants/recipe.yaml b/tests/recipe/pin-subpackage-variants/recipe.yaml new file mode 100644 index 00000000..3f895f56 --- /dev/null +++ b/tests/recipe/pin-subpackage-variants/recipe.yaml @@ -0,0 +1,27 @@ +# regression test that pin subpackage works with `my.package` +context: + name: my-package + version: 0.1.0 + +recipe: + version: ${{ version }} + +build: + number: 0 + +outputs: + - package: + name: ${{ name }} + requirements: + host: + - python + build: + noarch: generic + + - package: + name: ${{ name }}-pinned + build: + noarch: generic + requirements: + run: + - ${{ pin_subpackage(name, exact=true) }} diff --git a/tests/recipe/pin-subpackage-variants/variants.yaml b/tests/recipe/pin-subpackage-variants/variants.yaml new file mode 100644 index 00000000..d1f51d50 --- /dev/null +++ b/tests/recipe/pin-subpackage-variants/variants.yaml @@ -0,0 +1,4 @@ +python: + - "3.8" + - "3.9" + - "3.10" diff --git a/tests/recipe/pin-subpackage/recipe.yaml b/tests/recipe/pin-subpackage/recipe.yaml new file mode 100644 index 00000000..ddaea97a --- /dev/null +++ b/tests/recipe/pin-subpackage/recipe.yaml @@ -0,0 +1,24 @@ +# regression test that pin subpackage works with `my.package` +context: + name: my-package + version: 0.1.0 + +recipe: + version: ${{ version }} + +build: + number: 0 + +outputs: + - package: + name: ${{ name }} + build: + noarch: generic + + - package: + name: ${{ name }}-a + build: + noarch: generic + requirements: + run: + - ${{ pin_subpackage(name, exact=true) }} diff --git a/tests/recipe/variants/recipe.yaml b/tests/recipe/variants/recipe.yaml new file mode 100644 index 00000000..dd2c06b4 --- /dev/null +++ b/tests/recipe/variants/recipe.yaml @@ -0,0 +1,9 @@ +package: + name: foobar + version: 1.0.0 + +requirements: + host: + - python + run: + - python diff --git a/tests/recipe/variants/variants.yaml b/tests/recipe/variants/variants.yaml new file mode 100644 index 00000000..d1f51d50 --- /dev/null +++ b/tests/recipe/variants/variants.yaml @@ -0,0 +1,4 @@ +python: + - "3.8" + - "3.9" + - "3.10"