Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
609bc41e6f | ||
|
|
89999119dc | ||
|
|
71043329f1 | ||
|
|
ecaf293c4f | ||
|
|
09cb1fbca9 | ||
|
|
23620d0b7a | ||
|
|
351dbdfdca | ||
|
|
128159a717 | ||
|
|
33f713fa5d | ||
|
|
f92aaa9f59 | ||
|
|
e70bbff278 | ||
|
|
d2f8263fb8 | ||
|
|
15421962e6 | ||
|
|
a10fd676f6 | ||
|
|
bc506e4f67 | ||
|
|
f156a27cf4 | ||
|
|
7166c00efc |
@@ -1,14 +0,0 @@
|
||||
[target.'cfg(true)']
|
||||
rustflags = [
|
||||
# rustc will pass `-nodefaultlibs` without this, but we need the C++ standard library.
|
||||
'-Cdefault-linker-libraries=yes',
|
||||
]
|
||||
|
||||
[target.'cfg(target_env = "musl")']
|
||||
rustflags = [
|
||||
'-Cdefault-linker-libraries=yes',
|
||||
# musl, at least in Nixpkgs, is not compiled with -fPIE.
|
||||
# XXX: nevermind? as of Nixpkgs 26.05??
|
||||
# Oh gods do we need to gate this??
|
||||
#'-Crelocation-model=static',
|
||||
]
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
/build
|
||||
/outputs
|
||||
outputs/
|
||||
|
||||
# GNU Global
|
||||
GPATH
|
||||
|
||||
Generated
+4
-878
@@ -2,320 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ar_archive_writer"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348"
|
||||
dependencies = [
|
||||
"object",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"askama_escape",
|
||||
"humansize",
|
||||
"num-traits",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
|
||||
dependencies = [
|
||||
"askama_parser",
|
||||
"basic-toml",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_escape"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
||||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[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 = "cc"
|
||||
version = "1.2.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "1.0.0-alpha.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e82d74e6c83060ec269fe9e0d408d6de4a1645d525f9a0bbbb841ba4efd91ac"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
"regex-automata 0.3.9",
|
||||
"serde",
|
||||
"stacker",
|
||||
"unicode-ident",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "countme"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[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 = "dissimilar"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "869b0adbda23651a9c5c0c3d270aac9fcb52e8622a8f2b17e57802d7791962f2"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||
|
||||
[[package]]
|
||||
name = "expect-test"
|
||||
version = "1.5.0"
|
||||
@@ -326,139 +24,12 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[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 = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.17.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "licxxbridge"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"zngur",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lix"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"lix-doc",
|
||||
"pkg-config",
|
||||
"regex",
|
||||
"rootcause",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
"zngur",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lix-doc"
|
||||
version = "0.0.1"
|
||||
@@ -469,87 +40,8 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
name = "lixutil-rs"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@@ -557,108 +49,6 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea"
|
||||
dependencies = [
|
||||
"ar_archive_writer",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b4431027dcd37fc2a73ef740b5f233aa805897935b8bce0195e41bbf9a3289a"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.14",
|
||||
"regex-syntax 0.8.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rnix"
|
||||
version = "0.12.0"
|
||||
@@ -668,28 +58,6 @@ dependencies = [
|
||||
"rowan",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rootcause"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b660d9968fae12f4e691f2b2be5d9a3a6de875300c682e8d2cb89a618dd60875"
|
||||
dependencies = [
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"rootcause-internals",
|
||||
"rustc-hash 2.1.3",
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rootcause-internals"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0184f6fcff3b58b7c963aee6e3cc915c04331aa6eef974f79d7d44d21e246c24"
|
||||
dependencies = [
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rowan"
|
||||
version = "0.15.16"
|
||||
@@ -697,8 +65,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d"
|
||||
dependencies = [
|
||||
"countme",
|
||||
"hashbrown 0.14.5",
|
||||
"rustc-hash 1.1.0",
|
||||
"hashbrown",
|
||||
"rustc-hash",
|
||||
"text-size",
|
||||
]
|
||||
|
||||
@@ -708,250 +76,8 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "18.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53f6a737db68eb1a8ccff86b584b2fc13eca6a7bb6f78ebc7c529547e3ab9684"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.2.2",
|
||||
"utf8parse",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline-derive"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e5587417a3c4e16a4415e8d7d07f80998ed835ade621d19dfbe9fbe3205b0f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "text-size"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b40688ea6389c8171614b25491f71d4a27946e0c7ce2da1c6de27e25abf1a0ae"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "zngur"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc912d12934b4d04aabc52c14db6fc88ae8aa5a47902f27e8759c2de254723f"
|
||||
dependencies = [
|
||||
"zngur-generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zngur-def"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f27e49a62db537cea43a6c122ded7eda99c4ac0ca1d7cfd74f2bf6a6c88712d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zngur-generator"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffad8c6994c477023aba7613bdf17c470003bbd191846d3096e84ffecf86d4c"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"sha2",
|
||||
"zngur-def",
|
||||
"zngur-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zngur-parser"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d66c1b85ca6eab9576df5de31758cb1f9046b4ce47b25f45dabcb0fc7ef8789"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
"itertools",
|
||||
"zngur-def",
|
||||
]
|
||||
|
||||
+1
-23
@@ -1,28 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"lix/lix-doc",
|
||||
"lix/lix-rs",
|
||||
"tools/licxxbridge",
|
||||
]
|
||||
members = ["lix/lix-doc", "lix/libutil"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
clap = "4"
|
||||
regex = "1.12.4"
|
||||
rootcause = "0.13.0"
|
||||
rustyline = "18"
|
||||
rustyline-derive = "0.12"
|
||||
syn = "2.0"
|
||||
zngur = "0.10"
|
||||
pkg-config = "0.3.33"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
||||
[profile.release]
|
||||
debug = "full"
|
||||
debug-assertions = true
|
||||
overflow-checks = true
|
||||
|
||||
+1
-2
@@ -10,7 +10,6 @@ import platform
|
||||
import shlex
|
||||
import textwrap
|
||||
import dataclasses
|
||||
from pathlib import Path
|
||||
|
||||
flake_args = ["--extra-experimental-features", "nix-command flakes"]
|
||||
cases = {
|
||||
@@ -19,7 +18,7 @@ cases = {
|
||||
*flake_args,
|
||||
"search",
|
||||
"--no-eval-cache",
|
||||
f"path:{Path('./bench/nixpkgs/').readlink()}",
|
||||
"github:nixos/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870",
|
||||
"hello",
|
||||
],
|
||||
"rebuild": lambda build: [
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
# Darwin: don't link liblix* into plugins (host process provides them at runtime).
|
||||
# Explicitly link curl so it binds to Nix-store libcurl, not /usr/lib/libcurl.
|
||||
if is_darwin
|
||||
plugin_deps = [
|
||||
liblix.partial_dependency(includes : true, compile_args : true),
|
||||
curl,
|
||||
]
|
||||
else
|
||||
plugin_deps = [liblix, curl]
|
||||
endif
|
||||
|
||||
plugin_mtls_store = shared_module(
|
||||
'plugin_mtls_store',
|
||||
'plugin_mtls_store.cc',
|
||||
# don't link liblix* into plugins (host process provides them at runtime).
|
||||
# Explicitly link curl so it binds to Nix-store libcurl, not /usr/lib/libcurl.
|
||||
dependencies : [
|
||||
liblix.partial_dependency(includes : true, compile_args : true),
|
||||
curl,
|
||||
],
|
||||
dependencies : plugin_deps,
|
||||
install : false,
|
||||
build_by_default : true,
|
||||
link_args : plugin_link_args,
|
||||
link_args : is_darwin ? shared_module_link_args : strict_shared_module_link_args,
|
||||
)
|
||||
|
||||
@@ -24,7 +24,8 @@ def map_contents_recursively(transformer):
|
||||
def process_command:
|
||||
.[0] as $context |
|
||||
.[1] as $body |
|
||||
$body | .items |= map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end))
|
||||
# XXX FUTURE: drop sections once mdBook is at 0.5.0 or above in nixpkgs
|
||||
$body | (.items? // .sections) |= map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end))
|
||||
;
|
||||
|
||||
process_command
|
||||
|
||||
@@ -31,11 +31,12 @@ command = "jq --from-file anchors.jq"
|
||||
|
||||
[output.markdown]
|
||||
|
||||
[output.linkcheck2]
|
||||
# XXX FUTURE: may be reenabled once mdBook 0.5.0 or above and matching mdbook-linkchecker are in nixpkgs
|
||||
#[output.linkcheck]
|
||||
# no Internet during the build (in the sandbox)
|
||||
follow-web-links = false
|
||||
#follow-web-links = false
|
||||
|
||||
# mdbook-linkcheck does not understand [foo]{#bar} style links, resulting in
|
||||
# excessive "Potential incomplete link" warnings. No other kind of warning was
|
||||
# produced at the time of writing.
|
||||
warning-policy = "ignore"
|
||||
#warning-policy = "ignore"
|
||||
|
||||
@@ -48,11 +48,6 @@ artemist:
|
||||
display_name: Artemis Tosini
|
||||
forgejo: artemist
|
||||
|
||||
astreaprtcl:
|
||||
display_name: Astreaprtcl
|
||||
forgejo: astreaprtcl
|
||||
github: astreaprtcl
|
||||
|
||||
bb010g:
|
||||
display_name: Dusk Banks
|
||||
forgejo: bb010g
|
||||
@@ -62,10 +57,6 @@ blitz:
|
||||
display_name: Julian Stecklina
|
||||
github: blitz
|
||||
|
||||
blokyk:
|
||||
display_name: blokyk
|
||||
github: blokyk
|
||||
|
||||
cole-h:
|
||||
display_name: Cole Helbling
|
||||
github: cole-h
|
||||
@@ -150,9 +141,6 @@ kasimeka:
|
||||
forgejo: janw4ld
|
||||
github: kasimeka
|
||||
|
||||
keysmashes:
|
||||
github: keysmashes
|
||||
|
||||
kfears:
|
||||
display_name: KFears
|
||||
forgejo: kfearsoff
|
||||
@@ -256,11 +244,6 @@ raito:
|
||||
forgejo: raito
|
||||
github: RaitoBezarius
|
||||
|
||||
rkjnsn:
|
||||
display_name: Erik Jensen
|
||||
forgejo: rkjnsn
|
||||
github: rkjnsn
|
||||
|
||||
roberth:
|
||||
display_name: Robert Hensing
|
||||
github: roberth
|
||||
@@ -275,10 +258,6 @@ sandydoo:
|
||||
seppel3210:
|
||||
github: Seppel3210
|
||||
|
||||
sterni:
|
||||
forgejo: sterni
|
||||
github: sternenseemann
|
||||
|
||||
stevalkr:
|
||||
github: stevalkr
|
||||
|
||||
@@ -336,8 +315,5 @@ yorickvp:
|
||||
yshui:
|
||||
github: yshui
|
||||
|
||||
ysndr:
|
||||
github: ysndr
|
||||
|
||||
zimbatm:
|
||||
github: zimbatm
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "allow setting nested attributes via `--arg`/`--argstr`"
|
||||
cls: [5338]
|
||||
category: "Features"
|
||||
credits: [ma27]
|
||||
issues: [fj#496]
|
||||
---
|
||||
|
||||
Passing `--arg config.allowUnfree true` to e.g. `nix-build` now results in `config` with value
|
||||
`{ allowUnfree = true; }` passed to the expression.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
synopsis: "check for missing ca-file or netrc-file if one is specified"
|
||||
cls: [5646]
|
||||
category: "Improvements"
|
||||
credits: [astreaprtcl]
|
||||
issues: [fj#1106]
|
||||
---
|
||||
|
||||
If the settings `ssl-cert-file` or `netrc-file` have been set by the user, check if those files actually exist and fail if they are missing.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "libexpr: allow empty attr-names in parseAttrPath if they are quoted"
|
||||
cls: [5375]
|
||||
category: "Miscellany"
|
||||
credits: [ma27]
|
||||
---
|
||||
|
||||
Empty strings are now allowed in attribute paths as consumed by e.g. `nix-build`.
|
||||
I.e. `nix-build -A 'foo."".bar'` works now.
|
||||
The quotes are necessary, i.e. `nix-build -A foo..bar` will throw an error.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "don't treat tarball fetches with empty or zero hash as locked"
|
||||
cls: []
|
||||
category: "Fixes"
|
||||
credits: [horrors]
|
||||
issues: [fj#1233]
|
||||
---
|
||||
|
||||
Lix no longer treats tarball fetches with empty or zero hashes as locked.
|
||||
All such fetches are now also affected by `tarball-ttl` as a consequence.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
synopsis: builtins.floor/builtins.ceil handle out-of-range inputs correctly
|
||||
issues: [nix#12899]
|
||||
cls: [3923]
|
||||
prs: [nix#13013]
|
||||
category: "Breaking Changes"
|
||||
credits: [jade, nan-git, rootile]
|
||||
---
|
||||
Previously, `builtins.floor` and `builtins.ceil` always cast the input into a floating point value before running the operation and casting the floating point result back into an integer.
|
||||
No checks were made for precision loss in either coercing integer inputs or converting the output to an integer (and in fact in the latter case, invoked undefined behaviour).
|
||||
|
||||
Now, Lix checks for precision loss on integer input (to avoid a silent eval semantics change if we were to simply pass it through as-is) and on integer output.
|
||||
If your code fails to evaluate after this change, use `--extra-deprecated-features floor-ceil-corrupt-integers`.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
synopsis: "builtins.break doesn't break expression anymore"
|
||||
issues: [1165]
|
||||
cls: [5422]
|
||||
category: "Fixes"
|
||||
credits: [blokyk]
|
||||
---
|
||||
|
||||
Wrapping an expression in `builtins.break` used to break some builtins like
|
||||
`map` and the `is*` functions, which could modify the execution path of code
|
||||
inadvertently, made debugging nix harder than it already is, and in some cases
|
||||
even crashed the interpreter. Now, using `break` should be completely
|
||||
transparent to whatever function receives it as an input, preventing the
|
||||
above-mentioned issues.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
synopsis: "flake config warnings are now printed to stderr"
|
||||
issues: [1155]
|
||||
cls: [5379]
|
||||
category: "Fixes"
|
||||
credits: [lheckemann]
|
||||
---
|
||||
|
||||
The settings listed in a flake-config confirmation prompt are now printed to stderr rather than stdout, which allows `nix print-dev-env` to emit valid bash again even in the presence of untrusted settings.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
synopsis: "Use a lock when fetching inputs"
|
||||
issues: [1122]
|
||||
cls: [5438]
|
||||
category: "Fixes"
|
||||
credits: [lheckemann]
|
||||
---
|
||||
|
||||
Up to now, attempting to fetch the same git input from multiple processes
|
||||
concurrently when the input is not yet cached presented multiple issues:
|
||||
|
||||
- If the input was not already present, it would unnecessarily be fetched
|
||||
multiple times;
|
||||
|
||||
- Access to the fetcher cache database was contentious, and could lead to
|
||||
evaluation or flake locking failing unnecessary because the fetcher cache
|
||||
was locked.
|
||||
|
||||
We now acquire a lock on a path based on a hash of the input specification
|
||||
before accessing the fetcher db, reducing contention significantly, and
|
||||
preventing more than one process from fetching the same path at the same time.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
synopsis: "Use mimalloc for faster evaluation"
|
||||
cls: [5645]
|
||||
category: Features
|
||||
credits: [getchoo, lovesegfault]
|
||||
---
|
||||
|
||||
Lix now links with [mimalloc](https://github.com/microsoft/mimalloc),
|
||||
replacing the system's default `malloc()` for all non-GC allocations.
|
||||
|
||||
This yields a **5–12% wall-clock improvement** on evaluation workloads,
|
||||
ranging from `nix-instantiate hello` to `nix-env -qa` and full NixOS
|
||||
configurations.
|
||||
|
||||
The allocator can be disabled at build time with `-Dmimalloc=disabled`,
|
||||
or by passing the `useMimalloc = false` override to the `lix` package.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "Lix now requires lowdown 1.4.0 or later"
|
||||
issues: []
|
||||
cls: [5374]
|
||||
category: Packaging
|
||||
credits: [sterni]
|
||||
---
|
||||
|
||||
Support for linking against `lowdown < 1.4.0` has been removed from Lix since
|
||||
all supported Nixpkgs channels distribute lowdown 2.0.4 or later.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
synopsis: "nix-eval-jobs support `--apply` flag"
|
||||
cls: [5748]
|
||||
category: "Features"
|
||||
credits: [isabelroses,mic92,ysndr]
|
||||
issues: [fj#1214]
|
||||
---
|
||||
|
||||
`nix-eval-jobs` now supports the `--apply` flag. With this you can apply the
|
||||
provided function to the each derivation, the result of this function will then
|
||||
be serialized as a JSON value and stored inside `"extraValue"` key of the json
|
||||
line output.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "Fix `nix-copy-closure --include-outputs`"
|
||||
issues: [gh#5105]
|
||||
cls: [5588]
|
||||
category: "Fixes"
|
||||
credits: [rkjnsn]
|
||||
---
|
||||
|
||||
The `--include-outputs` flag for `nix-copy-closure` now works as intended.
|
||||
Previously, the option was accepted but silently ignored.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
synopsis: "Improve nix doctor"
|
||||
cls: [5316, 5317, 5318, 5319, 5320, 5768, 5829]
|
||||
category: Features
|
||||
credits: [rootile, raito]
|
||||
---
|
||||
The `nix doctor` diagnosics interface now provides a lot more useful information including, but not limited to:
|
||||
- General system information (OS, Hardware etc)
|
||||
- Nix Information like Sandbox, Version, Store, State and other directories
|
||||
- Flake registry
|
||||
- Search path Information
|
||||
- Nixpkgs provenance
|
||||
- Remote builder configuration (including remote connection)
|
||||
- fix crash when having relative Paths in PATH
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
synopsis: "Shadowing internal files through the Nix search path is now an error"
|
||||
issues: [998]
|
||||
cls: [4632, 5370]
|
||||
category: "Breaking Changes"
|
||||
credits: [thubrecht, jade, horrors]
|
||||
---
|
||||
|
||||
As Lix uses the path `<nix/fetchurl.nix>` for bootstrapping purposes, the ability to shadow it by adding `nix=/some/path` (or `/other/path` that contains a `nix` directory) to the search path is not desirable.
|
||||
|
||||
Lix 2.95 deprecated this behavior with a warning, Lix 2.96 now turns it into a hard error if the `nix-path-shadow` deprecated feature isn't enabled. This deprecated feature is slated to be removed in Lix 2.98.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
synopsis: "Remove `max-connections` store parameters for `ssh://` and `ssh-ng://` stores"
|
||||
cls: []
|
||||
category: Miscellany
|
||||
credits: [horrors]
|
||||
---
|
||||
|
||||
The `max-connections` parameter was undocumented, untested, and (in the case of `ssh`) even ignored
|
||||
entirely for remote builds. During a survey of public nixos configurations we have found *two* uses
|
||||
of `max-connections` for `ssh-ng`, and none at all for `ssh`. Since it is so rarely used but brings
|
||||
significant internal complexity that hinders improvements we have decided to remove these features.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
synopsis: "Allow moving between stack frames relative to current debugger frame"
|
||||
issues: [1156]
|
||||
cls: [5411]
|
||||
category: "Improvements"
|
||||
credits: [blokyk]
|
||||
---
|
||||
|
||||
Debugging functional programs often involve switching between a bunch of stack
|
||||
frames to get the full context of what's happening and who's calling who.
|
||||
Before this change, going up or down the stack in the nix debugger with `:st`
|
||||
meant remembering the absolute index of each stack frame, instead of their
|
||||
positions relative to one another; this got tiring *fast*.
|
||||
|
||||
Now, you can prepend `:st`'s argument with a + or - sign to indicate you want to
|
||||
move relative to the current stack frame. For example, typing `:st +3` when you
|
||||
were on frame `10` will go frame `13`; vice-versa, typing `:st -4` on frame `6`
|
||||
will go to frame `2`.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
synopsis: "Print REPL backtraces in more convenient order"
|
||||
issues: []
|
||||
cls: [5491]
|
||||
category: "Improvements"
|
||||
credits: [blokyk]
|
||||
---
|
||||
|
||||
When using the debugger, stack traces printed with the `:bt` command were
|
||||
previously printed in reverse order compared to most other situations where they
|
||||
appeared: the current stack frame would be printed at the very top, with the
|
||||
most outer frame at the bottom, meaning that you'd have to scroll up to get a
|
||||
sense of where you are.
|
||||
|
||||
With this change, the stack frames are printed such that the most relevant ones
|
||||
are immediatly visible at the bottom, just like other traces in lix (e.g.
|
||||
ones caused by errors).
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
synopsis: "invalid arguments to :st now print an error"
|
||||
cls: [5386]
|
||||
category: "Improvements"
|
||||
credits: [blokyk]
|
||||
---
|
||||
|
||||
When using the debugger, the `:st` command used to traverse the call stack would
|
||||
silently fail and put the debugger in an invalid state if the argument given to
|
||||
it wasn't a valid stack frame index.
|
||||
|
||||
This change adds an error message warning the user if the given index wasn't a
|
||||
valid frame (telling them the range of valid indices), as well as if it wasn't
|
||||
even a valid integer to begin with.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
synopsis: "REPL now uses rustyline"
|
||||
cls: [5703]
|
||||
category: "Improvements"
|
||||
credits: [horrors]
|
||||
issues: []
|
||||
---
|
||||
|
||||
The REPL now uses [rustyline](https://github.com/kkawakam/rustyline) for input processing instead
|
||||
of editline. This comes with some improvements to REPL behavior: wrapping lines no longer confuse
|
||||
the line editor, unicode is fully supported, pasting multiline expressions is noew possible, even
|
||||
undo commands are now available! We plan to improve the REPL further using these newfound powers.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
synopsis: "Hash mismatch diagnostics now work with `structuredAttrs`"
|
||||
issues: [fj#1175]
|
||||
cls: [5441]
|
||||
category: Fixes
|
||||
credits: [keysmashes]
|
||||
---
|
||||
|
||||
Nixpkgs fetchers like `fetchurl` now use `structuredAttrs`, which broke the
|
||||
hash mismatch diagnostics added in Lix 2.91. This has been fixed and the likely
|
||||
URL is now shown again.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
synopsis: "Changes to `flake.nix` validation"
|
||||
cls: [5523]
|
||||
category: "Breaking Changes"
|
||||
credits: [piegames, Qyriad, horrors]
|
||||
issues: [gh#4945]
|
||||
---
|
||||
|
||||
Flakes try to keep their inputs and metadata "simple", to make sure no unbounded computation may happen when calling e.g. `nix flake show`.
|
||||
Those checks were haphazard, a maintenance burden, and also easily circumventable.
|
||||
|
||||
Lix has now replaced all the old checks by a simple rule: **No function calls outside of `outputs`.**
|
||||
This is easier to reason about than the previous set of inconsistent rules, and crucially now also allows syntax features that users felt like they *should* have worked in the past, like let bindings.
|
||||
However, some warts still remain for now: Some syntax constructs like `-1` internally desugar to `__sub 0 1`, which is a function call and thus remains forbidden.
|
||||
This will be rectified as soon as the deprecation period of the respective anti-features has been completed.
|
||||
|
||||
This change is **breaking** in the sense that flakes which are written with the newly allowed language features will not evaluate with an older Lix version which still uses the old, more restrictive checks.
|
||||
Crucially, this also affects **all transitive dependants** of such Flakes.
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
synopsis: "Fix unsigned overflow leading to out-of-band write in the NAR parser"
|
||||
cls: [5554]
|
||||
category: "Fixes"
|
||||
credits: [horrors, raito, edef, sandydoo]
|
||||
issues: []
|
||||
---
|
||||
|
||||
The NAR parser contained an unsigned integer overflow that could be used by an
|
||||
attacker to write arbitrary data to an unknown memory location and possibly
|
||||
achieve code execution. A successful attack on the system-wide Lix daemon
|
||||
could lead to privilege escalation to root. Any process that involves NAR
|
||||
serialization could trigger this issue, including (but not limited to)
|
||||
|
||||
- local user interaction, whether the users are trusted or untrusted
|
||||
- malicious substituters sending malformed NARs
|
||||
- remote builders sending malformed build results
|
||||
- remote daemons sending malformed inputs when requesting remote builds
|
||||
|
||||
Successful attacks using this bug require ASLR weakening of some sort, whether
|
||||
by architecture constraints (e.g. on 32 bit systems, where little randomization
|
||||
is possible) or system configuration (e.g. low ASLR entropy when loading
|
||||
libraries), and millions of attempts. Local attacks can be mounted in less than
|
||||
an hour. Remote builds typically require a fresh SSH connection for each build
|
||||
and are thus less susceptible. Only one attempt can be made by substituters for
|
||||
every build using substituters, they are thus not a likely vector for attacks.
|
||||
|
||||
At the time of writing, MITRE has not assigned this a CVE yet.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
synopsis: "Always print frames from `addErrorContext` in error traces"
|
||||
cls: [5847]
|
||||
category: "Improvements"
|
||||
credits: [blokyk]
|
||||
issues: []
|
||||
---
|
||||
|
||||
The [`builtins.addErrorContext`](@docroot@/language/builtins.md#builtins-addErrorContext)
|
||||
function allows an author to add artificial stack frames with custom messages to
|
||||
help end-users understand the context of an error and the path the code took to
|
||||
get there, without having to read and understand the original source code. A
|
||||
particularly notable user of this is the Nixpkgs module system, which adds
|
||||
custom frames detailing what option it's evaluating or which definition it's
|
||||
looking at.
|
||||
|
||||
However, previously, these frames would end up treated just as any other,
|
||||
meaning they would most often not be visible without `--show-trace`; yet, using
|
||||
`--show-trace`, they would be drowned out in the noise of the hundreds of other
|
||||
frames, rendering them just as unusable.
|
||||
|
||||
With this change, these frames are now unconditionally shown, even without
|
||||
`--show-trace`, which makes basic error traces much more informative.
|
||||
@@ -177,12 +177,6 @@ Most commands in Lix accept the following command-line options:
|
||||
You can override this using `--arg`, e.g., `nix-env --install --attr pkgname --arg system \"i686-freebsd\"`.
|
||||
(Note that since the argument is a Nix string literal, you have to escape the quotes.)
|
||||
|
||||
Additionally, dots are interpreted as attribute-path separators.
|
||||
I.e. `nix-instantiate '<nixpkgs>' -A hello-unfree --arg config.allowUnfree true` will result in an argument `config` with value `{ allowUnfree = true; }` being passed to `<nixpkgs>`.
|
||||
|
||||
Please note that merging of different arguments is rejected.
|
||||
I.e. `--arg config '{ cudaSupport = true; }' --arg config.allowUnfree true` will not work whereas `--arg config.cudaSupport true --arg config.allowUnfree true` is accepted.
|
||||
|
||||
- <span id="opt-argstr">[`--argstr`](#opt-argstr)</span> *name* *value*
|
||||
|
||||
This option is like `--arg`, only the value is not a Nix expression but a string.
|
||||
|
||||
@@ -51,64 +51,28 @@ $ nix-shell -A native-clangStdenvPackages
|
||||
|
||||
### Building from the development shell
|
||||
|
||||
We have a [justfile](https://just.systems/) for extra convenient building.
|
||||
It defaults to using `./build` as the build directory, and `$out` (`./outputs/out`) as the install directory.
|
||||
For most cases, you can clean-build, install, and run the tests with:
|
||||
|
||||
```bash
|
||||
$ just setup --wipe && just test
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The `--wipe` argument to `meson setup` conveniently works whether you have an existing build directory or not.
|
||||
>
|
||||
> However, it is *mostly*, but not *exactly* equivalent to deleting the build directory first.
|
||||
> In particular, previously specified `-D` build options are **preserved** with `--wipe` (for some reason).
|
||||
> For example, if you fetch and checkout a new version of Lix, and that new version *removes* a Meson build option from `./meson.options`, *and* a previous invocation in that build directory explicitly set that option, then `meson setup --wipe build` will error, complaining about the unknown option.
|
||||
> For these cases, `just clean` will give you a well-and-truly-this-time-for-real clean build.
|
||||
|
||||
Because the integration tests require installation to work, `just test` automatically also calls `just install`, and Meson helpfully will automatically build any targets that need building when trying to install them.
|
||||
|
||||
You can override the build directory or install directory by setting the justfile [variables](https://just.systems/man/en/setting-variables-from-the-command-line.html) `outdir` and `builddir` on the command-line:
|
||||
|
||||
```bash
|
||||
$ just builddir=build-before-bisect outdir=out-before-bisect setup
|
||||
$ just builddir=build-before-bisect test
|
||||
```
|
||||
|
||||
You'll have to set `builddir` for every target, but `outdir` only needs to be set for `setup`.
|
||||
Run a clean build and test with `just clean setup build install test`.
|
||||
|
||||
You can also run the unit tests and integration tests separately:
|
||||
|
||||
```bash
|
||||
$ just setup
|
||||
$ just test-unit
|
||||
$ just test-integration
|
||||
$ just setup build test-unit
|
||||
$ just install test-integration
|
||||
```
|
||||
|
||||
Most justfile targets forward all further arguments to the underlying Meson invocation.
|
||||
Many justfile aliases have a `-custom` variant which pass extra arguments to `meson`.
|
||||
For example, to work on both Lix and nix-eval-jobs you can run:
|
||||
|
||||
```bash
|
||||
$ just setup -Dnix-eval-jobs=enabled
|
||||
```
|
||||
$ just setup-custom -Dnix-eval-jobs=enabled
|
||||
$ # or
|
||||
$ mesonFlags=-Dnix-eval-jobs=enabled just setup
|
||||
```
|
||||
|
||||
Note that only targets which *don't* accept extra arguments can have other targets following them.
|
||||
`just clean setup` is equivalent to `just clean && just setup`, but `just build test` runs the `build` target with the argument `test`.
|
||||
This means that if you want to, for example, build with lower parallelism, and then test, you will have to do something like this:
|
||||
|
||||
```bash
|
||||
$ just build -j4
|
||||
$ just test
|
||||
```
|
||||
|
||||
Finally, the rewrite of the integration test suite, functional2, also has its own justfile target which allows passing extra arguments to pytest.
|
||||
For example, to collect and list all functional2 tests without running them, you can pass pytest's `--collect-only` argument:
|
||||
|
||||
```bash
|
||||
$ just test-functional2 --collect-only
|
||||
```
|
||||
Note that only targets which don't accept extra arguments can be used when
|
||||
running multiple targets at once; `just setup build` is fine, but `just
|
||||
setup-custom build` is an error. The `test` target is usually the last one to
|
||||
run, so it always accepts extra arguments.
|
||||
|
||||
You can also build Lix manually:
|
||||
|
||||
@@ -183,13 +147,13 @@ The development shell on Linux uses LLD by default for faster link times.
|
||||
This is set using `mesonFlags`, so to override it, you can simplify re-specify the linker to Meson:
|
||||
|
||||
```bash
|
||||
$ just setup -Dc_link_args=-fuse-ld=ld -Dcpp_link_args=-fuse-ld=ld
|
||||
$ just setup-custom -Dc_link_args=-fuse-ld=ld -Dcpp_link_args=-fuse-ld=ld
|
||||
```
|
||||
|
||||
While using LLD, you may find it helpful to use ThinLTO for even further improvements to link times for incremental builds:
|
||||
|
||||
```bash
|
||||
$ just setup -Db_lto=true -Db_lto_mode=thin -Db_thinlto_cache=true
|
||||
$ just setup-custom -Db_lto=true -Db_lto_mode=thin -Db_thinlto_cache=true
|
||||
```
|
||||
|
||||
## Sending changes to Gerrit for review {#sending-to-gerrit}
|
||||
@@ -200,7 +164,7 @@ Our instance is at <https://gerrit.lix.systems>.
|
||||
There's much more information about how to use Gerrit in the [wiki section on Gerrit][wiki-gerrit] including how to use Jujutsu, how to use the UI and more.
|
||||
The Snix project also has some Gerrit information [in their contributing docs][snix-gerrit].
|
||||
|
||||
[wiki-gerrit]: https://wiki.lix.systems/books/contributing/chapter/intro-to-gerrit
|
||||
[wiki-gerrit]: https://wiki.lix.systems/books/lix-contributors/chapter/gerrit
|
||||
[snix-gerrit]: https://snix.dev/docs/guides/contributing/
|
||||
|
||||
The gist is that once you have your SSH key and git remote set up, you can send commits for review with:
|
||||
@@ -226,7 +190,7 @@ The `Code-Review+2` from before will stick around through trivial rebases so no
|
||||
We use Buildkite for our CI, usually you will not have to interact directly with it other than reviewing any errors it produces, which are linked from Gerrit.
|
||||
|
||||
However in certain cases a CI run will fail due to transient issues not related to your code and you will need to rerun it by hand.
|
||||
You can log in to the CI via [SSO](https://buildkite.com/sso/afnix). On your job you can then hit the "Retry failed" button to rerun it, normally you will not have a repeat of the transient issue.
|
||||
You can log in to the CI via [SSO](https://buildkite.com/sso/lix-project). On your job you can then hit the "Retry failed" button to rerun it, normally you will not have a repeat of the transient issue.
|
||||
If the build still fails on CI issues or all builds are failing this should be reported via [Zulip on #T-infra](https://zulip.lix.systems/#narrow/channel/7-T-infra) or [Matrix on #dev](https://matrix.to/#/%23dev%3Alix.systems?via=lix.systems).
|
||||
|
||||
## Building Lix with `nix`
|
||||
@@ -566,7 +530,7 @@ Then add the new file there, and don't forget to register it in the appropriate
|
||||
The following metadata properties are supported for builtin functions:
|
||||
* `name` (required): the language-facing name (as a member of the `builtins` attribute set) of the function.
|
||||
* `implementation` (optional): a C++ expression specifying the implementation of the builtin.
|
||||
It must be a function of signature `Value(EvalState &, PosIdx, Value * *)`.
|
||||
It must be a function of signature `void(EvalState &, PosIdx, Value * *, Value &)`.
|
||||
If not specified, defaults to `prim_${name}`.
|
||||
* `renameInGlobalScope` (optional): whether the definition should be "hidden" in the global scope by prefixing its name with two underscores.
|
||||
If not specified, defaults to `true`.
|
||||
|
||||
@@ -50,6 +50,10 @@ The most current alternative to this section is to read `package.nix` and see wh
|
||||
- The `boost` library of version 1.66.0 or higher. It can be obtained
|
||||
from the official web site <https://www.boost.org/>.
|
||||
|
||||
- The `editline` library of version 1.14.0 or higher. It can be
|
||||
obtained from the its repository
|
||||
<https://github.com/troglobit/editline>.
|
||||
|
||||
- Recent versions of Bison and Flex to build the parser. (This is
|
||||
because Nix needs GLR support in Bison and reentrancy support in
|
||||
Flex.) For Bison, you need version 2.6, which can be obtained from
|
||||
|
||||
@@ -17,12 +17,6 @@ the attributes of which specify the inputs of the build.
|
||||
string. This is used as a symbolic name for the package by
|
||||
`nix-env`, and it is appended to the output paths of the derivation.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Names can only contain alphanumerical characters (0-9, a-z, A-Z)
|
||||
> as well as `+`, `-`, `.`, `_`, `?` and `=`. Names must be neither
|
||||
> `.` nor `..`, and must not start with `.-` or `..-`.
|
||||
|
||||
- There must be an attribute named [`builder`]{#attr-builder} that identifies the
|
||||
program that is executed to perform the build. It can be either a
|
||||
derivation or a source (a local file reference, e.g.,
|
||||
|
||||
@@ -164,7 +164,7 @@ Note that lists are only lazy in values, and they are strict in length.
|
||||
|
||||
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
|
||||
|
||||
An attribute name can be an identifier or a [double-quoted string](#type-string).
|
||||
An attribute name can be an identifier or a [string](#type-string).
|
||||
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
|
||||
|
||||
> *name* = *identifier* | *string* \
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
FIXME(Lix): This chapter is quite outdated with respect to recommended practices in 2024 and needs updating.
|
||||
The commands in here will work, however, and the installation section is up to date.
|
||||
|
||||
For more updated guidance, see the links on <https://wiki.lix.systems/books/lix-users/page/nix-resources>
|
||||
For more updated guidance, see the links on <https://lix.systems/resources/>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,53 @@
|
||||
# Lix 2.95 "Kakigōri" (2026-03-13)
|
||||
# Lix 2.95.2 (2026-05-04)
|
||||
## Fixes
|
||||
|
||||
- Fix unsigned overflow leading to out-of-band write in the NAR parser [cl/5550](https://gerrit.lix.systems/c/lix/+/5550)
|
||||
|
||||
The NAR parser contained an unsigned integer overflow that could be used by an
|
||||
attacker to write arbitrary data to an unknown memory location and possibly
|
||||
achieve code execution. A successful attack on the system-wide Lix daemon
|
||||
could lead to privilege escalation to root. Any process that involves NAR
|
||||
serialization could trigger this issue, including (but not limited to)
|
||||
|
||||
- local user interaction, whether the users are trusted or untrusted
|
||||
- malicious substituters sending malformed NARs
|
||||
- remote builders sending malformed build results
|
||||
- remote daemons sending malformed inputs when requesting remote builds
|
||||
|
||||
Successful attacks using this bug require ASLR weakening of some sort, whether
|
||||
by architecture constraints (e.g. on 32 bit systems, where little randomization
|
||||
is possible) or system configuration (e.g. low ASLR entropy when loading
|
||||
libraries), and millions of attempts. Local attacks can be mounted in less than
|
||||
an hour. Remote builds typically require a fresh SSH connection for each build
|
||||
and are thus less susceptible. Only one attempt can be made by substituters for
|
||||
every build using substituters, they are thus not a likely vector for attacks.
|
||||
|
||||
At the time of writing, MITRE has not assigned this a CVE yet.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae), [Raito Bezarius](https://git.lix.systems/raito), [edef](https://github.com/edef1c), and [sandydoo](https://github.com/sandydoo) for this.
|
||||
|
||||
|
||||
|
||||
|
||||
# Lix 2.95.1 (2026-03-19)
|
||||
## Fixes
|
||||
|
||||
- fix static builds [cl/5385](https://gerrit.lix.systems/c/lix/+/5385)
|
||||
|
||||
Static builds using musl were broken in 2.95.0 and should work again now.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- flake config warnings are now printed to stderr [lix#1155](https://git.lix.systems/lix-project/lix/issues/1155) [cl/5379](https://gerrit.lix.systems/c/lix/+/5379)
|
||||
|
||||
The settings listed in a flake-config confirmation prompt are now printed to stderr rather than stdout, which allows `nix print-dev-env` to emit valid bash again even in the presence of untrusted settings.
|
||||
|
||||
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Lix 2.95.0 (2026-03-13)
|
||||
|
||||
@@ -70,6 +70,11 @@ def do_include(content: str, relative_md_path: Path, source_root: Path, search_p
|
||||
|
||||
def recursive_replace(data, book_root, search_path):
|
||||
match data:
|
||||
# XXX FUTURE: drop sections once mdBook is at 0.5.0 or above in nixpkgs
|
||||
case {'sections': sections}:
|
||||
return data | dict(
|
||||
sections = [recursive_replace(section, book_root, search_path) for section in sections],
|
||||
)
|
||||
case {'items': items}:
|
||||
return data | dict(
|
||||
items = [recursive_replace(item, book_root, search_path) for item in items],
|
||||
|
||||
Generated
+4
-4
@@ -106,16 +106,16 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1783770249,
|
||||
"narHash": "sha256-K8pGvFito5dp9T0+clr60q+bJPGEskK75aAJ39w7HBM=",
|
||||
"lastModified": 1773082486,
|
||||
"narHash": "sha256-TKUDrM0nKUo5s/b8jhjXa2prcu5KU5Cck3HBTRLDjfo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "62463162b3ce92919f19ada41a70a0d943a08da8",
|
||||
"rev": "7f8b8875bdb38a70c7b5ceb9ba6a6a8d69859e16",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-26.05-small",
|
||||
"ref": "nixos-25.11-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "Lix: A modern, delicious implementation of the Nix package manager";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05-small";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11-small";
|
||||
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
|
||||
# Required because Nix 2.18 is not in Nixpkgs ≥ 25.05 anymore.
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
lixSrc = self;
|
||||
|
||||
# This notice gets echoed as a dev shell hook, and can be turned off with
|
||||
# `touch .nocontribmsg`
|
||||
@@ -77,35 +76,207 @@
|
||||
(Run `touch .nocontribmsg` to hide this message.)
|
||||
'';
|
||||
|
||||
scope = import ./nix-support/build/inputs.nix {
|
||||
inherit
|
||||
lib
|
||||
nixpkgs
|
||||
nix_2_18
|
||||
nix2container
|
||||
lixSrc
|
||||
nixpkgs-regression
|
||||
;
|
||||
};
|
||||
versionJson = builtins.fromJSON (builtins.readFile ./version.json);
|
||||
officialRelease = versionJson.official_release;
|
||||
|
||||
inherit (scope)
|
||||
crossSystems
|
||||
darwinSystems
|
||||
forAllStdenvs
|
||||
forAllSystems
|
||||
forAvailableSystems
|
||||
linux64BitSystems
|
||||
nixpkgsFor
|
||||
overlayFor
|
||||
systems
|
||||
versionSuffix
|
||||
;
|
||||
# Set to true to build the release notes for the next release.
|
||||
buildUnreleasedNotes = true;
|
||||
|
||||
inherit (scope.callPackage ./nix-support/build/outputs.nix { })
|
||||
packages
|
||||
ciArtifacts
|
||||
tests
|
||||
;
|
||||
versionSuffix =
|
||||
if officialRelease then
|
||||
""
|
||||
else
|
||||
"pre${
|
||||
builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")
|
||||
}-dev_${self.shortRev or "dirty"}";
|
||||
|
||||
linux32BitSystems = [ "i686-linux" ];
|
||||
linux64BitSystems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
linuxSystems = linux32BitSystems ++ linux64BitSystems;
|
||||
darwinSystems = [
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
nonDarwinSystems = linuxSystems;
|
||||
systems = linuxSystems ++ darwinSystems;
|
||||
|
||||
# If you add something here, please update the list in doc/manual/src/contributing/hacking.md.
|
||||
# Thanks~
|
||||
crossSystems = [
|
||||
"armv6l-linux"
|
||||
"armv7l-linux"
|
||||
"riscv64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-freebsd"
|
||||
# FIXME: broken dev shell due to python
|
||||
# "x86_64-netbsd"
|
||||
];
|
||||
|
||||
stdenvs = [
|
||||
# see assertion in package.nix why these two are disabled
|
||||
# "stdenv"
|
||||
# "gccStdenv"
|
||||
"clangStdenv"
|
||||
"libcxxStdenv"
|
||||
"ccacheStdenv"
|
||||
];
|
||||
|
||||
forAllSystems = lib.genAttrs systems;
|
||||
# Same as forAllSystems, but removes nulls, in case something is broken
|
||||
# on that system.
|
||||
forAvailableSystems =
|
||||
f: lib.filterAttrs (name: value: value != null && value != { }) (forAllSystems f);
|
||||
|
||||
forAllCrossSystems = lib.genAttrs crossSystems;
|
||||
|
||||
forAllStdenvs =
|
||||
f:
|
||||
lib.listToAttrs (
|
||||
map (stdenvName: {
|
||||
name = "${stdenvName}Packages";
|
||||
value = f stdenvName;
|
||||
}) stdenvs
|
||||
)
|
||||
// {
|
||||
# TODO delete this and reënable gcc stdenvs once gcc compiles kj coros correctly
|
||||
stdenvPackages = f "clangStdenv";
|
||||
};
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
nixpkgsFor = forAllSystems (
|
||||
system:
|
||||
let
|
||||
make-pkgs =
|
||||
crossSystem: stdenv:
|
||||
import nixpkgs {
|
||||
localSystem = {
|
||||
inherit system;
|
||||
};
|
||||
crossSystem = if crossSystem == null then null else { system = crossSystem; };
|
||||
overlays = [ (overlayFor (p: p.${stdenv})) ];
|
||||
};
|
||||
stdenvs = forAllStdenvs (make-pkgs null);
|
||||
native = stdenvs.stdenvPackages;
|
||||
in
|
||||
{
|
||||
inherit stdenvs native;
|
||||
static = native.pkgsStatic;
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "clangStdenv");
|
||||
}
|
||||
);
|
||||
|
||||
overlayFor =
|
||||
getStdenv: final: prev:
|
||||
let
|
||||
currentStdenv = getStdenv final;
|
||||
in
|
||||
{
|
||||
nixStable = prev.nix;
|
||||
|
||||
nixVersions = prev.nixVersions // {
|
||||
# Nix 2.18 has been removed from Nixpkgs ≥ 25.05, so we need to reintroduce it ourselves for our tests.
|
||||
nix_2_18 =
|
||||
nix_2_18.outputs.packages.${currentStdenv.hostPlatform.system}.default.overrideAttrs
|
||||
(_: {
|
||||
pname = "nix";
|
||||
});
|
||||
};
|
||||
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
||||
check-syscalls = final.buildPackages.callPackage ./maintainers/check-syscalls.nix { };
|
||||
|
||||
default-busybox-sandbox-shell = final.busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
'';
|
||||
};
|
||||
|
||||
nix = final.callPackage ./package.nix {
|
||||
inherit versionSuffix officialRelease;
|
||||
stdenv = currentStdenv;
|
||||
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
|
||||
# See below
|
||||
lowdown = final.lowdown_3_0;
|
||||
lowdown-unsandboxed = final.lowdown_3_0.override { enableDarwinSandbox = false; };
|
||||
};
|
||||
|
||||
lix-clang-tidy = final.callPackage ./subprojects/lix-clang-tidy { };
|
||||
|
||||
nix-eval-jobs = final.callPackage ./subprojects/nix-eval-jobs {
|
||||
stdenv = currentStdenv;
|
||||
srcDir = ./subprojects/nix-eval-jobs;
|
||||
};
|
||||
|
||||
# HACK: We need nix-prefetch-git for fetchCargoVendor for Rust stuff,
|
||||
# so it can't use Lix, or we infrec:
|
||||
# lix -> Rust stuff -> fetchCargoVendor -> nix-prefetch-git -> nix (lix)
|
||||
# This will eventually become a problem upstream, but until then,
|
||||
# apply some duct tape and pray.
|
||||
nix-prefetch-git =
|
||||
if (lib.functionArgs prev.nix-prefetch-git.override) ? "nix" then
|
||||
prev.nix-prefetch-git.override { nix = prev.nix; }
|
||||
else
|
||||
prev.nix-prefetch-git;
|
||||
|
||||
# Export the patched version of boehmgc that Lix uses into the overlay
|
||||
# for consumers of this flake.
|
||||
boehmgc-nix = final.nix.passthru.boehmgc-nix;
|
||||
# And same thing for our build-release-notes package.
|
||||
build-release-notes = final.nix.passthru.build-release-notes;
|
||||
|
||||
# As soon as Nixpkgs updates to >= 3.0.0, change to lowdown_2_0!
|
||||
# We don't change the default version in order to not change the hash
|
||||
# of Nix/Lix from upstream Nixpkgs.
|
||||
lowdown_3_0 =
|
||||
assert lib.versionOlder prev.lowdown.version "3.0.0";
|
||||
prev.lowdown.overrideAttrs (
|
||||
finalAttrs: _prevAttrs: {
|
||||
version = "3.0.0";
|
||||
|
||||
src = final.fetchurl {
|
||||
url = "https://kristaps.bsd.lv/lowdown/snapshots/lowdown-${finalAttrs.version}.tar.gz";
|
||||
sha512 = "94e97234d598382c3c3dc27f9bfdb3a3a2fcf7dbb6a8df3c85ee09f27f792449034a41d49d9cfd3d8450d2de01b8562c20c3d120e65c81af4d7d6c9454119e93";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
capnproto = prev.capnproto.overrideAttrs (old: {
|
||||
patches =
|
||||
old.patches or [ ]
|
||||
++ [
|
||||
# backport of https://github.com/capnproto/capnproto/pull/1810
|
||||
./misc/capnproto-promise-nodiscard.patch
|
||||
]
|
||||
++ lib.optionals (lib.versionOlder old.version "1.2.0") [
|
||||
# backport of https://github.com/capnproto/capnproto/pull/2296
|
||||
./misc/capnproto-monotonic-clocks-are-a-lie.patch
|
||||
];
|
||||
});
|
||||
};
|
||||
in
|
||||
{
|
||||
# for repl debugging
|
||||
@@ -115,13 +286,178 @@
|
||||
# 'nix.perl-bindings' packages.
|
||||
overlays.default = overlayFor (p: p.clangStdenv);
|
||||
|
||||
hydraJobs = ciArtifacts // {
|
||||
hydraJobs = {
|
||||
# Binary package for various platforms.
|
||||
build = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
# Ensure support for lowdown < 3.0 doesn't regress for NixOS 25.11
|
||||
build-lowdown_2_0.aarch64-linux = lib.genAttrs [ "aarch64-linux" ] (
|
||||
system:
|
||||
self.packages.${system}.nix.override {
|
||||
lowdown = nixpkgsFor.${system}.native.lowdown;
|
||||
lowdown-unsandboxed = nixpkgsFor.${system}.native.lowdown-unsandboxed;
|
||||
}
|
||||
);
|
||||
|
||||
# Building Lix twice in CI is expensive, but we can catch a lot of static
|
||||
# build regressions by at least making sure it evals and configures.
|
||||
configure-static = lib.genAttrs linux64BitSystems (
|
||||
system:
|
||||
self.packages.${system}.nix-static.overrideAttrs {
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
echo "configure-static complete. exiting with success"
|
||||
mkdir -p "$out"
|
||||
exit 0
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
devShell = forAllSystems (system: {
|
||||
default = self.devShells.${system}.default;
|
||||
clang = self.devShells.${system}.native-clangStdenvPackages;
|
||||
});
|
||||
|
||||
inherit tests;
|
||||
rl-next = forAllSystems (
|
||||
system:
|
||||
let
|
||||
rl-next-check =
|
||||
name: dir:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system}.native;
|
||||
in
|
||||
pkgs.buildPackages.runCommand "test-${name}-release-notes" { } ''
|
||||
LANG=C.UTF-8 ${lib.getExe pkgs.build-release-notes} --change-authors ${./doc/manual/change-authors.yml} ${dir} >$out
|
||||
'';
|
||||
in
|
||||
{
|
||||
user = rl-next-check "rl-next" ./doc/manual/rl-next;
|
||||
}
|
||||
);
|
||||
|
||||
# Completion tests for the Nix REPL.
|
||||
repl-completion = forAllSystems (
|
||||
system: nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }
|
||||
);
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
|
||||
|
||||
# nix-eval-jobs can be built against this Lix.
|
||||
nix-eval-jobs = forAllSystems (system: nixpkgsFor.${system}.native.nix-eval-jobs);
|
||||
|
||||
# Binary tarball for various platforms, containing a Nix store
|
||||
# with the closure of 'nix' package.
|
||||
binaryTarball = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.binaryTarball);
|
||||
|
||||
# docker image with Lix inside
|
||||
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||
|
||||
# API docs for Nix's unstable internal C++ interfaces.
|
||||
internal-api-docs =
|
||||
let
|
||||
nixpkgs = nixpkgsFor.x86_64-linux.native;
|
||||
inherit (nixpkgs) pkgs;
|
||||
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit versionSuffix officialRelease buildUnreleasedNotes;
|
||||
inherit (pkgs) build-release-notes;
|
||||
# Required since we don't support gcc stdenv
|
||||
stdenv = pkgs.clangStdenv;
|
||||
internalApiDocs = true;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
|
||||
};
|
||||
in
|
||||
nix.overrideAttrs (prev: {
|
||||
# This Hydra job is just for the internal API docs.
|
||||
# We don't need the build artifacts here.
|
||||
dontBuild = true;
|
||||
doCheck = false;
|
||||
doInstallCheck = false;
|
||||
});
|
||||
|
||||
# System tests.
|
||||
tests =
|
||||
import ./tests/nixos {
|
||||
inherit
|
||||
self
|
||||
lib
|
||||
nixpkgs
|
||||
nixpkgsFor
|
||||
;
|
||||
}
|
||||
// {
|
||||
# the n-e-j test suite is unusably slow in darwin ci. disbled until anywho fixes this.
|
||||
nix-eval-jobs = (lib.genAttrs nonDarwinSystems) (
|
||||
system: self.packages.${system}.nix-eval-jobs.tests.nix-eval-jobs
|
||||
);
|
||||
|
||||
# This is x86_64-linux only, just because we have significantly
|
||||
# cheaper x86_64-linux compute in CI.
|
||||
# It is clangStdenv because clang's sanitizers are nicer.
|
||||
asanBuild = self.packages.x86_64-linux.nix-clangStdenv.override {
|
||||
# Improve caching of non-code changes by not changing the
|
||||
# derivation name every single time, since this will never be seen
|
||||
# by users anyway.
|
||||
versionSuffix = "";
|
||||
sanitize = [
|
||||
"address"
|
||||
"undefined"
|
||||
];
|
||||
# it is very hard to make *every* CI build use this option such
|
||||
# that we don't wind up building Lix twice, so we do it here where
|
||||
# we are already doing so.
|
||||
werror = true;
|
||||
};
|
||||
|
||||
# Although this might be nicer to do with pre-commit, that would
|
||||
# require adding 12MB of nodejs to the dev shell, whereas building it
|
||||
# in CI with Nix avoids that at a cost of slower feedback on rarely
|
||||
# touched files.
|
||||
jsSyntaxCheck =
|
||||
let
|
||||
nixpkgs = nixpkgsFor.x86_64-linux.native;
|
||||
inherit (nixpkgs) pkgs;
|
||||
docSources = lib.fileset.toSource {
|
||||
root = ./doc;
|
||||
fileset = lib.fileset.fileFilter (f: f.hasExt "js") ./doc;
|
||||
};
|
||||
in
|
||||
pkgs.runCommand "js-syntax-check" { } ''
|
||||
find ${docSources} -type f -print -exec ${pkgs.nodejs-slim}/bin/node --check '{}' ';'
|
||||
touch $out
|
||||
'';
|
||||
|
||||
# clang-tidy run against the Lix codebase using the Lix clang-tidy plugin
|
||||
clang-tidy = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system}.native;
|
||||
in
|
||||
pkgs.callPackage ./package.nix {
|
||||
# Required since we don't support gcc stdenv
|
||||
stdenv = pkgs.clangStdenv;
|
||||
versionSuffix = "";
|
||||
lintInsteadOfBuild = true;
|
||||
}
|
||||
);
|
||||
|
||||
# Make sure that nix-env still produces the exact same result
|
||||
# on a particular version of Nixpkgs.
|
||||
evalNixpkgs = nixpkgsFor.x86_64-linux.native.callPackage ./tests/nixpkgs/eval.nix {
|
||||
inherit nixpkgs-regression;
|
||||
};
|
||||
|
||||
nixpkgsLibTests = forAllSystems (
|
||||
system:
|
||||
nixpkgsFor.${system}.native.callPackage ./tests/nixpkgs/lib.nix {
|
||||
inherit nixpkgs system;
|
||||
inherit (self.packages.${system}) nix;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
pre-commit = forAvailableSystems (
|
||||
system:
|
||||
@@ -181,7 +517,45 @@
|
||||
}
|
||||
);
|
||||
|
||||
inherit packages;
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
rec {
|
||||
inherit (nixpkgsFor.${system}.native) nix;
|
||||
default = nix;
|
||||
|
||||
inherit (nixpkgsFor.${system}.native) lix-clang-tidy nix-eval-jobs;
|
||||
}
|
||||
// (
|
||||
lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||
# python doesn't work in static builds as of 2025-06-27
|
||||
nix-static = nixpkgsFor.${system}.static.nix.overrideAttrs (_: {
|
||||
doCheck = false;
|
||||
});
|
||||
dockerImage =
|
||||
let
|
||||
pkgs = nixpkgsFor.${system}.native;
|
||||
nix2container' = import nix2container { inherit pkgs; };
|
||||
in
|
||||
import ./docker.nix {
|
||||
inherit pkgs;
|
||||
nix2container = nix2container'.nix2container;
|
||||
tag = pkgs.nix.version;
|
||||
};
|
||||
}
|
||||
// builtins.listToAttrs (
|
||||
map (crossSystem: {
|
||||
name = "nix-${crossSystem}";
|
||||
value = nixpkgsFor.${system}.cross.${crossSystem}.nix;
|
||||
}) crossSystems
|
||||
)
|
||||
// builtins.listToAttrs (
|
||||
map (stdenvName: {
|
||||
name = "nix-${stdenvName}";
|
||||
value = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nix;
|
||||
}) stdenvs
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
devShells =
|
||||
let
|
||||
@@ -214,30 +588,12 @@
|
||||
in
|
||||
(makeShells "native" nixpkgsFor.${system}.native)
|
||||
// (makeShells "static" nixpkgsFor.${system}.static)
|
||||
// (lib.listToAttrs (
|
||||
# Provide e.g., both '.#native-aarch64-linux` and `.#static-aarch64-linux`,
|
||||
# for each cross-system.
|
||||
# "native" feels like a misnomer here since it's literally cross compiling,
|
||||
# but at least it's consistent with the native/static dichotomy we've set up.
|
||||
lib.concatMap (
|
||||
crossSystem:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system}.cross.${crossSystem};
|
||||
inherit (pkgs) pkgsStatic;
|
||||
native = makeShell pkgs pkgs.clangStdenv;
|
||||
static = makeShell pkgsStatic pkgsStatic.clangStdenv;
|
||||
in
|
||||
[
|
||||
{
|
||||
name = "native-${crossSystem}";
|
||||
value = native;
|
||||
}
|
||||
{
|
||||
name = "static-${crossSystem}";
|
||||
value = static;
|
||||
}
|
||||
]
|
||||
) crossSystems
|
||||
// (forAllCrossSystems (
|
||||
crossSystem:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system}.cross.${crossSystem};
|
||||
in
|
||||
makeShell pkgs pkgs.clangStdenv
|
||||
))
|
||||
// {
|
||||
default = self.devShells.${system}.native-clangStdenvPackages;
|
||||
|
||||
@@ -1,67 +1,51 @@
|
||||
# https://just.systems/man/en/
|
||||
#
|
||||
# Take a look at ./doc/manual/src/contributing/hacking.md for a detailed
|
||||
# explanation on how to use this file!
|
||||
|
||||
# Pin the shell to bash (anything sufficiently POSIX-y would do)
|
||||
# HACK: We use https://github.com/casey/just#positional-arguments
|
||||
# and `"@$"` to forward arguments to the inner commands.
|
||||
# The reason we require this is that `{{ OPTIONS }}` does not escape any values,
|
||||
# and thus requires one additional level of escaping when running `just` commands with e.g. spaces in them.
|
||||
# just provides no good solution to this problem, so we have to rely on its forwarding of arguments and shell semantics.
|
||||
set shell := ["bash", "-uc"]
|
||||
|
||||
outdir := x"${out:-$PWD/outputs/out}"
|
||||
builddir := "build"
|
||||
|
||||
# List all available targets
|
||||
list:
|
||||
just --list
|
||||
|
||||
# Clean build artifacts and outputs.
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
rm -rf {{ quote(builddir) }}/* {{ quote(builddir) }}/.* {{ quote(outdir) }}/* {{ quote(outdir) }}/.*
|
||||
cargo clean
|
||||
rm -rf build
|
||||
|
||||
# Prepare meson for building.
|
||||
[positional-arguments]
|
||||
setup *OPTIONS:
|
||||
meson setup {{ builddir }} --reconfigure --prefix="{{outdir}}" $mesonFlags "$@"
|
||||
# Prepare meson for building with extra options
|
||||
setup-custom *OPTIONS:
|
||||
meson setup build --prefix="$PWD/outputs/out" $mesonFlags {{ OPTIONS }}
|
||||
|
||||
# Prepare meson for building
|
||||
setup: (setup-custom)
|
||||
|
||||
# Build lix with extra options
|
||||
[positional-arguments]
|
||||
build *OPTIONS:
|
||||
meson compile -C {{ builddir }} "$@"
|
||||
build-custom *OPTIONS:
|
||||
meson compile -C build {{ OPTIONS }}
|
||||
|
||||
# Build lix
|
||||
build: (build-custom)
|
||||
|
||||
alias compile := build
|
||||
|
||||
# `meson install` will automatically build anything that needs to be built to install it.
|
||||
[doc("Install Lix for local development")]
|
||||
[positional-arguments]
|
||||
install *OPTIONS:
|
||||
meson install --quiet -C {{ builddir }} "$@"
|
||||
# Install lix for local development with extra options
|
||||
install-custom *OPTIONS: (build-custom OPTIONS)
|
||||
meson install -C build
|
||||
|
||||
# Run all tests tests (installs first).
|
||||
[positional-arguments]
|
||||
test *OPTIONS: (install)
|
||||
meson test -C {{ builddir }} --print-errorlogs --max-lines 10000 "$@"
|
||||
# Install lix for local development
|
||||
install: (install-custom)
|
||||
|
||||
# Run tests (usually requires `install`) with extra options
|
||||
test *OPTIONS:
|
||||
meson test -C build --print-errorlogs --max-lines 10000 {{ OPTIONS }}
|
||||
|
||||
# Run unit tests only
|
||||
test-unit *OPTIONS: (test "--suite" "check")
|
||||
|
||||
# Run integration tests only
|
||||
test-integration *OPTIONS: (test "--suite" "installcheck" OPTIONS)
|
||||
test-integration *OPTIONS: install (test "--suite" "installcheck")
|
||||
|
||||
# Run functional2 tests using pytest directly, allowing for additional arguments to be passed to pytest e.g. for more granular test selection
|
||||
[positional-arguments]
|
||||
test-functional2 *OPTIONS:
|
||||
cd tests/functional2 && python -m pytest -v "$@"
|
||||
cd tests/functional2 && python -m pytest -v {{ OPTIONS }}
|
||||
|
||||
# special target for cargo because meson cannot be convinced to not mangle cargo test output,
|
||||
# and getting properly colored test output any other way also doesn't look all that possible.
|
||||
[positional-arguments]
|
||||
test-rs *OPTIONS:
|
||||
meson test -C {{ builddir }} --interactive lix-rs-tests "$@"
|
||||
alias clang-tidy := lint
|
||||
|
||||
# Lint with `clang-tidy`
|
||||
lint:
|
||||
|
||||
@@ -52,7 +52,7 @@ class ExtraFeature:
|
||||
{
|
||||
"\n ".join(
|
||||
[
|
||||
f"- {event.date}, {event.release}: {event.message} [{", ".join([f'[CL {cl}](https://gerrit.lix.systems/c/lix/+/{cl})' for cl in event.cls])}]"
|
||||
f"- {event.date}, {event.release}: {event.message} [{", ".join([f'[CL {cl}](https://git.lix.systems/c/lix/+/{cl})' for cl in event.cls])}]"
|
||||
for event in self.timeline
|
||||
]
|
||||
)
|
||||
|
||||
+112
-32
@@ -5,7 +5,6 @@
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/logging-rpc.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/rpc.hh"
|
||||
#include "lix/libutil/types-rpc.hh" // IWYU pragma: keep
|
||||
#include "lix/libutil/types.hh"
|
||||
@@ -51,7 +50,7 @@ struct Instance final : rpc::build_remote::HookInstance::Server
|
||||
|
||||
kj::Promise<void> init(InitContext context) override;
|
||||
|
||||
kj::Promise<Result<void>> buildImpl(BuildContext context);
|
||||
kj::Promise<void> buildImpl(BuildContext context);
|
||||
kj::Promise<void> build(BuildContext context) override;
|
||||
};
|
||||
}
|
||||
@@ -97,7 +96,7 @@ static std::tuple<bool, Machine *, AutoCloseFD> selectBestMachine(
|
||||
uint64_t bestLoad = 0;
|
||||
|
||||
for (auto & m : machines) {
|
||||
debug("considering building on remote machine '%s'", m.name);
|
||||
debug("considering building on remote machine '%s'", m.storeUri);
|
||||
|
||||
if (m.enabled && m.systemSupported(neededSystem) && m.allSupported(requiredFeatures)
|
||||
&& m.mandatoryMet(requiredFeatures))
|
||||
@@ -185,6 +184,67 @@ struct BuilderConnection
|
||||
AutoCloseFD slotLock;
|
||||
std::shared_ptr<Store> sshStore;
|
||||
std::string storeUri;
|
||||
Pipe logPipe;
|
||||
|
||||
// start the thread that reads ssh stderr and turns it into log items.
|
||||
// this future *must* outlive sshStore, otherwise it will never finish
|
||||
kj::Promise<Result<void>> startLogThread(std::string buildDescription, std::string drvPath)
|
||||
try {
|
||||
if (!logPipe.readSide) {
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
logPipe.writeSide.close();
|
||||
|
||||
// NOTE this is very similar to handleBuilderOutput in DerivationGoal, but unlike
|
||||
// the derivation goal we do not need to handle EIO from a pty here. we also have
|
||||
// no timeouts or limits to keep track of, which makes deduplication less useful.
|
||||
auto act = logger->startActivity(
|
||||
lvlInfo, actBuild, buildDescription, Logger::Fields{drvPath, storeUri, 1, 1}
|
||||
);
|
||||
|
||||
std::map<ActivityId, Activity> activities;
|
||||
|
||||
auto reader = AIO().lowLevelProvider.wrapInputFd(logPipe.readSide.get());
|
||||
|
||||
LogLineSplitter splitter;
|
||||
|
||||
auto flushLine = [&](const std::string & line) {
|
||||
if (const auto state =
|
||||
handleJSONLogMessage(line, act, activities, "the derivation builder"))
|
||||
{
|
||||
return *state;
|
||||
} else {
|
||||
return act.result(resBuildLogLine, line);
|
||||
}
|
||||
};
|
||||
|
||||
auto buf = kj::heapArray<char>(4096);
|
||||
while (true) {
|
||||
const auto got = co_await reader->tryRead(buf.begin(), 1, buf.size());
|
||||
if (got == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string_view data{buf.begin(), got};
|
||||
while (!data.empty()) {
|
||||
if (auto line = splitter.feed(data)) {
|
||||
if (flushLine(*line) == Logger::BufferState::NeedsFlush) {
|
||||
TRY_AWAIT(act.getLogger().flush());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto line = splitter.finish(); !line.empty()) {
|
||||
(void) flushLine(line);
|
||||
TRY_AWAIT(act.getLogger().flush());
|
||||
}
|
||||
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
|
||||
struct AcceptedBuild final : rpc::build_remote::HookInstance::AcceptedBuild::Server
|
||||
@@ -201,7 +261,7 @@ struct AcceptedBuild final : rpc::build_remote::HookInstance::AcceptedBuild::Ser
|
||||
{
|
||||
}
|
||||
|
||||
kj::Promise<Result<void>> runImpl(RunContext context);
|
||||
kj::Promise<void> runImpl(RunContext context);
|
||||
kj::Promise<void> run(RunContext context) override;
|
||||
};
|
||||
|
||||
@@ -266,15 +326,26 @@ try {
|
||||
lock.reset();
|
||||
|
||||
std::shared_ptr<Store> sshStore;
|
||||
Pipe logPipe;
|
||||
|
||||
try {
|
||||
auto act =
|
||||
logger->startActivity(lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->name));
|
||||
auto act = logger->startActivity(
|
||||
lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri)
|
||||
);
|
||||
|
||||
sshStore = TRY_AWAIT(bestMachine->openStore());
|
||||
co_return BuilderConnection{std::move(bestSlotLock), sshStore, bestMachine->storeUri};
|
||||
std::tie(sshStore, logPipe) = TRY_AWAIT(bestMachine->openStore());
|
||||
TRY_AWAIT(sshStore->connect());
|
||||
co_return BuilderConnection{
|
||||
std::move(bestSlotLock), sshStore, bestMachine->storeUri, std::move(logPipe)
|
||||
};
|
||||
} catch (std::exception & e) { // NOLINT(lix-foreign-exceptions)
|
||||
printError("cannot build on '%s': %s", bestMachine->name, e.what());
|
||||
std::string msg = logPipe.readSide ? chomp(drainFD(logPipe.readSide.get(), false)) : "";
|
||||
printError(
|
||||
"cannot build on '%s': %s%s",
|
||||
bestMachine->storeUri,
|
||||
e.what(),
|
||||
msg.empty() ? "" : ": " + msg
|
||||
);
|
||||
bestMachine->enabled = false;
|
||||
}
|
||||
}
|
||||
@@ -295,7 +366,7 @@ static int main_build_remote(AsyncIoRoot & aio, std::string programName, Strings
|
||||
if (argv.size() != 1)
|
||||
throw UsageError("called without required arguments");
|
||||
|
||||
setVerbosity((Verbosity) std::stoll(argv.front()));
|
||||
verbosity = (Verbosity) std::stoll(argv.front());
|
||||
|
||||
auto conn = aio.kj.lowLevelProvider->wrapUnixSocketFd(1);
|
||||
capnp::TwoPartyServer srv(kj::heap<Instance>());
|
||||
@@ -324,14 +395,16 @@ kj::Promise<void> Instance::init(InitContext context)
|
||||
initPlugins();
|
||||
|
||||
initialized = true;
|
||||
|
||||
context.getResults().initResult().setGood();
|
||||
} catch (...) {
|
||||
rpc::rethrow_as_rpc_error();
|
||||
RPC_FILL(context.getResults(), initResult, std::current_exception());
|
||||
}
|
||||
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
kj::Promise<Result<void>> Instance::buildImpl(BuildContext context)
|
||||
kj::Promise<void> Instance::buildImpl(BuildContext context)
|
||||
try {
|
||||
if (!initialized) {
|
||||
throw Error("build hook not fully initialized");
|
||||
@@ -358,8 +431,8 @@ try {
|
||||
debug("got %d remote builders", machines.size());
|
||||
|
||||
if (machines.empty()) {
|
||||
context.getResults().initResult().setDeclinePermanently();
|
||||
co_return result::success();
|
||||
context.getResults().initResult().initGood().setDeclinePermanently();
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto amWilling = context.getParams().getAmWilling();
|
||||
@@ -375,23 +448,21 @@ try {
|
||||
if (auto immediateResponse = std::get_if<BuildRejected>(&result)) {
|
||||
switch (*immediateResponse) {
|
||||
case BuildRejected::Temporarily:
|
||||
context.getResults().initResult().setPostpone();
|
||||
co_return result::success();
|
||||
context.getResults().initResult().initGood().setPostpone();
|
||||
co_return;
|
||||
case BuildRejected::Permanently:
|
||||
context.getResults().initResult().setDecline();
|
||||
co_return result::success();
|
||||
context.getResults().initResult().initGood().setDecline();
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
auto builder = std::get_if<BuilderConnection>(&result);
|
||||
assert(builder);
|
||||
|
||||
auto ac = context.getResults().initResult().initAccept();
|
||||
auto ac = context.getResults().initResult().initGood().initAccept();
|
||||
ac.setMachine(kj::heap<AcceptedBuild>(store, drvPath, std::move(*builder)));
|
||||
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
RPC_FILL(context.getResults(), initResult, std::current_exception());
|
||||
}
|
||||
|
||||
kj::Promise<void> Instance::build(BuildContext context)
|
||||
@@ -400,12 +471,11 @@ try {
|
||||
throw Error("build hooks can only accept a single job");
|
||||
}
|
||||
used = true; // lock out other rpc calls during processing
|
||||
auto result = co_await buildImpl(context);
|
||||
co_await buildImpl(context);
|
||||
TRY_AWAIT(logger->flush());
|
||||
used = result.has_value() && context.getResults().getResult().isAccept();
|
||||
result.value();
|
||||
used = context.getResults().getResult().getGood().isAccept();
|
||||
} catch (...) {
|
||||
rpc::rethrow_as_rpc_error();
|
||||
RPC_FILL(context.getResults(), getResult, std::current_exception());
|
||||
}
|
||||
|
||||
kj::Promise<void> AcceptedBuild::run(RunContext context)
|
||||
@@ -423,17 +493,23 @@ kj::Promise<void> AcceptedBuild::run(RunContext context)
|
||||
throw Error("build hooks builds are single-use items");
|
||||
}
|
||||
used = true;
|
||||
auto result = co_await runImpl(context);
|
||||
co_await runImpl(context);
|
||||
TRY_AWAIT(logger->flush());
|
||||
result.value();
|
||||
} catch (...) {
|
||||
rpc::rethrow_as_rpc_error();
|
||||
RPC_FILL(context.getResults(), getResult, std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
kj::Promise<Result<void>> AcceptedBuild::runImpl(RunContext context)
|
||||
kj::Promise<void> AcceptedBuild::runImpl(RunContext context)
|
||||
{
|
||||
try {
|
||||
auto logHandler = builder.startLogThread(
|
||||
fmt("%s on '%s'",
|
||||
rpc::to<std::string_view>(context.getParams().getDescription()),
|
||||
builder.storeUri),
|
||||
store->printStorePath(drvPath)
|
||||
);
|
||||
|
||||
auto & sshStore = builder.sshStore;
|
||||
auto & storeUri = builder.storeUri;
|
||||
|
||||
@@ -543,9 +619,13 @@ kj::Promise<Result<void>> AcceptedBuild::runImpl(RunContext context)
|
||||
);
|
||||
}
|
||||
|
||||
co_return result::success();
|
||||
// drop store connection, let log handler process any remaining input
|
||||
builder.sshStore = nullptr;
|
||||
TRY_AWAIT(logHandler);
|
||||
|
||||
context.getResults().initResult().setGood();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
RPC_FILL(context.getResults(), initResult, std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ static int main_builtin_builder(AsyncIoRoot & aio, std::string programName, Stri
|
||||
};
|
||||
|
||||
if (auto val = string2Int<int>(getArg("verbosity"))) {
|
||||
setVerbosity(verbosityFromIntClamped(*val));
|
||||
verbosity = verbosityFromIntClamped(*val);
|
||||
} else {
|
||||
throw Error("expected a verbosity argument");
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
auto autoArgsWithInNixShell = autoArgs;
|
||||
if (runEnv) {
|
||||
auto newArgs = evaluator->buildBindings(autoArgsWithInNixShell->size() + 1);
|
||||
newArgs.insert("inNixShell", {NewValueAs::boolean, true});
|
||||
newArgs.alloc("inNixShell") = {NewValueAs::boolean, true};
|
||||
for (auto & i : *autoArgs) newArgs.insert(i);
|
||||
autoArgsWithInNixShell = newArgs.finish();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ static int main_nix_copy_closure(AsyncIoRoot & aio, std::string programName, Str
|
||||
for (auto & path : storePaths)
|
||||
storePaths2.insert(from->followLinksToStorePath(path));
|
||||
|
||||
aio.blockOn(copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, useSubstitutes, includeOutputs));
|
||||
aio.blockOn(copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, useSubstitutes));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,8 @@ static void getAllExprs(Evaluator & state,
|
||||
Value vArg = {NewValueAs::string, path2.canonical().abs()};
|
||||
if (seen.size() == maxAttrs)
|
||||
throw Error("too many Nix expressions in directory '%1%'", path);
|
||||
attrs.insert(attrName, {NewValueAs::app, state.mem, state.builtins.get("import"), vArg});
|
||||
attrs.alloc(attrName
|
||||
) = {NewValueAs::app, state.mem, state.builtins.get("import"), vArg};
|
||||
}
|
||||
else if (st.type == InputAccessor::tDirectory)
|
||||
/* `path2' is a directory (with no default.nix in it);
|
||||
@@ -179,7 +180,7 @@ static Value loadSourceExpr(EvalState & state, const SourcePath & path_)
|
||||
directory). */
|
||||
else if (st.type == InputAccessor::tDirectory) {
|
||||
auto attrs = state.ctx.buildBindings(maxAttrs);
|
||||
attrs.insert("_combineChannels", Value::EMPTY_LIST);
|
||||
attrs.alloc("_combineChannels") = Value::EMPTY_LIST;
|
||||
StringSet seen;
|
||||
getAllExprs(state.ctx, path, seen, attrs);
|
||||
return {NewValueAs::attrs, attrs};
|
||||
|
||||
@@ -42,7 +42,11 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
|
||||
NixStringContext context;
|
||||
if (evalOnly) {
|
||||
Value vRes = autoArgs.empty() ? v : state.autoCallFunction(autoArgs, v, noPos);
|
||||
Value vRes;
|
||||
if (autoArgs.empty())
|
||||
vRes = v;
|
||||
else
|
||||
vRes = state.autoCallFunction(autoArgs, v, noPos);
|
||||
if (output == okRaw)
|
||||
std::cout << *state.coerceToString(noPos, vRes, context, "while generating the nix-instantiate output", StringCoercionMode::Strict);
|
||||
// We intentionally don't output a newline here. The default PS1 for Bash in NixOS starts with a newline
|
||||
|
||||
@@ -948,7 +948,7 @@ opServe(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, String
|
||||
auto getBuildSettings = [&]() {
|
||||
// FIXME: changing options here doesn't work if we're
|
||||
// building through the daemon.
|
||||
setVerbosity(lvlError);
|
||||
verbosity = lvlError;
|
||||
settings.keepLog.override(false);
|
||||
settings.useSubstitutes.override(false);
|
||||
settings.maxSilentTime.override(readNum<unsigned>(in));
|
||||
|
||||
+17
-18
@@ -46,31 +46,30 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
|
||||
auto attrs = state.ctx.buildBindings(7 + outputs.size());
|
||||
|
||||
attrs.insert(state.ctx.symbols.sym_type, {NewValueAs::string, "derivation"});
|
||||
attrs.insert(state.ctx.symbols.sym_name, {NewValueAs::string, i.queryName(state)});
|
||||
attrs.alloc(state.ctx.symbols.sym_type) = {NewValueAs::string, "derivation"};
|
||||
attrs.alloc(state.ctx.symbols.sym_name) = {NewValueAs::string, i.queryName(state)};
|
||||
auto system = i.querySystem(state);
|
||||
if (!system.empty())
|
||||
attrs.insert(state.ctx.symbols.sym_system, {NewValueAs::string, system});
|
||||
attrs.insert(
|
||||
state.ctx.symbols.sym_outPath,
|
||||
{NewValueAs::string, state.ctx.store->printStorePath(i.queryOutPath(state))}
|
||||
);
|
||||
attrs.alloc(state.ctx.symbols.sym_system) = {NewValueAs::string, system};
|
||||
attrs.alloc(state.ctx.symbols.sym_outPath) = {
|
||||
NewValueAs::string, state.ctx.store->printStorePath(i.queryOutPath(state))
|
||||
};
|
||||
if (drvPath)
|
||||
attrs.insert(
|
||||
state.ctx.symbols.sym_drvPath, {NewValueAs::string, state.ctx.store->printStorePath(*drvPath)}
|
||||
);
|
||||
attrs.alloc(state.ctx.symbols.sym_drvPath) = {
|
||||
NewValueAs::string, state.ctx.store->printStorePath(*drvPath)
|
||||
};
|
||||
|
||||
// Copy each output meant for installation.
|
||||
auto & vOutputs = attrs.alloc(state.ctx.symbols.sym_outputs);
|
||||
auto outputsList = state.ctx.mem.newList(outputs.size());
|
||||
attrs.insert(state.ctx.symbols.sym_outputs, {NewValueAs::list, outputsList});
|
||||
vOutputs = {NewValueAs::list, outputsList};
|
||||
for (const auto & [m, j] : enumerate(outputs)) {
|
||||
outputsList->elems[m] = {NewValueAs::string, j.first};
|
||||
auto outputAttrs = state.ctx.buildBindings(2);
|
||||
outputAttrs.insert(
|
||||
state.ctx.symbols.sym_outPath,
|
||||
{NewValueAs::string, state.ctx.store->printStorePath(*j.second)}
|
||||
);
|
||||
attrs.insert(j.first, {NewValueAs::attrs, outputAttrs});
|
||||
outputAttrs.alloc(state.ctx.symbols.sym_outPath) = {
|
||||
NewValueAs::string, state.ctx.store->printStorePath(*j.second)
|
||||
};
|
||||
attrs.alloc(j.first) = {NewValueAs::attrs, outputAttrs};
|
||||
|
||||
/* This is only necessary when installing store paths, e.g.,
|
||||
`nix-env -i /nix/store/abcd...-foo'. */
|
||||
@@ -88,7 +87,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
meta.insert(state.ctx.symbols.create(j), *v);
|
||||
}
|
||||
|
||||
attrs.insert(state.ctx.symbols.sym_meta, {NewValueAs::attrs, meta});
|
||||
attrs.alloc(state.ctx.symbols.sym_meta) = {NewValueAs::attrs, meta};
|
||||
|
||||
manifest->elems[n++] = {NewValueAs::attrs, attrs};
|
||||
|
||||
@@ -112,7 +111,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
/* Construct a Nix expression that calls the user environment
|
||||
builder with the manifest as argument. */
|
||||
auto attrs = state.ctx.buildBindings(3);
|
||||
attrs.insert("manifest", state.ctx.paths.mkStorePathString(manifestFile));
|
||||
state.ctx.paths.mkStorePathString(manifestFile, attrs.alloc("manifest"));
|
||||
attrs.insert(state.ctx.symbols.create("derivations"), vManifest);
|
||||
Value args = {NewValueAs::attrs, attrs};
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#include "libexpr/value.hh"
|
||||
#include "libutil/strings.hh"
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libexpr/eval-settings.hh"
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libmain/shared.hh"
|
||||
@@ -13,10 +10,31 @@
|
||||
#include "lix/libcmd/command.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include <deque>
|
||||
#include "lix/libutil/regex.hh"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
|
||||
static std::regex const identifierRegex = regex::parse("^[A-Za-z_][A-Za-z0-9_'-]*$");
|
||||
static void checkValidNixIdentifier(const std::string & name)
|
||||
{
|
||||
std::smatch match;
|
||||
if (!std::regex_match(name, match, identifierRegex)) {
|
||||
throw UsageError(
|
||||
"This invocation specifies a value for argument '%s' "
|
||||
"which isn't a valid Nix identifier. "
|
||||
"The project is dropping support for this so that it's possible to make e.g. "
|
||||
"'%s' evaluating to '%s' in the future. "
|
||||
"If you depend on this behavior, please reach out in "
|
||||
"<https://git.lix.systems/lix-project/lix/issues/496> so we can discuss your use-case.",
|
||||
name,
|
||||
"--arg config.allowUnfree true",
|
||||
"{ config.allowUnfree = true; }"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MixEvalArgs::MixEvalArgs()
|
||||
{
|
||||
addFlag(
|
||||
@@ -24,7 +42,10 @@ MixEvalArgs::MixEvalArgs()
|
||||
.description = "Pass the value *expr* as the argument *name* to Nix functions.",
|
||||
.category = category,
|
||||
.labels = {"name", "expr"},
|
||||
.handler = {[&](std::string name, std::string expr) { autoArgs[name] = ExprArgument(expr); }}}
|
||||
.handler = {[&](std::string name, std::string expr) {
|
||||
checkValidNixIdentifier(name);
|
||||
autoArgs[name] = 'E' + expr;
|
||||
}}}
|
||||
);
|
||||
|
||||
addFlag({
|
||||
@@ -32,7 +53,10 @@ MixEvalArgs::MixEvalArgs()
|
||||
.description = "Pass the string *string* as the argument *name* to Nix functions.",
|
||||
.category = category,
|
||||
.labels = {"name", "string"},
|
||||
.handler = {[&](std::string name, std::string s) { autoArgs[name] = StringArgument(s); }},
|
||||
.handler = {[&](std::string name, std::string s) {
|
||||
checkValidNixIdentifier(name);
|
||||
autoArgs[name] = 'S' + s;
|
||||
}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
@@ -155,90 +179,18 @@ MixEvalArgs::MixEvalArgs()
|
||||
});
|
||||
}
|
||||
|
||||
struct AutoArgsContainer
|
||||
{
|
||||
std::map<Symbol, std::variant<Value, AutoArgsContainer>> data;
|
||||
|
||||
Bindings * toBindings(Evaluator & state)
|
||||
{
|
||||
auto bb = state.buildBindings(data.size());
|
||||
|
||||
for (auto & [sym, v] : data) {
|
||||
bb.insert(
|
||||
sym,
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](Value & v) { return v; },
|
||||
[&](AutoArgsContainer & aac) -> Value {
|
||||
return {NewValueAs::attrs, aac.toBindings(state)};
|
||||
}
|
||||
},
|
||||
v
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return bb.finish();
|
||||
}
|
||||
};
|
||||
|
||||
static void addAutoArgRecursive(
|
||||
AutoArgsContainer & container,
|
||||
Evaluator & state,
|
||||
std::vector<std::string> && path,
|
||||
Value & val,
|
||||
const std::string_view pathStr
|
||||
)
|
||||
{
|
||||
auto * data = &container.data;
|
||||
auto size = path.size();
|
||||
for (auto [i, pathCmp] : enumerate(path)) {
|
||||
auto next = state.symbols.create(pathCmp);
|
||||
auto entry = data->find(next);
|
||||
|
||||
if (entry == data->end()) {
|
||||
if (i == size - 1) {
|
||||
(*data)[next] = val;
|
||||
} else {
|
||||
(*data)[next] = AutoArgsContainer{};
|
||||
data = &std::get<AutoArgsContainer>((*data)[next]).data;
|
||||
}
|
||||
} else {
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](Value & v) {
|
||||
throw Error(
|
||||
"Cannot set %s via --arg/--argstr when it's the path-extension of another "
|
||||
"auto-argument!",
|
||||
pathStr
|
||||
);
|
||||
},
|
||||
[&](AutoArgsContainer & v) { data = &v.data; }
|
||||
},
|
||||
entry->second
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
|
||||
{
|
||||
AutoArgsContainer aac;
|
||||
for (auto & [name, value] : autoArgs) {
|
||||
Value v = std::visit(
|
||||
overloaded{
|
||||
[&](StringArgument & str) -> Value { return {NewValueAs::string, (std::string_view) str.value}; },
|
||||
[&](ExprArgument & e) -> Value {
|
||||
return state.evalLazily(state.parseExprFromString(e.expr, CanonPath::fromCwd()));
|
||||
}
|
||||
},
|
||||
value
|
||||
);
|
||||
|
||||
addAutoArgRecursive(aac, state, parseAttrPath(name, false), v, name);
|
||||
auto res = state.buildBindings(autoArgs.size());
|
||||
for (auto & i : autoArgs) {
|
||||
Value v;
|
||||
if (i.second[0] == 'E')
|
||||
v = state.evalLazily(state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()));
|
||||
else
|
||||
v = {NewValueAs::string, ((std::string_view) i.second).substr(1)};
|
||||
res.insert(state.symbols.create(i.first), v);
|
||||
}
|
||||
|
||||
return aac.toBindings(state);
|
||||
return res.finish();
|
||||
}
|
||||
|
||||
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
|
||||
|
||||
@@ -14,15 +14,6 @@ class EvalState;
|
||||
class Bindings;
|
||||
struct SourcePath;
|
||||
|
||||
struct StringArgument
|
||||
{
|
||||
std::string value;
|
||||
};
|
||||
struct ExprArgument
|
||||
{
|
||||
std::string expr;
|
||||
};
|
||||
|
||||
struct MixEvalArgs : virtual Args, virtual MixRepair
|
||||
{
|
||||
static constexpr auto category = "Common evaluation options";
|
||||
@@ -36,7 +27,7 @@ struct MixEvalArgs : virtual Args, virtual MixRepair
|
||||
std::optional<std::string> evalStoreUrl;
|
||||
|
||||
private:
|
||||
std::map<std::string, std::variant<StringArgument, ExprArgument>> autoArgs;
|
||||
std::map<std::string, std::string> autoArgs;
|
||||
};
|
||||
|
||||
/** @brief Resolve an argument that is generally a file, but could be something that
|
||||
|
||||
+11
-11
@@ -446,18 +446,18 @@ Installables SourceExprCommand::parseInstallables(
|
||||
throw UsageError("'--file' and '--expr' are exclusive");
|
||||
|
||||
auto evaluator = getEvaluator();
|
||||
Value vFile;
|
||||
|
||||
Value vFile = [&](NeverAsync = {}) {
|
||||
if (file == "-") {
|
||||
auto & e = evaluator->parseStdin();
|
||||
return state.eval(e);
|
||||
} else if (file) {
|
||||
return state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap());
|
||||
} else {
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
return state.eval(e);
|
||||
}
|
||||
}();
|
||||
if (file == "-") {
|
||||
auto & e = evaluator->parseStdin();
|
||||
vFile = state.eval(e);
|
||||
}
|
||||
else if (file)
|
||||
vFile = state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap());
|
||||
else {
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
vFile = state.eval(e);
|
||||
}
|
||||
|
||||
for (auto & s : ss) {
|
||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||
|
||||
@@ -55,6 +55,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown, StandardOutputSt
|
||||
|
||||
struct lowdown_opts opts{
|
||||
.type = LOWDOWN_TERM,
|
||||
#ifdef LOWDOWN_SEPARATE_TERM_OPTS
|
||||
.term =
|
||||
{
|
||||
.cols = lowdown_cols,
|
||||
@@ -64,7 +65,14 @@ std::string renderMarkdownToTerminal(std::string_view markdown, StandardOutputSt
|
||||
.vmargin = 0,
|
||||
.centre = 0,
|
||||
},
|
||||
// maxdepth needs to be part of the ifdefs to match declaration order
|
||||
.maxdepth = 20,
|
||||
#else
|
||||
.maxdepth = 20,
|
||||
.cols = lowdown_cols,
|
||||
.hmargin = 0,
|
||||
.vmargin = 0,
|
||||
#endif /* LOWDOWN_SEPARATE_TERM_OPTS */
|
||||
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
|
||||
#ifdef LOWDOWN_CONSOLIDATED_OFLAGS
|
||||
.oflags = LOWDOWN_NOLINK,
|
||||
|
||||
+215
-60
@@ -1,101 +1,251 @@
|
||||
#include "libutil/fmt.hh"
|
||||
#include "libutil/terminal.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/lix-rs/main.gen.hh"
|
||||
#include "lix/lix-rs/utils.hh"
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cerrno>
|
||||
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
// This results in linker errors due to to name-mangling of editline C symbols.
|
||||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
}
|
||||
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
#include "lix/libcmd/repl-interacter.hh"
|
||||
|
||||
namespace rust {
|
||||
Vec<String> Impl<lix::repl::CxxCompleter, lix::repl::ReplCompleter>::complete(
|
||||
Ref<lix::repl::CxxCompleter> self, Ref<Str> input
|
||||
)
|
||||
try {
|
||||
auto s = to_std_string(input);
|
||||
|
||||
auto result = std::vec::Vec<std::string::String>::new_();
|
||||
for (auto & possible : self.cpp().completePrefix(s)) {
|
||||
result.push(rust::to_string(possible));
|
||||
}
|
||||
return result;
|
||||
} catch (...) {
|
||||
// the completer should have logged anything interesting.
|
||||
return std::vec::Vec<std::string::String>::new_();
|
||||
}
|
||||
}
|
||||
|
||||
namespace nix {
|
||||
|
||||
ReadlineLikeInteracter::ReadlineLikeInteracter(std::string historyFile) : historyFile(historyFile) {}
|
||||
namespace {
|
||||
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
|
||||
volatile sig_atomic_t g_signal_received = 0;
|
||||
|
||||
void ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
|
||||
void sigintHandler(int signo)
|
||||
{
|
||||
g_signal_received = signo;
|
||||
}
|
||||
|
||||
static detail::ReplCompleterMixin * curRepl; // ugly
|
||||
|
||||
/**
|
||||
* @return a null-terminated list of completions as expected by `el_print_columns`
|
||||
*/
|
||||
char ** copyCompletions(const StringSet& possible)
|
||||
{
|
||||
int ac = 0;
|
||||
char ** vp = nullptr;
|
||||
|
||||
auto check = [&](auto * p) {
|
||||
if (!p) {
|
||||
if (vp) {
|
||||
while (--ac >= 0)
|
||||
free(vp[ac]);
|
||||
free(static_cast<void *>(vp));
|
||||
}
|
||||
throw Error("allocation failure");
|
||||
}
|
||||
return p;
|
||||
};
|
||||
vp = check(static_cast<char **>(malloc(possible.size() * sizeof(char *))));
|
||||
for (auto & p : possible)
|
||||
vp[ac++] = check(strdup(p.c_str()));
|
||||
|
||||
return vp;
|
||||
}
|
||||
|
||||
// Instead of using the readline-provided prefix, do our own tokenization
|
||||
// to avoid the default behavior of treating dots/quotes as word boundaries.
|
||||
// See the definition of SEPS for what it treats as a boundary:
|
||||
// https://github.com/troglobit/editline/blob/caf4b3c0ce3b0785791198b11de6f3134e9f05d8/src/editline.c
|
||||
std::string getLastTokenBeforeCursor()
|
||||
{
|
||||
std::string_view line{rl_line_buffer, static_cast<size_t>(rl_point)};
|
||||
|
||||
auto tokens = tokenizeString<std::vector<std::string>>(
|
||||
line,
|
||||
// Same as editline's SEPS, except for double and single quotes:
|
||||
"#$&()*:;<=>?[\\]^`{|}~\n\t "
|
||||
);
|
||||
|
||||
if (tokens.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return tokens.back();
|
||||
}
|
||||
|
||||
// Sometimes inserting text or listing possible completions has a side effect
|
||||
// of hiding the text after the cursor (even though it remains in the buffer).
|
||||
// This helper just refreshes the display while keeping the cursor in place.
|
||||
//
|
||||
// Inserting text also sometimes moves the whole buffer down one line, usually
|
||||
// if the cursor is inside a quoted attr name. I'm not sure why (vs unquoted)
|
||||
// but it still seems to work pretty well and is just a visual artifact.
|
||||
el_status_t redisplay()
|
||||
{
|
||||
int cursorPos = rl_point;
|
||||
rl_refresh_line(0, 0);
|
||||
rl_point = cursorPos;
|
||||
return (rl_point == rl_end) ? CSstay : CSmove;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
static el_status_t doCompletion() {
|
||||
auto s = getLastTokenBeforeCursor();
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
|
||||
if (possible.empty()) {
|
||||
return el_ring_bell();
|
||||
}
|
||||
|
||||
if (possible.size() == 1) {
|
||||
const auto completion = *possible.cbegin();
|
||||
if (completion.size() > s.size()) {
|
||||
rl_insert_text(requireCString(completion.substr(s.size())));
|
||||
return redisplay();
|
||||
}
|
||||
|
||||
return el_ring_bell();
|
||||
}
|
||||
|
||||
auto checkAllHaveSameAt = [&](size_t pos) {
|
||||
auto & first = *possible.begin();
|
||||
for (auto & p : possible) {
|
||||
if (p.size() <= pos || p[pos] != first[pos]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
size_t start = s.size();
|
||||
size_t len = 0;
|
||||
while (checkAllHaveSameAt(start + len)) {
|
||||
++len;
|
||||
}
|
||||
if (len > 0) {
|
||||
auto commonPrefix = possible.begin()->substr(start, len);
|
||||
rl_insert_text(requireCString(commonPrefix));
|
||||
el_ring_bell();
|
||||
return redisplay();
|
||||
}
|
||||
|
||||
char** columns = copyCompletions(possible);
|
||||
el_print_columns(possible.size(), columns);
|
||||
return redisplay();
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
|
||||
{
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
|
||||
auto rl = repl::Rustyline::new_(rust::to_string(historyFile).as_str(), *repl);
|
||||
match_result(
|
||||
std::move(rl),
|
||||
[&](repl::Rustyline ok) { this->rl = std::make_unique<repl::Rustyline>(std::move(ok)); },
|
||||
[&](rust::Box<rust::Dyn<rust::std::error::Error>> err) {
|
||||
throw Error("%s", Uncolored(to_std_string(err.to_string())));
|
||||
}
|
||||
);
|
||||
el_hist_size = 1000;
|
||||
read_history(requireCString(historyFile));
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
// editline does its own escaping of completions, so we rebind tab
|
||||
// to our own completion function to skip that and do nix escaping
|
||||
// instead of shell escaping.
|
||||
el_bind_key(CTL('I'), doCompletion);
|
||||
return restoreRepl;
|
||||
}
|
||||
|
||||
static rust::Ref<rust::Str> promptForType(ReplPromptType promptType)
|
||||
static constexpr const char * promptForType(ReplPromptType promptType)
|
||||
{
|
||||
switch (promptType) {
|
||||
case ReplPromptType::ReplPrompt:
|
||||
return "nix-repl> "_rs;
|
||||
return "nix-repl> ";
|
||||
case ReplPromptType::ContinuationPrompt:
|
||||
return " "_rs;
|
||||
return " ";
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
auto s = rl->ask(promptForType(promptType));
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
// rustyline temporarily sets a SIGWINCH handler
|
||||
KJ_DEFER(invalidateWindowSize());
|
||||
auto setupSignals = [&]() {
|
||||
act.sa_handler = sigintHandler;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGINT, &act, &old))
|
||||
throw SysError("installing handler for SIGINT");
|
||||
|
||||
return match_result(
|
||||
std::move(s),
|
||||
[&](rust::String ok) {
|
||||
input += to_std_string(ok);
|
||||
input += '\n';
|
||||
return true;
|
||||
},
|
||||
[&](rust::rustyline::error::ReadlineError err) {
|
||||
if (err.matches_Interrupted()) {
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
|
||||
throw SysError("unblocking SIGINT");
|
||||
};
|
||||
auto restoreSignals = [&]() {
|
||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||
throw SysError("restoring signals");
|
||||
|
||||
if (err.matches_Eof()) {
|
||||
return false;
|
||||
}
|
||||
if (sigaction(SIGINT, &old, 0))
|
||||
throw SysError("restoring handler for SIGINT");
|
||||
};
|
||||
|
||||
throw Error("%s", Uncolored(to_std_string(err.into().to_string())));
|
||||
}
|
||||
);
|
||||
setupSignals();
|
||||
char * s = readline(promptForType(promptType)); // NOLINT(lix-unsafe-c-calls)
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
this->writeHistory();
|
||||
input += s;
|
||||
input += '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadlineLikeInteracter::writeHistory()
|
||||
{
|
||||
if (rl) {
|
||||
rl->write_history();
|
||||
int ret = write_history(requireCString(historyFile));
|
||||
int writeHistErr = errno;
|
||||
|
||||
if (ret == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the open fails, editline returns EOF. If the close fails, editline
|
||||
// forwards the return value of fclose(), which is EOF on error.
|
||||
// readline however, returns the errno.
|
||||
// So if we didn't get exactly EOF, then consider the return value the error
|
||||
// code; otherwise use the errno we saved above.
|
||||
// https://github.com/troglobit/editline/issues/66
|
||||
if (ret != EOF) {
|
||||
writeHistErr = ret;
|
||||
}
|
||||
|
||||
// In any of these cases, we should explicitly ignore the error, but log
|
||||
// them so the user isn't confused why their history is getting eaten.
|
||||
|
||||
std::string_view const errMsg(std::strerror(writeHistErr));
|
||||
printTaggedWarning("ignoring error writing repl history to %s: %s", this->historyFile, errMsg);
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
||||
@@ -103,6 +253,11 @@ ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
||||
this->writeHistory();
|
||||
}
|
||||
|
||||
AutomationInteracter::Guard AutomationInteracter::init(detail::ReplCompleterMixin *)
|
||||
{
|
||||
return Guard([] {});
|
||||
}
|
||||
|
||||
// ASCII ENQ character
|
||||
constexpr const char * automationPrompt = "\x05";
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
#pragma once
|
||||
/// @file
|
||||
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace rust::lix::repl {
|
||||
struct Rustyline;
|
||||
}
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace detail {
|
||||
@@ -27,7 +24,9 @@ enum class ReplPromptType {
|
||||
class ReplInteracter
|
||||
{
|
||||
public:
|
||||
virtual void init(detail::ReplCompleterMixin * repl) {}
|
||||
using Guard = Finally<std::function<void()>>;
|
||||
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
|
||||
/** Returns a boolean of whether the interacter got EOF */
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
|
||||
virtual ~ReplInteracter(){};
|
||||
@@ -36,12 +35,12 @@ public:
|
||||
class ReadlineLikeInteracter final : public ReplInteracter
|
||||
{
|
||||
std::string historyFile;
|
||||
std::unique_ptr<rust::lix::repl::Rustyline> rl;
|
||||
|
||||
public:
|
||||
ReadlineLikeInteracter(std::string historyFile);
|
||||
|
||||
virtual void init(detail::ReplCompleterMixin * repl) override;
|
||||
ReadlineLikeInteracter(std::string historyFile)
|
||||
: historyFile(historyFile)
|
||||
{
|
||||
}
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
/** Writes the current history to the history file.
|
||||
*
|
||||
@@ -55,6 +54,7 @@ class AutomationInteracter final : public ReplInteracter
|
||||
{
|
||||
public:
|
||||
AutomationInteracter() = default;
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~AutomationInteracter() override = default;
|
||||
};
|
||||
|
||||
+40
-124
@@ -1,12 +1,10 @@
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <editline.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "libutil/logging.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libcmd/repl-interacter.hh"
|
||||
@@ -379,7 +377,7 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
std::list<ReplLoadable> loadables = std::exchange(loaded, {});
|
||||
loadFiles(loadables);
|
||||
|
||||
interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
||||
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
||||
|
||||
/* Stop the progress bar because it interferes with the display of
|
||||
the repl. */
|
||||
@@ -440,10 +438,15 @@ StringSet NixRepl::completePrefix(const std::string &prefix)
|
||||
{
|
||||
StringSet completions;
|
||||
|
||||
if (prefix.starts_with(':')) {
|
||||
// We should only complete colon commands if there's a colon at the beginning,
|
||||
// but editline (for... whatever reason) doesn't *give* us the colon in the
|
||||
// completion callback. If the user types :rel<TAB>, `prefix` will only be `rel`.
|
||||
// Luckily, editline provides a global variable for its current buffer, so we can
|
||||
// check for the presence of a colon there.
|
||||
if (rl_line_buffer != nullptr && rl_line_buffer[0] == ':') {
|
||||
for (auto const & [colonCmd, cmd] : registeredCommands) {
|
||||
if ((!cmd->attributes.debugModeOnly || inDebugger()) && colonCmd.starts_with(prefix.substr(1))) {
|
||||
completions.insert(":" + colonCmd);
|
||||
if ((!cmd->attributes.debugModeOnly || inDebugger()) && colonCmd.starts_with(prefix)) {
|
||||
completions.insert(std::string(colonCmd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,135 +696,44 @@ void NixRepl::initDebugBuiltinCommands()
|
||||
addCommand(
|
||||
"backtrace",
|
||||
[](NixRepl & repl, const std::string & _arg) {
|
||||
auto tracesGenerator = repl.evaluator.debug->traces();
|
||||
|
||||
// since we want to print the stack trace in reverse order,
|
||||
// we have to first traverse all frames and accumulate them
|
||||
// in a list (in which we store each new trace at the /beginning/)
|
||||
std::list<std::pair<size_t, const DebugTrace *>> reversedTraces;
|
||||
for (const auto trace : tracesGenerator) {
|
||||
// because the original traces are indexed from 0 upto N,
|
||||
// this gives us their original index
|
||||
auto idx = reversedTraces.size();
|
||||
reversedTraces.push_front({idx, trace});
|
||||
}
|
||||
|
||||
for (const auto & [traceIdx, trace] : reversedTraces) {
|
||||
std::cout << "\n" << ANSI_BLUE << traceIdx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, repl.evaluator.positions, *trace);
|
||||
auto traces = repl.evaluator.debug->traces();
|
||||
for (const auto & [idx, i] : enumerate(traces)) {
|
||||
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, repl.evaluator.positions, *i);
|
||||
}
|
||||
return ProcessLineResult::PromptAgain;
|
||||
},
|
||||
{.aliases = {"bt"}, .debugModeOnly = true, .help = "Show trace stack", .section = "Debug mode"}
|
||||
{.aliases = {"bt"},
|
||||
.debugModeOnly = true,
|
||||
.help = "Show trace stack",
|
||||
.section = "Debug mode"}
|
||||
);
|
||||
|
||||
addCommand(
|
||||
"show-trace",
|
||||
// this command has a bit of nuance to its function and error states.
|
||||
// it can either:
|
||||
// 1. be called without any argument
|
||||
// -> just display the current stack frame (still have to walk up the stack :/)
|
||||
// 2. be called with an absolute index
|
||||
// -> try to go to that frame
|
||||
// -> if it doesn't exist, print an "arg out of range" error
|
||||
// 3. be called with a relative index
|
||||
// -> if the final offset is in-bounds, go to that frame
|
||||
// -> otherwise: clamp the index, i.e. go to 0/$max instead of out-of-bounds
|
||||
//
|
||||
// because the collection of frames is lazy and isn't a random-access list,
|
||||
// we need to iterate the whole stack for most of these if we want to have
|
||||
// good error messages; this is the biggest reason why this function is so
|
||||
// long/complex compared to its role
|
||||
//
|
||||
[](NixRepl & repl, const std::string & arg) {
|
||||
auto setTrace = [&](size_t traceIdx, const DebugTrace * trace) {
|
||||
repl.debugTraceIndex = traceIdx;
|
||||
std::cout << "\n" << ANSI_BLUE << traceIdx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, repl.evaluator.positions, *trace);
|
||||
std::cout << std::endl;
|
||||
printEnvBindings(repl.state, trace->expr, trace->env);
|
||||
repl.loadDebugTraceEnv(*trace);
|
||||
};
|
||||
try {
|
||||
repl.debugTraceIndex = stoi(arg);
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
// tries to find a trace at a given index.
|
||||
// - if it is found, it returns the requested trace, along with its
|
||||
// index, which will be *the same* as requested
|
||||
// - otherwise, it returns the last (=outermost) trace, along with
|
||||
// its index, which will be *different* than the one requested
|
||||
auto tryFindTrace = [&](size_t traceIdx) -> std::pair<size_t, const DebugTrace *> {
|
||||
size_t lastIndex = 0;
|
||||
const DebugTrace * lastTrace;
|
||||
auto traces = repl.evaluator.debug->traces();
|
||||
for (const auto & [idx, i] : enumerate(traces)) {
|
||||
lastTrace = i;
|
||||
lastIndex = idx;
|
||||
if (idx == traceIdx) {
|
||||
return std::pair(idx, i);
|
||||
}
|
||||
auto traces = repl.evaluator.debug->traces();
|
||||
for (const auto & [idx, i] : enumerate(traces)) {
|
||||
if (idx == repl.debugTraceIndex) {
|
||||
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, repl.evaluator.positions, *i);
|
||||
std::cout << std::endl;
|
||||
printEnvBindings(repl.state, i->expr, i->env);
|
||||
repl.loadDebugTraceEnv(*i);
|
||||
break;
|
||||
}
|
||||
return std::pair(lastIndex, lastTrace);
|
||||
};
|
||||
|
||||
bool isRelativeIdx = false;
|
||||
int requestedTraceIdx;
|
||||
if (arg.length() == 0) {
|
||||
// if there's no argument, just re-print the current frame
|
||||
requestedTraceIdx = repl.debugTraceIndex;
|
||||
} else {
|
||||
std::optional<int> maybeIdx = string2Int<int>(arg);
|
||||
if (!maybeIdx) {
|
||||
throw Error("argument '%s' is not a valid integer", arg);
|
||||
}
|
||||
|
||||
isRelativeIdx = arg.starts_with('+') || arg.starts_with('-');
|
||||
requestedTraceIdx =
|
||||
isRelativeIdx ? maybeIdx.value() + repl.debugTraceIndex : maybeIdx.value();
|
||||
}
|
||||
|
||||
auto [actualTraceIdx, trace] = tryFindTrace((size_t) requestedTraceIdx);
|
||||
|
||||
// if we *did* find the frame we wanted originally, all is well
|
||||
// in the world and we can just load it and exit
|
||||
if (actualTraceIdx == (size_t) requestedTraceIdx) {
|
||||
setTrace(actualTraceIdx, trace);
|
||||
return ProcessLineResult::PromptAgain;
|
||||
}
|
||||
|
||||
// if we couldn't immediately find the requested trace on the "happy path", then either:
|
||||
|
||||
// a) it was an absolute index but didn't exist
|
||||
// -> print a specific error showing the exact valid range
|
||||
if (!isRelativeIdx) {
|
||||
throw Error(
|
||||
"stack index must be between %ld and %ld (inclusive), but was %ld",
|
||||
0,
|
||||
actualTraceIdx, // tryFindTrace sets *idx to the final (max) frame index if it fails
|
||||
requestedTraceIdx
|
||||
);
|
||||
}
|
||||
|
||||
// b) it was a relative index
|
||||
// -> clamp the index to the bounds and print a warning
|
||||
if (requestedTraceIdx < 0) {
|
||||
// just load frame 0 but print a warning about the bounds
|
||||
std::tie(actualTraceIdx, trace) = tryFindTrace(0);
|
||||
setTrace(actualTraceIdx, trace);
|
||||
printTaggedWarning("stopped at stack frame %ld, cannot go any deeper", 0);
|
||||
return ProcessLineResult::PromptAgain;
|
||||
} else {
|
||||
// (if we're here, then requestedTraceIdx > $max, since tryFindTrace failed)
|
||||
// load the max frame (that `tryFindFrame` kindly already got for us),
|
||||
// but print a warning that we can't go any further
|
||||
setTrace(actualTraceIdx, trace);
|
||||
printTaggedWarning("stopped at stack frame %ld, cannot go any higher", actualTraceIdx);
|
||||
return ProcessLineResult::PromptAgain;
|
||||
}
|
||||
return ProcessLineResult::PromptAgain;
|
||||
},
|
||||
{.aliases = {"st"},
|
||||
.debugModeOnly = true,
|
||||
.help = "Show current trace. If an integer is provided, this switches to that stack "
|
||||
"beforehand. If the integer has an explicit + or - sign, it is treated as "
|
||||
"relative to the current stack index.",
|
||||
"beforehand.",
|
||||
.section = "Debug mode",
|
||||
.positionalArgsSpecifiers = {{.placeholderText = "integer index", .optional = true}}}
|
||||
);
|
||||
@@ -1405,10 +1317,12 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
|
||||
.kind = ReplLoadKind::Flake,
|
||||
};
|
||||
|
||||
Value v;
|
||||
|
||||
try {
|
||||
loaded.remove(loadable);
|
||||
loaded.push_back(loadable);
|
||||
Value v = flake::callFlake(
|
||||
v = flake::callFlake(
|
||||
state,
|
||||
flake::lockFlake(
|
||||
state,
|
||||
@@ -1536,6 +1450,7 @@ Value NixRepl::getReplOverlaysEvalFunction()
|
||||
}
|
||||
|
||||
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
|
||||
*replOverlaysEvalFunction = Value{};
|
||||
auto code =
|
||||
#include "repl-overlays.nix.gen.hh"
|
||||
;
|
||||
@@ -1545,15 +1460,16 @@ Value NixRepl::getReplOverlaysEvalFunction()
|
||||
evaluator.builtins.staticEnv
|
||||
);
|
||||
|
||||
*replOverlaysEvalFunction = state.eval(expr);
|
||||
**replOverlaysEvalFunction = state.eval(expr);
|
||||
|
||||
return **replOverlaysEvalFunction;
|
||||
}
|
||||
|
||||
Value NixRepl::replOverlays()
|
||||
{
|
||||
Value replInits;
|
||||
auto replInitStorage = evaluator.mem.newList(evalSettings.replOverlays.get().size());
|
||||
Value replInits = {NewValueAs::list, replInitStorage};
|
||||
replInits = {NewValueAs::list, replInitStorage};
|
||||
|
||||
size_t i = 0;
|
||||
for (auto path : evalSettings.replOverlays.get()) {
|
||||
|
||||
@@ -36,12 +36,9 @@ int helperMain(const char * name, std::span<char *> args) noexcept
|
||||
other things, determines if kill(-1, signo) affects the
|
||||
calling process. In the OSX libc, it's set to true,
|
||||
which means "follow POSIX", which we don't want here */
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
if (syscall(SYS_kill, -1, SIGKILL, false) == 0) {
|
||||
break;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
if (kill(-1, SIGKILL) == 0) {
|
||||
break;
|
||||
|
||||
@@ -59,7 +59,7 @@ static void setPersonality(std::string_view system)
|
||||
|
||||
bool pathExists(const fs::path & path)
|
||||
{
|
||||
return fs::exists(fs::symlink_status(path)); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
return fs::exists(fs::symlink_status(path));
|
||||
}
|
||||
|
||||
void bindPath(const fs::path & source, const fs::path & target, bool optional = false)
|
||||
@@ -72,7 +72,7 @@ void bindPath(const fs::path & source, const fs::path & target, bool optional =
|
||||
}
|
||||
};
|
||||
|
||||
auto st = fs::symlink_status(source); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
auto st = fs::symlink_status(source);
|
||||
if (st.type() == fs::file_type::not_found) {
|
||||
if (optional) {
|
||||
return;
|
||||
@@ -82,14 +82,14 @@ void bindPath(const fs::path & source, const fs::path & target, bool optional =
|
||||
}
|
||||
|
||||
if (st.type() == fs::file_type::directory) {
|
||||
fs::create_directories(target); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
fs::create_directories(target);
|
||||
bindMount();
|
||||
} else if (st.type() == fs::file_type::symlink) {
|
||||
// Symlinks can (apparently) not be bind-mounted, so just copy it
|
||||
fs::create_directories(target.parent_path()); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
fs::copy_symlink(source, target); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
fs::create_directories(target.parent_path());
|
||||
fs::copy_symlink(source, target);
|
||||
} else {
|
||||
fs::create_directories(target.parent_path()); // NOLINT(lix-foreign-exceptions): caught by main
|
||||
fs::create_directories(target.parent_path());
|
||||
if (kj::AutoCloseFd file{open(target.c_str(), O_RDWR | O_CREAT, 0644)}; file == nullptr) {
|
||||
throw SysError("could not create %s", target);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
LIBEXEC_HELPER(5)
|
||||
|
||||
static int resultFd = -1;
|
||||
|
||||
static void sendResult(int result)
|
||||
{
|
||||
// NOTE posix says pipe writes smaller than PIPE_BUF must be atomic, so this either
|
||||
// succeeds or fails (pipe bufs of four bytes make no sense at all for our systems)
|
||||
int error = result;
|
||||
DIE_UNLESS_SYS("writing result", ::write(resultFd, &error, sizeof(error)));
|
||||
}
|
||||
LIBEXEC_HELPER(4)
|
||||
|
||||
int helperMain(const char *, std::span<char *> args) noexcept
|
||||
{
|
||||
@@ -20,12 +10,8 @@ int helperMain(const char *, std::span<char *> args) noexcept
|
||||
std::string_view method = args[1];
|
||||
const auto dir = args[2];
|
||||
const auto name = args[3];
|
||||
resultFd = argToInt<int>("result-fd", args[4]);
|
||||
|
||||
if (chdir(dir)) {
|
||||
sendResult(errno);
|
||||
return 0;
|
||||
}
|
||||
DIE_UNLESS_SYS("chdir", chdir(dir));
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
@@ -37,11 +23,9 @@ int helperMain(const char *, std::span<char *> args) noexcept
|
||||
}
|
||||
|
||||
if (method == "bind") {
|
||||
auto result = bind(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ? errno : 0;
|
||||
sendResult(result);
|
||||
DIE_UNLESS_SYS("bind", bind(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
|
||||
} else if (method == "connect") {
|
||||
auto result = connect(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ? errno : 0;
|
||||
sendResult(result);
|
||||
DIE_UNLESS_SYS("connect", connect(socket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)));
|
||||
} else {
|
||||
die(std::format("invalid method %s", method));
|
||||
}
|
||||
|
||||
+13
-30
@@ -7,7 +7,8 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s, bool allowRhsTrailingDot)
|
||||
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::string cur;
|
||||
@@ -15,22 +16,6 @@ std::vector<std::string> parseAttrPath(std::string_view const s, bool allowRhsTr
|
||||
auto i = s.begin();
|
||||
while (i != s.end()) {
|
||||
if (*i == '.') {
|
||||
if (!haveData) {
|
||||
if (res.empty()) {
|
||||
throw ParseError(
|
||||
"Leading dot in attribute selection path '%1%' is not allowed! If the attribute name "
|
||||
"is an empty string, use '\"\".foo.bar'",
|
||||
s
|
||||
);
|
||||
} else {
|
||||
throw ParseError(
|
||||
"consecutive dots not allowed in selection path '%1%', use 'foo.\"\".bar' to denote "
|
||||
"an "
|
||||
"empty attribute name",
|
||||
s
|
||||
);
|
||||
}
|
||||
}
|
||||
res.push_back(cur);
|
||||
haveData = false;
|
||||
cur.clear();
|
||||
@@ -51,11 +36,7 @@ std::vector<std::string> parseAttrPath(std::string_view const s, bool allowRhsTr
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (haveData) {
|
||||
res.push_back(cur);
|
||||
} else if (!allowRhsTrailingDot) {
|
||||
throw ParseError("Trailing dot on the right-hand side of path expr '%1%' is not allowed!", s);
|
||||
};
|
||||
if (haveData) res.push_back(cur);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -109,6 +90,9 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (!attrIndex) {
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
if (v.type() != nAttrs) {
|
||||
auto pathPart =
|
||||
std::vector<std::string>(tokens.begin(), tokens.begin() + attrPathIdx);
|
||||
@@ -179,14 +163,13 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au
|
||||
|
||||
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
{
|
||||
Value v2 = [&]() {
|
||||
try {
|
||||
auto dummyArgs = state.ctx.mem.allocBindings(0);
|
||||
return findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
|
||||
} catch (Error &) {
|
||||
throw NoPositionInfo("package '%s' has no source location information", what);
|
||||
}
|
||||
}();
|
||||
Value v2;
|
||||
try {
|
||||
auto dummyArgs = state.ctx.mem.allocBindings(0);
|
||||
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
|
||||
} catch (Error &) {
|
||||
throw NoPositionInfo("package '%s' has no source location information", what);
|
||||
}
|
||||
|
||||
// FIXME: is it possible to extract the Pos object instead of doing this
|
||||
// toString + parsing?
|
||||
|
||||
@@ -25,7 +25,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
||||
* Such an attr path is a dot-separated sequence of attribute names, which are possibly quoted.
|
||||
* No escaping is performed; attribute names containing double quotes are unrepresentable.
|
||||
*/
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s, bool allowRhsTrailingDot = true);
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s);
|
||||
|
||||
/**
|
||||
* Converts an attr path from a list of strings into a string once more.
|
||||
|
||||
+11
-2
@@ -23,11 +23,20 @@ Bindings * EvalMemory::allocBindings(size_t capacity)
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings();
|
||||
}
|
||||
|
||||
void BindingsBuilder::insert(std::string_view name, Value value, PosIdx pos)
|
||||
|
||||
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||
{
|
||||
return insert(symbols.create(name), value, pos);
|
||||
bindings->push_back(Attr(name, {}, pos));
|
||||
return (bindings->end() - 1)->value;
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
|
||||
{
|
||||
return alloc(symbols.create(name), pos);
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
if (size_) std::sort(begin(), end());
|
||||
|
||||
@@ -25,8 +25,7 @@ struct Attr
|
||||
PosIdx pos;
|
||||
mutable Value value;
|
||||
Attr(Symbol name, Value value, PosIdx pos = noPos) : name(name), pos(pos), value(value) {}
|
||||
[[deprecated]]
|
||||
Attr() {};
|
||||
Attr() { };
|
||||
bool operator < (const Attr & a) const
|
||||
{
|
||||
return name < a.name;
|
||||
@@ -73,9 +72,8 @@ public:
|
||||
|
||||
const Attr * get(Symbol name)
|
||||
{
|
||||
iterator i = std::lower_bound(begin(), end(), name, [](const Attr & value, const Symbol & compare) {
|
||||
return value.name < compare;
|
||||
});
|
||||
Attr key(name, {});
|
||||
iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return &*i;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -142,8 +140,6 @@ public:
|
||||
insert(Attr(name, value, pos));
|
||||
}
|
||||
|
||||
void insert(std::string_view name, Value value, PosIdx pos = noPos);
|
||||
|
||||
void insert(const Attr & attr)
|
||||
{
|
||||
push_back(attr);
|
||||
@@ -155,6 +151,10 @@ public:
|
||||
bindings->push_back(attr);
|
||||
}
|
||||
|
||||
Value & alloc(Symbol name, PosIdx pos = noPos);
|
||||
|
||||
Value & alloc(std::string_view name, PosIdx pos = noPos);
|
||||
|
||||
[[nodiscard("must use created bindings")]]
|
||||
Bindings * finish()
|
||||
{
|
||||
|
||||
@@ -23,14 +23,70 @@ countDown 2
|
||||
Then, evaluating the file will give the following stack trace:
|
||||
|
||||
```console
|
||||
$ nix-instantiate err.nix
|
||||
$ nix-instantiate --show-trace err.nix
|
||||
error:
|
||||
… from call site
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:9:1:
|
||||
8| in
|
||||
9| countDown 2
|
||||
| ^
|
||||
10|
|
||||
|
||||
… while calling 'countDown'
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:3:5:
|
||||
2| countDown =
|
||||
3| n:
|
||||
| ^
|
||||
4| if n == 0 then
|
||||
|
||||
… while calling the 'addErrorContext' builtin
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:7:7:
|
||||
6| else
|
||||
7| builtins.addErrorContext "while counting down; n = ${toString n}" ("x" + countDown (n - 1));
|
||||
| ^
|
||||
8| in
|
||||
|
||||
… while counting down; n = 2
|
||||
|
||||
… from call site
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:7:80:
|
||||
6| else
|
||||
7| builtins.addErrorContext "while counting down; n = ${toString n}" ("x" + countDown (n - 1));
|
||||
| ^
|
||||
8| in
|
||||
|
||||
… while calling 'countDown'
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:3:5:
|
||||
2| countDown =
|
||||
3| n:
|
||||
| ^
|
||||
4| if n == 0 then
|
||||
|
||||
… while calling the 'addErrorContext' builtin
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:7:7:
|
||||
6| else
|
||||
7| builtins.addErrorContext "while counting down; n = ${toString n}" ("x" + countDown (n - 1));
|
||||
| ^
|
||||
8| in
|
||||
|
||||
… while counting down; n = 1
|
||||
|
||||
… from call site
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:7:80:
|
||||
6| else
|
||||
7| builtins.addErrorContext "while counting down; n = ${toString n}" ("x" + countDown (n - 1));
|
||||
| ^
|
||||
8| in
|
||||
|
||||
… while calling 'countDown'
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:3:5:
|
||||
2| countDown =
|
||||
3| n:
|
||||
| ^
|
||||
4| if n == 0 then
|
||||
|
||||
… caused by explicit throw
|
||||
at err.nix:5:7:
|
||||
at /home/plop/git.lix.systems/lix-project/lix/err.nix:5:7:
|
||||
4| if n == 0 then
|
||||
5| throw "kaboom"
|
||||
| ^
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
name: ceil
|
||||
args: [number]
|
||||
args: [double]
|
||||
---
|
||||
Returns an integer value containing the smallest integer greater than or equal to *number* (which is either a floating-point or integer value).
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next higher integer.
|
||||
|
||||
If the result is out of range for the integer type, such as NaN, infinity, or a number with magnitude out of range, Lix throws an evaluation error.
|
||||
|
||||
Lix currently throws an evaluation error for some *integer* inputs between 2\*\*52 and 2\*\*63 - 1 as those previously experienced floating-point precision loss due to a Nix bug (https://github.com/NixOS/nix/issues/12899).
|
||||
In a future release, such integers will be passed through.
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
name: floor
|
||||
args: [number]
|
||||
args: [double]
|
||||
---
|
||||
Returns an integer value containing the largest integer less than or equal to *number* (which is either a float or integer).
|
||||
Converts an IEEE-754 double-precision floating-point number (*double*) to
|
||||
the next lower integer.
|
||||
|
||||
If the result is out of range for the integer type, such as NaN, infinity, or a number with magnitude out of range, Lix throws an evaluation error.
|
||||
|
||||
Lix currently throws an evaluation error for some *integer* inputs between 2\*\*52 and 2\*\*63 - 1 as those previously experienced floating-point precision loss due to a Nix bug (https://github.com/NixOS/nix/issues/12899).
|
||||
In a future release, such integers will be passed through.
|
||||
If the datatype is neither an integer nor a "float", an evaluation error will be
|
||||
thrown.
|
||||
|
||||
@@ -10,8 +10,7 @@ present in *args*. All are optional except `path`:
|
||||
|
||||
- name\
|
||||
The name of the path when added to the store. This can used to
|
||||
reference paths that have
|
||||
[nix-illegal characters in their names](./derivations.md),
|
||||
reference paths that have nix-illegal characters in their names,
|
||||
like `@`.
|
||||
|
||||
- filter\
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: scopedImport
|
||||
implementation: "[](EvalState & state, Value ** args) -> Value { return import(state, *args[1], args[0]); }"
|
||||
implementation: "[](EvalState & state, Value ** args, Value & v) { import(state, *args[1], args[0], v); }"
|
||||
args: [scope, path]
|
||||
renameInGlobalScope: false
|
||||
---
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
ref<AttrCursor> getRoot();
|
||||
};
|
||||
|
||||
enum AttrType : uint8_t {
|
||||
enum AttrType {
|
||||
Placeholder = 0,
|
||||
FullAttrs = 1,
|
||||
String = 2,
|
||||
|
||||
+89
-78
@@ -1,5 +1,4 @@
|
||||
#include "eval.hh"
|
||||
#include "libexpr/nixexpr.hh"
|
||||
#include "primops.hh"
|
||||
#include "gc-small-vector.hh"
|
||||
|
||||
@@ -11,19 +10,14 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
Value Expr::makeThunk(Evaluator & ctx, Env & env)
|
||||
{
|
||||
ctx.stats.nrThunks++;
|
||||
return {NewValueAs::thunk, ctx.mem, env, *this};
|
||||
}
|
||||
|
||||
/* Create a thunk for the delayed computation of the given expression
|
||||
in the given environment. But if the expression is a variable,
|
||||
then look it up right away. This significantly reduces the number
|
||||
of thunks allocated. */
|
||||
Value Expr::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
return makeThunk(state.ctx, env);
|
||||
state.ctx.stats.nrThunks++;
|
||||
return {NewValueAs::thunk, state.ctx.mem, env, *this};
|
||||
}
|
||||
|
||||
Value ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||
@@ -105,10 +99,18 @@ Value ExprSet::eval(EvalState & state, Env & env)
|
||||
in the original environment. */
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs) {
|
||||
Env & thunkEnv = *i.second.chooseByKind(&env2, &env, inheritEnv);
|
||||
Value vAttr = hasOverrides && i.second.kind != ExprAttrs::AttrDef::Kind::Inherited
|
||||
? i.second.e->makeThunk(state.ctx, thunkEnv)
|
||||
: i.second.e->maybeThunk(state, thunkEnv);
|
||||
Value vAttr;
|
||||
if (hasOverrides && i.second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
||||
vAttr = {
|
||||
NewValueAs::thunk,
|
||||
state.ctx.mem,
|
||||
*i.second.chooseByKind(&env2, &env, inheritEnv),
|
||||
*i.second.e
|
||||
};
|
||||
state.ctx.stats.nrThunks++;
|
||||
} else {
|
||||
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
}
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs()->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||
}
|
||||
@@ -160,18 +162,18 @@ Value ExprSet::eval(EvalState & state, Env & env)
|
||||
* http://github.com/NixOS/nix/issues/7012. Any accesses to the output attrset will thus infrec.
|
||||
*/
|
||||
Value vBackup = v;
|
||||
Symbol nameSym;
|
||||
Value nameVal;
|
||||
{
|
||||
KJ_DEFER(v = vBackup);
|
||||
v = Value{NewValueAs::blackhole};
|
||||
Value nameVal = i.nameExpr->eval(state, *dynamicEnv);
|
||||
nameVal = i.nameExpr->eval(state, *dynamicEnv);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type() == nNull) {
|
||||
continue;
|
||||
}
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
nameSym = state.ctx.symbols.create(nameVal.str());
|
||||
}
|
||||
auto nameSym = state.ctx.symbols.create(nameVal.str());
|
||||
auto j = v.attrs()->get(nameSym);
|
||||
if (j) {
|
||||
state.ctx.errors
|
||||
@@ -185,29 +187,7 @@ Value ExprSet::eval(EvalState & state, Env & env)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
/* This line is so wrong that it is best kept in here with the documentation why it is wrong,
|
||||
* lest some naive soul may add it once again some year in the future.
|
||||
* See the following witness as to why it is wrong:
|
||||
*
|
||||
* nix-repl> fun = (name: { ${name} = x: x; }) # This function creates a dynamic attribute with a variable name
|
||||
* Added fun.
|
||||
* nix-repl> revSeq = x: y: builtins.seq x (builtins.seq y x) # evaluate x, then y in sequence, then return x
|
||||
* Added revSeq.
|
||||
* nix-repl> fun "foo" # The code seemingly works
|
||||
* { foo = «lambda foo @ «string»:1:26»; }
|
||||
* nix-repl> fun "bar" #
|
||||
* { bar = «lambda bar @ «string»:1:26»; }
|
||||
* nix-repl> revSeq (fun "foo") (fun "bar") # Until it doesn't
|
||||
* { foo = «lambda bar @ «string»:1:26»; }
|
||||
*
|
||||
* What happened? Expressions are AST bound, therefore all lambdas share the same Expr and thus *the same name*.
|
||||
* Using `setName` here updates the name of *all* lambdas from that expression, not just of the value at hand.
|
||||
* And this is why all expressions must be treated as immutable after parsing.
|
||||
*/
|
||||
/* i.valueExpr->setName(nameSym); */
|
||||
// clang-format on
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
v.attrs()->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
|
||||
v.attrs()->sort(); // FIXME: inefficient
|
||||
@@ -349,21 +329,57 @@ Value ExprOpUpdate::eval(EvalState & state, Env & env)
|
||||
Value v2 = e2->eval(state, env);
|
||||
state.checkAttrs(v2, env, *e2);
|
||||
|
||||
return state.updateAttrs(v1, v2);
|
||||
state.ctx.stats.nrOpUpdates++;
|
||||
|
||||
if (v1.attrs()->size() == 0) {
|
||||
return v2;
|
||||
}
|
||||
if (v2.attrs()->size() == 0) {
|
||||
return v1;
|
||||
}
|
||||
|
||||
auto attrs = state.ctx.buildBindings(v1.attrs()->size() + v2.attrs()->size());
|
||||
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs()->begin();
|
||||
Bindings::iterator j = v2.attrs()->begin();
|
||||
|
||||
while (i != v1.attrs()->end() && j != v2.attrs()->end()) {
|
||||
if (i->name == j->name) {
|
||||
attrs.insert(*j);
|
||||
++i;
|
||||
++j;
|
||||
} else if (i->name < j->name) {
|
||||
attrs.insert(*i++);
|
||||
} else {
|
||||
attrs.insert(*j++);
|
||||
}
|
||||
}
|
||||
|
||||
while (i != v1.attrs()->end()) {
|
||||
attrs.insert(*i++);
|
||||
}
|
||||
while (j != v2.attrs()->end()) {
|
||||
attrs.insert(*j++);
|
||||
}
|
||||
|
||||
Value v = {NewValueAs::attrs, attrs.alreadySorted()};
|
||||
|
||||
state.ctx.stats.nrOpUpdateValuesCopied += v.attrs()->size();
|
||||
return v;
|
||||
}
|
||||
|
||||
Value ExprOpConcatLists::eval(EvalState & state, Env & env)
|
||||
{
|
||||
state.ctx.stats.nrListConcats++;
|
||||
|
||||
/* We could simply call into `state.concatLists`, but that would add a redundant trace to our errors,
|
||||
* and to fix that we would need to make the error on it and `forceList` optional, and *sigh*
|
||||
*/
|
||||
/* We don't call into `concatLists` as that loses the position information of the expressions. */
|
||||
|
||||
Value v1 = e1->eval(state, env);
|
||||
state.checkList(v1, env, *this); // Pass in `this` instead of `e1` to make the error point to the `++`
|
||||
state.checkList(v1, env, *e1);
|
||||
Value v2 = e2->eval(state, env);
|
||||
state.checkList(v2, env, *this); // Pass in `this` instead of `e2` to make the error point to the `++`
|
||||
state.checkList(v2, env, *e2);
|
||||
|
||||
size_t l1 = v1.listSize(), l2 = v2.listSize(), len = l1 + l2;
|
||||
|
||||
@@ -413,12 +429,12 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
};
|
||||
|
||||
// List of returned strings. References to these Values must NOT be persisted.
|
||||
SmallTemporaryValueVector<conservativeStackReservation> values;
|
||||
values.reserve(es.size());
|
||||
SmallTemporaryValueVector<conservativeStackReservation> values(es.size());
|
||||
Value * vTmpP = values.data();
|
||||
|
||||
for (auto & [i_pos, i] : es) {
|
||||
values.push_back(i->eval(state, env));
|
||||
Value & vTmp = values.back();
|
||||
Value & vTmp = *vTmpP++;
|
||||
vTmp = i->eval(state, env);
|
||||
|
||||
/* If the first element is a path, then the result will also
|
||||
be a path, we don't copy anything (yet - that's done later,
|
||||
@@ -436,7 +452,7 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
} else {
|
||||
state.ctx.errors
|
||||
.make<EvalError>("integer overflow in adding %1% + %2%", n, vTmp.integer())
|
||||
.atPos(isInterpolation ? i_pos : pos)
|
||||
.atPos(i_pos)
|
||||
.debugThrow();
|
||||
}
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
@@ -446,7 +462,7 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
nf += vTmp.fpoint();
|
||||
} else {
|
||||
state.ctx.errors.make<EvalError>("cannot add %1% to an integer", showType(vTmp))
|
||||
.atPos(isInterpolation ? i_pos : pos)
|
||||
.atPos(i_pos)
|
||||
.withFrame(env, *this)
|
||||
.debugThrow();
|
||||
}
|
||||
@@ -457,7 +473,7 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
nf += vTmp.fpoint();
|
||||
} else {
|
||||
state.ctx.errors.make<EvalError>("cannot add %1% to a float", showType(vTmp))
|
||||
.atPos(isInterpolation ? i_pos : pos)
|
||||
.atPos(i_pos)
|
||||
.withFrame(env, *this)
|
||||
.debugThrow();
|
||||
}
|
||||
@@ -473,18 +489,13 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
: StringCoercionMode::Strict;
|
||||
|
||||
/* skip canonization of first path, which would only be not
|
||||
canonized in the first place if it's coming from a ./${foo} type
|
||||
path */
|
||||
canonized in the first place if it's coming from a ./${foo} type
|
||||
path */
|
||||
auto part = state.coerceToString(
|
||||
isInterpolation ? i_pos : pos,
|
||||
i_pos,
|
||||
vTmp,
|
||||
context,
|
||||
(isInterpolation && firstType == nPath)
|
||||
? "while evaluating a path interpolation"
|
||||
: (isInterpolation ? "while evaluating a string interpolation"
|
||||
// TODO: to the person who eventually cleans up all of this mess,
|
||||
// please turn this into "cannot concatenate $type to a $type" instead.
|
||||
: "while concatenating"),
|
||||
"while evaluating a path segment",
|
||||
coercionMode,
|
||||
firstType == nString,
|
||||
!first
|
||||
@@ -516,7 +527,9 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env)
|
||||
|
||||
Value ExprPos::eval(EvalState & state, Env & env)
|
||||
{
|
||||
return state.mkPos(pos);
|
||||
Value v;
|
||||
state.mkPos(v, pos);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value ExprBlackHole::eval(EvalState & state, Env & env)
|
||||
@@ -606,20 +619,19 @@ Value ExprSelect::eval(EvalState & state, Env & env)
|
||||
// Position for the current selector in this select chain.
|
||||
PosIdx posCurrentSyntax;
|
||||
|
||||
Value baseSelectee = [&]() {
|
||||
try {
|
||||
// Evaluate the original thing we're selecting on.
|
||||
return e->eval(state, env);
|
||||
} catch (Error & e) {
|
||||
// clang-format off
|
||||
e.addTrace(state.ctx.positions[getPos()], HintFmt(
|
||||
"while evaluating an expression to select '%s' on it",
|
||||
showAttrPath(state.ctx.symbols, attrPath)
|
||||
));
|
||||
// clang-format on
|
||||
throw;
|
||||
}
|
||||
}();
|
||||
Value baseSelectee;
|
||||
try {
|
||||
// Evaluate the original thing we're selecting on.
|
||||
baseSelectee = e->eval(state, env);
|
||||
} catch (Error & e) {
|
||||
// clang-format off
|
||||
e.addTrace(state.ctx.positions[getPos()], HintFmt(
|
||||
"while evaluating an expression to select '%s' on it",
|
||||
showAttrPath(state.ctx.symbols, attrPath)
|
||||
));
|
||||
// clang-format on
|
||||
throw;
|
||||
}
|
||||
|
||||
try {
|
||||
// With the original selectee evaluated, we'll walk the selection path starting
|
||||
@@ -705,10 +717,9 @@ Value ExprCall::eval(EvalState & state, Env & env)
|
||||
// 5: under 10
|
||||
// This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150
|
||||
// total.
|
||||
SmallValueVector<4> vArgs;
|
||||
vArgs.reserve(args.size());
|
||||
SmallValueVector<4> vArgs(args.size());
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
vArgs.push_back(args[i]->maybeThunk(state, env));
|
||||
vArgs[i] = args[i]->maybeThunk(state, env);
|
||||
}
|
||||
|
||||
return state.callFunction(vFun, vArgs, pos);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/gc-alloc.hh"
|
||||
#include "value.hh"
|
||||
#include <cstdint>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
+79
-107
@@ -297,29 +297,30 @@ EvalPaths::EvalPaths(
|
||||
}
|
||||
}
|
||||
|
||||
#if LIX_MAJOR >= 3 || (LIX_MAJOR == 2 && LIX_MINOR >= 98)
|
||||
#if LIX_MAJOR >= 3 || (LIX_MAJOR == 2 && LIX_MINOR >= 96)
|
||||
#warning \
|
||||
"The feature nix-path-shadow was deprecated in 2.95 with a warning, error in 2.96, and we should consider removing the bypass in 2.98"
|
||||
"The feature nix-path-shadow was deprecated in 2.95 with a warning, which needs to be turned into an error in 2.96"
|
||||
#endif
|
||||
|
||||
if (!featureSettings.isEnabled(DeprecatedFeature::NixPathShadow)) {
|
||||
for (auto & [prefix, path] : searchPath_.elements) {
|
||||
// Match on the 'nix' prefix
|
||||
if (prefix.s == "nix") {
|
||||
throw EvalError(HintFmt(
|
||||
"The prefix '%s' is reserved for internal use by Lix in the Nix search "
|
||||
"path, its usage is deprecated and will be forbidden in the future.\n"
|
||||
"Use %s to silence this error.\n"
|
||||
"This is due to adding '%s=%s' in the Nix search path, either through the "
|
||||
"environment variable '%s' or by passing the flag %s to the nix "
|
||||
"invocation.",
|
||||
"nix",
|
||||
"--extra-deprecated-features nix-path-shadow",
|
||||
prefix.s,
|
||||
path.s,
|
||||
"NIX_PATH",
|
||||
"-I"
|
||||
));
|
||||
logWarning(
|
||||
{.msg = HintFmt(
|
||||
"The prefix '%s' is reserved for internal use by Lix in the Nix search "
|
||||
"path, its usage is deprecated and will be forbidden in the future.\n"
|
||||
"Use %s to silence this warning.\n"
|
||||
"This is due to adding '%s=%s' in the Nix search path, either through the "
|
||||
"environment variable '%s' or by passing the flag %s to the nix "
|
||||
"invocation.",
|
||||
"nix",
|
||||
"--extra-deprecated-features nix-path-shadow",
|
||||
prefix.s,
|
||||
path.s,
|
||||
"NIX_PATH",
|
||||
"-I"
|
||||
)}
|
||||
);
|
||||
} else
|
||||
// Match prefixless paths that contain a `nix` directory
|
||||
if (auto res =
|
||||
@@ -332,19 +333,21 @@ EvalPaths::EvalPaths(
|
||||
});
|
||||
}))
|
||||
{
|
||||
throw EvalError(HintFmt(
|
||||
"Shadowing '%s' by configuring the nix-path is deprecated and "
|
||||
"will be forbidden in the future.\n"
|
||||
"Use %s to silence this error.\n"
|
||||
"This is due to adding '%s' to the nix-path without a prefix, "
|
||||
"either by passing the flag '-I %s' to the nix invocation or by "
|
||||
"adding this path to the environment variable '%s'.",
|
||||
"<nix/...>",
|
||||
"--extra-deprecated-features nix-path-shadow",
|
||||
path.s,
|
||||
path.s,
|
||||
"NIX_PATH"
|
||||
));
|
||||
logWarning(
|
||||
{.msg = HintFmt(
|
||||
"Shadowing '%s' by configuring the nix-path is deprecated and "
|
||||
"will be forbidden in the future.\n"
|
||||
"Use %s to silence this warning.\n"
|
||||
"This is due to adding '%s' to the nix-path without a prefix, "
|
||||
"either by passing the flag '-I %s' to the nix invocation or by "
|
||||
"adding this path to the environment variable '%s'.",
|
||||
"<nix/...>",
|
||||
"--extra-deprecated-features nix-path-shadow",
|
||||
path.s,
|
||||
path.s,
|
||||
"NIX_PATH"
|
||||
)}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,11 +437,11 @@ void EvalPaths::allowPath(const StorePath & storePath)
|
||||
allowPath(store->toRealPath(storePath));
|
||||
}
|
||||
|
||||
Value EvalPaths::allowAndSetStorePathString(const StorePath & storePath)
|
||||
void EvalPaths::allowAndSetStorePathString(const StorePath & storePath, Value & v)
|
||||
{
|
||||
allowPath(storePath);
|
||||
|
||||
return mkStorePathString(storePath);
|
||||
mkStorePathString(storePath, v);
|
||||
}
|
||||
|
||||
CheckedSourcePath EvalPaths::checkSourcePath(const SourcePath & path_)
|
||||
@@ -733,13 +736,13 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En
|
||||
// add 'with' bindings.
|
||||
Bindings::iterator j = env.values[0].attrs()->begin();
|
||||
while (j != env.values[0].attrs()->end()) {
|
||||
vm.insert_or_assign(std::string(st[j->name]), j->value);
|
||||
vm[std::string(st[j->name])] = j->value;
|
||||
++j;
|
||||
}
|
||||
} else {
|
||||
// iterate through staticenv bindings and add them.
|
||||
for (auto & i : se.vars)
|
||||
vm.insert_or_assign(std::string(st[i.first]), env.values[i.second]);
|
||||
vm[std::string(st[i.first])] = env.values[i.second];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -847,26 +850,26 @@ Value::List * EvalMemory::newList(size_t size)
|
||||
|
||||
Value Evaluator::evalLazily(Expr & e)
|
||||
{
|
||||
return e.makeThunk(*this, builtins.env);
|
||||
stats.nrThunks++;
|
||||
return {NewValueAs::thunk, mem, builtins.env, e};
|
||||
}
|
||||
|
||||
Value EvalState::mkPos(PosIdx p)
|
||||
void EvalState::mkPos(Value & v, PosIdx p)
|
||||
{
|
||||
auto origin = ctx.positions.originOf(p);
|
||||
if (auto path = std::get_if<CheckedSourcePath>(&origin)) {
|
||||
auto attrs = ctx.buildBindings(3);
|
||||
attrs.insert(ctx.symbols.sym_file, {NewValueAs::string, path->to_string()});
|
||||
auto [line, col] = makePositionThunks(*this, p);
|
||||
attrs.insert(ctx.symbols.sym_line, line);
|
||||
attrs.insert(ctx.symbols.sym_column, col);
|
||||
return {NewValueAs::attrs, attrs};
|
||||
attrs.alloc(ctx.symbols.sym_file) = {NewValueAs::string, path->to_string()};
|
||||
makePositionThunks(*this, p, attrs.alloc(ctx.symbols.sym_line), attrs.alloc(ctx.symbols.sym_column));
|
||||
v = {NewValueAs::attrs, attrs};
|
||||
} else
|
||||
return Value::VNULL;
|
||||
v.mkNull();
|
||||
}
|
||||
|
||||
Value EvalPaths::mkStorePathString(const StorePath & p)
|
||||
|
||||
void EvalPaths::mkStorePathString(const StorePath & p, Value & v)
|
||||
{
|
||||
return {
|
||||
v = {
|
||||
NewValueAs::string,
|
||||
store->printStorePath(p),
|
||||
NixStringContext{
|
||||
@@ -882,9 +885,13 @@ std::string EvalState::mkOutputStringRaw(
|
||||
return ctx.store->printStorePath(staticOutputPath);
|
||||
}
|
||||
|
||||
Value EvalState::mkOutputString(const SingleDerivedPath::Built & b, const StorePath & staticOutputPath)
|
||||
|
||||
void EvalState::mkOutputString(
|
||||
Value & value,
|
||||
const SingleDerivedPath::Built & b,
|
||||
const StorePath & staticOutputPath)
|
||||
{
|
||||
return {NewValueAs::string, mkOutputStringRaw(staticOutputPath), NixStringContext{b}};
|
||||
value = {NewValueAs::string, mkOutputStringRaw(staticOutputPath), NixStringContext{b}};
|
||||
}
|
||||
|
||||
|
||||
@@ -907,6 +914,19 @@ std::string EvalState::mkSingleDerivedPathStringRaw(
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkSingleDerivedPathString(
|
||||
const SingleDerivedPath & p,
|
||||
Value & v)
|
||||
{
|
||||
v = {
|
||||
NewValueAs::string,
|
||||
mkSingleDerivedPathStringRaw(p),
|
||||
NixStringContext{
|
||||
std::visit([](auto && v) -> NixStringContextElem { return v; }, p),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct CachedEvalFile
|
||||
{
|
||||
Value result;
|
||||
@@ -1118,9 +1138,8 @@ Env & AttrsPattern::match(
|
||||
|
||||
Value EvalState::callFunction(Value & fun, std::span<Value> args, const PosIdx pos)
|
||||
{
|
||||
if (callDepth >= evalSettings.maxCallDepth) {
|
||||
if (callDepth > evalSettings.maxCallDepth)
|
||||
ctx.errors.make<EvalError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow();
|
||||
}
|
||||
MaintainCount _level(callDepth);
|
||||
|
||||
auto trace = evalSettings.traceFunctionCalls
|
||||
@@ -1189,24 +1208,18 @@ Value EvalState::callFunction(Value & fun, std::span<Value> args, const PosIdx p
|
||||
for (unsigned i = 0; i < argsLeft; i++) {
|
||||
pargs[i] = &args[i];
|
||||
}
|
||||
vCur = fn->fun(*this, pargs.data());
|
||||
fn->fun(*this, pargs.data(), vCur);
|
||||
} catch (ThrownError & e) {
|
||||
// Distinguish between an error that simply happened while "throw"
|
||||
// was being evaluated and an explicit thrown error.
|
||||
if (fn->name == "throw" && !e.hasTrace()) {
|
||||
if (fn->name == "throw") {
|
||||
e.addTrace(ctx.positions[pos], "caused by explicit %s", "throw");
|
||||
}
|
||||
// otherwise, print a trace of this builtin, as long as it isn't
|
||||
// a 'addErrorContext' call (which would just create noise)
|
||||
else if (fn->name != "addErrorContext")
|
||||
{
|
||||
} else {
|
||||
e.addTrace(ctx.positions[pos], "while calling the '%s' builtin", fn->name);
|
||||
}
|
||||
throw;
|
||||
} catch (Error & e) {
|
||||
if (fn->name != "addErrorContext") {
|
||||
e.addTrace(ctx.positions[pos], "while calling the '%1%' builtin", fn->name);
|
||||
}
|
||||
e.addTrace(ctx.positions[pos], "while calling the '%1%' builtin", fn->name);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -1250,11 +1263,9 @@ Value EvalState::callFunction(Value & fun, std::span<Value> args, const PosIdx p
|
||||
// 1. Unify this and above code. Heavily redundant.
|
||||
// 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2 etc)
|
||||
// so the debugger allows to inspect the wrong parameters passed to the builtin.
|
||||
vCur = fn->fun(*this, vArgs.data());
|
||||
fn->fun(*this, vArgs.data(), vCur);
|
||||
} catch (Error & e) {
|
||||
if (fn->name != "addErrorContext") {
|
||||
e.addTrace(ctx.positions[pos], "while calling the '%1%' builtin", fn->name);
|
||||
}
|
||||
e.addTrace(ctx.positions[pos], "while calling the '%1%' builtin", fn->name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -1353,49 +1364,9 @@ https://docs.lix.systems/manual/lix/stable/language/constructs.html#functions)",
|
||||
return callFunction(fun, vAttrs, pos);
|
||||
}
|
||||
|
||||
Value EvalState::updateAttrs(const Value & v1, const Value & v2)
|
||||
{
|
||||
ctx.stats.nrOpUpdates++;
|
||||
|
||||
if (v1.attrs()->size() == 0) {
|
||||
return v2;
|
||||
}
|
||||
if (v2.attrs()->size() == 0) {
|
||||
return v1;
|
||||
}
|
||||
|
||||
auto attrs = ctx.buildBindings(v1.attrs()->size() + v2.attrs()->size());
|
||||
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs()->begin();
|
||||
Bindings::iterator j = v2.attrs()->begin();
|
||||
|
||||
while (i != v1.attrs()->end() && j != v2.attrs()->end()) {
|
||||
if (i->name == j->name) {
|
||||
attrs.insert(*j);
|
||||
++i;
|
||||
++j;
|
||||
} else if (i->name < j->name) {
|
||||
attrs.insert(*i++);
|
||||
} else {
|
||||
attrs.insert(*j++);
|
||||
}
|
||||
}
|
||||
|
||||
while (i != v1.attrs()->end()) {
|
||||
attrs.insert(*i++);
|
||||
}
|
||||
while (j != v2.attrs()->end()) {
|
||||
attrs.insert(*j++);
|
||||
}
|
||||
|
||||
Value v = {NewValueAs::attrs, attrs.alreadySorted()};
|
||||
ctx.stats.nrOpUpdateValuesCopied += v.attrs()->size();
|
||||
return v;
|
||||
}
|
||||
|
||||
Value EvalState::concatLists(std::span<Value> lists, const PosIdx pos, std::string_view errorCtx)
|
||||
void EvalState::concatLists(
|
||||
Value & v, std::span<Value> lists, const PosIdx pos, std::string_view errorCtx
|
||||
)
|
||||
{
|
||||
ctx.stats.nrListConcats++;
|
||||
|
||||
@@ -1411,10 +1382,12 @@ Value EvalState::concatLists(std::span<Value> lists, const PosIdx pos, std::stri
|
||||
}
|
||||
|
||||
if (nonEmpty && len == nonEmpty->listSize()) {
|
||||
return *nonEmpty;
|
||||
v = *nonEmpty;
|
||||
return;
|
||||
}
|
||||
|
||||
auto list = ctx.mem.newList(len);
|
||||
v = {NewValueAs::list, list};
|
||||
auto out = list->elems;
|
||||
for (size_t n = 0, pos = 0; n < lists.size(); ++n) {
|
||||
auto l = lists[n].listSize();
|
||||
@@ -1423,7 +1396,6 @@ Value EvalState::concatLists(std::span<Value> lists, const PosIdx pos, std::stri
|
||||
}
|
||||
pos += l;
|
||||
}
|
||||
return {NewValueAs::list, list};
|
||||
}
|
||||
|
||||
// always force this to be separate, otherwise forceValue may inline it and take
|
||||
|
||||
+20
-6
@@ -370,7 +370,7 @@ public:
|
||||
/**
|
||||
* Allow access to a store path and return it as a string.
|
||||
*/
|
||||
Value allowAndSetStorePathString(const StorePath & storePath);
|
||||
void allowAndSetStorePathString(const StorePath & storePath, Value & v);
|
||||
|
||||
/**
|
||||
* Check whether access to a path is allowed and throw an error if
|
||||
@@ -449,7 +449,7 @@ public:
|
||||
* The string is the printed store path with a context containing a
|
||||
* single `NixStringContextElem::Opaque` element of that store path.
|
||||
*/
|
||||
Value mkStorePathString(const StorePath & storePath);
|
||||
void mkStorePathString(const StorePath & storePath, Value & v);
|
||||
};
|
||||
|
||||
struct EvalStatistics
|
||||
@@ -800,7 +800,7 @@ public:
|
||||
*/
|
||||
Value autoCallFunction(Bindings & args, Value & fun, PosIdx pos);
|
||||
|
||||
Value mkPos(PosIdx pos);
|
||||
void mkPos(Value & v, PosIdx pos);
|
||||
|
||||
/**
|
||||
* Create a string representing a `SingleDerivedPath::Built`.
|
||||
@@ -809,16 +809,30 @@ public:
|
||||
* single `NixStringContextElem::Built` element of the drv path and
|
||||
* output name.
|
||||
*
|
||||
* @param value Value we are settings
|
||||
*
|
||||
* @param b the drv whose output we are making a string for, and the
|
||||
* output
|
||||
*
|
||||
* @param staticOutputPath Output path for that string.
|
||||
* Will be printed to form string.
|
||||
*/
|
||||
Value mkOutputString(const SingleDerivedPath::Built & b, const StorePath & staticOutputPath);
|
||||
void mkOutputString(
|
||||
Value & value,
|
||||
const SingleDerivedPath::Built & b,
|
||||
const StorePath & staticOutputPath);
|
||||
|
||||
Value updateAttrs(const Value & v1, const Value & v2);
|
||||
Value concatLists(std::span<Value> lists, const PosIdx pos, std::string_view errorCtx);
|
||||
/**
|
||||
* Create a string representing a `SingleDerivedPath`.
|
||||
*
|
||||
* A combination of `mkStorePathString` and `mkOutputString`.
|
||||
*/
|
||||
void mkSingleDerivedPathString(
|
||||
const SingleDerivedPath & p,
|
||||
Value & v);
|
||||
|
||||
void
|
||||
concatLists(Value & v, std::span<Value> lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -8,24 +8,25 @@ namespace nix {
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
Value prim_addDrvOutputDependencies(EvalState & state, Value ** args);
|
||||
Value prim_fetchTree(EvalState & state, Value ** args);
|
||||
Value prim_fetchGit(EvalState & state, Value ** args);
|
||||
Value prim_fetchMercurial(EvalState & state, Value ** args);
|
||||
Value prim_fetchTarball(EvalState & state, Value ** args);
|
||||
Value prim_fetchurl(EvalState & state, Value ** args);
|
||||
Value prim_fromTOML(EvalState & state, Value ** args);
|
||||
Value prim_appendContext(EvalState & state, Value ** args);
|
||||
Value prim_getContext(EvalState & state, Value ** args);
|
||||
Value prim_hasContext(EvalState & state, Value ** args);
|
||||
Value prim_unsafeDiscardOutputDependency(EvalState & state, Value ** args);
|
||||
Value prim_unsafeDiscardStringContext(EvalState & state, Value ** args);
|
||||
void prim_addDrvOutputDependencies(EvalState & state, Value * * args, Value & v);
|
||||
void prim_fetchTree(EvalState & state, Value * * args, Value & v);
|
||||
void prim_fetchGit(EvalState & state, Value * * args, Value & v);
|
||||
void prim_fetchMercurial(EvalState & state, Value ** args, Value & v);
|
||||
void prim_fetchTarball(EvalState & state, Value * * args, Value & v);
|
||||
void prim_fetchurl(EvalState & state, Value * * args, Value & v);
|
||||
void prim_fromTOML(EvalState & state, Value * * args, Value & v);
|
||||
void prim_appendContext(EvalState & state, Value ** args, Value & v);
|
||||
void prim_getContext(EvalState & state, Value * * args, Value & v);
|
||||
void prim_hasContext(EvalState & state, Value * * args, Value & v);
|
||||
void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v);
|
||||
void prim_unsafeDiscardStringContext(EvalState & state, Value ** args, Value & v);
|
||||
|
||||
namespace flake {
|
||||
|
||||
Value prim_flakeRefToString(EvalState & state, Value ** args);
|
||||
Value prim_getFlake(EvalState & state, Value ** args);
|
||||
Value prim_parseFlakeRef(EvalState & state, Value ** args);
|
||||
void prim_flakeRefToString(EvalState & state, Value * * args, Value & v);
|
||||
void prim_getFlake(EvalState & state, Value * * args, Value & v);
|
||||
void prim_parseFlakeRef(EvalState & state, Value * * args, Value & v);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+69
-125
@@ -4,7 +4,6 @@
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
#include "lix/libfetchers/fetch-settings.hh"
|
||||
#include <cctype>
|
||||
|
||||
namespace nix::flake {
|
||||
|
||||
@@ -31,64 +30,6 @@ static void writeTrustedList(const TrustedList & trustedList)
|
||||
writeFile(path, JSON(trustedList).dump());
|
||||
}
|
||||
|
||||
static bool
|
||||
askForEachSetting(TrustedList & trustedList, std::map<std::string, std::string> & untrustedSettings)
|
||||
{
|
||||
// clang-format off
|
||||
constexpr auto prompt =
|
||||
"[" ANSI_BOLD "y" ANSI_NORMAL "]es for now/"
|
||||
"[" ANSI_BOLD "N" ANSI_NORMAL "]o for now/"
|
||||
"[" ANSI_BOLD "a" ANSI_NORMAL "]lways allow";
|
||||
// clang-format on
|
||||
|
||||
auto didTrustedListChange = false;
|
||||
int acceptedCount = 0;
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
auto reply =
|
||||
logger
|
||||
->ask(
|
||||
fmt("Do you want to allow setting '" ANSI_MAGENTA "%s = %s" ANSI_NORMAL "'? (%s) ",
|
||||
name,
|
||||
valueS,
|
||||
prompt)
|
||||
)
|
||||
.value_or("n");
|
||||
|
||||
reply = toLower(reply);
|
||||
|
||||
static const std::string yes = "yes for now";
|
||||
static const std::string no = "no for now";
|
||||
static const std::string always = "always allow";
|
||||
|
||||
while (true) {
|
||||
if (no.starts_with(reply)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (yes.starts_with(reply) || always.starts_with(reply)) {
|
||||
if (reply[0] == 'a') {
|
||||
trustedList[name][valueS] = true;
|
||||
didTrustedListChange = true;
|
||||
}
|
||||
|
||||
globalConfig.set(name, valueS);
|
||||
acceptedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
// if the reply wasn't a prefix of any answer, ask the user again
|
||||
reply = logger->ask(fmt("Couldn't understand reply.\n%s: ", prompt)).value_or("n");
|
||||
}
|
||||
}
|
||||
|
||||
if (didTrustedListChange) {
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
// return false if *none* of the settings were accepted
|
||||
return acceptedCount > 0;
|
||||
}
|
||||
|
||||
static bool batchAskForSetting(
|
||||
bool & negativeTrustOverride,
|
||||
TrustedList & trustedList,
|
||||
@@ -102,74 +43,77 @@ static bool batchAskForSetting(
|
||||
|
||||
printWarning("%s", warning);
|
||||
|
||||
// clang-format off
|
||||
constexpr auto globalPrompt =
|
||||
"[" ANSI_BOLD "y" ANSI_NORMAL "]es for now/"
|
||||
"[" ANSI_BOLD "n" ANSI_NORMAL "]o for now/"
|
||||
"[" ANSI_BOLD "a" ANSI_NORMAL "]lways allow/"
|
||||
"[" ANSI_BOLD "I" ANSI_NORMAL "]ndividually review";
|
||||
// clang-format on
|
||||
auto reply = logger
|
||||
->ask(
|
||||
fmt("Do you want to allow configuration settings to be applied?\nThis may allow the "
|
||||
"flake to gain root, see the nix.conf manual page (" ANSI_BOLD "y" ANSI_NORMAL
|
||||
"es for now/" ANSI_BOLD "A" ANSI_NORMAL "llow always/" ANSI_BOLD "n" ANSI_NORMAL
|
||||
"o/" ANSI_BOLD "N" ANSI_NORMAL "o to all) ")
|
||||
)
|
||||
.value_or('n');
|
||||
|
||||
auto reply =
|
||||
logger
|
||||
->ask(
|
||||
fmt("Do you want to allow these configuration settings to be applied?\n" ANSI_BOLD
|
||||
"This may allow the flake to gain root" ANSI_NORMAL ", see the nix.conf manual page.\n"
|
||||
"(%s) ",
|
||||
globalPrompt)
|
||||
)
|
||||
.value_or("I"); // if the answer is empty (or there is no interactive prompt),
|
||||
// just default to reviewing each individually
|
||||
|
||||
reply = toLower(reply);
|
||||
|
||||
static const std::string yes = "yes for now";
|
||||
static const std::string no = "no for now";
|
||||
static const std::string always = "always allow";
|
||||
static const std::string review = "individually review";
|
||||
|
||||
// when interactive, loops and reprompts until the reply is one of y/n/a/i (or any prefix of the answers)
|
||||
while (true) {
|
||||
if (no.starts_with(reply)) {
|
||||
printWarning("Rejecting all untrusted nix.conf entries");
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied by "
|
||||
"flakes",
|
||||
"accept-flake-config",
|
||||
false
|
||||
);
|
||||
negativeTrustOverride = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (yes.starts_with(reply) || always.starts_with(reply)) {
|
||||
auto alwaysAllow = reply[0] == 'a';
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
if (alwaysAllow) {
|
||||
trustedList[name][valueS] = true;
|
||||
}
|
||||
globalConfig.set(name, valueS);
|
||||
}
|
||||
|
||||
if (alwaysAllow) {
|
||||
printTaggedWarning(
|
||||
"adding these configuration settings to the trusted list at %s, "
|
||||
"edit it if you want to remove them in the future",
|
||||
trustedListPath()
|
||||
);
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (review.starts_with(reply)) {
|
||||
return askForEachSetting(trustedList, untrustedSettings);
|
||||
}
|
||||
|
||||
// if the reply wasn't a prefix of any, ask the user again
|
||||
reply = logger->ask(fmt("Couldn't understand reply.\n%s: ", globalPrompt)).value_or("n");
|
||||
if (reply == 'N') {
|
||||
printWarning("Rejecting all untrusted nix.conf entries");
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied by "
|
||||
"flakes",
|
||||
"accept-flake-config",
|
||||
false
|
||||
);
|
||||
negativeTrustOverride = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply == 'y' || reply == 'A') {
|
||||
auto alwaysAllow = reply == 'A';
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
if (alwaysAllow) {
|
||||
trustedList[name][valueS] = true;
|
||||
}
|
||||
globalConfig.set(name, valueS);
|
||||
}
|
||||
|
||||
if (alwaysAllow) {
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied "
|
||||
"by flakes",
|
||||
"accept-flake-config",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
auto didTrustedListChange = false;
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
auto individualReply = logger
|
||||
->ask(
|
||||
fmt("Do you want to allow setting '%s = %s'? (" ANSI_BOLD
|
||||
"y" ANSI_NORMAL "es for now/" ANSI_BOLD "A" ANSI_NORMAL
|
||||
"llow always/" ANSI_BOLD "n" ANSI_NORMAL "o for now) ",
|
||||
name,
|
||||
valueS)
|
||||
)
|
||||
.value_or('n');
|
||||
|
||||
if (individualReply == 'y' || individualReply == 'A') {
|
||||
if (individualReply == 'A') {
|
||||
trustedList[name][valueS] = true;
|
||||
didTrustedListChange = true;
|
||||
}
|
||||
|
||||
globalConfig.set(name, valueS);
|
||||
}
|
||||
}
|
||||
|
||||
if (didTrustedListChange) {
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigFile::apply()
|
||||
|
||||
+31
-39
@@ -78,21 +78,10 @@ try {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
/** Force a value that cannot contain any function calls */
|
||||
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
||||
{
|
||||
/* /piegames sighs at the settings API */
|
||||
auto prevOverridden = evalSettings.maxCallDepth.overridden;
|
||||
auto prevMaxCallDepth = evalSettings.maxCallDepth;
|
||||
evalSettings.maxCallDepth.override(0);
|
||||
|
||||
/* /piegames sighs at C++ */
|
||||
KJ_DEFER({
|
||||
evalSettings.maxCallDepth.override(prevMaxCallDepth);
|
||||
evalSettings.maxCallDepth.overridden = prevOverridden;
|
||||
});
|
||||
|
||||
state.forceValue(value, pos);
|
||||
if (value.isThunk() && value.isTrivial())
|
||||
state.forceValue(value, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,18 +327,12 @@ static Flake getFlake(
|
||||
Expr & flakeExpr = state.ctx.parseExprFromFile(resolvedFlakeFile);
|
||||
|
||||
// Enforce that 'flake.nix' is a direct attrset, not a computation.
|
||||
// We do this by disallowing any function calls (maxCallDepth 0) and checking that the resulting value is
|
||||
// an attrset.
|
||||
// The logic is already implemented in `forceTrivialValue`, but that takes a value instead of an Expr, so
|
||||
// we wrap the expression in a thunk to be able to call it.
|
||||
Value vInfo = flakeExpr.maybeThunk(state, state.ctx.builtins.env);
|
||||
forceTrivialValue(state, vInfo, flakeExpr.getPos());
|
||||
|
||||
if (vInfo.type() != nAttrs) {
|
||||
state.ctx.errors.make<EvalError>("file '%s' must be an attribute set", resolvedFlakeFile)
|
||||
.debugThrow();
|
||||
if (!flakeExpr.try_cast<ExprAttrs>()) {
|
||||
state.ctx.errors.make<EvalError>("file '%s' must be an attribute set", resolvedFlakeFile).debugThrow();
|
||||
}
|
||||
|
||||
Value vInfo = state.eval(flakeExpr);
|
||||
|
||||
if (auto description = vInfo.attrs()->get(state.ctx.symbols.sym_description)) {
|
||||
expectType(state, nString, description->value, description->pos);
|
||||
flake.description = description->value.str();
|
||||
@@ -959,7 +942,10 @@ LockedFlake lockFlake(
|
||||
|
||||
Value callFlake(EvalState & state, const LockedFlake & lockedFlake)
|
||||
{
|
||||
Value vLocks = {NewValueAs::string, lockedFlake.lockFile.to_string()};
|
||||
Value vLocks;
|
||||
Value vRootSubdir;
|
||||
|
||||
vLocks = {NewValueAs::string, lockedFlake.lockFile.to_string()};
|
||||
|
||||
Value vRootSrc = emitTreeAttrs(
|
||||
state.ctx,
|
||||
@@ -969,13 +955,14 @@ Value callFlake(EvalState & state, const LockedFlake & lockedFlake)
|
||||
lockedFlake.flake.forceDirty
|
||||
);
|
||||
|
||||
Value vRootSubdir = {NewValueAs::string, lockedFlake.flake.lockedRef.subdir};
|
||||
vRootSubdir = {NewValueAs::string, lockedFlake.flake.lockedRef.subdir};
|
||||
|
||||
if (!state.ctx.caches.vCallFlake) {
|
||||
state.ctx.caches.vCallFlake = allocRootValue(state.eval(state.ctx.parseExprFromString(
|
||||
state.ctx.caches.vCallFlake = allocRootValue({});
|
||||
*state.ctx.caches.vCallFlake = state.eval(state.ctx.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, CanonPath::root
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
Value vTmp1 = state.callFunction(*state.ctx.caches.vCallFlake, vLocks, noPos);
|
||||
@@ -983,14 +970,14 @@ Value callFlake(EvalState & state, const LockedFlake & lockedFlake)
|
||||
return state.callFunction(vTmp2, vRootSubdir, noPos);
|
||||
}
|
||||
|
||||
Value prim_getFlake(EvalState & state, Value ** args)
|
||||
void prim_getFlake(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], noPos, "while evaluating the argument passed to builtins.getFlake"));
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s' (use --impure to override)", flakeRefS);
|
||||
|
||||
return callFlake(
|
||||
v = callFlake(
|
||||
state,
|
||||
lockFlake(
|
||||
state,
|
||||
@@ -1005,7 +992,10 @@ Value prim_getFlake(EvalState & state, Value ** args)
|
||||
);
|
||||
}
|
||||
|
||||
Value prim_parseFlakeRef(EvalState & state, Value ** args)
|
||||
void prim_parseFlakeRef(
|
||||
EvalState & state,
|
||||
Value * * args,
|
||||
Value & v)
|
||||
{
|
||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], noPos,
|
||||
"while evaluating the argument passed to builtins.parseFlakeRef"));
|
||||
@@ -1013,26 +1003,28 @@ Value prim_parseFlakeRef(EvalState & state, Value ** args)
|
||||
auto binds = state.ctx.buildBindings(attrs.size());
|
||||
for (const auto & [key, value] : attrs) {
|
||||
auto s = state.ctx.symbols.create(key);
|
||||
Value vv = std::visit(
|
||||
auto & vv = binds.alloc(s);
|
||||
std::visit(
|
||||
overloaded{
|
||||
[](const std::string & value) -> Value { return {NewValueAs::string, value}; },
|
||||
[](const uint64_t & value) -> Value { return {NewValueAs::integer, NixInt::Inner(value)}; },
|
||||
[](const Explicit<bool> & value) -> Value { return {NewValueAs::boolean, value.t}; }
|
||||
[&vv](const std::string & value) { vv = {NewValueAs::string, value}; },
|
||||
[&vv](const uint64_t & value) { vv = {NewValueAs::integer, NixInt::Inner(value)}; },
|
||||
[&vv](const Explicit<bool> & value) { vv = {NewValueAs::boolean, value.t}; }
|
||||
},
|
||||
value
|
||||
);
|
||||
binds.insert(s, vv);
|
||||
}
|
||||
return {NewValueAs::attrs, binds};
|
||||
v = {NewValueAs::attrs, binds};
|
||||
}
|
||||
|
||||
Value prim_flakeRefToString(EvalState & state, Value ** args)
|
||||
void prim_flakeRefToString(
|
||||
EvalState & state,
|
||||
Value * * args,
|
||||
Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], noPos,
|
||||
"while evaluating the argument passed to builtins.flakeRefToString");
|
||||
fetchers::Attrs attrs;
|
||||
for (const auto & attr : *args[0]->attrs()) {
|
||||
state.forceValue(attr.value, noPos);
|
||||
auto t = attr.value.type();
|
||||
if (t == nInt) {
|
||||
auto intValue = attr.value.integer().value;
|
||||
@@ -1059,7 +1051,7 @@ Value prim_flakeRefToString(EvalState & state, Value ** args)
|
||||
}
|
||||
}
|
||||
auto flakeRef = FlakeRef::fromAttrs(attrs);
|
||||
return {NewValueAs::string, flakeRef.to_string()};
|
||||
v = {NewValueAs::string, flakeRef.to_string()};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,6 +67,16 @@ FlakeRef parseFlakeRef(
|
||||
return flakeRef;
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> maybeParseFlakeRef(
|
||||
const std::string & url, const std::optional<Path> & baseDir)
|
||||
{
|
||||
try {
|
||||
return parseFlakeRef(url, baseDir);
|
||||
} catch (Error &) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
@@ -224,6 +234,16 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||
const std::string & url, const std::optional<Path> & baseDir)
|
||||
{
|
||||
try {
|
||||
return parseFlakeRefWithFragment(url, baseDir);
|
||||
} catch (Error & e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs)
|
||||
{
|
||||
auto attrs2(attrs);
|
||||
|
||||
@@ -70,12 +70,18 @@ FlakeRef parseFlakeRef(
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
|
||||
std::optional<FlakeRef> maybeParseFlake(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||
|
||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
|
||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||
|
||||
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
|
||||
@@ -1,75 +1,115 @@
|
||||
#include "lix/libexpr/json-to-value.hh"
|
||||
#include "gc-alloc.hh"
|
||||
#include "libutil/types.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
|
||||
#include <limits>
|
||||
#include <variant>
|
||||
|
||||
namespace nix {
|
||||
|
||||
// for more information, refer to
|
||||
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
|
||||
class JSONSax : nlohmann::json_sax<JSON> {
|
||||
struct WIPObject
|
||||
{
|
||||
GcMap<Symbol, Value> entries;
|
||||
Symbol nextKey;
|
||||
};
|
||||
using WIPArray = GcVector<Value>;
|
||||
|
||||
// A stack of all of the currently opened and not yet closed JSON objects and arrays at the current moment
|
||||
// of parsing
|
||||
std::vector<std::variant<WIPObject, WIPArray>> stack;
|
||||
|
||||
std::optional<Value> final_value;
|
||||
|
||||
void addValue(Value v)
|
||||
{
|
||||
if (stack.size() == 0) {
|
||||
assert(!final_value);
|
||||
final_value = v;
|
||||
return;
|
||||
class JSONState {
|
||||
protected:
|
||||
std::unique_ptr<JSONState> parent;
|
||||
RootValue v;
|
||||
public:
|
||||
virtual std::unique_ptr<JSONState> resolve(EvalState &)
|
||||
{
|
||||
assert(false && "tried to close toplevel json parser state");
|
||||
}
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](WIPObject & currentObject) {
|
||||
currentObject.entries.insert_or_assign(currentObject.nextKey, v);
|
||||
currentObject.nextKey = {}; // clear the current symbol
|
||||
},
|
||||
[&](WIPArray & arr) { arr.push_back(v); }
|
||||
},
|
||||
stack.back()
|
||||
);
|
||||
}
|
||||
explicit JSONState(std::unique_ptr<JSONState> && p) : parent(std::move(p)) {}
|
||||
JSONState() = default;
|
||||
JSONState(JSONState & p) = delete;
|
||||
Value & value()
|
||||
{
|
||||
if (!v) {
|
||||
v = allocRootValue({});
|
||||
}
|
||||
return *v;
|
||||
}
|
||||
virtual ~JSONState() {}
|
||||
virtual void add() {}
|
||||
};
|
||||
|
||||
class JSONObjectState : public JSONState {
|
||||
using JSONState::JSONState;
|
||||
GcMap<Symbol, Value> attrs;
|
||||
Symbol _key;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto attrs2 = state.ctx.buildBindings(attrs.size());
|
||||
for (auto & i : attrs)
|
||||
attrs2.insert(i.first, i.second);
|
||||
parent->value() = {NewValueAs::attrs, attrs2.alreadySorted()};
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override
|
||||
{
|
||||
attrs.insert_or_assign(_key, value());
|
||||
v = nullptr;
|
||||
}
|
||||
public:
|
||||
void key(string_t & name, EvalState & state)
|
||||
{
|
||||
_key = state.ctx.symbols.create(name);
|
||||
}
|
||||
};
|
||||
|
||||
class JSONListState : public JSONState {
|
||||
GcVector<Value> values;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto list = state.ctx.mem.newList(values.size());
|
||||
parent->value() = {NewValueAs::list, list};
|
||||
for (size_t n = 0; n < values.size(); ++n) {
|
||||
list->elems[n] = values[n];
|
||||
}
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override
|
||||
{
|
||||
values.push_back(*v);
|
||||
v = nullptr;
|
||||
}
|
||||
public:
|
||||
JSONListState(std::unique_ptr<JSONState> && p, std::size_t reserve) : JSONState(std::move(p))
|
||||
{
|
||||
values.reserve(reserve);
|
||||
}
|
||||
};
|
||||
|
||||
EvalState & state;
|
||||
std::unique_ptr<JSONState> rs;
|
||||
|
||||
public:
|
||||
JSONSax(EvalState & state) : state(state) {};
|
||||
JSONSax(EvalState & state) : state(state), rs(new JSONState()) {};
|
||||
|
||||
Value result()
|
||||
{
|
||||
return final_value.value();
|
||||
return rs->value();
|
||||
}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
addValue(Value::VNULL);
|
||||
rs->value().mkNull();
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
addValue({NewValueAs::boolean, val});
|
||||
rs->value() = {NewValueAs::boolean, val};
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val) override
|
||||
{
|
||||
addValue({NewValueAs::integer, val});
|
||||
rs->value() = {NewValueAs::integer, val};
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,19 +121,22 @@ public:
|
||||
return number_float(static_cast<number_float_t>(val_), "");
|
||||
}
|
||||
NixInt::Inner val = val_;
|
||||
addValue({NewValueAs::integer, val});
|
||||
rs->value() = {NewValueAs::integer, val};
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t & s) override
|
||||
{
|
||||
addValue({NewValueAs::floating, val});
|
||||
rs->value() = {NewValueAs::floating, val};
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t & val) override
|
||||
{
|
||||
addValue({NewValueAs::string, val});
|
||||
rs->value() = {NewValueAs::string, val};
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -108,66 +151,29 @@ public:
|
||||
|
||||
bool start_object(std::size_t len) override
|
||||
{
|
||||
stack.emplace_back(std::in_place_type<WIPObject>);
|
||||
rs = std::make_unique<JSONObjectState>(std::move(rs));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t & name) override
|
||||
{
|
||||
auto & back = stack.back();
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](WIPObject & frame) {
|
||||
assert(!frame.nextKey);
|
||||
frame.nextKey = state.ctx.symbols.create(name);
|
||||
},
|
||||
[](const WIPArray &) { assert(false && "tried adding a json key to an array value??"); }
|
||||
},
|
||||
back
|
||||
);
|
||||
dynamic_cast<JSONObjectState*>(rs.get())->key(name, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override {
|
||||
auto back = std::move(stack.back());
|
||||
stack.pop_back();
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const WIPObject & frame) {
|
||||
auto attrs2 = state.ctx.buildBindings(frame.entries.size());
|
||||
for (auto & i : frame.entries) {
|
||||
attrs2.insert(i.first, i.second);
|
||||
}
|
||||
addValue({NewValueAs::attrs, attrs2.alreadySorted()});
|
||||
},
|
||||
[](const WIPArray &) { assert(false && "tried to close a JSON object while in an array"); }
|
||||
},
|
||||
back
|
||||
);
|
||||
rs = rs->resolve(state);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override {
|
||||
auto back = std::move(stack.back());
|
||||
stack.pop_back();
|
||||
std::visit(
|
||||
overloaded{
|
||||
[](const WIPObject &) { assert(false && "tried to close a JSON array while in an object"); },
|
||||
[&](const WIPArray & values) {
|
||||
auto list = state.ctx.mem.newList(values.size());
|
||||
std::ranges::copy(values, list->elems);
|
||||
addValue({NewValueAs::list, list});
|
||||
}
|
||||
},
|
||||
back
|
||||
);
|
||||
return true;
|
||||
return end_object();
|
||||
}
|
||||
|
||||
bool start_array(size_t len) override {
|
||||
auto v = WIPArray{};
|
||||
v.reserve(len != std::numeric_limits<size_t>::max() ? len : 128);
|
||||
stack.push_back(std::move(v));
|
||||
rs = std::make_unique<JSONListState>(std::move(rs),
|
||||
len != std::numeric_limits<size_t>::max() ? len : 128);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -178,13 +184,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Value parseJSON(EvalState & state, const std::string_view & s_)
|
||||
void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
|
||||
{
|
||||
JSONSax parser(state);
|
||||
bool res = JSON::sax_parse(s_, &parser);
|
||||
if (!res)
|
||||
throw JSONParseError("Invalid JSON Value");
|
||||
return parser.result();
|
||||
v = parser.result();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ struct Value;
|
||||
|
||||
MakeError(JSONParseError, Error);
|
||||
|
||||
Value parseJSON(EvalState & state, const std::string_view & s);
|
||||
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,13 @@ JSON ExprVar::toJSON(const SymbolTable & symbols) const
|
||||
return {{"_type", "ExprVar"}, {"value", stringToJSON(symbols[name])}};
|
||||
}
|
||||
|
||||
JSON ExprInheritFrom::toJSON(SymbolTable const & symbols) const
|
||||
{
|
||||
return {
|
||||
{"_type", "ExprInheritFrom"}
|
||||
};
|
||||
}
|
||||
|
||||
JSON ExprSelect::toJSON(const SymbolTable & symbols) const
|
||||
{
|
||||
JSON out = {
|
||||
@@ -740,6 +747,17 @@ void ExprLambda::setName(Symbol name)
|
||||
body->setName(name);
|
||||
}
|
||||
|
||||
|
||||
std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||
{
|
||||
std::string id(name
|
||||
? concatStrings("'", state.ctx.symbols[name], "'")
|
||||
: "anonymous function");
|
||||
return fmt("%1% at %2%", id, state.ctx.positions[pos]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Position table. */
|
||||
|
||||
Pos PosTable::operator[](PosIdx p) const
|
||||
|
||||
+21
-46
@@ -126,15 +126,10 @@ public:
|
||||
std::unique_ptr<Expr> parsed, Evaluator & es, const std::shared_ptr<const StaticEnv> & env
|
||||
);
|
||||
|
||||
virtual JSON toJSON(const SymbolTable & symbols) const = 0;
|
||||
virtual JSON toJSON(const SymbolTable & symbols) const;
|
||||
virtual void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) = 0;
|
||||
virtual Value eval(EvalState & state, Env & env);
|
||||
Value makeThunk(Evaluator & ctx, Env & env);
|
||||
virtual Value maybeThunk(EvalState & state, Env & env);
|
||||
/* Lambdas have a name associated with them, when they are declared in a binding:
|
||||
* `identity = x: x` will print the resulting value as `«lambda identity @ «string»:1:14»`.
|
||||
* This is set in the parser. After parsing, all expressions are immutable.
|
||||
*/
|
||||
virtual void setName(Symbol name);
|
||||
PosIdx getPos() const { return pos; }
|
||||
|
||||
@@ -180,7 +175,7 @@ struct ExprLiteral : Expr
|
||||
{
|
||||
protected:
|
||||
Value v;
|
||||
ExprLiteral(const PosIdx pos, Value v) : Expr(pos), v(v) {};
|
||||
ExprLiteral(const PosIdx pos) : Expr(pos) {};
|
||||
public:
|
||||
Value maybeThunk(EvalState & state, Env & env) override;
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
@@ -188,46 +183,34 @@ public:
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
};
|
||||
|
||||
struct ExprInt : private std::tuple<Value::Int>, ExprLiteral
|
||||
struct ExprInt : ExprLiteral
|
||||
{
|
||||
ExprInt(const PosIdx pos, NixInt n)
|
||||
: tuple({{Value::Acb::tInt}, n})
|
||||
, ExprLiteral(
|
||||
pos,
|
||||
Value::isTaggableInteger(n) ? Value{NewValueAs::integer, n} : Value(std::get<Value::Int>(*this))
|
||||
)
|
||||
Value::Int i;
|
||||
ExprInt(const PosIdx pos, NixInt n) : ExprLiteral(pos), i{{Value::Acb::tInt}, n}
|
||||
{
|
||||
v = Value::isTaggableInteger(n) ? Value{NewValueAs::integer, n} : Value(i);
|
||||
}
|
||||
ExprInt(const PosIdx pos, NixInt::Inner n) : ExprInt(pos, NixInt(n)) {}
|
||||
};
|
||||
|
||||
struct ExprFloat : private std::tuple<Value::Float>, ExprLiteral
|
||||
struct ExprFloat : ExprLiteral
|
||||
{
|
||||
Value::Float f;
|
||||
ExprFloat(const PosIdx pos, NewValueAs::floating_t, double f)
|
||||
: tuple({{Value::Acb::tFloat}, f})
|
||||
, ExprLiteral(pos, Value(std::get<Value::Float>(*this)))
|
||||
: ExprLiteral(pos)
|
||||
, f{{Value::Acb::tFloat}, f}
|
||||
{
|
||||
v = Value(this->f);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprStringBase
|
||||
struct ExprString : ExprLiteral
|
||||
{
|
||||
std::unique_ptr<Value::Str, Value::Str::Deleter> contents;
|
||||
Value::String strcb;
|
||||
protected:
|
||||
ExprStringBase(std::string_view s, const char ** context = nullptr)
|
||||
: contents(Value::Str::copy(s))
|
||||
, strcb{.content = contents.get(), .context = context}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprString : private ExprStringBase, ExprLiteral
|
||||
{
|
||||
ExprString(const PosIdx pos, std::string s)
|
||||
: ExprStringBase(s)
|
||||
, ExprLiteral(pos, Value{NewValueAs::string, &strcb})
|
||||
Value::String strcb{.content = contents.get(), .context = nullptr};
|
||||
ExprString(const PosIdx pos, std::string s) : ExprLiteral(pos), contents(Value::Str::copy(s))
|
||||
{
|
||||
v = {NewValueAs::string, &strcb};
|
||||
}
|
||||
|
||||
std::string_view str() const
|
||||
@@ -236,11 +219,11 @@ struct ExprString : private ExprStringBase, ExprLiteral
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprPath : private ExprStringBase, ExprLiteral
|
||||
struct ExprPath : ExprLiteral
|
||||
{
|
||||
ExprPath(const PosIdx pos, std::string s)
|
||||
: ExprStringBase(s, Value::String::path)
|
||||
, ExprLiteral(pos, Value{NewValueAs::path, &strcb})
|
||||
std::unique_ptr<Value::Str, Value::Str::Deleter> contents;
|
||||
Value::String strcb{.content = contents.get(), .context = Value::String::path};
|
||||
ExprPath(const PosIdx pos, std::string s) : ExprLiteral(pos), contents(Value::Str::copy(s))
|
||||
{
|
||||
v = Value{NewValueAs::path, &strcb};
|
||||
}
|
||||
@@ -303,11 +286,7 @@ struct ExprInheritFrom : Expr
|
||||
{
|
||||
}
|
||||
|
||||
JSON toJSON(const SymbolTable & symbols) const override
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
Value eval(EvalState & state, Env & env) override;
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
};
|
||||
@@ -522,6 +501,7 @@ struct ExprLambda : Expr
|
||||
{
|
||||
}
|
||||
void setName(Symbol name) override;
|
||||
std::string showNamePos(const EvalState & state) const;
|
||||
|
||||
/** Returns the name of the lambda,
|
||||
* or "anonymous lambda" if it doesn't have one.
|
||||
@@ -660,11 +640,6 @@ struct ExprPos : Expr
|
||||
/* only used to mark thunks as black holes. */
|
||||
struct ExprBlackHole : Expr
|
||||
{
|
||||
JSON toJSON(const SymbolTable & symbols) const override
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
Value eval(EvalState & state, Env & env) override;
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
};
|
||||
|
||||
@@ -133,24 +133,14 @@ inline void State::badEscapeFound(const PosIdx pos, char found, bool isIndented)
|
||||
{
|
||||
auto escape = std::string(isIndented ? "''\\" : "\\");
|
||||
auto interpolEscape = std::string(isIndented ? "''${" : "\\${");
|
||||
auto backslashChar = std::string("\\") + found;
|
||||
|
||||
HintFmt dedicatedInsert = isIndented
|
||||
? HintFmt("you can simply write it as %s in the string", backslashChar)
|
||||
: HintFmt("you need to escape the %s itself: %s", escape, escape + escape + found);
|
||||
|
||||
HintFmt msg = HintFmt(
|
||||
"%s is an ill-defined escape sequence. In Nix, it simply means %s, therefore the %s is redundant and "
|
||||
"should be removed. If the intent of the string was to mean %s instead (e.g. in a regex), %s. "
|
||||
"%s is an ill-defined escape. You can drop the %s and simply write %s instead. "
|
||||
"Use %s to silence this warning.",
|
||||
escape + found,
|
||||
found,
|
||||
escape,
|
||||
backslashChar,
|
||||
Uncolored(dedicatedInsert.str()),
|
||||
found,
|
||||
"--extra-deprecated-features broken-string-escape"
|
||||
);
|
||||
|
||||
/* Special case some common escapes to provide better messages */
|
||||
if (found == '$' || found == '{') {
|
||||
/* Someone possibly tried to escape an interpolation but used the wrong sequence.
|
||||
|
||||
+338
-425
File diff suppressed because it is too large
Load Diff
@@ -48,12 +48,13 @@ public:
|
||||
/**
|
||||
* Load a ValueInitializer from a DSO and return whatever it initializes
|
||||
*/
|
||||
Value prim_importNative(EvalState & state, Value ** args);
|
||||
void prim_importNative(EvalState & state, Value * * args, Value & v);
|
||||
|
||||
/**
|
||||
* Execute a program and parse its output
|
||||
*/
|
||||
Value prim_exec(EvalState & state, Value ** args);
|
||||
void prim_exec(EvalState & state, Value * * args, Value & v);
|
||||
|
||||
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
|
||||
|
||||
std::tuple<Value, Value> makePositionThunks(EvalState & state, const PosIdx pos);
|
||||
}
|
||||
|
||||
@@ -7,21 +7,22 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
Value prim_unsafeDiscardStringContext(EvalState & state, Value ** args)
|
||||
void prim_unsafeDiscardStringContext(EvalState & state, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(noPos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
||||
return {NewValueAs::string, *s};
|
||||
v = {NewValueAs::string, *s};
|
||||
}
|
||||
|
||||
Value prim_hasContext(EvalState & state, Value ** args)
|
||||
void prim_hasContext(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
state.forceString(*args[0], context, noPos, "while evaluating the argument passed to builtins.hasContext");
|
||||
return {NewValueAs::boolean, !context.empty()};
|
||||
v = {NewValueAs::boolean, !context.empty()};
|
||||
}
|
||||
|
||||
Value prim_unsafeDiscardOutputDependency(EvalState & state, Value ** args)
|
||||
|
||||
void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(noPos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
|
||||
@@ -38,10 +39,11 @@ Value prim_unsafeDiscardOutputDependency(EvalState & state, Value ** args)
|
||||
}
|
||||
}
|
||||
|
||||
return {NewValueAs::string, *s, context2};
|
||||
v = {NewValueAs::string, *s, context2};
|
||||
}
|
||||
|
||||
Value prim_addDrvOutputDependencies(EvalState & state, Value ** args)
|
||||
|
||||
void prim_addDrvOutputDependencies(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(noPos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
|
||||
@@ -80,7 +82,7 @@ Value prim_addDrvOutputDependencies(EvalState & state, Value ** args)
|
||||
}, context.begin()->raw) }),
|
||||
};
|
||||
|
||||
return {NewValueAs::string, *s, context2};
|
||||
v = {NewValueAs::string, *s, context2};
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +105,7 @@ Value prim_addDrvOutputDependencies(EvalState & state, Value ** args)
|
||||
Note that for a given path any combination of the above attributes
|
||||
may be present.
|
||||
*/
|
||||
Value prim_getContext(EvalState & state, Value ** args)
|
||||
void prim_getContext(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
struct ContextInfo {
|
||||
bool path = false;
|
||||
@@ -134,19 +136,20 @@ Value prim_getContext(EvalState & state, Value ** args)
|
||||
for (const auto & info : contextInfos) {
|
||||
auto infoAttrs = state.ctx.buildBindings(3);
|
||||
if (info.second.path)
|
||||
infoAttrs.insert(state.ctx.symbols.sym_path, {NewValueAs::boolean, true});
|
||||
infoAttrs.alloc(state.ctx.symbols.sym_path) = {NewValueAs::boolean, true};
|
||||
if (info.second.allOutputs)
|
||||
infoAttrs.insert(sAllOutputs, {NewValueAs::boolean, true});
|
||||
infoAttrs.alloc(sAllOutputs) = {NewValueAs::boolean, true};
|
||||
if (!info.second.outputs.empty()) {
|
||||
auto & outputsVal = infoAttrs.alloc(state.ctx.symbols.sym_outputs);
|
||||
auto content = state.ctx.mem.newList(info.second.outputs.size());
|
||||
infoAttrs.insert(state.ctx.symbols.sym_outputs, {NewValueAs::list, content});
|
||||
outputsVal = {NewValueAs::list, content};
|
||||
for (const auto & [i, output] : enumerate(info.second.outputs))
|
||||
content->elems[i] = {NewValueAs::string, output};
|
||||
}
|
||||
attrs.insert(state.ctx.store->printStorePath(info.first), {NewValueAs::attrs, infoAttrs});
|
||||
attrs.alloc(state.ctx.store->printStorePath(info.first)) = {NewValueAs::attrs, infoAttrs};
|
||||
}
|
||||
|
||||
return {NewValueAs::attrs, attrs};
|
||||
v = {NewValueAs::attrs, attrs};
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +158,7 @@ Value prim_getContext(EvalState & state, Value ** args)
|
||||
See the commentary above unsafeGetContext for details of the
|
||||
context representation.
|
||||
*/
|
||||
Value prim_appendContext(EvalState & state, Value ** args)
|
||||
void prim_appendContext(EvalState & state, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
|
||||
@@ -229,6 +232,6 @@ Value prim_appendContext(EvalState & state, Value ** args)
|
||||
}
|
||||
}
|
||||
|
||||
return {NewValueAs::string, orig, context};
|
||||
v = {NewValueAs::string, orig, context};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
Value prim_fetchMercurial(EvalState & state, Value ** args)
|
||||
void prim_fetchMercurial(EvalState & state, Value ** args, Value & v)
|
||||
{
|
||||
std::string url;
|
||||
std::optional<Hash> rev;
|
||||
@@ -88,18 +88,18 @@ Value prim_fetchMercurial(EvalState & state, Value ** args)
|
||||
auto [tree, input2] = state.aio.blockOn(input.fetch(state.ctx.store));
|
||||
|
||||
auto attrs2 = state.ctx.buildBindings(8);
|
||||
attrs2.insert(state.ctx.symbols.sym_outPath, state.ctx.paths.mkStorePathString(tree.storePath));
|
||||
state.ctx.paths.mkStorePathString(tree.storePath, attrs2.alloc(state.ctx.symbols.sym_outPath));
|
||||
if (input2.getRef())
|
||||
attrs2.insert("branch", {NewValueAs::string, *input2.getRef()});
|
||||
attrs2.alloc("branch") = {NewValueAs::string, *input2.getRef()};
|
||||
// Backward compatibility: set 'rev' to
|
||||
// 0000000000000000000000000000000000000000 for a dirty tree.
|
||||
auto rev2 = input2.getRev().value_or(Hash(HashType::SHA1));
|
||||
attrs2.insert("rev", {NewValueAs::string, rev2.gitRev()});
|
||||
attrs2.insert("shortRev", {NewValueAs::string, rev2.gitRev().substr(0, 12)});
|
||||
attrs2.alloc("rev") = {NewValueAs::string, rev2.gitRev()};
|
||||
attrs2.alloc("shortRev") = {NewValueAs::string, rev2.gitRev().substr(0, 12)};
|
||||
if (auto revCount = input2.getRevCount())
|
||||
attrs2.insert("revCount", {NewValueAs::integer, NixInt::Inner(*revCount)});
|
||||
attrs2.alloc("revCount") = {NewValueAs::integer, NixInt::Inner(*revCount)};
|
||||
v = {NewValueAs::attrs, attrs2};
|
||||
|
||||
state.ctx.paths.allowPath(tree.storePath);
|
||||
return {NewValueAs::attrs, attrs2};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,51 +26,49 @@ Value emitTreeAttrs(
|
||||
|
||||
auto attrs = state.buildBindings(10);
|
||||
|
||||
attrs.insert(state.symbols.sym_outPath, state.paths.mkStorePathString(tree.storePath));
|
||||
state.paths.mkStorePathString(tree.storePath, attrs.alloc(state.symbols.sym_outPath));
|
||||
|
||||
// FIXME: support arbitrary input attributes.
|
||||
|
||||
auto narHash = input.getNarHash();
|
||||
assert(narHash);
|
||||
attrs.insert("narHash", {NewValueAs::string, narHash->to_string()});
|
||||
attrs.alloc("narHash") = {NewValueAs::string, narHash->to_string()};
|
||||
|
||||
if (input.getType() == "git")
|
||||
attrs.insert(
|
||||
"submodules",
|
||||
{NewValueAs::boolean, fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)}
|
||||
);
|
||||
attrs.alloc("submodules") = {
|
||||
NewValueAs::boolean, fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)
|
||||
};
|
||||
|
||||
if (!forceDirty) {
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
attrs.insert("rev", {NewValueAs::string, rev->gitRev()});
|
||||
attrs.insert("shortRev", {NewValueAs::string, rev->gitShortRev()});
|
||||
attrs.alloc("rev") = {NewValueAs::string, rev->gitRev()};
|
||||
attrs.alloc("shortRev") = {NewValueAs::string, rev->gitShortRev()};
|
||||
} else if (emptyRevFallback) {
|
||||
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
|
||||
auto emptyHash = Hash(HashType::SHA1);
|
||||
attrs.insert("rev", {NewValueAs::string, emptyHash.gitRev()});
|
||||
attrs.insert("shortRev", {NewValueAs::string, emptyHash.gitShortRev()});
|
||||
attrs.alloc("rev") = {NewValueAs::string, emptyHash.gitRev()};
|
||||
attrs.alloc("shortRev") = {NewValueAs::string, emptyHash.gitShortRev()};
|
||||
}
|
||||
|
||||
if (auto revCount = input.getRevCount())
|
||||
attrs.insert("revCount", {NewValueAs::integer, NixInt::Inner(*revCount)});
|
||||
attrs.alloc("revCount") = {NewValueAs::integer, NixInt::Inner(*revCount)};
|
||||
else if (emptyRevFallback)
|
||||
attrs.insert("revCount", {NewValueAs::integer, 0});
|
||||
attrs.alloc("revCount") = {NewValueAs::integer, 0};
|
||||
}
|
||||
|
||||
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
|
||||
attrs.insert("dirtyRev", {NewValueAs::string, *dirtyRev});
|
||||
attrs.insert(
|
||||
"dirtyShortRev", {NewValueAs::string, *fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev")}
|
||||
);
|
||||
attrs.alloc("dirtyRev") = {NewValueAs::string, *dirtyRev};
|
||||
attrs.alloc("dirtyShortRev") = {
|
||||
NewValueAs::string, *fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev")
|
||||
};
|
||||
}
|
||||
|
||||
if (auto lastModified = input.getLastModified()) {
|
||||
attrs.insert("lastModified", {NewValueAs::integer, *lastModified});
|
||||
attrs.insert(
|
||||
"lastModifiedDate",
|
||||
{NewValueAs::string, fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))}
|
||||
);
|
||||
attrs.alloc("lastModified") = {NewValueAs::integer, *lastModified};
|
||||
attrs.alloc("lastModifiedDate") = {
|
||||
NewValueAs::string, fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))
|
||||
};
|
||||
}
|
||||
|
||||
return {NewValueAs::attrs, attrs};
|
||||
@@ -108,14 +106,14 @@ struct FetchTreeParams {
|
||||
bool allowNameArgument = false;
|
||||
};
|
||||
|
||||
static Value fetchTree(
|
||||
static void fetchTree(
|
||||
EvalState & state,
|
||||
const PosIdx pos,
|
||||
Value ** args,
|
||||
Value * * args,
|
||||
Value & v,
|
||||
std::optional<std::string> type,
|
||||
const FetchTreeParams & params = FetchTreeParams{}
|
||||
)
|
||||
{
|
||||
) {
|
||||
fetchers::Input input;
|
||||
NixStringContext context;
|
||||
|
||||
@@ -225,17 +223,16 @@ static Value fetchTree(
|
||||
|
||||
state.ctx.paths.allowPath(tree.storePath);
|
||||
|
||||
return emitTreeAttrs(state.ctx, tree, input2, params.emptyRevFallback, false);
|
||||
v = emitTreeAttrs(state.ctx, tree, input2, params.emptyRevFallback, false);
|
||||
}
|
||||
|
||||
Value prim_fetchTree(EvalState & state, Value ** args)
|
||||
void prim_fetchTree(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
return fetchTree(state, noPos, args, std::nullopt, FetchTreeParams{.allowNameArgument = false});
|
||||
fetchTree(state, noPos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||
}
|
||||
|
||||
static Value fetch(
|
||||
EvalState & state, const PosIdx pos, Value ** args, const std::string & who, bool unpack, std::string name
|
||||
)
|
||||
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
|
||||
const std::string & who, bool unpack, std::string name)
|
||||
{
|
||||
std::optional<std::string> url;
|
||||
std::optional<Hash> expectedHash;
|
||||
@@ -300,7 +297,8 @@ static Value fetch(
|
||||
});
|
||||
|
||||
if (state.aio.blockOn(state.ctx.store->isValidPath(expectedPath))) {
|
||||
return state.ctx.paths.allowAndSetStorePathString(expectedPath);
|
||||
state.ctx.paths.allowAndSetStorePathString(expectedPath, v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,16 +306,10 @@ static Value fetch(
|
||||
// https://github.com/NixOS/nix/issues/4313
|
||||
auto storePath = unpack
|
||||
? state.aio
|
||||
.blockOn(
|
||||
fetchers::downloadTarball(
|
||||
state.ctx.store, *url, name, expectedHash != Hash(HashType::SHA256)
|
||||
)
|
||||
)
|
||||
.blockOn(fetchers::downloadTarball(state.ctx.store, *url, name, (bool) expectedHash))
|
||||
.tree.storePath
|
||||
: state.aio
|
||||
.blockOn(
|
||||
fetchers::downloadFile(state.ctx.store, *url, name, expectedHash != Hash(HashType::SHA256))
|
||||
)
|
||||
.blockOn(fetchers::downloadFile(state.ctx.store, *url, name, (bool) expectedHash))
|
||||
.storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
@@ -337,24 +329,22 @@ static Value fetch(
|
||||
}
|
||||
}
|
||||
|
||||
return state.ctx.paths.allowAndSetStorePathString(storePath);
|
||||
state.ctx.paths.allowAndSetStorePathString(storePath, v);
|
||||
}
|
||||
|
||||
Value prim_fetchurl(EvalState & state, Value ** args)
|
||||
void prim_fetchurl(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
return fetch(state, noPos, args, "fetchurl", false, "");
|
||||
fetch(state, noPos, args, v, "fetchurl", false, "");
|
||||
}
|
||||
|
||||
Value prim_fetchTarball(EvalState & state, Value ** args)
|
||||
void prim_fetchTarball(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
return fetch(state, noPos, args, "fetchTarball", true, "source");
|
||||
fetch(state, noPos, args, v, "fetchTarball", true, "source");
|
||||
}
|
||||
|
||||
Value prim_fetchGit(EvalState & state, Value ** args)
|
||||
void prim_fetchGit(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
return fetchTree(
|
||||
state, noPos, args, "git", FetchTreeParams{.emptyRevFallback = true, .allowNameArgument = true}
|
||||
);
|
||||
fetchTree(state, noPos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
Value prim_fromTOML(EvalState & state, Value ** args)
|
||||
void prim_fromTOML(EvalState & state, Value ** args, Value & val)
|
||||
{
|
||||
auto toml = state.forceStringNoCtx(
|
||||
*args[0], noPos, "while evaluating the argument passed to builtins.fromTOML"
|
||||
@@ -15,53 +15,62 @@ Value prim_fromTOML(EvalState & state, Value ** args)
|
||||
|
||||
std::istringstream tomlStream(std::string{toml});
|
||||
|
||||
auto visit = [&](this const auto & self, toml::value t) -> Value {
|
||||
auto visit = [&](this const auto & self, Value & v, toml::value t) -> void {
|
||||
switch (t.type()) {
|
||||
case toml::value_t::table: {
|
||||
auto table = toml::get<toml::table>(t);
|
||||
auto attrs = state.ctx.buildBindings(table.size());
|
||||
|
||||
for (auto & elem : table) {
|
||||
attrs.insert(elem.first, self(elem.second));
|
||||
self(attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
return {NewValueAs::attrs, attrs};
|
||||
}
|
||||
v = {NewValueAs::attrs, attrs};
|
||||
} break;
|
||||
case toml::value_t::array: {
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
size_t size = array.size();
|
||||
auto list = state.ctx.mem.newList(size);
|
||||
v = {NewValueAs::list, list};
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
list->elems[i] = self(array[i]);
|
||||
self(list->elems[i], array[i]);
|
||||
}
|
||||
return {NewValueAs::list, list};
|
||||
}
|
||||
} break;
|
||||
case toml::value_t::boolean:
|
||||
return {NewValueAs::boolean, toml::get<bool>(t)};
|
||||
v = {NewValueAs::boolean, toml::get<bool>(t)};
|
||||
break;
|
||||
case toml::value_t::integer:
|
||||
return {NewValueAs::integer, toml::get<int64_t>(t)};
|
||||
v = {NewValueAs::integer, toml::get<int64_t>(t)};
|
||||
break;
|
||||
case toml::value_t::floating:
|
||||
return {NewValueAs::floating, toml::get<NixFloat>(t)};
|
||||
v = {NewValueAs::floating, toml::get<NixFloat>(t)};
|
||||
break;
|
||||
case toml::value_t::string:
|
||||
return {NewValueAs::string, toml::get<std::string>(t)};
|
||||
v = {NewValueAs::string, toml::get<std::string>(t)};
|
||||
break;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time:
|
||||
// NOLINTNEXTLINE(lix-foreign-exceptions)
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
break;
|
||||
case toml::value_t::empty:
|
||||
return Value::VNULL;
|
||||
v.mkNull();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return visit(
|
||||
visit(
|
||||
val,
|
||||
toml::parse(
|
||||
tomlStream,
|
||||
"fromTOML", /* the "filename" */
|
||||
toml::spec::v(1, 0, 0) // Be explicit that we are parsing TOML 1.0.0 without extensions
|
||||
toml::spec::v(
|
||||
1, 0, 0
|
||||
) // Be explicit that we are parsing TOML 1.0.0 without extensions
|
||||
)
|
||||
);
|
||||
} catch (std::exception & e) { // NOLINT(lix-foreign-exceptions) // TODO: toml::syntax_error
|
||||
|
||||
@@ -17,10 +17,10 @@ Each file is called with three arguments:
|
||||
except that it's available in
|
||||
[`pure-eval`](@docroot@/command-ref/conf-file.html#conf-pure-eval)
|
||||
mode).
|
||||
2. The final top-level bindings produced by calling all
|
||||
`repl-overlays`.
|
||||
3. The top-level bindings produced by the previous `repl-overlays`
|
||||
2. The top-level bindings produced by the previous `repl-overlays`
|
||||
value (or the default top-level bindings).
|
||||
3. The final top-level bindings produced by calling all
|
||||
`repl-overlays`.
|
||||
|
||||
For example, the following file would alias `pkgs` to
|
||||
`legacyPackages.${info.currentSystem}` (if that attribute is defined):
|
||||
|
||||
@@ -12,10 +12,8 @@ namespace nix
|
||||
|
||||
static const Value::List emptyListData{.size = 0};
|
||||
Value Value::EMPTY_LIST{Value::list_t{}, &emptyListData};
|
||||
Value Value::EMPTY_SET{attrs_t{}, &Bindings::EMPTY};
|
||||
|
||||
const Value::Null Value::NULL_ACB = {{Value::Acb::tNull}};
|
||||
Value Value::VNULL{null_t{}};
|
||||
|
||||
static_assert(alignof(Value::String) >= Value::TAG_ALIGN);
|
||||
static_assert(alignof(Bindings) >= Value::TAG_ALIGN);
|
||||
@@ -53,6 +51,15 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
||||
printValue(state, str, *this, options);
|
||||
}
|
||||
|
||||
bool Value::isTrivial() const
|
||||
{
|
||||
return internalType() != tApp
|
||||
&& (internalType() != tThunk
|
||||
|| (thunk().expr->try_cast<ExprSet>()
|
||||
&& static_cast<ExprSet *>(thunk().expr)->dynamicAttrs.empty())
|
||||
|| thunk().expr->try_cast<ExprLambda>() || thunk().expr->try_cast<ExprList>());
|
||||
}
|
||||
|
||||
Value::Value(string_t, Str * s, const NixStringContext & context)
|
||||
: Value(NewValueAs::string, s, copyContext(context))
|
||||
{
|
||||
|
||||
+18
-6
@@ -32,7 +32,7 @@ struct Value;
|
||||
/**
|
||||
* Function that implements a primop.
|
||||
*/
|
||||
using PrimOpImpl = Value(EvalState & state, Value ** args);
|
||||
using PrimOpImpl = void(EvalState & state, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Info about a primitive operation, and its implementation
|
||||
@@ -284,7 +284,6 @@ private:
|
||||
|
||||
InternalType internalType() const
|
||||
{
|
||||
// NOLINTNEXTLINE(lix-cast-to-non-fixed-enum): TAG_MASK ensures it's in range
|
||||
return InternalType(raw & TAG_MASK);
|
||||
}
|
||||
|
||||
@@ -357,8 +356,6 @@ public:
|
||||
* Empty list constant.
|
||||
*/
|
||||
static Value EMPTY_LIST;
|
||||
static Value EMPTY_SET;
|
||||
static Value VNULL;
|
||||
|
||||
struct String;
|
||||
struct Acb;
|
||||
@@ -724,8 +721,6 @@ public:
|
||||
|
||||
Type type() const
|
||||
{
|
||||
// TAG_MASK == 7, max enumerator is 5, so all possible values are in range
|
||||
// NOLINTNEXTLINE(lix-cast-to-non-fixed-enum)
|
||||
return Type(raw & TAG_MASK);
|
||||
}
|
||||
};
|
||||
@@ -769,6 +764,16 @@ public:
|
||||
*/
|
||||
inline ValueType type(bool invalidIsThunk = false) const;
|
||||
|
||||
inline void mkNull()
|
||||
{
|
||||
*this = {NewValueAs::null};
|
||||
}
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
*this = {NewValueAs::external, *e};
|
||||
}
|
||||
|
||||
bool isList() const
|
||||
{
|
||||
return internalType() == tList;
|
||||
@@ -778,6 +783,13 @@ public:
|
||||
|
||||
size_t listSize() const;
|
||||
|
||||
/**
|
||||
* Check whether forcing this value requires a trivial amount of
|
||||
* computation. In particular, function applications are
|
||||
* non-trivial.
|
||||
*/
|
||||
bool isTrivial() const;
|
||||
|
||||
auto listItems() const
|
||||
{
|
||||
struct ListIterable
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "lix/libfetchers/cache.hh"
|
||||
#include "libstore/pathlocks.hh"
|
||||
#include "libutil/hash.hh"
|
||||
#include "lix/libstore/sqlite.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/sync.hh"
|
||||
@@ -92,28 +90,6 @@ struct CacheImpl : Cache
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
kj::Promise<Result<std::variant<std::pair<Attrs, StorePath>, PathLock>>> lookupOrLock(ref<Store> store, const Attrs & inAttrs) override try {
|
||||
// In order to avoid fetching the same input multiple times
|
||||
// concurrently, we first acquire a lock based on the input attributes.
|
||||
auto hashResult = hashString(HashType::SHA256, attrsToJSON(inAttrs).dump());
|
||||
auto lockPath = getCacheDir() + "/nix/fetcher-lock-" + hashResult.to_string(HashFormat::Base32, false);
|
||||
auto pathLock = TRY_AWAIT(lockPathAsync(lockPath));
|
||||
// Once this lock is acquired, we check if the input is already present
|
||||
// in the cache (which requires locking the cache db)
|
||||
auto lookedUp = TRY_AWAIT(lookup(store, inAttrs));
|
||||
// We return either the cache hit or the lock; this allows fetchers to
|
||||
// keep a lock on fetching the input in question if it's not already in
|
||||
// the cache, and otherwise frees the cache db up again.
|
||||
std::variant<std::pair<Attrs, StorePath>, PathLock> result(std::move(pathLock));
|
||||
if (lookedUp) {
|
||||
// Reassigning the variant frees the lock
|
||||
result = *lookedUp;
|
||||
}
|
||||
co_return result;
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
kj::Promise<Result<std::optional<LookupResult>>> lookupExpired(
|
||||
ref<Store> store,
|
||||
const Attrs & inAttrs) override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "libstore/pathlocks.hh"
|
||||
#include "lix/libfetchers/fetchers.hh"
|
||||
#include "lix/libstore/path.hh"
|
||||
|
||||
@@ -22,10 +21,6 @@ struct Cache
|
||||
ref<Store> store,
|
||||
const Attrs & inAttrs) = 0;
|
||||
|
||||
virtual kj::Promise<Result<std::variant<std::pair<Attrs, StorePath>, PathLock>>> lookupOrLock(
|
||||
ref<Store> store,
|
||||
const Attrs & inAttrs) = 0;
|
||||
|
||||
struct LookupResult
|
||||
{
|
||||
bool expired = false;
|
||||
|
||||
+16
-43
@@ -1,4 +1,3 @@
|
||||
#include "libfetchers/attrs.hh"
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
@@ -16,10 +15,9 @@
|
||||
#include "lix/libutil/url-parts.hh"
|
||||
#include "lix/libstore/pathlocks.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
#include "lix/libutil/git.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/lix-rs/main.gen.hh"
|
||||
#include "lix/lix-rs/utils.hh"
|
||||
|
||||
#include "lix/libfetchers/fetch-settings.hh"
|
||||
|
||||
@@ -82,17 +80,17 @@ try {
|
||||
));
|
||||
|
||||
std::string_view line = output;
|
||||
auto line_rs = rust::to_string(line.substr(0, line.find("\n")));
|
||||
if (const auto parseResult =
|
||||
to_std(rust::lix::fetchers::git::LsRemoteRefLine::try_from(line_rs.as_str()).ok()))
|
||||
{
|
||||
auto target = to_std_string(parseResult->target.as_str());
|
||||
if (parseResult->kind.matches_Object()) {
|
||||
debug("resolved HEAD rev '%s' for repo '%s'", target, path);
|
||||
} else {
|
||||
debug("resolved HEAD ref '%s' for repo '%s'", target, path);
|
||||
line = line.substr(0, line.find("\n"));
|
||||
if (const auto parseResult = git::parseLsRemoteLine(line)) {
|
||||
switch (parseResult->kind) {
|
||||
case git::LsRemoteRefLine::Kind::Symbolic:
|
||||
debug("resolved HEAD ref '%s' for repo '%s'", parseResult->target, path);
|
||||
break;
|
||||
case git::LsRemoteRefLine::Kind::Object:
|
||||
debug("resolved HEAD rev '%s' for repo '%s'", parseResult->target, path);
|
||||
break;
|
||||
}
|
||||
co_return to_std_string(parseResult->target.as_str());
|
||||
co_return parseResult->target;
|
||||
}
|
||||
co_return std::nullopt;
|
||||
} catch (ExecError &) {
|
||||
@@ -607,18 +605,9 @@ struct GitInputScheme : InputScheme
|
||||
return {std::move(storePath), input};
|
||||
};
|
||||
|
||||
// If the input isn't present in the fetcher cache, we get a lock for
|
||||
// fetching the given input. We want to hold this lock until we're done
|
||||
// fetching, so we keep it in this scope.
|
||||
std::optional<PathLock> fetchLock;
|
||||
if (input.getRev()) {
|
||||
auto resOrLock = TRY_AWAIT(getCache()->lookupOrLock(store, getLockedAttrs()));
|
||||
|
||||
if (auto res = std::get_if<std::pair<Attrs, StorePath>>(&resOrLock))
|
||||
if (auto res = TRY_AWAIT(getCache()->lookup(store, getLockedAttrs())))
|
||||
co_return makeResult(res->first, std::move(res->second));
|
||||
auto lock = std::get_if<PathLock>(&resOrLock);
|
||||
assert(lock);
|
||||
fetchLock = std::move(*lock);
|
||||
}
|
||||
|
||||
auto [isLocal, actualUrl_] = getActualUrl(input);
|
||||
@@ -687,10 +676,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
}
|
||||
|
||||
// If the input isn't in the cache, we get a lock for fetching it.
|
||||
// Make sure to keep this until we're done fetching!
|
||||
auto cached = TRY_AWAIT(getCache()->lookupOrLock(store, unlockedAttrs));
|
||||
if (auto res = std::get_if<std::pair<Attrs, StorePath>>(&cached)) {
|
||||
if (auto res = TRY_AWAIT(getCache()->lookup(store, unlockedAttrs))) {
|
||||
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), HashType::SHA1);
|
||||
if (!input.getRev() || input.getRev() == rev2) {
|
||||
input.attrs.insert_or_assign("rev", rev2.gitRev());
|
||||
@@ -872,21 +858,8 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
/* Now that we know the ref, check again whether we have it in
|
||||
the store. */
|
||||
// If we already have a lock for fetching this path, we can't use lookupOrLock because it would deadlock.
|
||||
if (fetchLock) {
|
||||
if (auto res = TRY_AWAIT(getCache()->lookup(store, getLockedAttrs()))) {
|
||||
co_return makeResult(res->first, std::move(res->second));
|
||||
}
|
||||
} else {
|
||||
// If we don't have the lock, we need to acquire it by using lookupOrLock.
|
||||
auto resultOrLock = TRY_AWAIT(getCache()->lookupOrLock(store, getLockedAttrs()));
|
||||
if (auto res = std::get_if<std::pair<Attrs, StorePath>>(&resultOrLock)) {
|
||||
co_return makeResult(res->first, std::move(res->second));
|
||||
}
|
||||
auto lock = std::get_if<PathLock>(&resultOrLock);
|
||||
assert(lock);
|
||||
fetchLock = std::move(*lock);
|
||||
}
|
||||
if (auto res = TRY_AWAIT(getCache()->lookup(store, getLockedAttrs())))
|
||||
co_return makeResult(res->first, std::move(res->second));
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmpDir(tmpDir, true);
|
||||
@@ -1001,7 +974,7 @@ struct GitInputScheme : InputScheme
|
||||
});
|
||||
Finally const _wait([&] { proc.waitAndCheck(); });
|
||||
|
||||
TRY_AWAIT(unpackTarfile(_input.toURLString(), *proc.getStdout(), tmpDir));
|
||||
TRY_AWAIT(unpackTarfile(*proc.getStdout(), tmpDir));
|
||||
}
|
||||
|
||||
auto storePath = TRY_AWAIT(
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libutil/url-parts.hh"
|
||||
#include "lix/libutil/git.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libfetchers/fetchers.hh"
|
||||
#include "lix/libfetchers/fetch-settings.hh"
|
||||
#include "lix/lix-rs/main.gen.hh"
|
||||
#include "lix/lix-rs/utils.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
@@ -487,16 +486,15 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
std::string line;
|
||||
getline(is, line);
|
||||
|
||||
auto remoteLine = to_std(
|
||||
rust::lix::fetchers::git::LsRemoteRefLine::try_from(rust::to_string(line).as_str()).ok()
|
||||
);
|
||||
auto remoteLine = git::parseLsRemoteLine(line);
|
||||
if (!remoteLine) {
|
||||
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
|
||||
}
|
||||
refUri = to_std_string(remoteLine->target.as_str());
|
||||
refUri = remoteLine->target;
|
||||
} else {
|
||||
refUri = fmt("refs/(heads|tags)/%s", ref);
|
||||
}
|
||||
std::regex refRegex = regex::parse(refUri);
|
||||
|
||||
auto file = store->toRealPath(
|
||||
TRY_AWAIT(downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers))
|
||||
@@ -507,12 +505,9 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
std::string line;
|
||||
std::optional<std::string> id;
|
||||
while(!id && getline(is, line)) {
|
||||
auto parsedLine = to_std(
|
||||
rust::lix::fetchers::git::LsRemoteRefLine::try_from(rust::to_string(line).as_str()).ok()
|
||||
);
|
||||
if (parsedLine && (*parsedLine).matches_ref_uri(rust::to_string(refUri).as_str())) {
|
||||
id = to_std_string(parsedLine->target.as_str());
|
||||
}
|
||||
auto parsedLine = git::parseLsRemoteLine(line);
|
||||
if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex))
|
||||
id = parsedLine->target;
|
||||
}
|
||||
|
||||
if(!id)
|
||||
|
||||
@@ -195,7 +195,7 @@ try {
|
||||
} else {
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete autoDelete(tmpDir, true);
|
||||
unpackTarfile(url, store->toRealPath(res.storePath), tmpDir);
|
||||
unpackTarfile(store->toRealPath(res.storePath), tmpDir);
|
||||
auto members = readDirectory(tmpDir);
|
||||
if (members.size() != 1)
|
||||
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user