diff --git a/crates/gourgeist/Cargo.lock b/crates/gourgeist/Cargo.lock new file mode 100644 index 000000000..fe7aa9f4c --- /dev/null +++ b/crates/gourgeist/Cargo.lock @@ -0,0 +1,1450 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "charset" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e9079d1a12a2cc2bffb5db039c43661836ead4082120d5844f02555aca2d46" +dependencies = [ + "base64 0.13.1", + "encoding_rs", +] + +[[package]] +name = "chumsky" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23170228b96236b5a7299057ac284a321457700bc8c41a4476052f0f4ba5349d" +dependencies = [ + "hashbrown", + "stacker", +] + +[[package]] +name = "clap" +version = "4.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "configparser" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glibc_version" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803ff7635f1ab4e2c064b68a0c60da917d3d18dc8d086130f689d62ce4f1c33e" +dependencies = [ + "regex", +] + +[[package]] +name = "goblin" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "gourgeist" +version = "0.0.4" +dependencies = [ + "camino", + "clap", + "configparser", + "dirs", + "fs-err", + "install-wheel-rs", + "minreq", + "rayon", + "seahash", + "serde", + "serde_json", + "tempfile", + "thiserror", + "tracing", + "tracing-subscriber", + "which", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "install-wheel-rs" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd8c1fd75d1c686f0e18aa3dd60532f119578b32501517576dd539e0be65f1e" +dependencies = [ + "base64 0.21.4", + "configparser", + "csv", + "fs-err", + "fs2", + "glibc_version", + "goblin", + "mailparse", + "once_cell", + "platform-info", + "plist", + "regex", + "rfc2047-decoder", + "serde", + "serde_json", + "sha2", + "target-lexicon", + "tempfile", + "thiserror", + "tracing", + "walkdir", + "zip", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "mailparse" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b56570f5f8c0047260d1c8b5b331f62eb9c660b9dd4071a8c46f8c7d3f280aa" +dependencies = [ + "charset", + "data-encoding", + "quoted_printable", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "minreq" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731ff3277257ac76a410e8e2e2465afb7a5e6a1d13bb68d306d97bf96605546c" +dependencies = [ + "log", + "once_cell", + "rustls", + "rustls-webpki", + "webpki-roots", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "platform-info" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6259c4860e53bf665016f1b2f46a8859cadfa717581dc9d597ae4069de6300f" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "plist" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +dependencies = [ + "base64 0.21.4", + "indexmap", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "rfc2047-decoder" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a46587af867184f5e2f5dd2eb0e32f76054f11e890760c0f5a6130abf7e3" +dependencies = [ + "base64 0.21.4", + "charset", + "chumsky", + "memchr", + "quoted_printable", + "thiserror", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustix" +version = "0.38.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] diff --git a/crates/gourgeist/Cargo.toml b/crates/gourgeist/Cargo.toml new file mode 100644 index 000000000..ec3e8a419 --- /dev/null +++ b/crates/gourgeist/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "gourgeist" +version = "0.0.4" +edition = "2021" +description = "virtualenv creation implemented in rust" +repository = "https://github.com/konstin/gourgeist" +license = "MIT OR Apache-2.0" +keywords = ["virtualenv", "venv", "python"] +readme = "Readme.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +camino = { version = "1.1.6", features = ["serde1"] } +clap = { version = "4.4.5", features = ["derive"] } +configparser = "3.0.2" +dirs = "5.0.1" +fs-err = "2.9.0" +install-wheel-rs = { version = "0.0.1", optional = true } +minreq = { version = "2.10.0", optional = true, features = ["https"] } +rayon = { version = "1.8.0", optional = true } +seahash = "4.1.0" +serde = { version = "1.0.188", features = ["derive"] } +serde_json = "1.0.107" +tempfile = "3.8.0" +thiserror = "1.0.49" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +which = "4.4.2" + +[features] +default = ["install"] +install = ["install-wheel-rs", "minreq"] +parallel = ["rayon"] + +# zip implementation +[profile.dev.package.adler] +opt-level = 3 + +[profile.profiling] +inherits = "release" +lto = "thin" +debug = true + +# The profile that 'cargo dist' will build with +[profile.dist] +inherits = "release" +lto = "thin" + +# Config for 'cargo dist' +[workspace.metadata.dist] +# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) +cargo-dist-version = "0.3.1" +# CI backends to support +ci = ["github"] +# The installers to generate for each app +installers = ["shell", "powershell"] +# Target platforms to build apps for (Rust target-triple syntax) +targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-pc-windows-msvc"] +# Publish jobs to run in CI +pr-run-mode = "plan" diff --git a/crates/gourgeist/Readme.md b/crates/gourgeist/Readme.md new file mode 100644 index 000000000..77a159d8f --- /dev/null +++ b/crates/gourgeist/Readme.md @@ -0,0 +1,33 @@ +# Gourgeist + +Gourgeist is a rust library to create python virtual environments. It also has a CLI. + +It currently supports only unix (linux/mac), windows support is missing. + +## Rust + +```rust +use camino::Utf8PathBuf; +use gourgeist::{create_venv, get_interpreter_info, parse_python_cli}; + +let location = cli.path.unwrap_or(Utf8PathBuf::from(".venv")); +let python = parse_python_cli(cli.python)?; +let data = get_interpreter_info(&python)?; +create_venv(&location, &python, &data, cli.bare)?; +``` + +## CLI + +Use `python` as base for a virtualenv `.venv`: +```bash +gourgeist +``` + +Or use custom defaults: +```bash +gourgeist -p 3.11 my_env +``` + +## Jessie's gourgeist + +![Jessie's gourgeist, a pokemon with a jack o'lantern as body](static/gourgeist.png) \ No newline at end of file diff --git a/crates/gourgeist/benchmark.sh b/crates/gourgeist/benchmark.sh new file mode 100644 index 000000000..af1de4d12 --- /dev/null +++ b/crates/gourgeist/benchmark.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +virtualenv --version + +#cargo build --profile profiling +cargo build --release #--features parallel +# Benchmarking trick! strip your binaries ٩( ∂‿∂ )۶ +strip target/release/gourgeist + +echo "## Bare" +hyperfine --warmup 1 --prepare "rm -rf target/a" "virtualenv -p 3.11 --no-seed target/a" "target/release/gourgeist -p 3.11 --bare target/a" +echo "## Default" +hyperfine --warmup 1 --prepare "rm -rf target/a" "virtualenv -p 3.11 target/a" "target/release/gourgeist -p 3.11 target/a" + diff --git a/crates/gourgeist/compare_in_git.sh b/crates/gourgeist/compare_in_git.sh new file mode 100644 index 000000000..516b1d1fb --- /dev/null +++ b/crates/gourgeist/compare_in_git.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e + +virtualenv_command() { + virtualenv -p 3.11 compare_venv # --no-pip --no-setuptools --no-wheel +} +rust_command() { + cargo run -- -p 3.11 compare_venv # --bare +} + +rm -rf compare_venv +virtualenv_command +rm compare_venv/.gitignore +git -C compare_venv init +git -C compare_venv add -A +git -C compare_venv commit -q -m "Initial commit" +rm -r compare_venv/* # This skips the hidden .git +mkdir -p target +mv compare_venv target/compare_venv2 +rust_command +rm compare_venv/.gitignore +cp -r compare_venv/* target/compare_venv2 +rm -r compare_venv +mv target/compare_venv2 compare_venv +git -C compare_venv/ status + diff --git a/crates/gourgeist/imasnake.py b/crates/gourgeist/imasnake.py new file mode 100644 index 000000000..aaa9d19d3 --- /dev/null +++ b/crates/gourgeist/imasnake.py @@ -0,0 +1,12 @@ +import sys + + +def main(): + print(sys.executable) + print(sys.version) + print(sys.base_prefix) + print(sys.prefix) + + +if __name__ == "__main__": + main() diff --git a/crates/gourgeist/oranda.json b/crates/gourgeist/oranda.json new file mode 100644 index 000000000..024115f9c --- /dev/null +++ b/crates/gourgeist/oranda.json @@ -0,0 +1,8 @@ +{ + "styles": { + "theme": "axo_light" + }, + "build": { + "path_prefix": "gourgeist" + } +} diff --git a/crates/gourgeist/src/_virtualenv.py b/crates/gourgeist/src/_virtualenv.py new file mode 100644 index 000000000..17f73b1d4 --- /dev/null +++ b/crates/gourgeist/src/_virtualenv.py @@ -0,0 +1,102 @@ +"""Patches that are applied at runtime to the virtual environment.""" + +from __future__ import annotations + +import os +import sys +from contextlib import suppress + +VIRTUALENV_PATCH_FILE = os.path.join(__file__) + + +def patch_dist(dist): + """ + Distutils allows user to configure some arguments via a configuration file: + https://docs.python.org/3/install/index.html#distutils-configuration-files. + + Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up. + """ # noqa: D205 + # we cannot allow some install config as that would get packages installed outside of the virtual environment + old_parse_config_files = dist.Distribution.parse_config_files + + def parse_config_files(self, *args, **kwargs): + result = old_parse_config_files(self, *args, **kwargs) + install = self.get_option_dict("install") + + if "prefix" in install: # the prefix governs where to install the libraries + install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix) + for base in ("purelib", "platlib", "headers", "scripts", "data"): + key = f"install_{base}" + if key in install: # do not allow global configs to hijack venv paths + install.pop(key, None) + return result + + dist.Distribution.parse_config_files = parse_config_files + + +# Import hook that patches some modules to ignore configuration values that break package installation in case +# of virtual environments. +_DISTUTILS_PATCH = "distutils.dist", "setuptools.dist" +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer + + +class _Finder: + """A meta path finder that allows patching the imported distutils modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup, + # because there are gevent-based applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): # noqa: ARG002 + if fullname in _DISTUTILS_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are simultaneously running into find_spec, + # observing .lock as empty, and further going into hereby initialization. However due to the GIL, + # list.append() operation is atomic and this way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + is_new_api = hasattr(spec.loader, "exec_module") + func_name = "exec_module" if is_new_api else "load_module" + old = getattr(spec.loader, func_name) + func = self.exec_module if is_new_api else self.load_module + if old is not func: + with suppress(AttributeError): # C-Extension loaders are r/o such as zipimporter with <3.7 + setattr(spec.loader, func_name, partial(func, old)) + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + + @staticmethod + def load_module(old, name): + module = old(name) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + return module + + +sys.meta_path.insert(0, _Finder()) diff --git a/crates/gourgeist/src/activate b/crates/gourgeist/src/activate new file mode 100644 index 000000000..401a9e7ea --- /dev/null +++ b/crates/gourgeist/src/activate @@ -0,0 +1,87 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV='{{ VIRTUAL_ENV_TEMPLATE_STRING }}' +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +if [ "x" != x ] ; then + VIRTUAL_ENV_PROMPT="" +else + VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + PS1="(${VIRTUAL_ENV_PROMPT}) ${PS1-}" + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it to forget past +# commands. Without forgetting past commands the $PATH changes +# we made may not be respected +hash -r 2>/dev/null diff --git a/crates/gourgeist/src/activator/activate b/crates/gourgeist/src/activator/activate new file mode 100644 index 000000000..df944c775 --- /dev/null +++ b/crates/gourgeist/src/activator/activate @@ -0,0 +1,87 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV='{{ VIRTUAL_ENV_DIR }}' +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +if [ "x" != x ] ; then + VIRTUAL_ENV_PROMPT="" +else + VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + PS1="(${VIRTUAL_ENV_PROMPT}) ${PS1-}" + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it to forget past +# commands. Without forgetting past commands the $PATH changes +# we made may not be respected +hash -r 2>/dev/null diff --git a/crates/gourgeist/src/activator/activate.csh b/crates/gourgeist/src/activator/activate.csh new file mode 100644 index 000000000..7a4546d92 --- /dev/null +++ b/crates/gourgeist/src/activator/activate.csh @@ -0,0 +1,55 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV '{{ VIRTUAL_ENV_DIR }}' + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q" + + + +if ('' != "") then + setenv VIRTUAL_ENV_PROMPT '' +else + setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q" +endif + +if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then + if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = '('"$VIRTUAL_ENV_PROMPT:q"') '"$prompt:q" + endif + endif +endif + +unset env_name +unset do_prompt + +alias pydoc python -m pydoc + +rehash diff --git a/crates/gourgeist/src/activator/activate.fish b/crates/gourgeist/src/activator/activate.fish new file mode 100644 index 000000000..dfdc0ca81 --- /dev/null +++ b/crates/gourgeist/src/activator/activate.fish @@ -0,0 +1,103 @@ +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH") + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + set -e _OLD_VIRTUAL_PATH + end + + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME" + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + and functions -q _old_fish_prompt + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV '{{ VIRTUAL_ENV_DIR }}' + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +set -gx PATH "$VIRTUAL_ENV"'/bin' $PATH + +# Prompt override provided? +# If not, just use the environment name. +if test -n '' + set -gx VIRTUAL_ENV_PROMPT '' +else + set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV") +end + +# Unset `$PYTHONHOME` if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Run the user's prompt first; it might depend on (pipe)status. + set -l prompt (_old_fish_prompt) + + printf '(%s) ' $VIRTUAL_ENV_PROMPT + + string join -- \n $prompt # handle multi-line prompts + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/crates/gourgeist/src/activator/activate.nu b/crates/gourgeist/src/activator/activate.nu new file mode 100644 index 000000000..1060b8161 --- /dev/null +++ b/crates/gourgeist/src/activator/activate.nu @@ -0,0 +1,96 @@ +# virtualenv activation module +# Activate with `overlay use activate.nu` +# Deactivate with `deactivate`, as usual +# +# To customize the overlay name, you can call `overlay use activate.nu as foo`, +# but then simply `deactivate` won't work because it is just an alias to hide +# the "activate" overlay. You'd need to call `overlay hide foo` manually. + +export-env { + def is-string [x] { + ($x | describe) == 'string' + } + + def has-env [...names] { + $names | each {|n| + $n in $env + } | all {|i| $i == true} + } + + # Emulates a `test -z`, but btter as it handles e.g 'false' + def is-env-true [name: string] { + if (has-env $name) { + # Try to parse 'true', '0', '1', and fail if not convertible + let parsed = (do -i { $env | get $name | into bool }) + if ($parsed | describe) == 'bool' { + $parsed + } else { + not ($env | get -i $name | is-empty) + } + } else { + false + } + } + + let virtual_env = '{{ VIRTUAL_ENV_DIR }}' + let bin = 'bin' + + let is_windows = ($nu.os-info.family) == 'windows' + let path_name = (if (has-env 'Path') { + 'Path' + } else { + 'PATH' + } + ) + + let venv_path = ([$virtual_env $bin] | path join) + let new_path = ($env | get $path_name | prepend $venv_path) + + # If there is no default prompt, then use the env name instead + let virtual_env_prompt = (if ('' | is-empty) { + ($virtual_env | path basename) + } else { + '' + }) + + let new_env = { + $path_name : $new_path + VIRTUAL_ENV : $virtual_env + VIRTUAL_ENV_PROMPT : $virtual_env_prompt + } + + let new_env = (if (is-env-true 'VIRTUAL_ENV_DISABLE_PROMPT') { + $new_env + } else { + # Creating the new prompt for the session + let virtual_prefix = $'(char lparen)($virtual_env_prompt)(char rparen) ' + + # Back up the old prompt builder + let old_prompt_command = (if (has-env 'PROMPT_COMMAND') { + $env.PROMPT_COMMAND + } else { + '' + }) + + let new_prompt = (if (has-env 'PROMPT_COMMAND') { + if 'closure' in ($old_prompt_command | describe) { + {|| $'($virtual_prefix)(do $old_prompt_command)' } + } else { + {|| $'($virtual_prefix)($old_prompt_command)' } + } + } else { + {|| $'($virtual_prefix)' } + }) + + $new_env | merge { + PROMPT_COMMAND : $new_prompt + VIRTUAL_PREFIX : $virtual_prefix + } + }) + + # Environment variables that will be loaded as the virtual env + load-env $new_env +} + +export alias pydoc = python -m pydoc +export alias deactivate = overlay hide activate diff --git a/crates/gourgeist/src/activator/activate.ps1 b/crates/gourgeist/src/activator/activate.ps1 new file mode 100644 index 000000000..47d185320 --- /dev/null +++ b/crates/gourgeist/src/activator/activate.ps1 @@ -0,0 +1,61 @@ +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global + } + + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue + } + + if ($env:VIRTUAL_ENV_PROMPT) { + Remove-Item env:VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue + } + + if (!$NonDestructive) { + # Self destruct! + Remove-Item function:deactivate + Remove-Item function:pydoc + } +} + +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +if ("" -ne "") { + $env:VIRTUAL_ENV_PROMPT = "" +} +else { + $env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf ) +} + +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH + +$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("(" + $env:VIRTUAL_ENV_PROMPT + ") " + $previous_prompt_value) + } +} diff --git a/crates/gourgeist/src/activator/activate_this.py b/crates/gourgeist/src/activator/activate_this.py new file mode 100644 index 000000000..9807b0288 --- /dev/null +++ b/crates/gourgeist/src/activator/activate_this.py @@ -0,0 +1,36 @@ +""" +Activate virtualenv for current interpreter: + +Use exec(open(this_file).read(), {'__file__': this_file}). + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" # noqa: D415 +from __future__ import annotations + +import os +import site +import sys + +try: + abs_file = os.path.abspath(__file__) +except NameError as exc: + msg = "You must use exec(open(this_file).read(), {'__file__': this_file}))" + raise AssertionError(msg) from exc + +bin_dir = os.path.dirname(abs_file) +base = bin_dir[: -len("bin") - 1] # strip away the bin part from the __file__, plus the path separator + +# prepend bin to PATH (this file is inside the bin directory) +os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)]) +os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory +os.environ["VIRTUAL_ENV_PROMPT"] = "" or os.path.basename(base) # noqa: SIM222 + +# add the virtual environments libraries to the host python import mechanism +prev_length = len(sys.path) +for lib in "{{ RELATIVE_SITE_PACKAGES }}".split(os.pathsep): + path = os.path.realpath(os.path.join(bin_dir, lib)) + site.addsitedir(path.decode("utf-8") if "" else path) +sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length] + +sys.real_prefix = sys.prefix +sys.prefix = base diff --git a/crates/gourgeist/src/bare.rs b/crates/gourgeist/src/bare.rs new file mode 100644 index 000000000..73887e129 --- /dev/null +++ b/crates/gourgeist/src/bare.rs @@ -0,0 +1,166 @@ +//! Create a bare virtualenv without any packages install + +use crate::interpreter::InterpreterInfo; +use camino::{Utf8Path, Utf8PathBuf}; +use fs_err as fs; +#[cfg(unix)] +use fs_err::os::unix::fs::symlink; +use fs_err::File; +use std::io; +use std::io::{BufWriter, Write}; +use tracing::info; + +/// The bash activate scripts with the venv dependent paths patches out +const ACTIVATE_TEMPLATES: &[(&str, &str)] = &[ + ("activate", include_str!("activator/activate")), + ("activate.csh", include_str!("activator/activate.csh")), + ("activate.fish", include_str!("activator/activate.fish")), + ("activate.nu", include_str!("activator/activate.nu")), + ("activate.ps1", include_str!("activator/activate.ps1")), + ( + "activate_this.py", + include_str!("activator/activate_this.py"), + ), +]; +const VIRTUALENV_PATCH: &str = include_str!("_virtualenv.py"); + +/// Very basic `.cfg` file format writer. +fn write_cfg(f: &mut impl Write, data: &[(&str, String); 8]) -> io::Result<()> { + for (key, value) in data { + writeln!(f, "{} = {}", key, value)?; + } + Ok(()) +} + +/// Absolute paths of the virtualenv +#[derive(Debug)] +pub struct VenvPaths { + /// The location of the virtualenv, e.g. `.venv` + pub root: Utf8PathBuf, + /// The python interpreter.rs inside the virtualenv, on unix `.venv/bin/python` + pub interpreter: Utf8PathBuf, + /// The directory with the scripts, on unix `.venv/bin` + pub bin: Utf8PathBuf, + /// The site-packages directory where all the packages are installed to, on unix + /// and python 3.11 `.venv/lib/python3.11/site-packages` + pub site_packages: Utf8PathBuf, +} + +/// Write all the files that belong to a venv without any packages installed. +pub fn create_bare_venv( + location: &Utf8Path, + base_python: &Utf8Path, + info: &InterpreterInfo, +) -> io::Result { + if location.exists() { + if location.join("pyvenv.cfg").is_file() { + info!("Removing existing directory"); + fs::remove_dir_all(location)?; + } else { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + format!("The directory {location} exists, but it is not virtualenv"), + )); + } + } + fs::create_dir_all(location)?; + // TODO: I bet on windows we'll have to strip the prefix again + let location = location.canonicalize_utf8()?; + let bin_dir = { + #[cfg(unix)] + { + location.join("bin") + } + #[cfg(windows)] + { + location.join("Bin") + } + #[cfg(not(any(unix, windows)))] + { + compile_error!("only unix (like mac and linux) and windows are supported") + } + }; + + fs::write(location.join(".gitignore"), "*")?; + + // Different names for the python interpreter + fs::create_dir(&bin_dir)?; + let venv_python = { + #[cfg(unix)] + { + bin_dir.join("python") + } + #[cfg(windows)] + { + bin_dir.join("python.exe") + } + #[cfg(not(any(unix, windows)))] + { + compile_error!("only unix (like mac and linux) and windows are supported") + } + }; + #[cfg(unix)] + { + symlink(base_python, &venv_python)?; + symlink("python", bin_dir.join(format!("python{}", info.major)))?; + symlink( + "python", + bin_dir.join(format!("python{}.{}", info.major, info.minor)), + )?; + } + + // Add all the activate scripts for different shells + for (name, template) in ACTIVATE_TEMPLATES { + let activator = template + .replace("{{ VIRTUAL_ENV_DIR }}", location.as_str()) + .replace( + "{{ RELATIVE_SITE_PACKAGES }}", + &format!("../lib/python{}.{}/site-packages", info.major, info.minor), + ); + fs::write(bin_dir.join(name), activator)?; + } + + // pyvenv.cfg + let python_home = base_python + .parent() + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "The python interpreter needs to have a parent directory", + ) + })? + .to_string(); + let pyvenv_cfg_data = &[ + ("home", python_home), + ("implementation", "CPython".to_string()), + ("version_info", info.python_version.clone()), + ("gourgeist", env!("CARGO_PKG_VERSION").to_string()), + // I wouldn't allow this option anyway + ("include-system-site-packages", "false".to_string()), + ("base-prefix", info.base_prefix.clone()), + ("base-exec-prefix", info.base_exec_prefix.clone()), + ("base-executable", base_python.to_string()), + ]; + let mut pyvenv_cfg = BufWriter::new(File::create(location.join("pyvenv.cfg"))?); + write_cfg(&mut pyvenv_cfg, pyvenv_cfg_data)?; + drop(pyvenv_cfg); + + // TODO: This is different on windows + let site_packages = location + .join("lib") + .join(format!("python{}.{}", info.major, info.minor)) + .join("site-packages"); + fs::create_dir_all(&site_packages)?; + // Install _virtualenv.py patch. + // Frankly no idea what that does, i just copied it from virtualenv knowing that + // distutils/setuptools will have their cursed reasons + fs::write(site_packages.join("_virtualenv.py"), VIRTUALENV_PATCH)?; + fs::write(site_packages.join("_virtualenv.pth"), "import _virtualenv")?; + + Ok(VenvPaths { + root: location.to_path_buf(), + interpreter: venv_python, + bin: bin_dir, + site_packages, + }) +} diff --git a/crates/gourgeist/src/interpreter.rs b/crates/gourgeist/src/interpreter.rs new file mode 100644 index 000000000..fc09d10fa --- /dev/null +++ b/crates/gourgeist/src/interpreter.rs @@ -0,0 +1,196 @@ +use crate::{crate_cache_dir, Error}; +use camino::{FromPathBufError, Utf8Path, Utf8PathBuf}; +use fs_err as fs; +use fs_err::File; +use serde::{Deserialize, Serialize}; +use std::io; +use std::io::{BufReader, Write}; +use std::process::{Command, Stdio}; +use std::time::SystemTime; +use tracing::{debug, error, info, warn}; + +const QUERY_PYTHON: &str = include_str!("query_python.py"); + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct InterpreterInfo { + pub base_exec_prefix: String, + pub base_prefix: String, + pub major: u8, + pub minor: u8, + pub python_version: String, +} + +/// Gets the interpreter.rs info, either cached or by running it. +pub fn get_interpreter_info(interpreter: &Utf8Path) -> Result { + let cache_dir = crate_cache_dir()?.join("interpreter_info"); + + let index = seahash::hash(interpreter.as_str().as_bytes()); + let cache_file = cache_dir.join(index.to_string()).with_extension("json"); + + let modified = fs::metadata(interpreter)? + .modified()? + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_millis(); + + if cache_file.exists() { + let cache_entry: Result = File::open(&cache_file) + .map_err(|err| err.to_string()) + .and_then(|cache_reader| { + serde_json::from_reader(BufReader::new(cache_reader)).map_err(|err| err.to_string()) + }); + match cache_entry { + Ok(cache_entry) => { + debug!("Using cache entry {cache_file}"); + if modified == cache_entry.modified && interpreter == cache_entry.interpreter { + return Ok(cache_entry.interpreter_info); + } else { + debug!( + "Removing mismatching cache entry {cache_file} ({} {} {} {})", + modified, cache_entry.modified, interpreter, cache_entry.interpreter + ); + if let Err(remove_err) = fs::remove_file(&cache_file) { + warn!("Failed to mismatching cache file at {cache_file}: {remove_err}") + } + } + } + Err(cache_err) => { + debug!("Removing broken cache entry {cache_file} ({cache_err})"); + if let Err(remove_err) = fs::remove_file(&cache_file) { + warn!("Failed to remove broken cache file at {cache_file}: {remove_err} (original error: {cache_err})") + } + } + } + } + + let interpreter_info = query_interpreter(interpreter)?; + fs::create_dir_all(&cache_dir)?; + let cache_entry = CacheEntry { + interpreter: interpreter.to_path_buf(), + modified, + interpreter_info: interpreter_info.clone(), + }; + let mut cache_writer = File::create(&cache_file)?; + serde_json::to_writer(&mut cache_writer, &cache_entry).map_err(io::Error::from)?; + + Ok(interpreter_info) +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +struct CacheEntry { + interpreter: Utf8PathBuf, + modified: u128, + interpreter_info: InterpreterInfo, +} + +/// Runs a python script that returns the relevant info about the interpreter.rs as json +fn query_interpreter(interpreter: &Utf8Path) -> Result { + let mut child = Command::new(interpreter) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + if let Some(mut stdin) = child.stdin.take() { + stdin + .write_all(QUERY_PYTHON.as_bytes()) + .map_err(|err| Error::PythonSubcommand { + interpreter: interpreter.to_path_buf(), + err, + })?; + } + let output = child.wait_with_output()?; + let stdout = String::from_utf8(output.stdout).unwrap_or_else(|err| { + // At this point, there was most likely an error caused by a non-utf8 character, so we're in + // an ugly case but still very much want to give the user a chance + error!( + "The stdout of the failed call of the call to {} contains non-utf8 characters", + interpreter + ); + String::from_utf8_lossy(err.as_bytes()).to_string() + }); + let stderr = String::from_utf8(output.stderr).unwrap_or_else(|err| { + error!( + "The stderr of the failed call of the call to {} contains non-utf8 characters", + interpreter + ); + String::from_utf8_lossy(err.as_bytes()).to_string() + }); + // stderr isn't technically a criterion for success, but i don't know of any cases where there + // should be stderr output and if there is, we want to know + if !output.status.success() || !stderr.trim().is_empty() { + return Err(Error::PythonSubcommand { + interpreter: interpreter.to_path_buf(), + err: io::Error::new( + io::ErrorKind::Other, + format!( + "Querying python at {} failed with status {}:\n--- stdout:\n{}\n--- stderr:\n{}", + interpreter, + output.status, + stdout.trim(), + stderr.trim() + ), + ) + }); + } + let data = serde_json::from_str::(&stdout).map_err(|err| + Error::PythonSubcommand { + interpreter: interpreter.to_path_buf(), + err: io::Error::new( + io::ErrorKind::Other, + format!( + "Querying python at {} did not return the expected data ({}):\n--- stdout:\n{}\n--- stderr:\n{}", + interpreter, + err, + stdout.trim(), + stderr.trim() + ) + ) + } + )?; + Ok(data) +} + +/// Parse the value of the `-p`/`--python` option, which can be e.g. `3.11`, `python3.11`, +/// `tools/bin/python3.11` or `/usr/bin/python3.11`. +pub fn parse_python_cli(cli_python: Option) -> Result { + let python = if let Some(python) = cli_python { + if let Some((major, minor)) = python + .as_str() + .split_once('.') + .and_then(|(major, minor)| Some((major.parse::().ok()?, minor.parse::().ok()?))) + { + if major != 3 { + return Err(crate::Error::InvalidPythonInterpreter( + "Only python 3 is supported".into(), + )); + } + info!("Looking for python {major}.{minor}"); + Utf8PathBuf::from(format!("python{major}.{minor}")) + } else { + python + } + } else { + Utf8PathBuf::from("python3".to_string()) + }; + + // Call `which` to find it in path, if not given a path + let python = if python.components().count() > 1 { + // Does this path contain a slash (unix) or backslash (windows)? In that case, assume it's + // relative or absolute path that we don't need to resolve + info!("Assuming {python} is a path"); + python + } else { + let python_in_path = which::which(python.as_std_path()) + .map_err(|err| { + crate::Error::InvalidPythonInterpreter( + format!("Can't find {python} ({err})").into(), + ) + })? + .try_into() + .map_err(|err: FromPathBufError| err.into_io_error())?; + info!("Resolved {python} to {python_in_path}"); + python_in_path + }; + Ok(python) +} diff --git a/crates/gourgeist/src/lib.rs b/crates/gourgeist/src/lib.rs new file mode 100644 index 000000000..c24c075c4 --- /dev/null +++ b/crates/gourgeist/src/lib.rs @@ -0,0 +1,85 @@ +use crate::bare::create_bare_venv; +use camino::{Utf8Path, Utf8PathBuf}; +use dirs::cache_dir; +use interpreter::InterpreterInfo; +use std::io; +use tempfile::PersistError; +use thiserror::Error; + +pub use interpreter::{get_interpreter_info, parse_python_cli}; + +mod bare; +mod interpreter; +#[cfg(feature = "install")] +mod packages; +#[cfg(not(feature = "install"))] +mod virtualenv_cache; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + IO(#[from] io::Error), + /// It's effectively an io error with extra info + #[error(transparent)] + Persist(#[from] PersistError), + /// Adds url and target path to the io error + #[error("Failed to download wheel from {url} to {path}")] + WheelDownload { + url: String, + path: Utf8PathBuf, + #[source] + err: io::Error, + }, + #[error("Failed to determine python interpreter to use")] + InvalidPythonInterpreter(#[source] Box), + #[error("Failed to query python interpreter at {interpreter}")] + PythonSubcommand { + interpreter: Utf8PathBuf, + #[source] + err: io::Error, + }, + #[cfg(feature = "install")] + #[error("Failed to contact pypi")] + MinReq(#[from] minreq::Error), + #[cfg(feature = "install")] + #[error("Failed to install {package}")] + InstallWheel { + package: String, + #[source] + err: install_wheel_rs::Error, + }, +} + +pub(crate) fn crate_cache_dir() -> io::Result { + Ok(cache_dir() + .and_then(|path| Utf8PathBuf::from_path_buf(path).ok()) + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Couldn't detect cache dir"))? + .join(env!("CARGO_PKG_NAME"))) +} + +/// Create a virtualenv and if not bare, install `wheel`, `pip` and `setuptools`. +pub fn create_venv( + location: &Utf8Path, + base_python: &Utf8Path, + info: &InterpreterInfo, + bare: bool, +) -> Result<(), Error> { + let paths = create_bare_venv(location, base_python, info)?; + + if !bare { + #[cfg(feature = "install")] + { + packages::install_base_packages(location, info, &paths)?; + } + #[cfg(not(feature = "install"))] + { + virtualenv_cache::install_base_packages( + &paths.bin, + &paths.interpreter, + &paths.site_packages, + )?; + } + } + + Ok(()) +} diff --git a/crates/gourgeist/src/main.rs b/crates/gourgeist/src/main.rs new file mode 100644 index 000000000..c7ce72543 --- /dev/null +++ b/crates/gourgeist/src/main.rs @@ -0,0 +1,51 @@ +use camino::Utf8PathBuf; +use clap::Parser; +use gourgeist::{create_venv, get_interpreter_info, parse_python_cli}; +use std::error::Error; +use std::process::ExitCode; +use std::time::Instant; +use tracing::info; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::{fmt, EnvFilter}; + +#[derive(Parser, Debug)] +struct Cli { + path: Option, + #[clap(short, long)] + python: Option, + #[clap(long)] + bare: bool, +} + +fn run() -> Result<(), gourgeist::Error> { + let cli = Cli::parse(); + let location = cli.path.unwrap_or(Utf8PathBuf::from(".venv")); + let python = parse_python_cli(cli.python)?; + let data = get_interpreter_info(&python)?; + create_venv(&location, &python, &data, cli.bare)?; + + Ok(()) +} + +fn main() -> ExitCode { + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); + + let start = Instant::now(); + let result = run(); + info!("Took {}ms", start.elapsed().as_millis()); + if let Err(err) = result { + eprintln!("💥 virtualenv creator failed"); + let mut last_error: Option<&(dyn Error + 'static)> = Some(&err); + while let Some(err) = last_error { + eprintln!(" Caused by: {}", err); + last_error = err.source(); + } + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + } +} diff --git a/crates/gourgeist/src/packages.rs b/crates/gourgeist/src/packages.rs new file mode 100644 index 000000000..d71c8261f --- /dev/null +++ b/crates/gourgeist/src/packages.rs @@ -0,0 +1,89 @@ +use crate::bare::VenvPaths; +use crate::interpreter::InterpreterInfo; +use crate::{crate_cache_dir, Error}; +use camino::{FromPathBufError, Utf8Path, Utf8PathBuf}; +use fs_err as fs; +use fs_err::File; +use install_wheel_rs::{install_wheel, InstallLocation, WheelFilename}; +#[cfg(feature = "parallel")] +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::io; +use std::io::BufWriter; +use std::str::FromStr; +use tempfile::NamedTempFile; +use tracing::info; + +pub fn download_wheel_cached(filename: &str, url: &str) -> Result { + let wheels_cache = crate_cache_dir()?.join("wheels"); + let cached_wheel = wheels_cache.join(filename); + if cached_wheel.is_file() { + info!("Using cached wheel at {cached_wheel}"); + return Ok(cached_wheel); + } + + info!("Downloading wheel from {url} to {cached_wheel}"); + fs::create_dir_all(&wheels_cache)?; + let mut tempfile = NamedTempFile::new_in(wheels_cache)?; + let tempfile_path: Utf8PathBuf = tempfile + .path() + .to_path_buf() + .try_into() + .map_err(|err: FromPathBufError| err.into_io_error())?; + let mut response = minreq::get(url).send_lazy()?; + io::copy(&mut response, &mut BufWriter::new(&mut tempfile)).map_err(|err| { + Error::WheelDownload { + url: url.to_string(), + path: tempfile_path.to_path_buf(), + err, + } + })?; + tempfile.persist(&cached_wheel)?; + Ok(cached_wheel) +} + +/// Install pip, setuptools and wheel from cache pypi with atm fixed wheels +pub fn install_base_packages( + location: &Utf8Path, + info: &InterpreterInfo, + paths: &VenvPaths, +) -> Result<(), Error> { + let install_location = InstallLocation::Venv { + venv_base: location.canonicalize()?, + python_version: (info.major, info.minor), + }; + let install_location = install_location.acquire_lock()?; + + // TODO: Use the json api instead + // TODO: Only check the json API so often (monthly? daily?) + let packages = [ + ("pip-23.2.1-py3-none-any.whl", "https://files.pythonhosted.org/packages/50/c2/e06851e8cc28dcad7c155f4753da8833ac06a5c704c109313b8d5a62968a/pip-23.2.1-py3-none-any.whl"), + ("setuptools-68.2.2-py3-none-any.whl", "https://files.pythonhosted.org/packages/bb/26/7945080113158354380a12ce26873dd6c1ebd88d47f5bc24e2c5bb38c16a/setuptools-68.2.2-py3-none-any.whl"), + ("wheel-0.41.2-py3-none-any.whl", "https://files.pythonhosted.org/packages/b8/8b/31273bf66016be6ad22bb7345c37ff350276cfd46e389a0c2ac5da9d9073/wheel-0.41.2-py3-none-any.whl"), + ]; + #[cfg(feature = "rayon")] + let iterator = packages.into_par_iter(); + #[cfg(not(feature = "rayon"))] + let iterator = packages.into_iter(); + iterator + .map(|(filename, url)| { + let wheel_file = download_wheel_cached(filename, url)?; + let parsed_filename = WheelFilename::from_str(filename).unwrap(); + install_wheel( + &install_location, + File::open(wheel_file)?, + parsed_filename, + false, + &[], + // Only relevant for monotrail style installation + "", + paths.interpreter.as_std_path(), + ) + .map_err(|err| Error::InstallWheel { + package: filename.to_string(), + err, + })?; + Ok(()) + }) + .collect::, Error>>()?; + Ok(()) +} diff --git a/crates/gourgeist/src/query_python.py b/crates/gourgeist/src/query_python.py new file mode 100644 index 000000000..f71eab275 --- /dev/null +++ b/crates/gourgeist/src/query_python.py @@ -0,0 +1,18 @@ +import json +import sys +from platform import python_version + + +def main(): + data = { + "base_exec_prefix": sys.base_exec_prefix, + "base_prefix": sys.base_prefix, + "major": sys.version_info.major, + "minor": sys.version_info.minor, + "python_version": python_version(), + } + print(json.dumps(data)) + + +if __name__ == "__main__": + main() diff --git a/crates/gourgeist/src/virtualenv_cache.rs b/crates/gourgeist/src/virtualenv_cache.rs new file mode 100644 index 000000000..8e8c5cde3 --- /dev/null +++ b/crates/gourgeist/src/virtualenv_cache.rs @@ -0,0 +1,109 @@ +//! Deprecated, use only as template when implementing caching + +use crate::Error; +use camino::{Utf8Path, Utf8PathBuf}; +use dirs::data_dir; +use fs_err as fs; +use std::io; +use std::path::Path; +use tracing::debug; + +/// Install wheel, pip and setuptools from the cache +pub(crate) fn install_base_packages( + bin_dir: &Utf8Path, + venv_python: &Utf8Path, + site_packages: &Utf8Path, +) -> Result<(), Error> { + // Install packages + // TODO: Implement our own logic: + // * Our own cache and logic to detect whether a wheel is present + // * Check if the version is recent (e.g. update if older than 1 month) + // * Query pypi API if no, parse versions (pep440) and their metadata + // * Download compatible wheel (py3-none-any should do) + // * Install into the cache directory + let prefix = "virtualenv/wheel/3.11/image/1/CopyPipInstall/"; + let wheel_tag = "py3-none-any"; + let packages = &[ + ("pip", "23.2.1"), + ("setuptools", "68.2.2"), + ("wheel", "0.41.2"), + ]; + let virtualenv_data_dir: Utf8PathBuf = data_dir().unwrap().try_into().unwrap(); + for (name, version) in packages { + // TODO: acquire lock + let unpacked_wheel = virtualenv_data_dir + .join(prefix) + .join(format!("{name}-{version}-{wheel_tag}")); + debug!("Installing {name} by copying from {unpacked_wheel}"); + copy_dir_all(&unpacked_wheel, site_packages.as_std_path())?; + + // Generate launcher + // virtualenv for some reason creates extra entrypoints that we don't + // https://github.com/pypa/virtualenv/blob/025e96fbad37f85617364002ae2a0064b09fc984/src/virtualenv/seed/embed/via_app_data/pip_install/base.py#L74-L95 + let ini_text = fs::read_to_string( + site_packages + .join(format!("{name}-{version}.dist-info")) + .join("entry_points.txt"), + )?; + let entry_points_mapping = configparser::ini::Ini::new_cs() + .read(ini_text) + .map_err(|err| format!("{name} entry_points.txt is invalid: {}", err)) + .unwrap(); + for (key, value) in entry_points_mapping + .get("console_scripts") + .cloned() + .unwrap_or_default() + { + let (import_from, function) = value + .as_ref() + .and_then(|value| value.split_once(':')) + .ok_or_else(|| { + format!("{name} entry_points.txt {key} has an invalid value {value:?}") + }) + .unwrap(); + let launcher = bin_dir.join(key); + let launcher_script = unix_launcher_script(venv_python, import_from, function); + fs::write(&launcher, launcher_script)?; + // We need to make the launcher executable + #[cfg(target_family = "unix")] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(launcher, std::fs::Permissions::from_mode(0o755))?; + } + } + } + Ok(()) +} + +/// https://stackoverflow.com/a/65192210/3549270 +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src.as_ref())? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +/// Template for the console scripts in the `bin` directory +pub fn unix_launcher_script(python: &Utf8Path, import_from: &str, function: &str) -> String { + format!( + r#"#!{python} + # -*- coding: utf-8 -*- +import re +import sys +from {import_from} import {function} +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit({function}()) +"#, + python = python, + import_from = import_from, + function = function + ) +} diff --git a/crates/gourgeist/static/gourgeist.png b/crates/gourgeist/static/gourgeist.png new file mode 100644 index 000000000..ae002915b Binary files /dev/null and b/crates/gourgeist/static/gourgeist.png differ diff --git a/crates/gourgeist/venv_checker.py b/crates/gourgeist/venv_checker.py new file mode 100644 index 000000000..839c48ba6 --- /dev/null +++ b/crates/gourgeist/venv_checker.py @@ -0,0 +1,32 @@ +from pathlib import Path +from subprocess import check_output, check_call + + +def main(): + project_root = Path(__file__).parent + venv_name = ".venv-rs" + venv_python = f"{venv_name}/bin/python" + venv_pip = f"{venv_name}/bin/pip" + + command = f". {venv_name}/bin/activate && which python" + output = check_output(["bash"], input=command, text=True).strip() + assert output == str(project_root.joinpath(venv_python)), output + + command = f". {venv_name}/bin/activate && wheel help" + output = check_output(["bash"], input=command, text=True).strip() + assert output.startswith("usage:"), output + + output = ( + check_output([venv_python, "imasnake.py"], text=True) + .strip() + .splitlines() + ) + assert output[0] == str(project_root.joinpath(venv_python)), output + assert not output[2].startswith(str(project_root)), output + assert output[3] == str(project_root.joinpath(venv_name)), output + + check_call([venv_pip, "install", "tqdm"]) + + +if __name__ == "__main__": + main()