Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de0447c2c6 | ||
|
|
da75fb29e7 |
@@ -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',
|
||||
]
|
||||
+3
-4
@@ -4,10 +4,9 @@ AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: DontAlign
|
||||
AlignTrailingComments: false
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
@@ -36,7 +35,7 @@ BreakAfterAttributes: Always
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ColumnLimit: 110
|
||||
ColumnLimit: 100
|
||||
EmptyLineAfterAccessModifier: Leave
|
||||
EmptyLineBeforeAccessModifier: Leave
|
||||
FixNamespaceComments: false
|
||||
|
||||
+7
-2
@@ -8,12 +8,16 @@ Checks:
|
||||
- -bugprone-narrowing-conversions
|
||||
# kind of nonsense
|
||||
- -bugprone-easily-swappable-parameters
|
||||
# too many warnings for now
|
||||
- -bugprone-implicit-widening-of-multiplication-result
|
||||
# Lix's exception handling is Questionable
|
||||
- -bugprone-empty-catch
|
||||
# many warnings
|
||||
- -bugprone-unchecked-optional-access
|
||||
# many warnings, seems like a questionable lint
|
||||
- -bugprone-branch-clone
|
||||
# extremely noisy before clang 19: https://github.com/llvm/llvm-project/issues/93959
|
||||
- -bugprone-multi-level-implicit-pointer-conversion
|
||||
# we don't compile out our asserts
|
||||
- -bugprone-assert-side-effect
|
||||
# FIXME(jade): figure out if this warning is any good
|
||||
@@ -25,6 +29,9 @@ Checks:
|
||||
# crimes must be appropriately declared as crimes
|
||||
- cppcoreguidelines-pro-type-cstyle-cast
|
||||
- lix-*
|
||||
# This can not yet be applied to Lix itself since we need to do source
|
||||
# reorganization so that lix/ include paths work.
|
||||
- -lix-fixincludes
|
||||
# This lint is included as an example, but the lib function it replaces is
|
||||
# already gone.
|
||||
- -lix-hasprefixsuffix
|
||||
@@ -33,5 +40,3 @@ Checks:
|
||||
CheckOptions:
|
||||
bugprone-reserved-identifier.AllowedIdentifiers: '__asan_default_options'
|
||||
bugprone-unused-return-value.AllowCastToVoid: true
|
||||
|
||||
ExtraArgs: ["-Werror=unnecessary-virtual-specifier"]
|
||||
|
||||
+1
-5
@@ -1,5 +1,4 @@
|
||||
/build
|
||||
/outputs
|
||||
outputs/
|
||||
|
||||
# GNU Global
|
||||
GPATH
|
||||
@@ -42,6 +41,3 @@ buildtime.bin
|
||||
*.pyc
|
||||
|
||||
**/.idea
|
||||
|
||||
# Yeah, I've got no clue.
|
||||
/subprojects/.wraplock
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
Fiona Behrens <me@kloenk.dev>
|
||||
Fiona Behrens <me@kloenk.dev> <me@kloenk.de>
|
||||
rootile <lix@rootile.de>
|
||||
rootile <lix@rootile.de> <commentator2.0@crystal-cavern.systems>
|
||||
rootile <lix@rootile.de> <lix@crystal-cavern.systems>
|
||||
|
||||
Generated
+2
-880
@@ -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"
|
||||
@@ -468,197 +39,12 @@ dependencies = [
|
||||
"rowan",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
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 +54,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 +61,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 +72,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"]
|
||||
|
||||
[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,14 @@
|
||||
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),
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixexpr,
|
||||
liblixfetchers,
|
||||
curl,
|
||||
],
|
||||
install : false,
|
||||
build_by_default : true,
|
||||
link_args : plugin_link_args,
|
||||
link_args : strict_shared_module_link_args,
|
||||
)
|
||||
|
||||
@@ -2,13 +2,12 @@ R"(
|
||||
|
||||
**Store URL format**: `https+mtls://...`
|
||||
|
||||
This store allows a binary cache to be accessed via HTTPS with mutual TLS (client certificate authentication).
|
||||
This store allows a binary cache to be accessed via the HTTPS
|
||||
protocol with mutual TLS mandated.
|
||||
|
||||
Both parameters are required:
|
||||
Two parameters can be passed to the query string:
|
||||
|
||||
- `tls-certificate`, a path to the TLS client certificate
|
||||
- `tls-private-key`, a path to the TLS private key backing the client certificate
|
||||
|
||||
If you don't need mTLS, use `https://` instead.
|
||||
- `tls-certificate`, a path to the TLS client certificate (optional)
|
||||
- `tls-private-key`, a path to the TLS private key backing the client certificate (required)
|
||||
|
||||
)"
|
||||
|
||||
@@ -25,14 +25,14 @@ struct mTLSBinaryCacheStoreConfig : HttpBinaryCacheStoreConfig
|
||||
this,
|
||||
"",
|
||||
"tls-certificate",
|
||||
"Path of the TLS client certificate in PEM format as expected by CURLOPT_SSLCERT"
|
||||
"Path of an optional TLS client certificate in PEM format as expected by CURLOPT_SSLCERT"
|
||||
};
|
||||
|
||||
PathsSetting<nix::Path> tlsKey{
|
||||
this,
|
||||
"",
|
||||
"tls-private-key",
|
||||
"Path of the TLS client certificate private key in PEM format as expected by CURLOPT_SSLKEY"
|
||||
"Path of an TLS client certificate private key in PEM format as expected by CURLOPT_SSLKEY"
|
||||
};
|
||||
};
|
||||
|
||||
@@ -72,19 +72,18 @@ struct mTLSBinaryCacheStoreImpl : public HttpBinaryCacheStore
|
||||
auto baseExtraSetup = std::move(options.extraSetup);
|
||||
auto keyring = this->keyring;
|
||||
|
||||
options.extraSetup = [keyring, baseExtraSetup{std::move(baseExtraSetup)}](CURL * req) {
|
||||
if (baseExtraSetup) {
|
||||
baseExtraSetup(req);
|
||||
}
|
||||
options.extraSetup =
|
||||
[keyring, baseExtraSetup{std::move(baseExtraSetup)}](CURL * req) {
|
||||
if (baseExtraSetup) {
|
||||
baseExtraSetup(req);
|
||||
}
|
||||
|
||||
const bool haveCert = !keyring->tlsCertificate.empty();
|
||||
const bool haveKey = !keyring->tlsKey.empty();
|
||||
if (!(haveCert && haveKey)) {
|
||||
throw Error("https+mtls requires both tls-certificate and tls-private-key");
|
||||
}
|
||||
curl_easy_setopt(req, CURLOPT_SSLCERT, keyring->tlsCertificate.c_str());
|
||||
curl_easy_setopt(req, CURLOPT_SSLKEY, keyring->tlsKey.c_str());
|
||||
};
|
||||
if (!keyring->tlsCertificate.empty()) {
|
||||
curl_easy_setopt(req, CURLOPT_SSLCERT, keyring->tlsCertificate.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(req, CURLOPT_SSLKEY, keyring->tlsKey.c_str());
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -96,6 +95,7 @@ struct mTLSBinaryCacheStoreImpl : public HttpBinaryCacheStore
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
extern "C" void nix_plugin_entry()
|
||||
{
|
||||
nix::StoreImplementations::add<nix::mTLSBinaryCacheStoreImpl, nix::mTLSBinaryCacheStoreConfig>();
|
||||
|
||||
+9
-15
@@ -1,15 +1,9 @@
|
||||
let
|
||||
lockFile = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
flake-compat-node = lockFile.nodes.${lockFile.nodes.root.inputs.flake-compat};
|
||||
flake-compat = builtins.fetchTarball {
|
||||
inherit (flake-compat-node.locked) url;
|
||||
sha256 = flake-compat-node.locked.narHash;
|
||||
};
|
||||
|
||||
flake = (
|
||||
import flake-compat {
|
||||
src = ./.;
|
||||
}
|
||||
);
|
||||
in
|
||||
flake.defaultNix
|
||||
(import (
|
||||
let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
in
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||
}
|
||||
) { src = ./.; }).defaultNix
|
||||
|
||||
@@ -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))
|
||||
;
|
||||
$body + {
|
||||
sections: $body.sections | map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end)),
|
||||
};
|
||||
|
||||
process_command
|
||||
|
||||
@@ -22,16 +22,16 @@ fold.level = 30
|
||||
# not want to disable the links preprocessor entirely though because that requires
|
||||
# disabling *all* built-in preprocessors and selectively reenabling those we want.
|
||||
[preprocessor.substitute]
|
||||
command = "python3 substitute.py"
|
||||
command = "python3 doc/manual/substitute.py"
|
||||
before = ["anchors", "links"]
|
||||
|
||||
[preprocessor.anchors]
|
||||
renderers = ["html"]
|
||||
command = "jq --from-file anchors.jq"
|
||||
command = "jq --from-file doc/manual/anchors.jq"
|
||||
|
||||
[output.markdown]
|
||||
|
||||
[output.linkcheck2]
|
||||
[output.linkcheck]
|
||||
# no Internet during the build (in the sandbox)
|
||||
follow-web-links = false
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -82,9 +73,6 @@ detroyejr:
|
||||
display_name: Jonathan De Troye
|
||||
github: detroyejr
|
||||
|
||||
edef:
|
||||
github: edef1c
|
||||
|
||||
edolstra:
|
||||
display_name: Eelco Dolstra
|
||||
github: edolstra
|
||||
@@ -111,9 +99,6 @@ goldstein:
|
||||
forgejo: goldstein
|
||||
github: GoldsteinE
|
||||
|
||||
gustavderdrache:
|
||||
github: gustavderdrache
|
||||
|
||||
horrors:
|
||||
display_name: eldritch horrors
|
||||
forgejo: pennae
|
||||
@@ -140,19 +125,11 @@ jade:
|
||||
just1602:
|
||||
forgejo: just1602
|
||||
|
||||
k900:
|
||||
display_name: K900
|
||||
forgejo: K900
|
||||
github: K900
|
||||
|
||||
kasimeka:
|
||||
display_name: ورد
|
||||
forgejo: janw4ld
|
||||
github: kasimeka
|
||||
|
||||
keysmashes:
|
||||
github: keysmashes
|
||||
|
||||
kfears:
|
||||
display_name: KFears
|
||||
forgejo: kfearsoff
|
||||
@@ -199,11 +176,6 @@ midnightveil:
|
||||
forgejo: midnightveil
|
||||
github: midnightveil
|
||||
|
||||
milibopp:
|
||||
display_name: Emilia Bopp
|
||||
forgejo: milibopp
|
||||
github: milibopp
|
||||
|
||||
nan-git:
|
||||
display_name: NaN-git
|
||||
github: NaN-git
|
||||
@@ -211,9 +183,6 @@ nan-git:
|
||||
ncfavier:
|
||||
github: ncfavier
|
||||
|
||||
nkk0:
|
||||
github: nkk0
|
||||
|
||||
not-my-profile:
|
||||
display_name: Martin Fischer
|
||||
github: not-my-profile
|
||||
@@ -256,32 +225,13 @@ raito:
|
||||
forgejo: raito
|
||||
github: RaitoBezarius
|
||||
|
||||
rkjnsn:
|
||||
display_name: Erik Jensen
|
||||
forgejo: rkjnsn
|
||||
github: rkjnsn
|
||||
|
||||
roberth:
|
||||
display_name: Robert Hensing
|
||||
github: roberth
|
||||
|
||||
rootile:
|
||||
display_name: rootile (Rutile)
|
||||
forgejo: rootile
|
||||
|
||||
sandydoo:
|
||||
github: sandydoo
|
||||
|
||||
seppel3210:
|
||||
github: Seppel3210
|
||||
|
||||
sterni:
|
||||
forgejo: sterni
|
||||
github: sternenseemann
|
||||
|
||||
stevalkr:
|
||||
github: stevalkr
|
||||
|
||||
teofilc:
|
||||
forgejo: teofilc
|
||||
github: TeofilC
|
||||
@@ -326,18 +276,11 @@ xanderio:
|
||||
xokdvium:
|
||||
github: xokdvium
|
||||
|
||||
xyenon:
|
||||
forgejo: xyenon
|
||||
github: xyenon
|
||||
|
||||
yorickvp:
|
||||
github: yorickvp
|
||||
|
||||
yshui:
|
||||
github: yshui
|
||||
|
||||
ysndr:
|
||||
github: ysndr
|
||||
|
||||
zimbatm:
|
||||
github: zimbatm
|
||||
|
||||
@@ -69,7 +69,7 @@ let
|
||||
let
|
||||
result = squash ''
|
||||
- ${
|
||||
if inlineHTML then ''<span id="conf-${name}">[`${name}`](#conf-${name})</span>'' else "`${name}`"
|
||||
if inlineHTML then ''<span id="conf-${name}">[`${name}`](#conf-${name})</span>'' else ''`${name}`''
|
||||
}
|
||||
|
||||
${indent " " body}
|
||||
|
||||
@@ -41,13 +41,9 @@ manual = custom_target(
|
||||
'-euo', 'pipefail',
|
||||
'-c',
|
||||
'''
|
||||
@0@ @INPUT0@ @3@ > @DEPFILE@
|
||||
|
||||
# Needs to be in lix/doc/manual for e.g. substitute.py
|
||||
pushd @3@
|
||||
@1@ build . -d @2@
|
||||
popd
|
||||
|
||||
@0@ @INPUT0@ @CURRENT_SOURCE_DIR@ > @DEPFILE@
|
||||
cd @SOURCE_ROOT@
|
||||
@1@ build doc/manual -d @2@ | { grep -Fv "because fragment resolution isn't implemented" || :; }
|
||||
rm -rf @2@/manual
|
||||
mv @2@/html @2@/manual
|
||||
find @2@/manual -iname meson.build -delete
|
||||
@@ -55,7 +51,6 @@ manual = custom_target(
|
||||
python.full_path(),
|
||||
mdbook.full_path(),
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir()
|
||||
),
|
||||
],
|
||||
input : [
|
||||
@@ -86,7 +81,7 @@ manual = custom_target(
|
||||
depfile : 'manual.d',
|
||||
env : {
|
||||
'RUST_LOG': 'info',
|
||||
'MANUAL_SUBSTITUTE_SEARCH': meson.current_build_dir() / 'src',
|
||||
'MDBOOK_SUBSTITUTE_SEARCH': meson.current_build_dir() / 'src',
|
||||
},
|
||||
)
|
||||
manual_md = manual[1]
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
synopsis: "mTLS store connections via a plugin"
|
||||
issues: []
|
||||
cls: [3754, 3696, 3697, 3698]
|
||||
category: Improvements
|
||||
credits: [raito, horrors, mic92, vlaci]
|
||||
---
|
||||
|
||||
To support use cases requiring mutual TLS (mTLS) authentication when connecting
|
||||
to remote Nix stores, e.g. private stores, we have introduced a **contributed**
|
||||
mTLS plugin extending the Lix store interface.
|
||||
|
||||
This design follows an extensibility model which was brought up [by a proposal
|
||||
of making Kerberos authentication possible in Lix
|
||||
directly](https://gerrit.lix.systems/c/lix/+/3637).
|
||||
|
||||
This mTLS plugin serves as a concrete example of how store connection
|
||||
mechanisms can be modularized through external plugins, without extending Lix
|
||||
core. This idea can be generalized to integrate automatic certificate renewal
|
||||
or advanced integrations with secrets engine or posture checks.
|
||||
|
||||
It enables custom TLS client certificates to be used for authenticating against
|
||||
a remote store that enforces mTLS.
|
||||
|
||||
To use the plugin, configure Lix manually by setting in your `nix.conf`:
|
||||
|
||||
```
|
||||
plugin-files = /a/path/to/libplugin_mtls_store.so
|
||||
```
|
||||
|
||||
Currently, this must be done explicitly. In the future, Nixpkgs will provide a
|
||||
mechanism to reference an up-to-date and curated set of plugins automatically.
|
||||
|
||||
Making plugins easily consumable outside of Nixpkgs (e.g., from external plugin
|
||||
registries or binary distributions) remains an open question and will require
|
||||
further design.
|
||||
|
||||
Contributed plugins come with significantly reduced **stability** and
|
||||
**maintenance** guarantees compared to the Lix core. We encourage users who
|
||||
depend on a given plugin to take on maintenance responsibilities and apply for
|
||||
ownership within the Lix mono-repository. These plugins are subject to removal
|
||||
at any time.
|
||||
@@ -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.
|
||||
@@ -200,7 +200,6 @@
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
- [Upcoming release](release-notes/rl-next.md)
|
||||
<!-- RELENG-AUTO-INSERTION-MARKER (see releng/release_notes.py) -->
|
||||
- [Lix 2.95 (2026-03-13)](release-notes/rl-2.95.md)
|
||||
- [Lix 2.94 (2025-11-17)](release-notes/rl-2.94.md)
|
||||
- [Lix 2.93 (2025-05-09)](release-notes/rl-2.93.md)
|
||||
- [Lix 2.92 (2025-01-18)](release-notes/rl-2.92.md)
|
||||
|
||||
@@ -41,17 +41,106 @@ contains Nix.
|
||||
> If you are building via the Lix daemon (default on Linux and macOS), it is the Lix daemon user account (that is, `root`) that should have SSH access to a user (not necessarily `root`) on the remote machine.
|
||||
>
|
||||
> Furthermore, `root` needs to have the public host keys for the remote system in its `.ssh/known_hosts`.
|
||||
> To add them to `known_hosts` for root, do `ssh-keyscan HOST | sudo tee -a ~root/.ssh/known_hosts`.
|
||||
> To add them to `known_hosts` for root, do `ssh-keyscan USER@HOST | sudo tee -a ~root/.ssh/known_hosts`.
|
||||
>
|
||||
> If you can’t or don’t want to configure `root` to be able to access the remote machine, you can use a private Nix store instead by passing e.g. `--store ~/my-nix` when running a Nix command from the local machine.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
The list of remote machines can be specified on the command line or in
|
||||
the Lix configuration file. The former is convenient for testing.
|
||||
Additionally, there are two supported formats to configure remote builders:
|
||||
The legacy, "space"-separated format and starting with Lix 2.95.0, a TOML.
|
||||
the Lix configuration file. The former is convenient for testing. For
|
||||
example, the following command allows you to build a derivation for
|
||||
`x86_64-darwin` on a Linux machine:
|
||||
|
||||
```console
|
||||
$ uname
|
||||
Linux
|
||||
|
||||
$ nix build --impure \
|
||||
--expr '(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
||||
--builders 'ssh://mac x86_64-darwin'
|
||||
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
|
||||
|
||||
$ cat ./result
|
||||
Darwin
|
||||
```
|
||||
|
||||
It is possible to specify multiple builders separated by a semicolon or
|
||||
a newline, e.g.
|
||||
|
||||
```console
|
||||
--builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd'
|
||||
```
|
||||
|
||||
Each machine specification consists of the following elements, separated
|
||||
by spaces. Only the first element is required. To leave a field at its
|
||||
default, set it to `-`.
|
||||
|
||||
1. The URI of the remote store in the format
|
||||
`ssh://[username@]hostname[?port=<port>]`, e.g. `ssh://nix@mac` or `ssh://mac`.
|
||||
If the ssh server is not listening on port 22 (e.g. port 1337 in this case)
|
||||
the URI would be `ssh://nix@mac?port=1337`
|
||||
For backward compatibility, `ssh://` may be omitted. The hostname
|
||||
may be an alias defined in your `~/.ssh/config`.
|
||||
|
||||
2. A comma-separated list of Nix platform type identifiers, such as
|
||||
`x86_64-darwin`. It is possible for a machine to support multiple
|
||||
platform types, e.g., `i686-linux,x86_64-linux`. If omitted, this
|
||||
defaults to the local platform type.
|
||||
|
||||
3. The SSH identity file to be used to log in to the remote machine. If
|
||||
omitted, SSH will use its regular identities.
|
||||
|
||||
4. The maximum number of builds that Lix will execute in parallel on
|
||||
the machine. Typically this should be equal to the number of CPU
|
||||
cores. For instance, the machine `itchy` in the example will execute
|
||||
up to 8 builds in parallel.
|
||||
|
||||
5. The “speed factor”, indicating the relative speed of the machine. If
|
||||
there are multiple machines of the right type, Lix will prefer the
|
||||
fastest, taking load into account.
|
||||
|
||||
6. A comma-separated list of *supported features*. If a derivation has
|
||||
the `requiredSystemFeatures` attribute, then Lix will only perform
|
||||
the derivation on a machine that has the specified features. For
|
||||
instance, the attribute
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "kvm" ];
|
||||
```
|
||||
|
||||
will cause the build to be performed on a machine that has the `kvm`
|
||||
feature.
|
||||
|
||||
7. A comma-separated list of *mandatory features*. A machine will only
|
||||
be used to build a derivation if all of the machine’s mandatory
|
||||
features appear in the derivation’s `requiredSystemFeatures`
|
||||
attribute.
|
||||
|
||||
8. The (base64-encoded) public host key of the remote machine. If omitted, SSH
|
||||
will use its regular known-hosts file. Specifically, the field is calculated
|
||||
via `base64 -w0 /etc/ssh/ssh_host_ed25519_key.pub`.
|
||||
|
||||
For example, the machine specification
|
||||
|
||||
nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 1 kvm
|
||||
nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 2
|
||||
nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 1 2 kvm benchmark
|
||||
|
||||
specifies several machines that can perform `i686-linux` builds.
|
||||
However, `poochie` will only do builds that have the attribute
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "benchmark" ];
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "benchmark" "kvm" ];
|
||||
```
|
||||
|
||||
`itchy` cannot do builds that require `kvm`, but `scratchy` does support
|
||||
such builds. For regular builds, `itchy` will be preferred over
|
||||
`scratchy` because it has a higher speed factor.
|
||||
|
||||
Remote builders can also be configured in `nix.conf`, e.g.
|
||||
|
||||
@@ -70,180 +159,3 @@ option `builders-use-substitutes` in your local `nix.conf`.
|
||||
|
||||
To build only on remote builders and disable building on the local
|
||||
machine, you can use the option `--max-jobs 0`.
|
||||
|
||||
---
|
||||
|
||||
Each machine specification consists of the following attributes.
|
||||
How those are combined within the configuration file differs for the formats, and will be explained further down.
|
||||
|
||||
1. `uri` (**required**)
|
||||
The URI of the remote store in the format
|
||||
`ssh[-ng]://[username@]hostname[?port=<port>]`, e.g. `ssh://nix@mac` or `ssh://mac`.
|
||||
If the ssh server is not listening on port 22 (e.g. port 1337 in this case)
|
||||
the URI would be `ssh[-ng]://nix@mac?port=1337`. The hostname
|
||||
may be an alias defined in your `~/.ssh/config`.
|
||||
|
||||
2. `system-types` (**optional**)
|
||||
A list of Nix platform type identifiers, such as
|
||||
`x86_64-darwin`. It is possible for a machine to support multiple
|
||||
platform types, e.g., `i686-linux` and `x86_64-linux`.
|
||||
|
||||
Defaults to the local platform type
|
||||
|
||||
3. `ssh-key` (**optional**)
|
||||
The SSH identity file to be used to log in to the remote machine.
|
||||
|
||||
Defaults to SSHs regular identities.
|
||||
|
||||
4. `jobs` (**optional**)
|
||||
The maximum number of builds that Lix will execute in parallel on
|
||||
the machine. Typically, this should be equal to the number of CPU
|
||||
cores divided by the cores within the target machines configuration, i.e. `jobs * cores ~= cpu cores`
|
||||
|
||||
Defaults to 1; must be a positive integer.
|
||||
|
||||
5. `speed-factor`
|
||||
The “speed factor”, indicating the relative speed of the machine. If
|
||||
there are multiple machines of the right type, Lix will prefer the
|
||||
fastest, taking load into account.
|
||||
|
||||
Defaults to 1; must be a positive float.
|
||||
|
||||
6. `supported-features` (**optional**)
|
||||
A list of *supported features*. If a derivation has
|
||||
the `requiredSystemFeatures` attribute, then Lix will only schedule
|
||||
the derivation on a machine that has the specified features. For
|
||||
example, the attribute
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "kvm" ];
|
||||
```
|
||||
|
||||
will cause the build to be performed on a machine that has the `kvm`
|
||||
feature.
|
||||
|
||||
Defaults to an empty list.
|
||||
|
||||
7. `mandatory-features` (**optional**)
|
||||
A list of *mandatory features*. A machine will only
|
||||
be used to build a derivation if all the machine’s mandatory
|
||||
features appear in the derivation’s `requiredSystemFeatures`
|
||||
attribute.
|
||||
|
||||
Defaults to an empty list.
|
||||
|
||||
8. `ssh-public-host-key` (**optional**)
|
||||
The public host key of the remote machine.
|
||||
|
||||
Defaults to basic ssh behavior (checking contents of the known-hosts file)
|
||||
|
||||
|
||||
### Using a TOML configuration
|
||||
|
||||
Each machine is configured as an attribute within the map called `machines`.
|
||||
The attributes name is the machines name.
|
||||
Attributes can be in any order.
|
||||
|
||||
For example:
|
||||
|
||||
```toml
|
||||
version = 1
|
||||
|
||||
[machines.andesite]
|
||||
uri = "ssh://lix@andesite.lix.systems" # toml also allows for comments
|
||||
system-types = ["i686-linux"]
|
||||
jobs = 8
|
||||
speed-factor = 1.0
|
||||
supported-features = ["kvm"]
|
||||
ssh-key = "/home/deepslate/.ssh/id_ed25519"
|
||||
|
||||
[machines.diorite]
|
||||
uri = "ssh://lix@diorite.lix.systems"
|
||||
system-types = ["i686-linux"]
|
||||
jobs = 8
|
||||
speed-factor = 2.0
|
||||
ssh-key = "/home/deepslate/.ssh/id_ed25519"
|
||||
|
||||
[machines.granite]
|
||||
uri = "ssh://lix@granite.lix.systems"
|
||||
system-types = ["i686-linux"]
|
||||
jobs = 1
|
||||
speed-factor = 2.0
|
||||
supported-features = ["kvm", "benchmark"]
|
||||
ssh-key = "/home/deepslate/.ssh/id_ed25519"
|
||||
|
||||
[machines.legacy]
|
||||
uri = "ssh://nix@nix-15-11.nixos.org"
|
||||
enable = false
|
||||
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> If the version tag is omitted (e.g. in the CLI), it defaults to the latest version.
|
||||
> It is strongly recommended to always provide a version tag for configuration within files to avoid breakage.
|
||||
|
||||
For testing purposes, one can also define a builder ad hoc on the CLI as follows:
|
||||
`--builders 'machines.andesite = {uri = "ssh://lix@andesite.lix.systems", jobs = 8}'`
|
||||
|
||||
|
||||
#### Special handling of fields
|
||||
- `enable` (**optional**)
|
||||
If set to false, the declared machine will not be loaded.
|
||||
This allows one to statically disable machines.
|
||||
|
||||
Defaults to true
|
||||
|
||||
### Using the legacy format
|
||||
> **Warning**
|
||||
>
|
||||
> This format is frozen and new features / configuration options will not be backported to this format.
|
||||
|
||||
It is possible to specify multiple builders separated by a semicolon or
|
||||
a newline, e.g.
|
||||
|
||||
```console
|
||||
--builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd'
|
||||
```
|
||||
|
||||
Every machine specification consists of the elements listed in the section above, seperated by any amount of spaces or tabs.
|
||||
The Attributes need to be provided **in order** and without names.
|
||||
To leave a field at its default, set it to `-`.
|
||||
Lists are colon seperated, without additional spaces.
|
||||
|
||||
```
|
||||
lix@andesite.lix.systems i686-linux /home/deepslate/.ssh/id_ed25519 8 1 kvm
|
||||
lix@diorite.lix.systems i686-linux /home/deepslate/.ssh/id_ed25519 8 2
|
||||
lix@granite.lix.systems i686-linux /home/deepslate/.ssh/id_ed25519 1 2 kvm benchmark
|
||||
```
|
||||
|
||||
#### Special handling of fields
|
||||
- `uri`: Due to backward compatibility, the `ssh://` may be omitted for the store-uri.
|
||||
- `ssh-public-host-key`: The key must be provided encoded in base64. Specifically calculated via `base64 -w0 /etc/ssh/ssh_host_ed25519_key.pub`
|
||||
|
||||
|
||||
### Format detection
|
||||
|
||||
At first, the given configuration is being parsed syntactically as a toml.
|
||||
If parsing fails and the given configuration contains a `"` the error is presented to the user, as those characters are necessary for TOML, but disallowed for the legacy format.
|
||||
Otherwise, parsing is retried using the legacy format.
|
||||
If non-syntactic errors are detected within the toml, the exception will always be shown to the user directly.
|
||||
|
||||
|
||||
## Builder selection
|
||||
The configuration(s) above specify several machines that can perform `i686-linux` builds.
|
||||
However, `granite` will only do builds that have the attribute
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "benchmark" ];
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```nix
|
||||
requiredSystemFeatures = [ "benchmark" "kvm" ];
|
||||
```
|
||||
|
||||
`diorite` cannot do builds that require `kvm`, but `andesite` does support
|
||||
such builds. For regular builds, `diorite` will be preferred over
|
||||
`andesite` because it has a higher speed factor.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -19,7 +19,7 @@ This description is not normative, but a feature removal may roughly happen like
|
||||
1. Add a warning when the feature is being used.
|
||||
2. Disable the feature by default, putting it behind a deprecated feature flag.
|
||||
- If disabling the feature started out as an opt-in experimental feature, turn that experimental flag into a no-op or remove it entirely.
|
||||
For example, `--extra-experimental-features no-url-literals` becomes `--extra-deprecated-features url-literals`.
|
||||
For example, `--extra-experimental-features=no-url-literals` becomes `--extra-deprecated-features=url-literals`.
|
||||
3. Decide on a time frame for how long that feature will still be supported for backwards compatibility, and clearly communicate that in the error messages.
|
||||
- Sometimes, automatic migration to alternatives is possible, and such should be provided if possible
|
||||
- At least one NixOS release cycle should be the minimum
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -177,21 +141,6 @@ To inspect the canonical source of truth on what the state of the buildsystem co
|
||||
$ meson introspect
|
||||
```
|
||||
|
||||
#### LLD
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Sending changes to Gerrit for review {#sending-to-gerrit}
|
||||
|
||||
We use Gerrit for all our code review in Lix.
|
||||
@@ -200,7 +149,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 +175,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 +515,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`.
|
||||
|
||||
@@ -383,10 +383,7 @@ I grepped `lix/` for `get[eE]nv\("` to find the mentions in Lix code.
|
||||
Overrides compile-time configuration of various locations used by Lix. See `lix/libstore/globals.cc`.
|
||||
|
||||
**Expected value**: a directory
|
||||
- `LIX_DAEMON_SOCKET_DIR` (optional) - Overrides the daemon socket directory from `$NIX_STATE_DIR/daemon-socket`.
|
||||
|
||||
**Expected value**: a directory
|
||||
- `NIX_DAEMON_SOCKET_PATH` (optional) - Overrides the daemon socket path from `$NIX_STATE_DIR/daemon-socket/socket`. Ignored if `LIX_DAEMON_SOCKET_DIR` is set.
|
||||
- `NIX_DAEMON_SOCKET_PATH` (optional) - Overrides the daemon socket path from `$NIX_STATE_DIR/daemon-socket/socket`.
|
||||
|
||||
**Expected value**: path to a socket
|
||||
- `NIX_LOG_FD` (output) - An FD number for logs in `internal-json` format to be sent to.
|
||||
@@ -404,6 +401,7 @@ I grepped `lix/` for `get[eE]nv\("` to find the mentions in Lix code.
|
||||
|
||||
**Expected value**: the path to an executable shell
|
||||
- `PRINT_PATH` - Undocumented. Used by `nix-prefetch-url` as an alternative form of `--print-path`. Why???
|
||||
- `_NIX_IN_TEST` - If present with any value, makes `fetchClosure` accept file URLs in addition to HTTP ones. Why is this not `_NIX_FORCE_HTTP`??
|
||||
|
||||
Not used anywhere else.
|
||||
- `NIX_ALLOW_EVAL` - Used by eval-cache tests to block evaluation if set to `0`.
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
[store path]: #gloss-store-path
|
||||
|
||||
- [file system object]{#gloss-file-system-object}
|
||||
- [file system object]{#gloss-store-object}
|
||||
|
||||
The Nix data model for representing simplified file system data.
|
||||
|
||||
|
||||
@@ -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 section does not document the most common modern practices in terms of avoiding channels, pinning, declarative software installation (see flakey-profile or home-manager or NixOS), or using flakes, etc.
|
||||
It is, however, likely correct at a technical level.
|
||||
|
||||
For more information on modern practices, see the [resources](https://wiki.lix.systems/books/lix-users/page/nix-resources) page on the Lix site.
|
||||
For more information on modern practices, see the [resources](https://lix.systems/resources) page on the Lix site.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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,546 +0,0 @@
|
||||
# Lix 2.95 "Kakigōri" (2026-03-13)
|
||||
|
||||
|
||||
# Lix 2.95.0 (2026-03-13)
|
||||
## Breaking Changes
|
||||
|
||||
- Deprecate shadowing internal files through the Nix search path [lix#998](https://git.lix.systems/lix-project/lix/issues/998) [cl/4632](https://gerrit.lix.systems/c/lix/+/4632)
|
||||
|
||||
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.
|
||||
|
||||
To alleviate potential issues, Lix now emits a warning when the Nix search path contains potential shadows for internal files, which will be changed to an error in a future release.
|
||||
|
||||
The warning can be disabled by enabling the deprecated feature `nix-path-shadow`.
|
||||
|
||||
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
|
||||
|
||||
- More deprecated features [cl/2092](https://gerrit.lix.systems/c/lix/+/2092) [cl/2310](https://gerrit.lix.systems/c/lix/+/2310) [cl/2311](https://gerrit.lix.systems/c/lix/+/2311) [cl/4638](https://gerrit.lix.systems/c/lix/+/4638) [cl/4652](https://gerrit.lix.systems/c/lix/+/4652) [cl/4764](https://gerrit.lix.systems/c/lix/+/4764)
|
||||
|
||||
This release cycle features a new batch of deprecated (anti-)features.
|
||||
You can opt in into the old behavior with `--extra-deprecated-features` or any equivalent configuration option.
|
||||
|
||||
- `broken-string-indentation` indented strings (those starting with `''`) might produce unintended results due to how the whitespace stripping is done. Those cases will now warn the user.
|
||||
- `broken-string-escape` "escaped" characters without a properly defined escape sequence evaluate to "themselves". This is in most cases unintended behaviour, both for writing regexes, and using legacy or uncommon escape sequences like `\f`. The user will now be warned, if those are present.
|
||||
- `floating-without-zero` so far, one was able to declare a float using something like `.123`. This can cause confusion about accessing attributes. Floating point numbers must now always include the leading zero, i.e. `0.123`
|
||||
- `rec-set-merges` Attribute sets like `{ foo = {}; foo.bar = 42;}` implicitly merge at parse time, however if one of them is marked as recursive but not the others then the recursive attribute may get lost (order-dependent). Therefore, merging attrs with mixed-`rec` is now forbidden.
|
||||
- `rec-set-dynamic-attrs` Dynamic attributes have weird semantics in the presence of recursive attrsets (they evaluate *after* the rest of the set). This is now forbidden.
|
||||
- `or-as-identifier` `or` as an identifier has always been weird since the `or` (almost-)keyword has been introduced. We are deprecating the backcompat hacks from the early days of Nix in favor of making `or` a full and proper keyword.
|
||||
- `tokens-no-whitespace` Function applications without space around the arguments like `0a`, `0.00.0` or `foo"1"2` are now forbidden. The same applies to list elements. The primary reason for this deprecation is to remove foot guns around surprising tokenization rules regarding number literals, but this will also free up some syntax for other purposes (e.g. `r""` strings) for reuse at some point in the future.
|
||||
- `shadow-internal-symbols` has been expanded to also forbid shadowing `null`, `true` and `false`.
|
||||
- `ancient-let` deprecation has been turned into a full parser error instead of a warning.
|
||||
- `rec-set-overrides` deprecation has been turned into a full parser error instead of a warning.
|
||||
|
||||
Many thanks to [piegames](https://git.lix.systems/piegames), [rootile (Rutile)](https://git.lix.systems/rootile), and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- Move `/root/.cache/nix` to `/var/cache/nix` by default [lix#634](https://git.lix.systems/lix-project/lix/issues/634) [cl/4671](https://gerrit.lix.systems/c/lix/+/4671)
|
||||
|
||||
By default, Lix attempts to locate a cache directory for its operations (such
|
||||
as the narinfo cache) by checking the value of `$XDG_CACHE_DIR`.
|
||||
|
||||
However, since the Nix daemon is a system service, using `$XDG_CACHE_DIR` is
|
||||
not typical in this context.
|
||||
|
||||
To address this, systemd provides a better solution. Specifically, when
|
||||
`CacheDirectory=` is set in the `[Service]` section of a systemd unit, it
|
||||
automatically sets the `$CACHE_DIRECTORY` environment variable and systemd will
|
||||
manage that cache directory for us.
|
||||
|
||||
Now, our systemd unit includes `CacheDirectory=nix`, which sets the
|
||||
`$CACHE_DIRECTORY` and takes precedence over `$XDG_CACHE_DIR`.
|
||||
|
||||
If the daemon is run under user units, systemd will automatically set
|
||||
`$XDG_CACHE_DIR`.
|
||||
|
||||
If neither of these variables is set, Lix falls back to its default behavior.
|
||||
By default, Lix will try to find a cache directory for its various operations
|
||||
(e.g. narinfo cache) by looking into `$XDG_CACHE_DIR`.
|
||||
|
||||
In summary, what was stored in `/root/.cache/nix` is now moved to
|
||||
`/var/cache/nix/nix`.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- Remove `fetch-closure` experimental feature [lix#1010](https://git.lix.systems/lix-project/lix/issues/1010) [cl/4595](https://gerrit.lix.systems/c/lix/+/4595)
|
||||
|
||||
The `fetch-closure` experimental feature has been removed.
|
||||
|
||||
Outside of allowing the user to import closure from binary cache,
|
||||
`fetchClosure` also allowed you to do the following:
|
||||
|
||||
* rewrite non-CA path to CA
|
||||
* reject non-CA paths at fetching time
|
||||
* reject CA paths at fetching time
|
||||
|
||||
Some people are using those mechanism to prevent users from having to build any
|
||||
package and force going via the declared cache or as a way to use ancient/old
|
||||
software without paying the evaluation cost of a second nixpkgs.
|
||||
|
||||
Both use cases are somewhat of an antipattern in Nix semantics. If the user
|
||||
cannot fetch a program directly via the substituter mechanism and fall back to
|
||||
local build, this is a feature *and* a misconfiguration. If the user cannot build
|
||||
certain derivations because they are too expensive, the build directives should
|
||||
pass `-j0` or similar.
|
||||
|
||||
As for the second usecase, there's a different way to do it that also allows to
|
||||
have a way to reproduce the paths that are hardcoded in that file, perform
|
||||
`import (fetchurl "https://my-cache/${hashparts storepath}.drv")` rather, i.e.
|
||||
an IFD to a possibly well known name. The backend can generate them on the fly
|
||||
or once, and possess stable names.
|
||||
|
||||
Finally, as for the non-CA → CA features, Lix removed ca-derivations.
|
||||
fetchClosure offers ca-derivations-like features which suffers from similar
|
||||
shortcomings albeit lessened. It only follows that we should deprecate
|
||||
and remove these capabilities.
|
||||
|
||||
Many thanks to [just1602](https://git.lix.systems/just1602) for this.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- `nix store add-path` now supports references [cl/5205](https://gerrit.lix.systems/c/lix/+/5205)
|
||||
|
||||
Lix supports two categories of hashes in store paths: input-addressed and output-addressed.
|
||||
|
||||
Currently, in Nix language, there is no way to produce output-addressed paths with references, as fixed-output derivations forbid references.
|
||||
However, the Nix store actually *supports* references in output-addressed paths.
|
||||
This is very useful for importing build products created outside of Lix that reference dependency store paths since such build products have no associated derivation so don't make any sense to input-address.
|
||||
Previously, output-addressed paths with references could only be created by writing a custom client to the rather-baroque Nix daemon protocol; now it's available in the CLI.
|
||||
|
||||
Using `nix store add-path --references-list-json REFS_LIST_FILE SOME_PATH` with a JSON list of string store paths, you can now create such paths with the Lix CLI.
|
||||
They may be consumed from Nix language using something like `builtins.storePath` or the following which also works in pure evaluation mode:
|
||||
|
||||
```nix
|
||||
# Hack from https://git.lix.systems/lix-project/lix/issues/402#issuecomment-5889
|
||||
path:
|
||||
builtins.appendContext path {
|
||||
${path} = {
|
||||
path = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||
|
||||
- Add `builtins.warn` for emitting warnings from Nix code [cl/2248](https://gerrit.lix.systems/c/lix/+/2248)
|
||||
|
||||
Lix now has a builtin function for emitting warnings.
|
||||
Like `builtins.trace`, it takes two arguments: the message to emit, and the expression to return.
|
||||
_Unlike_ `builtins.trace`, `builtins.warn` requires the first argument — the message — to be a string.
|
||||
In the future we may extend `builtins.warn` to accept a more structured API.
|
||||
|
||||
To go along with this, we also have two new config settings:
|
||||
- [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn), which, when used with `--debugger`, makes `builtins.warn` also function like [`builtins.break`](@docroot@/language/builtins.md#builtins-break).
|
||||
- [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn), which aborts evaluation entirely after the warning is emitted.
|
||||
|
||||
Many thanks to [Emilia Bopp](https://git.lix.systems/milibopp) and [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||
|
||||
- `keep-env-derivations` is now supported for nix3 CLI (`nix profile`) [lix#1095](https://git.lix.systems/lix-project/lix/issues/1095) [cl/5332](https://gerrit.lix.systems/c/lix/+/5332)
|
||||
|
||||
The `keep-env-derivations` feature is now available for `nix profile`. This allows users to prevent the garbage collection of derivations used to install a profile, even when `keep-derivations = false` (set to `true` by default).
|
||||
|
||||
Previously, `nix-env` supported this feature, but `nix profile` **never** did. This caused issues when garbage collection removed the associated `.drv` files, which are required, for example, by vulnerability management tools (e.g. [vulnix](https://github.com/nix-community/vulnix)) for proper operation.
|
||||
|
||||
This issue has now been resolved.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- Make `log-format` a setting [cl/4686](https://gerrit.lix.systems/c/lix/+/4686)
|
||||
|
||||
The [`--log-format` CLI option](@docroot@/command-ref/opt-common.md#opt-log-format) can now be set in [`nix.conf`](@docroot@/command-ref/conf-file.md#conf-log-format)!
|
||||
For example, you can now persistently enable the `multiline-with-logs` log format [added in Lix 2.91](@docroot@/release-notes/rl-2.91.md) by adding the following to your `nix.conf`:
|
||||
|
||||
```conf
|
||||
log-format = multiline-with-logs
|
||||
```
|
||||
|
||||
Or the equivalent in a NixOS configuration:
|
||||
```nix
|
||||
{
|
||||
nix.settings.log-format = "multiline-with-logs";
|
||||
}
|
||||
```
|
||||
|
||||
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||
|
||||
- Allow remote builders to be configured using TOML [cl/4533](https://gerrit.lix.systems/c/lix/+/4533)
|
||||
|
||||
Lix now supports configuring remote builders using a TOML file instead of the old, very cursed and incomprehensible format.
|
||||
This comes with not only a human-understandable file, but also with better messages and error reports on misconfiguration.
|
||||
|
||||
A more detailed Documentation can be found on the [distributed-builds](@docroot@/advanced-topics/distributed-builds.md) documentation page.
|
||||
|
||||
Many thanks to [rootile (Rutile)](https://git.lix.systems/rootile) and [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||
|
||||
- Emit warnings when encountering IFD with `warn-import-from-derivation` [nix#13279](https://github.com/NixOS/nix/pull/13279) [cl/3879](https://gerrit.lix.systems/c/lix/+/3879)
|
||||
|
||||
Instead of only being able to toggle the use of [Import from
|
||||
Derivation](https://nix.dev/manual/nix/stable/language/import-from-derivation) with
|
||||
`allow-import-from-derivation`, Lix is now able to warn users whenever IFD is encountered with
|
||||
`warn-import-from-derivation`.
|
||||
|
||||
Many thanks to [Seth Flynn](https://git.lix.systems/getchoo), [gustavderdrache](https://github.com/gustavderdrache), and [Eelco Dolstra](https://github.com/edolstra) for this.
|
||||
|
||||
|
||||
## Improvements
|
||||
|
||||
- Collect Flakes untrusted settings into one prompt [lix#682](https://git.lix.systems/lix-project/lix/issues/682) [cl/2921](https://gerrit.lix.systems/c/lix/+/2921)
|
||||
|
||||
When working with Flakes containing untrusted settings, a prompt is shown for each setting, asking whether to vet or approve it. This looks like:
|
||||
|
||||
```
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all)
|
||||
```
|
||||
|
||||
In Flakes with a large number of settings to approve or reject, this process can become tedious as each option must be handled individually.
|
||||
|
||||
To address this, all untrusted settings are now consolidated into a single prompt: allowing for bulk acceptance permanently or not, rejection, or detailed review. For example:
|
||||
|
||||
### Scrutiny scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) n
|
||||
warning: you can set 'accept-flake-config' to 'false' to automatically reject configuration options supplied by flakes
|
||||
Do you want to allow setting 'allow-dirty = false'? (yes for now/Allow always/no for now) y
|
||||
Do you want to allow setting 'sandbox = false'? (yes for now/Allow always/no for now) n
|
||||
```
|
||||
|
||||
### Reject everything scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) N
|
||||
Rejecting all untrusted nix.conf entries
|
||||
warning: you can set 'accept-flake-config' to 'false' to automatically reject configuration options supplied by flakes
|
||||
```
|
||||
|
||||
### Accept everything scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) y
|
||||
```
|
||||
|
||||
### Accept everything PERMANENTLY scenario
|
||||
|
||||
Note that accepting everything permanently will authorize these options for any
|
||||
further operations.
|
||||
|
||||
The file containing this trust information is usually located in
|
||||
`~/.local/share/nix/trusted-settings.json` and can be edited manually to revoke
|
||||
this permission until Lix provides a first-class command for this manipulation.
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) A
|
||||
```
|
||||
|
||||
Many thanks to [isabelroses](https://git.lix.systems/isabelroses), [Raito Bezarius](https://git.lix.systems/raito), and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- `--check` or `--rebuild` is clearer about a missing path [lix#485](https://git.lix.systems/lix-project/lix/issues/485)
|
||||
|
||||
Previously, when running Lix with --check or --rebuild, failures often surfaced
|
||||
as an unhelpful error:
|
||||
|
||||
> "some outputs of '...' are not valid, so checking is not possible"
|
||||
|
||||
This message could mean two different things:
|
||||
|
||||
- The requested output paths don't exist at all, or,
|
||||
- Some outputs exist but are not known to Lix
|
||||
|
||||
Lix cannot reliably distinguish these cases, so it treated them the same.
|
||||
|
||||
We've updated the error messages to clarify what Lix can determine: whether any
|
||||
valid outputs (> 0) are present or whether no outputs are available.
|
||||
|
||||
When no valid outputs can be found, Lix will now suggest building the derivation
|
||||
normally (without --check or --rebuild) before trying again.
|
||||
|
||||
When some valid outputs are present, Lix now reports which ones are valid,
|
||||
shows the full list of known outputs, and also suggests building the derivation
|
||||
normally.
|
||||
|
||||
In the future, Lix may automate this recovery step when it knows how to rebuild
|
||||
the paths, but implementing that safely requires more extensive changes to the
|
||||
codebase.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- `nix develop` no longer ignores the env variable `SSL_CERT_FILE` [cl/5042](https://gerrit.lix.systems/c/lix/+/5042)
|
||||
|
||||
Running `nix develop` and `nix print-dev-env` on shells that define the environment variable `SSL_CERT_FILE` now works correctly by exporting that variable inside the built shell.
|
||||
|
||||
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
|
||||
|
||||
- Linux sandbox launch overhead greatly reduced [cl/5030](https://gerrit.lix.systems/c/lix/+/5030) [cl/5073](https://gerrit.lix.systems/c/lix/+/5073) [cl/5074](https://gerrit.lix.systems/c/lix/+/5074)
|
||||
|
||||
Sandboxed builds are now much cheaper to launch on Linux, with constant management
|
||||
overhead. This will mostly be noticeable when building derivation trees containing
|
||||
many small derivations like nixpkgs' `writeFile` or `runCommand` with scripts that
|
||||
exit quickly. In synthetic tests we have seen build times of 3000 small runCommand
|
||||
drop from 80 seconds to 14 seconds, which is the most optimistic case in practice.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- mTLS store connections via a plugin [cl/3754](https://gerrit.lix.systems/c/lix/+/3754) [cl/3696](https://gerrit.lix.systems/c/lix/+/3696) [cl/3697](https://gerrit.lix.systems/c/lix/+/3697) [cl/3698](https://gerrit.lix.systems/c/lix/+/3698)
|
||||
|
||||
To support use cases requiring mutual TLS (mTLS) authentication when connecting
|
||||
to remote Nix stores, e.g. private stores, we have introduced a **contributed**
|
||||
mTLS plugin extending the Lix store interface.
|
||||
|
||||
This design follows an extensibility model which was brought up [by a proposal
|
||||
of making Kerberos authentication possible in Lix
|
||||
directly](https://gerrit.lix.systems/c/lix/+/3637).
|
||||
|
||||
This mTLS plugin serves as a concrete example of how store connection
|
||||
mechanisms can be modularized through external plugins, without extending Lix
|
||||
core. This idea can be generalized to integrate automatic certificate renewal
|
||||
or advanced integrations with secrets engine or posture checks.
|
||||
|
||||
It enables custom TLS client certificates to be used for authenticating against
|
||||
a remote store that enforces mTLS.
|
||||
|
||||
To use the plugin, configure Lix manually by setting in your `nix.conf`:
|
||||
|
||||
```
|
||||
plugin-files = /a/path/to/libplugin_mtls_store.so
|
||||
```
|
||||
|
||||
Currently, this must be done explicitly. In the future, Nixpkgs will provide a
|
||||
mechanism to reference an up-to-date and curated set of plugins automatically.
|
||||
|
||||
Making plugins easily consumable outside of Nixpkgs (e.g., from external plugin
|
||||
registries or binary distributions) remains an open question and will require
|
||||
further design.
|
||||
|
||||
Contributed plugins come with significantly reduced **stability** and
|
||||
**maintenance** guarantees compared to the Lix core. We encourage users who
|
||||
depend on a given plugin to take on maintenance responsibilities and apply for
|
||||
ownership within the Lix mono-repository. These plugins are subject to removal
|
||||
at any time.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito), [eldritch horrors](https://git.lix.systems/pennae), [mic92](https://github.com/mic92), [vlaci](https://github.com/vlaci), and [nkk0](https://github.com/nkk0) for this.
|
||||
|
||||
- Add an indication of nix-shell nesting depth [lix#826](https://git.lix.systems/lix-project/lix/issues/826) [cl/4657](https://gerrit.lix.systems/c/lix/+/4657)
|
||||
|
||||
When in a nix shell (either via a `nix-shell` or a `nix develop` invocation), a variable `NIX_SHELL_LEVEL` is exported to indicate the nesting depth of nix shells.
|
||||
|
||||
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
|
||||
|
||||
- `nix store delete` can now unlink a GC root before deleting its closure [cl/4660](https://gerrit.lix.systems/c/lix/+/4660)
|
||||
|
||||
Ever build something, and then you want to delete it and whatever dependencies it downloaded?
|
||||
Before you had to resolve the `result` symlink and copy it, then delete it, *then* `nix store delete --delete-closure --skip-live` on the path you copied.
|
||||
Now you can just pass `--unlink` and the `result` symlink itself.
|
||||
|
||||
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||
|
||||
- `nix path-info` no longer lies to the user about fetching paths [lix#323](https://git.lix.systems/lix-project/lix/issues/323) [cl/4866](https://gerrit.lix.systems/c/lix/+/4866)
|
||||
|
||||
When running `nix path-info` with an installable that is not present in the store, Lix no longer
|
||||
tells the user which paths are missing and that they will be fetched, as the documentation clearly
|
||||
states that this command does not fetch missing paths.
|
||||
|
||||
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
|
||||
|
||||
- Derivations can now be printed in detail in `nix repl` [cl/3842](https://gerrit.lix.systems/c/lix/+/3842)
|
||||
|
||||
Traditionally derivations printed in the REPL would only print a formatted object
|
||||
representing the path of the derivation file it refers to. This makes inspecting
|
||||
the enhanced derivation attribute sets encountered from `mkDerivation` or similar
|
||||
wrappers more difficult. Even the `:p`/`:print` command would not elaborate attribute sets
|
||||
tagged as a derivation.
|
||||
|
||||
With this change you can now use `:p`/`:print` to directly inspect a derivation
|
||||
by providing one as the top-level object. Derivation attribute sets will only be
|
||||
printed two levels deep and internal derivation attrsets will remain in unexpanded
|
||||
path form as before. `drvAttrs` will also be elided as these attributes are already
|
||||
present in the top-level attribute set of the derivation. These heuristics provide
|
||||
a balance between readability and functionality. When the `:p`/`:print` is omitted,
|
||||
a bare derivation is printed in the path format as before.
|
||||
|
||||
Many thanks to [Lunaphied](https://git.lix.systems/Lunaphied) for this.
|
||||
|
||||
- Reject `__json` in structured attributes derivations [lix#380](https://git.lix.systems/lix-project/lix/issues/380) [cl/5286](https://gerrit.lix.systems/c/lix/+/5286)
|
||||
|
||||
In structured attributes derivations, `__json` is used internally to store the
|
||||
JSON representation of the `env` attribute field that users can set.
|
||||
|
||||
Unfortunately, a user can set `__json` *and* enable structured attributes,
|
||||
resulting in a broken derivation from a semantic point of view.
|
||||
|
||||
As no user can benefit from setting `__json` *and* enable structured attributes,
|
||||
we disallow that possibility and throw an error from now on.
|
||||
|
||||
This is not seen as a breaking change because there's no user code that can
|
||||
benefit from this behavior, hence, it's an improvement to user experience.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- Shells support `$NIX_LOG_FD` now [lix#336](https://git.lix.systems/lix-project/lix/issues/336) [cl/4694](https://gerrit.lix.systems/c/lix/+/4694) [cl/4695](https://gerrit.lix.systems/c/lix/+/4695)
|
||||
|
||||
Lix's "debugging" shells (`nix3-develop` and `nix-shell`) now set the
|
||||
`$NIX_LOG_FD` environment variable.
|
||||
|
||||
This means that [hook logging in
|
||||
stdenv](https://github.com/NixOS/nixpkgs/pull/310387) appears while debugging
|
||||
derivations via `nix3-develop` or `nix-shell`.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- Supplementary groups are now supported for daemon authentication [lix#968](https://git.lix.systems/lix-project/lix/issues/968) [cl/5021](https://gerrit.lix.systems/c/lix/+/5021)
|
||||
|
||||
macOS, FreeBSD and Linux now support receiving supplementary groups during UNIX domain authentication to a Lix daemon.
|
||||
|
||||
This change is particularly beneficial for systemd units with `DynamicUser=true` that need to connect to a Lix daemon, using a `SupplementaryGroups=` allocated by systemd in the context of the process. This is desirable if you wish to harden Lix clients.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito), [Tom Hubrecht](https://git.lix.systems/tom-hubrecht), [alois31](https://git.lix.systems/alois31), and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
- Nix shells' `$NIX_BUILD_TOP` are shorter [lix#1044](https://git.lix.systems/lix-project/lix/issues/1044) [cl/4663](https://gerrit.lix.systems/c/lix/+/4663)
|
||||
|
||||
Following the changes in 2.94.0 to shorten build directory paths, aimed at [resolving UNIX domain socket length issues](https://gerrit.lix.systems/c/lix/+/4168/13) and [improving nix-shell](https://git.lix.systems/lix-project/lix/issues/940), we inadvertently introduced an excessively long path for the `$NIX_BUILD_TOP` environment variable used by Nix shells (their effective temporary `/build` directory).
|
||||
|
||||
To fix this, we replaced the `build-top-$HASH` directory name with simply `build-top`, reducing these paths by at least 30 characters.
|
||||
|
||||
We also added a test to ensure that Nix shells do not introduce more than 50 extra characters relative to their base directory (e.g., `/tmp` when `$TMPDIR` is not set).
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
|
||||
|
||||
- Fix resolving of symlinks in flake paths [lix#106](https://git.lix.systems/lix-project/lix/issues/106) [lix#12286](https://git.lix.systems/lix-project/lix/pulls/12286) [cl/4783](https://gerrit.lix.systems/c/lix/+/4783)
|
||||
|
||||
Flake paths are now canonicalized to resolve symlinks. This ensures that when a flake is accessed via a symlink, paths are resolved relative to the target directory, not the symlink's location.
|
||||
|
||||
Many thanks to [stevalkr](https://github.com/stevalkr) and [xyenon](https://git.lix.systems/xyenon) for this.
|
||||
|
||||
- The REPL no longer considers failed loads for `:reload` [lix#50](https://git.lix.systems/lix-project/lix/issues/50) [cl/4864](https://gerrit.lix.systems/c/lix/+/4864) [cl/4865](https://gerrit.lix.systems/c/lix/+/4865) [cl/4700](https://gerrit.lix.systems/c/lix/+/4700) [cl/4889](https://gerrit.lix.systems/c/lix/+/4889)
|
||||
|
||||
The [REPL](@docroot@/command-ref/new-cli/nix3-repl.md) allows "loading" files, flakes, and expressions into the environment, with the commands `:load`/`:l`, `:load-flake`/`:lf`, and `:add`/`:a` respectively.
|
||||
The results of those stay in the environment as-is even if their sources change, until the `:reload` command is used.
|
||||
However `:reload` would re-perform *all* instances of `:l`/`:lf`/`:a`, meaning you would get things like this:
|
||||
|
||||
```nix
|
||||
nix-repl> :l /tmp/texting.nix
|
||||
error: getting status of '/tmp/texting.nix': No such file or directory
|
||||
# oops, typo.
|
||||
nix-repl> :l /tmp/testing.nix
|
||||
|
||||
# Do some stuff…
|
||||
|
||||
nix-repl> :reload
|
||||
error: getting status of '/tmp/texting.nix': No such file or directory
|
||||
```
|
||||
|
||||
This is pretty silly, but also *incredibly* annoying, as it would stop there and *not* reload the correct files anymore.
|
||||
This effectively meant typoing any of the load commands would make `:reload` useless for the rest of the entire `nix repl` session!
|
||||
|
||||
This has been fixed, so now only *successful* loads count towards `:reload`.
|
||||
|
||||
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||
|
||||
- Consistently use commit hash as rev when locking git inputs [cl/4762](https://gerrit.lix.systems/c/lix/+/4762)
|
||||
|
||||
Lix will now use commit hashes instead of tag object hashes in the `rev` field
|
||||
when fetching git inputs by tag in `flake.lock` and `builtins.fetchTree` output.
|
||||
Note that this means that Lix may change some `flake.lock` files on re-locking. Old `flake.lock` files still remain valid.
|
||||
|
||||
Many thanks to [goldstein](https://git.lix.systems/goldstein) for this.
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
- Functional lang migration [lix#856](https://git.lix.systems/lix-project/lix/issues/856) [cl/3213](https://gerrit.lix.systems/c/lix/+/3213)
|
||||
|
||||
We have done it! The functional/lang framework has now been fully migrated to functional2/lang.
|
||||
This means: no more `just clean` and `just install` mess and whatever because one removed a test.
|
||||
The lang test suite is also getting a face lift, with an improved folder structure and restructuring of many tests.
|
||||
|
||||
Only the first CL of the chain is provided but there's way more changes associated to this project.
|
||||
|
||||
Many thanks to [piegames](https://git.lix.systems/piegames) and [rootile (Rutile)](https://git.lix.systems/rootile) for this.
|
||||
|
||||
|
||||
## Miscellany
|
||||
|
||||
- Warn instead of erroring when the final destination of a transfer changes in-flight [lix#1004](https://git.lix.systems/lix-project/lix/issues/1004) [cl/4641](https://gerrit.lix.systems/c/lix/+/4641)
|
||||
|
||||
Lix will now emit a warning during downloads where the final destination changes suddently mid-transfer instead of throwing an error.
|
||||
This transfer behavior has been known to happen very rarely while fetching from some CDNs.
|
||||
|
||||
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
|
||||
|
||||
- `impersonate-linux-26` setting removed [cl/5047](https://gerrit.lix.systems/c/lix/+/5047)
|
||||
|
||||
Linux 3.0 was released 15 years ago. The `impersonate-linux-26` setting was added
|
||||
14 years ago with no mention of it being necessary to build anything, only saying
|
||||
that it improves determinism—which isn't accurate since impersonating Linux 2.6.x
|
||||
still allows the version string to change, and the final component of the version
|
||||
does still change with each Linux release. Since this setting should be no longer
|
||||
necessary in modern systems and workarounds for building old code exist (by using
|
||||
e.g. `setarch --uname-2.6` to wrap builds) we are removing this setting from Lix.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- Default to showing build logs in the new-style (nix3) CLI [cl/4674](https://gerrit.lix.systems/c/lix/+/4674)
|
||||
|
||||
Lix will now show logs by default, in addition to the progress bar, when invoked through the new-style "nix3" CLI (`nix build`, etc)
|
||||
|
||||
Many thanks to [K900](https://git.lix.systems/K900) for this.
|
||||
|
||||
- Lix daemons are now fully socket-activated on systemd setups [lix#1030](https://git.lix.systems/lix-project/lix/issues/1030)
|
||||
|
||||
When launched by systemd, Lix no longer uses a persistent daemon process and uses systemd socket
|
||||
activation instead. This is necessary to support the `cgroups` and `auto-allocate-uids` features
|
||||
and may improve observability of daemon behavior with common systemd-based monitoring solutions.
|
||||
|
||||
The old behavior with a single persistent daemon is still available, but disabled by default. It
|
||||
is not possible to enable both a persistent daemon and socket activation, starting one stops the
|
||||
other automatically. Existing installations should not require any changes when they're updated.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
|
||||
- Plugin interfaces have changed (again) [lix#359](https://git.lix.systems/lix-project/lix/issues/359) [cl/4933](https://gerrit.lix.systems/c/lix/+/4933) [cl/4934](https://gerrit.lix.systems/c/lix/+/4934)
|
||||
|
||||
The `RegisterPrimOp` class used to register builtins has been removed. Plugins
|
||||
must now call `PluginPrimOps::add` from their `nix_plugin_entry` with the same
|
||||
parameters previously passed to `RegisterRrimOp` to register any new builtins.
|
||||
|
||||
The `GlobalConfig::Register` helper class has also been removed. Adding config
|
||||
options to the system is now done with `GlobalConfig::registerGlobalConfig`; a
|
||||
plugin can add config values by calling this function from `nix_plugin_entry`.
|
||||
|
||||
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||
@@ -70,9 +70,9 @@ def do_include(content: str, relative_md_path: Path, source_root: Path, search_p
|
||||
|
||||
def recursive_replace(data, book_root, search_path):
|
||||
match data:
|
||||
case {'items': items}:
|
||||
case {'sections': sections}:
|
||||
return data | dict(
|
||||
items = [recursive_replace(item, book_root, search_path) for item in items],
|
||||
sections = [recursive_replace(section, book_root, search_path) for section in sections],
|
||||
)
|
||||
case {'Chapter': chapter}:
|
||||
path_to_chapter = Path(chapter['path'])
|
||||
@@ -119,10 +119,10 @@ def main():
|
||||
context, book = json.load(sys.stdin)
|
||||
|
||||
# book_root is the directory where book contents leave (ie, src/)
|
||||
book_root = Path(context['root']) / context['config']['book'].get('src', 'src')
|
||||
book_root = Path(context['root']) / context['config']['book']['src']
|
||||
|
||||
# includes pointing into @generated@ will look here
|
||||
search_path = Path(os.environ['MANUAL_SUBSTITUTE_SEARCH'])
|
||||
search_path = Path(os.environ['MDBOOK_SUBSTITUTE_SEARCH'])
|
||||
|
||||
# Find @var@ in all parts of our recursive book structure.
|
||||
replaced_content = recursive_replace(book, book_root, search_path)
|
||||
|
||||
+1
-1
@@ -381,7 +381,7 @@ image
|
||||
pkgs.buildPackages.runCommand "docker-image-tarball-${pkgs.nix.version}"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.buildPackages.bubblewrap ];
|
||||
meta.description = "Docker image tarball with Lix for ${pkgs.stdenv.hostPlatform.system}";
|
||||
meta.description = "Docker image tarball with Lix for ${pkgs.system}";
|
||||
}
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
|
||||
Generated
+19
-17
@@ -3,15 +3,17 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1751685974,
|
||||
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
|
||||
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
|
||||
"type": "tarball",
|
||||
"url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz"
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
@@ -33,11 +35,11 @@
|
||||
"nix2container": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767195068,
|
||||
"narHash": "sha256-+OMnL79ZjqM/PCz2hoQ12MnXNoSSfBGnsYBOZnA9XbI=",
|
||||
"lastModified": 1724996935,
|
||||
"narHash": "sha256-njRK9vvZ1JJsP8oV2OgkBrpJhgQezI03S7gzskCcHos=",
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"rev": "bb6801be998ba857a62c002cb77ece66b0a57298",
|
||||
"rev": "fa6bb0a1159f55d071ba99331355955ae30b3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -106,16 +108,16 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1783770249,
|
||||
"narHash": "sha256-K8pGvFito5dp9T0+clr60q+bJPGEskK75aAJ39w7HBM=",
|
||||
"lastModified": 1757198069,
|
||||
"narHash": "sha256-m3VUcOD4rTs8J7S+3dOjWMrAjw6RcITC3XYQ98zhEFs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "62463162b3ce92919f19ada41a70a0d943a08da8",
|
||||
"rev": "0747026fc57ecb9c28901c7f7a2b5dc40e8af43c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-26.05-small",
|
||||
"ref": "nixos-25.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -123,11 +125,11 @@
|
||||
"pre-commit-hooks": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1769939035,
|
||||
"narHash": "sha256-Fok2AmefgVA0+eprw2NDwqKkPGEI5wvR+twiZagBvrg=",
|
||||
"lastModified": 1733318908,
|
||||
"narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "a8ca480175326551d6c4121498316261cbb5b260",
|
||||
"rev": "6f4e2a2112050951a314d2733a994fbab94864c6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -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.05-small";
|
||||
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
|
||||
# Required because Nix 2.18 is not in Nixpkgs ≥ 25.05 anymore.
|
||||
@@ -24,7 +24,7 @@
|
||||
flake = false;
|
||||
};
|
||||
flake-compat = {
|
||||
url = "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz";
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
@@ -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,204 @@
|
||||
(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_3 = prev.nixVersions.nix_2_3.overrideAttrs (old: {
|
||||
meta = old.meta // {
|
||||
knownVulnerabilities = [ ];
|
||||
};
|
||||
});
|
||||
# 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;
|
||||
};
|
||||
|
||||
# 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;
|
||||
};
|
||||
|
||||
lix-clang-tidy = final.callPackage ./subprojects/lix-clang-tidy { };
|
||||
|
||||
nix-eval-jobs = final.callPackage ./subprojects/nix-eval-jobs {
|
||||
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;
|
||||
|
||||
lowdown_1_3 =
|
||||
# If the stable channel we are using ships lowdown >= 1.4, we need
|
||||
# to swap this around, take the default lowdown from the stable
|
||||
# channel and add an overridden one for the legacy version.
|
||||
assert lib.versionOlder prev.lowdown.version "1.4.0";
|
||||
prev.lowdown;
|
||||
lowdown = prev.lowdown.overrideAttrs (prevAttrs: rec {
|
||||
version = "2.0.2";
|
||||
src = final.fetchurl {
|
||||
url = "https://kristaps.bsd.lv/lowdown/snapshots/lowdown-${version}.tar.gz";
|
||||
sha512 = "2a4d0rqh8gkw4ca3gkzddp0hjpmmw74cbks8k0inhh0vizmgbn188zdv6m1kgmr019b99g7insli8js3ci1ji7y4n5nk704bswf3z3i";
|
||||
};
|
||||
nativeBuildInputs = prevAttrs.nativeBuildInputs ++ [ final.buildPackages.bmake ];
|
||||
postInstall = lib.replaceStrings [ "lowdown.so.1" ] [ "lowdown.so.2" ] prevAttrs.postInstall;
|
||||
});
|
||||
|
||||
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 +283,196 @@
|
||||
# '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);
|
||||
|
||||
# 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
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
# Ensure support for lowdown < 1.4 doesn't regress
|
||||
build-lowdown_1_3 = forAllSystems (
|
||||
system:
|
||||
self.packages.${system}.nix.override {
|
||||
lowdown = nixpkgsFor.${system}.native.lowdown_1_3;
|
||||
}
|
||||
);
|
||||
|
||||
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 =
|
||||
let
|
||||
nixpkgs = nixpkgsFor.x86_64-linux.native;
|
||||
inherit (nixpkgs) pkgs;
|
||||
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 =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
runCommand "eval-nixos" { buildInputs = [ nix ]; } ''
|
||||
type -p nix-env
|
||||
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
|
||||
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
|
||||
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
|
||||
mkdir $out
|
||||
'';
|
||||
|
||||
nixpkgsLibTests = forAllSystems (
|
||||
system:
|
||||
let
|
||||
inherit (self.packages.${system}) nix;
|
||||
pkgs = nixpkgsFor.${system}.native;
|
||||
testWithNix = import (nixpkgs + "/lib/tests/test-with-nix.nix") { inherit pkgs lib nix; };
|
||||
in
|
||||
pkgs.symlinkJoin {
|
||||
name = "nixpkgs-lib-tests";
|
||||
paths = [
|
||||
testWithNix
|
||||
]
|
||||
# NOTE: nixpkgs 25.05 is being ... *creative*, and requires this dance to override
|
||||
# the evaluator used for the test. it will break again in the future, don't worry.
|
||||
++ lib.optionals pkgs.stdenv.isLinux [
|
||||
((pkgs.callPackage "${nixpkgs}/ci/eval" { inherit nix; }).attrpathsSuperset {
|
||||
evalSystem = system;
|
||||
})
|
||||
];
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
pre-commit = forAvailableSystems (
|
||||
system:
|
||||
@@ -167,6 +518,7 @@
|
||||
# devShells and packages already get checked by nix flake check, so
|
||||
# this is just jobs that are special
|
||||
|
||||
build-lowdown_1_3 = self.hydraJobs.build-lowdown_1_3.${system};
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
nix-eval-jobs = self.hydraJobs.nix-eval-jobs.${system};
|
||||
@@ -181,7 +533,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 system; };
|
||||
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
|
||||
@@ -192,11 +582,8 @@
|
||||
inherit stdenv versionSuffix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
|
||||
internalApiDocs = false;
|
||||
includeSanitizerLibs = true;
|
||||
# Use LLD in the dev shell by default for faster link times.
|
||||
useLld = stdenv.hostPlatform.isLinux;
|
||||
};
|
||||
pre-commit = self.hydraJobs.pre-commit.${pkgs.stdenv.hostPlatform.system} or { };
|
||||
pre-commit = self.hydraJobs.pre-commit.${pkgs.system} or { };
|
||||
in
|
||||
pkgs.callPackage nix.mkDevShell {
|
||||
pre-commit-checks = pre-commit;
|
||||
@@ -214,30 +601,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 {{ 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 && python -m pytest -v {{ OPTIONS }} functional2
|
||||
|
||||
# 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:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# noqa: N999 # consistency with rest of the codebase
|
||||
@@ -1,113 +1,93 @@
|
||||
import dataclasses
|
||||
from enum import Enum
|
||||
from textwrap import dedent, indent
|
||||
from typing import NamedTuple
|
||||
from typing import List, NamedTuple
|
||||
|
||||
from common import cxx_literal, generate_file, load_data
|
||||
|
||||
from common import cxx_literal, generate_file, load_data, get_argument_parser
|
||||
KNOWN_KEYS = set([
|
||||
'name',
|
||||
'type',
|
||||
'constructorArgs',
|
||||
'implementation',
|
||||
'impure',
|
||||
'renameInGlobalScope',
|
||||
])
|
||||
|
||||
IMPURE_NOTE = """
|
||||
> **Note**
|
||||
class BuiltinConstant(NamedTuple):
|
||||
name: str
|
||||
type: str
|
||||
implementation: str
|
||||
impure: bool
|
||||
rename_in_global_scope: bool
|
||||
documentation: str
|
||||
|
||||
def parse(datum):
|
||||
unknown_keys = set(datum.keys()) - KNOWN_KEYS
|
||||
if unknown_keys:
|
||||
raise Exception('unknown keys', unknown_keys)
|
||||
return BuiltinConstant(
|
||||
name = datum['name'],
|
||||
type = datum['type'],
|
||||
implementation = ('{' + ', '.join([f'NewValueAs::{datum["type"]}', *datum['constructorArgs']]) + '}') if 'constructorArgs' in datum else datum['implementation'],
|
||||
impure = datum.get('impure', False),
|
||||
rename_in_global_scope = datum.get('renameInGlobalScope', True),
|
||||
documentation = datum.content,
|
||||
)
|
||||
|
||||
VALUE_TYPES = {
|
||||
'attrs': 'nAttrs',
|
||||
'boolean': 'nBool',
|
||||
'integer': 'nInt',
|
||||
'list': 'nList',
|
||||
'null': 'nNull',
|
||||
'string': 'nString',
|
||||
}
|
||||
|
||||
HUMAN_TYPES = {
|
||||
'attrs': 'set',
|
||||
'boolean': 'Boolean',
|
||||
'integer': 'integer',
|
||||
'list': 'list',
|
||||
'null': 'null',
|
||||
'string': 'string',
|
||||
}
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--header', help='Path of the header to generate')
|
||||
ap.add_argument('--docs', help='Path of the documentation file to generate')
|
||||
ap.add_argument('defs', help='Builtin definition files', nargs='+')
|
||||
args = ap.parse_args()
|
||||
|
||||
builtin_constants = load_data(args.defs, BuiltinConstant.parse)
|
||||
|
||||
generate_file(args.header, builtin_constants, lambda constant:
|
||||
# `builtins` is magic and must come first
|
||||
'' if constant.name == 'builtins' else constant.name,
|
||||
lambda constant:
|
||||
f'''{'if (!evalSettings.pureEval) ' if constant.impure else ''}{{
|
||||
addConstant({cxx_literal(('__' if constant.rename_in_global_scope else '') + constant.name)}, {constant.implementation}, {{
|
||||
.type = {VALUE_TYPES[constant.type]},
|
||||
.doc = {cxx_literal(constant.documentation)},
|
||||
.impureOnly = {cxx_literal(constant.impure)},
|
||||
}});
|
||||
}}
|
||||
''')
|
||||
generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda constant:
|
||||
f'''<dt id="builtins-{constant.name}">
|
||||
<a href="#builtins-{constant.name}"><code>{constant.name}</code></a> ({HUMAN_TYPES[constant.type]})
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
{constant.documentation}
|
||||
|
||||
''' + ('''> **Note**
|
||||
>
|
||||
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||
|
||||
"""
|
||||
''' if constant.impure else '') + '''</dd>
|
||||
|
||||
''')
|
||||
|
||||
class TypeName(NamedTuple):
|
||||
human: str
|
||||
code: str
|
||||
|
||||
|
||||
class BuiltinType(TypeName, Enum):
|
||||
attrs = TypeName("set", "nAttrs")
|
||||
boolean = TypeName("boolean", "nBool")
|
||||
integer = TypeName("integer", "nInt")
|
||||
list = TypeName("list", "nList")
|
||||
null = TypeName("null", "nNull")
|
||||
string = TypeName("string", "nString")
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, t_name: str) -> "BuiltinType":
|
||||
for t in cls:
|
||||
if t_name == t.name:
|
||||
return t
|
||||
msg = f"Invalid builtin type: {t_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class BuiltinConstant:
|
||||
name: str
|
||||
documentation: str
|
||||
|
||||
# Fields with different name in the Post than in here
|
||||
# our fields
|
||||
type: BuiltinType = dataclasses.field(init=False)
|
||||
|
||||
# Post fields
|
||||
type_str: dataclasses.InitVar[str]
|
||||
constructor_args: dataclasses.InitVar[list[str] | None] = None
|
||||
|
||||
implementation: str = ""
|
||||
impure: bool = False
|
||||
rename_in_global_scope: bool = True
|
||||
|
||||
def __post_init__(self, type_str: str, constructor_args: list[str] | None):
|
||||
self.type = BuiltinType.from_string(type_str)
|
||||
if constructor_args is not None:
|
||||
args = [f"NewValueAs::{type_str}"] + constructor_args
|
||||
self.implementation = f"{{{','.join(args)}}}"
|
||||
|
||||
@property
|
||||
def code(self) -> str:
|
||||
cond = "if (!evalSettings.pureEval) " if self.impure else ""
|
||||
return dedent(f"""
|
||||
{cond} {{
|
||||
addConstant(
|
||||
{cxx_literal(("__" if self.rename_in_global_scope else "") + self.name)},
|
||||
{self.implementation},
|
||||
{{
|
||||
.type = {self.type.code},
|
||||
.doc = {cxx_literal(self.documentation)},
|
||||
.impureOnly = {cxx_literal(self.impure)},
|
||||
}}
|
||||
);
|
||||
}}
|
||||
""")
|
||||
|
||||
@property
|
||||
def docs(self) -> str:
|
||||
indentation = " " * 3
|
||||
return dedent(f"""
|
||||
<dt id="builtins-{self.name}">
|
||||
<a href="#builtins-{self.name}"><code>{self.name}</code></a> ({self.type.human})
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
{indent(self.documentation, indentation)}
|
||||
{indent(IMPURE_NOTE, indentation) if self.impure else ""}
|
||||
|
||||
</dd>
|
||||
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
args = get_argument_parser().parse_args()
|
||||
|
||||
builtin_constants = load_data(args.defs, BuiltinConstant)
|
||||
|
||||
generate_file(
|
||||
args.header,
|
||||
builtin_constants,
|
||||
lambda constant:
|
||||
# `builtins` is magic and must come first
|
||||
"" if constant.name == "builtins" else constant.name,
|
||||
lambda b: b.code,
|
||||
)
|
||||
generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda b: b.docs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,90 +1,82 @@
|
||||
import dataclasses
|
||||
from textwrap import dedent, indent
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
from common import (
|
||||
cxx_literal,
|
||||
generate_file,
|
||||
load_data,
|
||||
get_argument_parser,
|
||||
get_experimental_features,
|
||||
)
|
||||
from build_experimental_features import ExperimentalFeature
|
||||
from common import cxx_literal, generate_file, load_data
|
||||
|
||||
KNOWN_KEYS = set([
|
||||
'name',
|
||||
'implementation',
|
||||
'renameInGlobalScope',
|
||||
'args',
|
||||
'experimentalFeature',
|
||||
])
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Builtin:
|
||||
class Builtin(NamedTuple):
|
||||
name: str
|
||||
implementation: str
|
||||
rename_in_global_scope: bool
|
||||
args: List[str]
|
||||
experimental_feature: Optional[str]
|
||||
documentation: str
|
||||
args: list[str]
|
||||
experimental_feature: str | None = None
|
||||
implementation: str = ""
|
||||
rename_in_global_scope: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
self.implementation = self.implementation or f"prim_{self.name}"
|
||||
|
||||
def generate_code(self, experimental_features: dict[str, str]) -> str:
|
||||
xf = experimental_features[self.experimental_feature]
|
||||
cond = (
|
||||
f"if (experimentalFeatureSettings.isEnabled({xf})) "
|
||||
if self.experimental_feature
|
||||
else ""
|
||||
def parse(datum):
|
||||
unknown_keys = set(datum.keys()) - KNOWN_KEYS
|
||||
if unknown_keys:
|
||||
raise Exception('unknown keys', unknown_keys)
|
||||
return Builtin(
|
||||
name = datum['name'],
|
||||
implementation = datum['implementation'] if 'implementation' in datum else f'prim_{datum["name"]}',
|
||||
rename_in_global_scope = datum.get('renameInGlobalScope', True),
|
||||
args = datum['args'],
|
||||
experimental_feature = datum.get('experimentalFeature', None),
|
||||
documentation = datum.content,
|
||||
)
|
||||
return dedent(f"""
|
||||
{cond}{{
|
||||
addPrimOp({{
|
||||
.name = {cxx_literal(("__" if self.rename_in_global_scope else "") + self.name)},
|
||||
.args = {cxx_literal(self.args)},
|
||||
.arity = {len(self.args)},
|
||||
.doc = {cxx_literal(self.documentation)},
|
||||
.fun = {self.implementation},
|
||||
.experimentalFeature = {xf},
|
||||
}});
|
||||
}}
|
||||
""")
|
||||
|
||||
@property
|
||||
def docs(self) -> str:
|
||||
return dedent(f"""
|
||||
<dt id="builtins-{self.name}">
|
||||
<a href="#builtins-{self.name}"><code>{self.name} {
|
||||
" ".join([f"<var>{arg}</var>" for arg in self.args])
|
||||
}</code></a>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
{indent(self.documentation, " " * 3)}
|
||||
|
||||
{
|
||||
f"This function is only available if the [{self.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{self.experimental_feature}) experimental feature is enabled."
|
||||
if self.experimental_feature is not None
|
||||
else ""
|
||||
}
|
||||
</dd>
|
||||
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
ap = get_argument_parser()
|
||||
ap.add_argument(
|
||||
"--experimental-features", help="Directory containing the experimental feature definitions"
|
||||
)
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--header', help='Path of the header to generate')
|
||||
ap.add_argument('--docs', help='Path of the documentation file to generate')
|
||||
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
|
||||
ap.add_argument('defs', help='Builtin definition files', nargs='+')
|
||||
args = ap.parse_args()
|
||||
|
||||
builtins = load_data(args.defs, Builtin)
|
||||
builtins = load_data(args.defs, Builtin.parse)
|
||||
|
||||
experimental_features = get_experimental_features(
|
||||
args.experimental_features, [b.experimental_feature for (_, b) in builtins]
|
||||
)
|
||||
experimental_feature_names = set([builtin.experimental_feature for (_, builtin) in builtins])
|
||||
experimental_feature_names.discard(None)
|
||||
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
|
||||
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
|
||||
experimental_features = dict(map(lambda path_and_feature:
|
||||
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
|
||||
experimental_features[None] = 'std::nullopt'
|
||||
|
||||
generate_file(
|
||||
args.header,
|
||||
builtins,
|
||||
lambda builtin: builtin.name,
|
||||
lambda b: b.generate_code(experimental_features),
|
||||
)
|
||||
generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda b: b.docs)
|
||||
generate_file(args.header, builtins, lambda builtin: builtin.name, lambda builtin:
|
||||
f'''{'' if builtin.experimental_feature is None else f'if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) '}{{
|
||||
addPrimOp({{
|
||||
.name = {cxx_literal(('__' if builtin.rename_in_global_scope else '') + builtin.name)},
|
||||
.args = {cxx_literal(builtin.args)},
|
||||
.arity = {len(builtin.args)},
|
||||
.doc = {cxx_literal(builtin.documentation)},
|
||||
.fun = {builtin.implementation},
|
||||
.experimentalFeature = {experimental_features[builtin.experimental_feature]},
|
||||
}});
|
||||
}}
|
||||
''')
|
||||
generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda builtin:
|
||||
f'''<dt id="builtins-{builtin.name}">
|
||||
<a href="#builtins-{builtin.name}"><code>{builtin.name} {' '.join([f'<var>{arg}</var>' for arg in builtin.args])}</code></a>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
{builtin.documentation}
|
||||
|
||||
if __name__ == "__main__":
|
||||
''' + (f'''This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
|
||||
|
||||
''' if builtin.experimental_feature is not None else '') + '''</dd>
|
||||
|
||||
''')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
from common import cxx_literal, generate_file, load_data
|
||||
|
||||
KNOWN_KEYS = set([
|
||||
'name',
|
||||
'internalName',
|
||||
])
|
||||
|
||||
class ExperimentalFeature(NamedTuple):
|
||||
name: str
|
||||
internal_name: str
|
||||
description: str
|
||||
|
||||
def parse(datum):
|
||||
unknown_keys = set(datum.keys()) - KNOWN_KEYS
|
||||
if unknown_keys:
|
||||
raise ValueError('unknown keys', unknown_keys)
|
||||
return ExperimentalFeature(
|
||||
name = datum['name'],
|
||||
internal_name = datum['internalName'],
|
||||
description = datum.content,
|
||||
)
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--deprecated', action='store_true', help='Generate deprecated features')
|
||||
ap.add_argument('--header', help='Path of the declaration header to generate')
|
||||
ap.add_argument('--impl-header', help='Path of the implementation header to generate')
|
||||
ap.add_argument('--descriptions', help='Path of the description file to generate')
|
||||
ap.add_argument('--shortlist', help='Path of the shortlist file to generate')
|
||||
ap.add_argument('defs', help='Experimental feature definition files', nargs='+')
|
||||
args = ap.parse_args()
|
||||
|
||||
features = load_data(args.defs, ExperimentalFeature.parse)
|
||||
|
||||
generate_file(args.header, features, lambda feature: feature.name, lambda feature:
|
||||
f' {feature.internal_name},\n')
|
||||
generate_file(args.impl_header, features, lambda feature: feature.name, lambda feature:
|
||||
f''' {{
|
||||
.tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name},
|
||||
.name = {cxx_literal(feature.name)},
|
||||
.description = {cxx_literal(feature.description)},
|
||||
}},
|
||||
''')
|
||||
generate_file(args.descriptions, features, lambda feature: feature.name, lambda feature:
|
||||
f'''## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
|
||||
|
||||
{feature.description}
|
||||
|
||||
''')
|
||||
generate_file(args.shortlist, features, lambda feature: feature.name, lambda feature:
|
||||
f' - [`{feature.name}`](@docroot@/contributing/{"deprecated" if args.deprecated else "experimental"}-features.md#{"dp" if args.deprecated else "xp"}-feature-{feature.name})\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,105 +0,0 @@
|
||||
import dataclasses
|
||||
from enum import Enum
|
||||
from textwrap import dedent
|
||||
from typing import ClassVar, NamedTuple
|
||||
|
||||
from common import cxx_literal, generate_file, load_data, get_argument_parser
|
||||
|
||||
|
||||
class FeatureTypeNames(NamedTuple):
|
||||
code_tag: str
|
||||
doc_tag: str
|
||||
|
||||
|
||||
class TimelineEvent(NamedTuple):
|
||||
date: str
|
||||
release: str
|
||||
message: str
|
||||
cls: list[int]
|
||||
|
||||
|
||||
class FeatureType(FeatureTypeNames, Enum):
|
||||
experimental = FeatureTypeNames("Xp", "xp")
|
||||
deprecated = FeatureTypeNames("Dep", "dp")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ExtraFeature:
|
||||
name: str
|
||||
internal_name: str
|
||||
documentation: str
|
||||
timeline: list[TimelineEvent] = dataclasses.field(default_factory=list)
|
||||
|
||||
type: ClassVar[FeatureType]
|
||||
|
||||
@property
|
||||
def code(self) -> str:
|
||||
return dedent(f"""
|
||||
{{
|
||||
.tag = {ExtraFeature.type.code_tag}::{self.internal_name},
|
||||
.name = {cxx_literal(self.name)},
|
||||
.description = {cxx_literal(self.documentation)},
|
||||
}},
|
||||
""")
|
||||
|
||||
@property
|
||||
def docs(self) -> str:
|
||||
timeline = (
|
||||
f"""
|
||||
|
||||
### Timeline
|
||||
|
||||
{
|
||||
"\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])}]"
|
||||
for event in self.timeline
|
||||
]
|
||||
)
|
||||
}
|
||||
"""
|
||||
if self.timeline
|
||||
else ""
|
||||
)
|
||||
return dedent(f"""
|
||||
## [`{self.name}`]{{#{ExtraFeature.type.doc_tag}-feature-{self.name}}}
|
||||
|
||||
{self.documentation.replace("\n", f"\n{' ' * 3}")}
|
||||
|
||||
{timeline}
|
||||
|
||||
""")
|
||||
|
||||
@property
|
||||
def short_docs(self) -> str:
|
||||
return f" - [`{self.name}`](@docroot@/contributing/{ExtraFeature.type.name}-features.md#{ExtraFeature.type.doc_tag}-feature-{self.name})\n"
|
||||
|
||||
|
||||
def main():
|
||||
ap = get_argument_parser()
|
||||
ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
|
||||
ap.add_argument("--impl-header", help="Path of the implementation header to generate")
|
||||
ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
|
||||
args = ap.parse_args()
|
||||
|
||||
ExtraFeature.type = FeatureType.deprecated if args.deprecated else FeatureType.experimental
|
||||
|
||||
def load(**kwargs) -> ExtraFeature:
|
||||
kwargs["timeline"] = [TimelineEvent(**args) for args in kwargs.get("timeline", [])]
|
||||
return ExtraFeature(**kwargs)
|
||||
|
||||
features = load_data(args.defs, load)
|
||||
|
||||
generate_file(
|
||||
args.header,
|
||||
features,
|
||||
lambda feature: feature.name,
|
||||
lambda feature: f" {feature.internal_name},\n",
|
||||
)
|
||||
generate_file(args.impl_header, features, lambda feature: feature.name, lambda f: f.code)
|
||||
generate_file(args.docs, features, lambda feature: feature.name, lambda f: f.docs)
|
||||
generate_file(args.shortlist, features, lambda feature: feature.name, lambda f: f.short_docs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,157 +1,141 @@
|
||||
import dataclasses
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
from common import (
|
||||
cxx_literal,
|
||||
generate_file,
|
||||
load_data,
|
||||
get_experimental_features,
|
||||
get_argument_parser,
|
||||
)
|
||||
from build_experimental_features import ExperimentalFeature
|
||||
from common import cxx_literal, generate_file, load_data
|
||||
|
||||
KNOWN_KEYS = set([
|
||||
'name',
|
||||
'internalName',
|
||||
'platforms',
|
||||
'type',
|
||||
'settingType',
|
||||
'default',
|
||||
'defaultExpr',
|
||||
'defaultText',
|
||||
'aliases',
|
||||
'experimentalFeature',
|
||||
'deprecated',
|
||||
])
|
||||
|
||||
PLATFORM_WARNING = """
|
||||
> **Note**
|
||||
> This setting is only available on {platforms} systems.
|
||||
class Setting(NamedTuple):
|
||||
name: str
|
||||
internal_name: str
|
||||
description: str
|
||||
platforms: Optional[List[str]]
|
||||
setting_type: str
|
||||
default_expr: str
|
||||
default_text: str
|
||||
aliases: List[str]
|
||||
experimental_feature: Optional[str]
|
||||
deprecated: bool
|
||||
|
||||
"""
|
||||
def parse(datum):
|
||||
unknown_keys = set(datum.keys()) - KNOWN_KEYS
|
||||
if unknown_keys:
|
||||
raise ValueError('unknown keys', unknown_keys)
|
||||
default_text = f'`{nix_conf_literal(datum["default"])}`' if 'default' in datum else datum['defaultText']
|
||||
if default_text == '``':
|
||||
default_text = '*empty*'
|
||||
return Setting(
|
||||
name = datum['name'],
|
||||
internal_name = datum['internalName'],
|
||||
description = datum.content,
|
||||
platforms = datum.get('platforms', None),
|
||||
setting_type = f'Setting<{datum["type"]}>' if 'type' in datum else datum['settingType'],
|
||||
default_expr = cxx_literal(datum['default']) if 'default' in datum else datum['defaultExpr'],
|
||||
default_text = default_text,
|
||||
aliases = datum.get('aliases', []),
|
||||
experimental_feature = datum.get('experimentalFeature', None),
|
||||
deprecated = datum.get('deprecated', False),
|
||||
)
|
||||
|
||||
XP_WARNING = """
|
||||
> **Warning**
|
||||
platform_names = {
|
||||
'darwin': 'Darwin',
|
||||
'linux': 'Linux',
|
||||
}
|
||||
|
||||
def nix_conf_literal(v):
|
||||
if v is None:
|
||||
return ''
|
||||
elif isinstance(v, bool) and v == False: # 0 == False
|
||||
return 'false'
|
||||
elif isinstance(v, bool) and v == True: # 1 == True
|
||||
return 'true'
|
||||
elif isinstance(v, int):
|
||||
return str(v)
|
||||
elif isinstance(v, str):
|
||||
return v
|
||||
elif isinstance(v, list):
|
||||
return ' '.join([nix_conf_literal(item) for item in v])
|
||||
else:
|
||||
raise NotImplementedError(f'Cannot represent {repr(v)} in nix.conf')
|
||||
|
||||
def indent(prefix, body):
|
||||
return ''.join(['\n' if line == '' else f'{prefix}{line}\n' for line in body.split('\n')])
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--kernel', help='Name of the kernel Lix will run on')
|
||||
ap.add_argument('--header', help='Path of the header to generate')
|
||||
ap.add_argument('--docs', help='Path of the documentation file to generate')
|
||||
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
|
||||
ap.add_argument('defs', help='Setting definition files', nargs='+')
|
||||
args = ap.parse_args()
|
||||
|
||||
settings = load_data(args.defs, Setting.parse)
|
||||
|
||||
experimental_feature_names = set([setting.experimental_feature for (_, setting) in settings])
|
||||
experimental_feature_names.discard(None)
|
||||
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
|
||||
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
|
||||
experimental_features = dict(map(lambda path_and_feature:
|
||||
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
|
||||
experimental_features[None] = 'std::nullopt'
|
||||
|
||||
generate_file(args.header, settings, lambda setting: setting.name, lambda setting:
|
||||
f'''{setting.setting_type} {setting.internal_name} {{
|
||||
this,
|
||||
{setting.default_expr},
|
||||
{cxx_literal(setting.name)},
|
||||
{cxx_literal(setting.description)},
|
||||
{cxx_literal(setting.aliases)},
|
||||
true,
|
||||
{experimental_features[setting.experimental_feature]},
|
||||
{cxx_literal(setting.deprecated)}
|
||||
}};
|
||||
|
||||
''' if setting.platforms is None or args.kernel in setting.platforms else '')
|
||||
generate_file(args.docs, settings, lambda setting: setting.name, lambda setting:
|
||||
f'''- <span id="conf-{setting.name}">[`{setting.name}`](#conf-{setting.name})</span>
|
||||
|
||||
{indent(" ", setting.description)}
|
||||
''' + (f''' > **Note**
|
||||
> This setting is only available on {', '.join([platform_names[platform] for platform in setting.platforms])} systems.
|
||||
|
||||
''' if setting.platforms is not None else '') + (f''' > **Warning**
|
||||
> This setting is part of an
|
||||
> [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||
|
||||
To change this setting, you need to make sure the corresponding experimental feature,
|
||||
[`{feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{feature}),
|
||||
[`{setting.experimental_feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{setting.experimental_feature}),
|
||||
is enabled.
|
||||
For example, include the following in [`nix.conf`](#):
|
||||
|
||||
```
|
||||
extra-experimental-features = {feature}
|
||||
{name} = ...
|
||||
extra-experimental-features = {setting.experimental_feature}
|
||||
{setting.name} = ...
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
DEPR_WARNING = """
|
||||
> **Warning**
|
||||
''' if setting.experimental_feature is not None else '') + (''' > **Warning**
|
||||
> This setting is deprecated and will be removed in a future version of Lix.
|
||||
|
||||
"""
|
||||
''' if setting.deprecated else '') + f''' **Default:** {setting.default_text}
|
||||
|
||||
''' + (f''' **Deprecated alias:** {', '.join([f'`{item}`' for item in setting.aliases])}
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Setting:
|
||||
name: str
|
||||
internal_name: str
|
||||
documentation: str
|
||||
''' if setting.aliases != [] else ''))
|
||||
|
||||
default_text: str = ""
|
||||
setting_type: str = ""
|
||||
default_expr: str = ""
|
||||
platforms: list[str] = dataclasses.field(default_factory=list)
|
||||
aliases: list[str] = dataclasses.field(default_factory=list)
|
||||
experimental_feature: str | None = None
|
||||
deprecated: bool = False
|
||||
|
||||
default: dataclasses.InitVar[str | None] = None
|
||||
type_str: dataclasses.InitVar[str | None] = None
|
||||
|
||||
def __post_init__(self, default: Any, type_str: str | None):
|
||||
if default is not None: # is not None nor an empty String
|
||||
self.default_text = f"`{nix_conf_literal(default)}`"
|
||||
self.default_expr = self.default_expr or cxx_literal(default)
|
||||
self.default_text = self.default_text or "*empty*"
|
||||
|
||||
if type_str is not None:
|
||||
self.setting_type = f"Setting<{type_str}>"
|
||||
|
||||
def generate_code(self, experimental_features: dict[str | None, str]) -> str:
|
||||
indentation = " " * 4
|
||||
expr = (indent(indentation, self.default_expr) + indentation) if "\n" in self.default_expr else self.default_expr
|
||||
return dedent(f"""
|
||||
{self.setting_type} {self.internal_name} {{
|
||||
this,
|
||||
{expr},
|
||||
{cxx_literal(self.name)},
|
||||
{cxx_literal(self.documentation)},
|
||||
{cxx_literal(self.aliases)},
|
||||
true,
|
||||
{experimental_features[self.experimental_feature]},
|
||||
{cxx_literal(self.deprecated)}
|
||||
}};
|
||||
""")
|
||||
|
||||
@property
|
||||
def docs(self) -> str:
|
||||
indentation = " " * 3
|
||||
platforms = [p.capitalize() for p in self.platforms]
|
||||
aliases = [f"`{item}`" for item in self.aliases]
|
||||
description = dedent(f"""
|
||||
|
||||
{indent(indentation, self.documentation)}
|
||||
|
||||
{indent(indentation, PLATFORM_WARNING.format(platforms=str(platforms)[1:-1])) if self.platforms else ""}
|
||||
{indent(indentation, XP_WARNING.format(feature=self.experimental_feature, name=self.name)) if self.experimental_feature is not None else ""}
|
||||
{indent(indentation, DEPR_WARNING) if self.deprecated else ""}
|
||||
**Default:** {self.default_text}
|
||||
{f"**Deprecated alias:** {str(aliases)[1:-1]}\n" if self.aliases else ""}
|
||||
""")
|
||||
return f'- <span id="conf-{self.name}">[`{self.name}`](#conf-{self.name})</span>' + indent(
|
||||
" ", # indent by two space to make it part of the list point
|
||||
description,
|
||||
)
|
||||
|
||||
|
||||
platform_names = {"darwin": "Darwin", "linux": "Linux"}
|
||||
|
||||
|
||||
def nix_conf_literal(v: Any) -> str:
|
||||
if v is None:
|
||||
return ""
|
||||
if v is False:
|
||||
return "false"
|
||||
if v is True:
|
||||
return "true"
|
||||
if isinstance(v, int):
|
||||
return str(v)
|
||||
if isinstance(v, str):
|
||||
return v
|
||||
if isinstance(v, list):
|
||||
return " ".join([nix_conf_literal(item) for item in v])
|
||||
msg = f"Cannot represent {v!r} in nix.conf"
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
|
||||
def indent(prefix: str, body: str) -> str:
|
||||
return "".join(["\n" if not line else f"{prefix}{line}\n" for line in body.split("\n")])
|
||||
|
||||
|
||||
def main():
|
||||
ap = get_argument_parser()
|
||||
ap.add_argument("--kernel", help="Name of the kernel Lix will run on")
|
||||
ap.add_argument(
|
||||
"--experimental-features", help="Directory containing the experimental feature definitions"
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
settings = load_data(args.defs, Setting)
|
||||
|
||||
experimental_features = get_experimental_features(
|
||||
args.experimental_features, [s.experimental_feature for (_, s) in settings]
|
||||
)
|
||||
|
||||
generate_file(
|
||||
args.header,
|
||||
settings,
|
||||
lambda setting: setting.name,
|
||||
lambda setting: setting.generate_code(experimental_features)
|
||||
if not setting.platforms or args.kernel in setting.platforms
|
||||
else "",
|
||||
)
|
||||
generate_file(args.docs, settings, lambda setting: setting.name, lambda setting: setting.docs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!@python@
|
||||
# ruff: noqa: SIM112 # ignore lowercase env variable names for capnpc as we have them in lower case as arguments
|
||||
|
||||
import argparse
|
||||
import capnp
|
||||
@@ -8,61 +7,58 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if lang := os.environ.get("lix_capnp_lang"):
|
||||
outputs = os.environ["lix_capnp_outputs"].split()
|
||||
old_cwd = os.environ["lix_capnp_old_cwd"]
|
||||
schema = capnp.load("@capnp_include@/capnp/schema.capnp", imports=["@capnp_include@"])
|
||||
if lang := os.environ.get('lix_capnp_lang'):
|
||||
outputs = os.environ['lix_capnp_outputs'].split()
|
||||
old_cwd = os.environ['lix_capnp_old_cwd']
|
||||
schema = capnp.load('@capnp_include@/capnp/schema.capnp', imports=['@capnp_include@'])
|
||||
request = schema.CodeGeneratorRequest.read(sys.stdin)
|
||||
|
||||
subprocess.run([lang], input=request.as_builder().to_bytes()).check_returncode()
|
||||
|
||||
base_dir = Path.cwd()
|
||||
base_dir = os.getcwd()
|
||||
os.chdir(old_cwd)
|
||||
|
||||
include = [str(Path(p).resolve()) for p in os.environ["lix_capnp_include"].split(":")]
|
||||
include = [ str(Path(p).resolve()) for p in os.environ['lix_capnp_include'].split(':') ]
|
||||
|
||||
if depfile := os.environ["lix_capnp_depfile"]:
|
||||
if depfile := os.environ['lix_capnp_depfile']:
|
||||
deps = ""
|
||||
for input_file in request.requestedFiles:
|
||||
deps += " ".join(f"{input_file.filename}.{o}" for o in outputs)
|
||||
for input in request.requestedFiles:
|
||||
deps += " ".join(f"{input.filename}.{o}" for o in outputs)
|
||||
deps += ":"
|
||||
for dep in input_file.imports:
|
||||
for dep in input.imports:
|
||||
if dep.name.startswith("/"):
|
||||
for candidate in (Path(i + dep.name) for i in include):
|
||||
if candidate.exists():
|
||||
deps += " " + str(candidate)
|
||||
break
|
||||
else:
|
||||
msg = "not handling relative includes"
|
||||
raise RuntimeError(msg)
|
||||
raise RuntimeError("not handling relative includes")
|
||||
deps += "\n\n"
|
||||
Path(depfile).write_text(deps)
|
||||
else:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--language")
|
||||
parser.add_argument("--outdir")
|
||||
parser.add_argument("--src-prefix")
|
||||
parser.add_argument("--depfile", default="")
|
||||
parser.add_argument("-I", "--include", action="append", default=["@capnp_include@"])
|
||||
parser.add_argument("inputs", nargs="+")
|
||||
parser.add_argument('--language')
|
||||
parser.add_argument('--outdir')
|
||||
parser.add_argument('--src-prefix')
|
||||
parser.add_argument('--depfile', default="")
|
||||
parser.add_argument('-I', '--include', action='append', default=['@capnp_include@'])
|
||||
parser.add_argument('inputs', nargs='+')
|
||||
args = parser.parse_args()
|
||||
|
||||
for infile in args.inputs:
|
||||
os.environ["lix_capnp_lang"] = f"capnpc-{args.language}"
|
||||
os.environ["lix_capnp_include"] = ":".join(args.include)
|
||||
os.environ["lix_capnp_depfile"] = args.depfile
|
||||
os.environ["lix_capnp_old_cwd"] = str(Path.cwd())
|
||||
os.environ['lix_capnp_lang'] = f"capnpc-{args.language}"
|
||||
os.environ['lix_capnp_include'] = ':'.join(args.include)
|
||||
os.environ['lix_capnp_depfile'] = args.depfile
|
||||
os.environ['lix_capnp_old_cwd'] = os.getcwd()
|
||||
if args.language == "c++":
|
||||
os.environ["lix_capnp_outputs"] = "c++ h"
|
||||
os.environ['lix_capnp_outputs'] = "c++ h"
|
||||
else:
|
||||
raise RuntimeError("unknown language " + args.language)
|
||||
subprocess.run(
|
||||
[
|
||||
"@capnp@",
|
||||
"compile",
|
||||
f"-o{sys.argv[0]}:{args.outdir}",
|
||||
f"--src-prefix={args.src_prefix}",
|
||||
*(f"-I{i}" for i in args.include),
|
||||
infile,
|
||||
]
|
||||
).check_returncode()
|
||||
subprocess.run([
|
||||
'@capnp@',
|
||||
'compile',
|
||||
f'-o{sys.argv[0]}:{args.outdir}',
|
||||
f'--src-prefix={args.src_prefix}',
|
||||
*(f"-I{i}" for i in args.include),
|
||||
infile
|
||||
]).check_returncode()
|
||||
|
||||
@@ -1,113 +1,60 @@
|
||||
import argparse
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import frontmatter
|
||||
import pathlib
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def cxx_escape_character(c: str) -> str:
|
||||
if 0x20 <= ord(c) < 0x7F and c != '"' and c != "?" and c != "\\":
|
||||
def cxx_escape_character(c):
|
||||
if ord(c) >= 0x20 and ord(c) < 0x7f and c != '"' and c != '?' and c != '\\':
|
||||
return c
|
||||
if c == "\t":
|
||||
return r"\t"
|
||||
if c == "\n":
|
||||
return r"\n"
|
||||
if c == "\r":
|
||||
return r"\r"
|
||||
if c == '"':
|
||||
return r"\""
|
||||
if c == "?":
|
||||
return r"\?"
|
||||
if c == "\\":
|
||||
return r"\\"
|
||||
if ord(c) <= 0xFFFF:
|
||||
return str.format(r"\u{:04x}", ord(c))
|
||||
return str.format(r"\U{:08x}", ord(c))
|
||||
elif c == '\t':
|
||||
return r'\t'
|
||||
elif c == '\n':
|
||||
return r'\n'
|
||||
elif c == '\r':
|
||||
return r'\r'
|
||||
elif c == '"':
|
||||
return r'\"'
|
||||
elif c == '?':
|
||||
return r'\?'
|
||||
elif c == '\\':
|
||||
return r'\\'
|
||||
elif ord(c) <= 0xffff:
|
||||
return str.format(r'\u{:04x}', ord(c))
|
||||
else:
|
||||
return str.format(r'\U{:08x}', ord(c))
|
||||
|
||||
|
||||
def cxx_literal(v: Any) -> str:
|
||||
def cxx_literal(v):
|
||||
if v is None:
|
||||
return "std::nullopt"
|
||||
if v is False:
|
||||
return "false"
|
||||
if v is True:
|
||||
return "true"
|
||||
if isinstance(v, int):
|
||||
return 'std::nullopt'
|
||||
elif isinstance(v, bool) and v == False: # 0 == False
|
||||
return 'false'
|
||||
elif isinstance(v, bool) and v == True: # 1 == True
|
||||
return 'true'
|
||||
elif isinstance(v, int):
|
||||
return str(v)
|
||||
if isinstance(v, str):
|
||||
return "".join(['"', *(cxx_escape_character(c) for c in v), '"'])
|
||||
if isinstance(v, list):
|
||||
return f"{{{', '.join([cxx_literal(item) for item in v])}}}"
|
||||
msg = f"cannot represent {v!r} in C++"
|
||||
raise NotImplementedError(msg)
|
||||
elif isinstance(v, str):
|
||||
return ''.join(['"', *(cxx_escape_character(c) for c in v), '"'])
|
||||
elif isinstance(v, list):
|
||||
return f'{{{", ".join([cxx_literal(item) for item in v])}}}'
|
||||
else:
|
||||
raise NotImplementedError(f'cannot represent {repr(v)} in C++')
|
||||
|
||||
|
||||
def get_experimental_features(
|
||||
base_path: str, human_names: list[str | None]
|
||||
) -> dict[str | None, str]:
|
||||
experimental_feature_files = {
|
||||
f"{base_path}/{xp_name}.md" for xp_name in human_names if xp_name is not None
|
||||
}
|
||||
|
||||
from build_extra_features import ExtraFeature # noqa: PLC0415 # Avoid cyclic import
|
||||
|
||||
experimental_features_data = load_data(list(experimental_feature_files), ExtraFeature)
|
||||
experimental_features: dict[str | None, str] = {
|
||||
xf.name: f"Xp::{xf.internal_name}" for _, xf in experimental_features_data
|
||||
}
|
||||
experimental_features[None] = "std::nullopt"
|
||||
return experimental_features
|
||||
|
||||
|
||||
FIELD_RENAMES = {"type": "type_str", "content": "documentation"}
|
||||
|
||||
|
||||
def load_data[T](defs: list[str], parse_function: type[T]) -> list[tuple[str, T]]:
|
||||
def load_data(defs, parse_function):
|
||||
data = []
|
||||
for path in defs:
|
||||
try:
|
||||
datum = {
|
||||
# convert camelCase to snake_case
|
||||
re.sub(r"(?<=.)([A-Z])", lambda m: f"_{m.group(1).lower()}", k): v
|
||||
for k, v in frontmatter.load(path).to_dict().items()
|
||||
}
|
||||
|
||||
for post_name, field_name in FIELD_RENAMES.items():
|
||||
if post_name in datum:
|
||||
datum[field_name] = datum.pop(post_name)
|
||||
|
||||
data.append((path, parse_function(**datum)))
|
||||
datum = frontmatter.load(path)
|
||||
data.append((path, parse_function(datum)))
|
||||
except Exception as e:
|
||||
e.add_note(f"in {path}")
|
||||
e.add_note(f'in {path}')
|
||||
raise
|
||||
return data
|
||||
|
||||
|
||||
def generate_file[T](
|
||||
path: str | None,
|
||||
data: list[T],
|
||||
sort_key_function: Callable[[T], str],
|
||||
generate_function: Callable[[T], str],
|
||||
):
|
||||
def generate_file(path, data, sort_key_function, generate_function):
|
||||
if path is not None:
|
||||
with Path(path).open("w") as out:
|
||||
for path, datum in sorted(
|
||||
data, key=lambda path_and_datum: sort_key_function(path_and_datum[1])
|
||||
):
|
||||
with open(path, 'w') as out:
|
||||
for path, datum in sorted(data, key=lambda pathAndDatum: sort_key_function(pathAndDatum[1])):
|
||||
try:
|
||||
text = generate_function(datum)
|
||||
out.write(text)
|
||||
out.write(generate_function(datum))
|
||||
except Exception as e:
|
||||
e.add_note(f"in {path}")
|
||||
e.add_note(f'in {path}')
|
||||
raise
|
||||
|
||||
|
||||
def get_argument_parser() -> argparse.ArgumentParser:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--header", help="Path of the header to generate")
|
||||
ap.add_argument("--docs", help="Path of the documentation file to generate")
|
||||
ap.add_argument("defs", help="Builtin definition files", nargs="+")
|
||||
|
||||
return ap
|
||||
|
||||
+114
-36
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -70,7 +69,7 @@ static std::string makeLockFilename(const std::string & storeUri) {
|
||||
// This avoids issues with the escaped URI being very long and causing
|
||||
// path too long errors, while also avoiding any possibility of collision
|
||||
// caused by simple truncation.
|
||||
auto hash = hashString(HashType::SHA256, storeUri).to_string(HashFormat::Base32, false);
|
||||
auto hash = hashString(HashType::SHA256, storeUri).to_string(Base::Base32, false);
|
||||
return escapeUri(storeUri).substr(0, 48) + "-" + hash.substr(0, 16);
|
||||
}
|
||||
|
||||
@@ -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,15 +395,17 @@ 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)
|
||||
try {
|
||||
kj::Promise<void> Instance::buildImpl(BuildContext context)
|
||||
{
|
||||
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,19 @@ 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();
|
||||
}
|
||||
|
||||
kj::Promise<void> Instance::build(BuildContext context)
|
||||
@@ -400,12 +469,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 +491,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;
|
||||
|
||||
@@ -453,7 +527,7 @@ kj::Promise<Result<void>> AcceptedBuild::runImpl(RunContext context)
|
||||
AIO().timeoutAfter(15 * kj::MINUTES, lockFileAsync(uploadLock.get(), ltWrite))
|
||||
);
|
||||
if (!result) {
|
||||
printError("somebody is hogging the upload lock for '%s', continuing...", storeUri);
|
||||
printError("somebody is hogging the upload lock for '%s', continuing...");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,9 +617,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
#include "lix/libcmd/legacy.hh"
|
||||
#include "lix/libstore/builtins.hh"
|
||||
#include "lix/libstore/builtins/buildenv.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include <string_view>
|
||||
|
||||
using std::literals::operator""sv;
|
||||
|
||||
namespace nix {
|
||||
|
||||
static int main_builtin_builder(AsyncIoRoot & aio, std::string programName, Strings argv)
|
||||
{
|
||||
logger = makeJSONLogger(*logger);
|
||||
|
||||
std::map<std::string, std::string> env;
|
||||
|
||||
auto argvIt = argv.begin();
|
||||
const auto argvEnd = argv.end();
|
||||
|
||||
// we do not use the argument parsing functions we have in libmain here, neither
|
||||
// the legacy versions nor the newer ones. the legacy version could work, but we
|
||||
// want to provide two sets of arguments separated by `--` and would need rather
|
||||
// unpleasant state handling to use the legacy parser. the more modern parser is
|
||||
// entirely incapable of doing this for us since it's all statically configured.
|
||||
const auto getArg = [&](std::string_view desc) {
|
||||
if (argvIt == argvEnd) {
|
||||
throw Error("expected a value for %s", desc);
|
||||
}
|
||||
return *argvIt++;
|
||||
};
|
||||
|
||||
if (auto val = string2Int<int>(getArg("verbosity"))) {
|
||||
setVerbosity(verbosityFromIntClamped(*val));
|
||||
} else {
|
||||
throw Error("expected a verbosity argument");
|
||||
}
|
||||
|
||||
while (argvIt != argvEnd) {
|
||||
const auto arg = getArg("option");
|
||||
if (arg == "--") {
|
||||
break;
|
||||
} else if (!arg.starts_with("--")) {
|
||||
throw Error("unexpected builtin option %s", arg);
|
||||
}
|
||||
auto value = unescapeNul(getArg(arg));
|
||||
globalConfig.set(arg.substr(2), value);
|
||||
}
|
||||
|
||||
while (argvIt != argvEnd) {
|
||||
const auto key = getArg("builder argument");
|
||||
if (!key.starts_with("--")) {
|
||||
throw Error("unexpected builtin builder argument %s", key);
|
||||
}
|
||||
env[unescapeNul(key.substr(2))] = unescapeNul(getArg(key));
|
||||
}
|
||||
|
||||
auto getAttr = [&](const std::string & name) {
|
||||
auto i = env.find(name);
|
||||
if (i == env.end()) {
|
||||
throw Error("attribute '%s' missing", name);
|
||||
}
|
||||
return i->second;
|
||||
};
|
||||
|
||||
const auto builder = getAttr("builder");
|
||||
|
||||
if (builder == "builtin:fetchurl") {
|
||||
const auto outputHashMode = getAttr("outputHashMode");
|
||||
const auto hash = outputHashMode == "flat" ? [&] -> std::optional<Hash> {
|
||||
const auto ht = parseHashTypeOpt(getAttr("outputHashAlgo"));
|
||||
return newHashAllowEmpty(getAttr("outputHash"), ht);
|
||||
}()
|
||||
: std::nullopt;
|
||||
BuiltinFetchurl{
|
||||
.storePath = getAttr("out"),
|
||||
.mainUrl = getAttr("url"),
|
||||
.unpack = getOr(env, "unpack", "0") == "1",
|
||||
.executable = getOr(env, "executable", "0") == "1",
|
||||
.hash = hash,
|
||||
}
|
||||
.run(aio);
|
||||
} else if (builder == "builtin:buildenv") {
|
||||
builtinBuildenv(getAttr("out"), tokenizeString<Strings>(getAttr("derivations")), getAttr("manifest"));
|
||||
} else if (builder == "builtin:unpack-channel") {
|
||||
builtinUnpackChannel(getAttr("out"), getAttr("channelName"), getAttr("src"));
|
||||
} else {
|
||||
throw Error("unknown builtin builder %s", builder);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void registerLegacyBuiltinBuilder()
|
||||
{
|
||||
LegacyCommandRegistry::add("builtin-builder", main_builtin_builder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
void registerLegacyBuiltinBuilder();
|
||||
}
|
||||
+10
-8
@@ -4,7 +4,9 @@
|
||||
#include "lix/libutil/result.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using std::cout;
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -40,31 +42,31 @@ static std::string makeNode(std::string_view id, std::string_view label,
|
||||
dotQuote(id), dotQuote(label), dotQuote(colour));
|
||||
}
|
||||
|
||||
kj::Promise<Result<std::string>> formatDotGraph(ref<Store> store, StorePathSet && roots)
|
||||
|
||||
kj::Promise<Result<void>> printDotGraph(ref<Store> store, StorePathSet && roots)
|
||||
try {
|
||||
StorePathSet workList(std::move(roots));
|
||||
StorePathSet doneSet;
|
||||
std::stringstream result;
|
||||
|
||||
result << "digraph G {\n";
|
||||
cout << "digraph G {\n";
|
||||
|
||||
while (!workList.empty()) {
|
||||
auto path = std::move(workList.extract(workList.begin()).value());
|
||||
|
||||
if (!doneSet.insert(path).second) continue;
|
||||
|
||||
result << makeNode(std::string(path.to_string()), path.name(), "#ff0000");
|
||||
cout << makeNode(std::string(path.to_string()), path.name(), "#ff0000");
|
||||
|
||||
for (auto & p : TRY_AWAIT(store->queryPathInfo(path))->references) {
|
||||
if (p != path) {
|
||||
workList.insert(p);
|
||||
result << makeEdge(std::string(p.to_string()), std::string(path.to_string()));
|
||||
cout << makeEdge(std::string(p.to_string()), std::string(path.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result << "}\n";
|
||||
co_return result.str();
|
||||
cout << "}\n";
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
kj::Promise<Result<std::string>> formatDotGraph(ref<Store> store, StorePathSet && roots);
|
||||
kj::Promise<Result<void>> printDotGraph(ref<Store> store, StorePathSet && roots);
|
||||
|
||||
}
|
||||
|
||||
+18
-16
@@ -5,7 +5,9 @@
|
||||
#include "lix/libutil/result.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using std::cout;
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -45,21 +47,21 @@ static std::string makeNode(const ValidPathInfo & info)
|
||||
(info.path.isDerivation() ? "derivation" : "output-path"));
|
||||
}
|
||||
|
||||
kj::Promise<Result<std::string>> formatGraphML(ref<Store> store, StorePathSet && roots)
|
||||
|
||||
kj::Promise<Result<void>> printGraphML(ref<Store> store, StorePathSet && roots)
|
||||
try {
|
||||
StorePathSet workList(std::move(roots));
|
||||
StorePathSet doneSet;
|
||||
std::pair<StorePathSet::iterator, bool> ret;
|
||||
std::stringstream result;
|
||||
|
||||
result << "<?xml version='1.0' encoding='utf-8'?>\n"
|
||||
<< "<graphml xmlns='http://graphml.graphdrawing.org/xmlns'\n"
|
||||
<< " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n"
|
||||
<< " xsi:schemaLocation='http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd'>\n"
|
||||
<< "<key id='narSize' for='node' attr.name='narSize' attr.type='long'/>"
|
||||
<< "<key id='name' for='node' attr.name='name' attr.type='string'/>"
|
||||
<< "<key id='type' for='node' attr.name='type' attr.type='string'/>"
|
||||
<< "<graph id='G' edgedefault='directed'>\n";
|
||||
cout << "<?xml version='1.0' encoding='utf-8'?>\n"
|
||||
<< "<graphml xmlns='http://graphml.graphdrawing.org/xmlns'\n"
|
||||
<< " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n"
|
||||
<< " xsi:schemaLocation='http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd'>\n"
|
||||
<< "<key id='narSize' for='node' attr.name='narSize' attr.type='long'/>"
|
||||
<< "<key id='name' for='node' attr.name='name' attr.type='string'/>"
|
||||
<< "<key id='type' for='node' attr.name='type' attr.type='string'/>"
|
||||
<< "<graph id='G' edgedefault='directed'>\n";
|
||||
|
||||
while (!workList.empty()) {
|
||||
auto path = std::move(workList.extract(workList.begin()).value());
|
||||
@@ -68,20 +70,20 @@ try {
|
||||
if (ret.second == false) continue;
|
||||
|
||||
auto info = TRY_AWAIT(store->queryPathInfo(path));
|
||||
result << makeNode(*info);
|
||||
cout << makeNode(*info);
|
||||
|
||||
for (auto & p : info->references) {
|
||||
if (p != path) {
|
||||
workList.insert(p);
|
||||
result << makeEdge(path.to_string(), p.to_string());
|
||||
cout << makeEdge(path.to_string(), p.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
result << "</graph>\n";
|
||||
result << "</graphml>\n";
|
||||
co_return result.str();
|
||||
cout << "</graph>\n";
|
||||
cout << "</graphml>\n";
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
kj::Promise<Result<std::string>> formatGraphML(ref<Store> store, StorePathSet && roots);
|
||||
kj::Promise<Result<void>> printGraphML(ref<Store> store, StorePathSet && roots);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ legacy_sources = files(
|
||||
# `build-remote` is not really legacy (it powers all remote builds), but it's
|
||||
# not a `nix3` command.
|
||||
'build-remote.cc',
|
||||
'builtin-builder.cc',
|
||||
'dotgraph.cc',
|
||||
'graphml.cc',
|
||||
'nix-build.cc',
|
||||
@@ -20,7 +19,6 @@ legacy_sources = files(
|
||||
|
||||
legacy_headers = files(
|
||||
'build-remote.hh',
|
||||
'builtin-builder.hh',
|
||||
'nix-build.hh',
|
||||
'nix-channel.hh',
|
||||
'nix-collect-garbage.hh',
|
||||
|
||||
+13
-27
@@ -25,7 +25,6 @@
|
||||
#include "lix/libutil/shlex.hh"
|
||||
#include "nix-build.hh"
|
||||
#include "lix/libstore/temporary-dir.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
extern char * * environ __attribute__((weak)); // Man what even is this
|
||||
|
||||
@@ -192,11 +191,7 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
throw UsageError("'-p' and '-E' are mutually exclusive");
|
||||
|
||||
AutoDelete tmpDir(createTempDir(myName));
|
||||
// NOTE: we assume there's no `build-top` directory created inside of `tmpDir` and we have
|
||||
// ownership of this.
|
||||
auto buildTopTmpDir = tmpDir + "/build-top";
|
||||
createDirs(buildTopTmpDir);
|
||||
|
||||
AutoDelete buildTopTmpDir(createTempSubdir(tmpDir, "build-top"));
|
||||
if (outLink.empty())
|
||||
outLink = (Path) tmpDir + "/result";
|
||||
|
||||
@@ -213,7 +208,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").mkBool(true);
|
||||
for (auto & i : *autoArgs) newArgs.insert(i);
|
||||
autoArgsWithInNixShell = newArgs.finish();
|
||||
}
|
||||
@@ -272,7 +267,8 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
if (attrPaths.empty()) attrPaths = {""};
|
||||
|
||||
for (auto e : exprs) {
|
||||
Value vRoot = state->eval(e);
|
||||
Value vRoot;
|
||||
state->eval(e, vRoot);
|
||||
|
||||
std::function<bool(const Value & v)> takesNixShellAttr;
|
||||
takesNixShellAttr = [&](const Value & v) {
|
||||
@@ -355,7 +351,8 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
"(import <nixpkgs> {}).bashInteractive",
|
||||
CanonPath::fromCwd());
|
||||
|
||||
Value v = state->eval(expr);
|
||||
Value v;
|
||||
state->eval(expr, v);
|
||||
|
||||
auto drv = getDerivation(*state, v, false);
|
||||
if (!drv)
|
||||
@@ -428,18 +425,6 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
env["__ETC_PROFILE_SOURCED"] = "1";
|
||||
}
|
||||
|
||||
// Set NIX_SHELL_LEVEL
|
||||
env["NIX_SHELL_LEVEL"] = std::to_string(
|
||||
getEnvNonEmpty("NIX_SHELL_LEVEL")
|
||||
.and_then([](std::string lvl) { return string2Int<size_t>(lvl); })
|
||||
.value_or(0)
|
||||
+ 1
|
||||
);
|
||||
|
||||
// We re-export similarly to what occurs inside of a derivation goal `NIX_LOG_FD` to stderr.
|
||||
// So that stdenv hooks that logs information can be observed inside this debugging tool.
|
||||
env["NIX_LOG_FD"] = "2";
|
||||
|
||||
// Don't use defaultTempDir() here! We want to preserve the user's TMPDIR for the shell
|
||||
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] =
|
||||
getEnvNonEmpty("TMPDIR").value_or(buildTopTmpDir);
|
||||
@@ -543,12 +528,13 @@ static int main_nix_build(AsyncIoRoot & aio, std::string programName, Strings ar
|
||||
|
||||
printMsg(lvlChatty, "running shell: %s", concatMapStringsSep(" ", args, shellEscape));
|
||||
|
||||
RunningProgram proc = runProgram2({
|
||||
.program = *shell,
|
||||
.searchPath = true,
|
||||
.args = args,
|
||||
.environment = env,
|
||||
});
|
||||
RunningProgram proc = runProgram2(
|
||||
{.program = *shell,
|
||||
.searchPath = true,
|
||||
.args = args,
|
||||
.environment = env,
|
||||
.dieWithParent = true}
|
||||
);
|
||||
|
||||
// NOTE: we wait and return the status check immediately.
|
||||
// If there's interruption, we will swallow it and wait again for termination.
|
||||
|
||||
@@ -64,7 +64,7 @@ static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName,
|
||||
{
|
||||
bool removeOld = false;
|
||||
|
||||
GCOptions options = {.action = GCOptions::gcDeleteDead};
|
||||
GCOptions options;
|
||||
|
||||
LegacyArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
||||
if (*arg == "--help")
|
||||
@@ -75,13 +75,12 @@ static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName,
|
||||
else if (*arg == "--delete-older-than") {
|
||||
removeOld = true;
|
||||
deleteOlderThan = getArg(*arg, arg, end);
|
||||
} else if (*arg == "--dry-run") {
|
||||
options.action = GCOptions::gcReturnDead;
|
||||
} else if (*arg == "--max-freed") {
|
||||
options.maxFreed = std::max(getIntArg<int64_t>(*arg, arg, end, true), (int64_t) 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
else if (*arg == "--dry-run") dryRun = true;
|
||||
else if (*arg == "--max-freed")
|
||||
options.maxFreed = std::max(getIntArg<int64_t>(*arg, arg, end, true), (int64_t) 0);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}).parseCmdline(argv);
|
||||
|
||||
@@ -93,12 +92,24 @@ static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName,
|
||||
}
|
||||
|
||||
// Run the actual garbage collector.
|
||||
if (!dryRun) {
|
||||
options.action = GCOptions::gcDeleteDead;
|
||||
} else {
|
||||
options.action = GCOptions::gcReturnDead;
|
||||
}
|
||||
auto store = aio.blockOn(openStore());
|
||||
auto & gcStore = require<GcStore>(*store);
|
||||
GCResults results;
|
||||
PrintFreed freed(options.action, results);
|
||||
PrintFreed freed(true, results);
|
||||
aio.blockOn(gcStore.collectGarbage(options, results));
|
||||
|
||||
if (dryRun) {
|
||||
// Only print results for dry run; when !dryRun, paths will be printed as they're deleted.
|
||||
for (auto & i : results.paths) {
|
||||
printInfo("%s", Uncolored(i));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+209
-251
@@ -151,10 +151,12 @@ static void getAllExprs(Evaluator & state,
|
||||
continue;
|
||||
}
|
||||
/* Load the expression on demand. */
|
||||
Value vArg = {NewValueAs::string, path2.canonical().abs()};
|
||||
Value vArg;
|
||||
vArg.mkString(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);
|
||||
@@ -163,13 +165,15 @@ static void getAllExprs(Evaluator & state,
|
||||
}
|
||||
}
|
||||
|
||||
static Value loadSourceExpr(EvalState & state, const SourcePath & path_)
|
||||
|
||||
|
||||
static void loadSourceExpr(EvalState & state, const SourcePath & path_, Value & v)
|
||||
{
|
||||
auto path = state.ctx.paths.checkSourcePath(path_);
|
||||
auto st = path.stat();
|
||||
|
||||
if (isNixExpr(state.ctx.paths, path, st))
|
||||
return state.evalFile(path);
|
||||
state.evalFile(path, v);
|
||||
|
||||
/* The path is a directory. Put the Nix expressions in the
|
||||
directory in a set, with the file name of each expression as
|
||||
@@ -179,10 +183,10 @@ 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};
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
else throw Error("path '%s' is not a directory or a Nix expression", path);
|
||||
@@ -193,7 +197,8 @@ static void loadDerivations(EvalState & state, const SourcePath & nixExprPath,
|
||||
std::string systemFilter, Bindings & autoArgs,
|
||||
const std::string & pathPrefix, DrvInfos & elems)
|
||||
{
|
||||
Value vRoot = loadSourceExpr(state, nixExprPath);
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, nixExprPath, vRoot);
|
||||
|
||||
Value v(findAlongAttrPath(state, pathPrefix, autoArgs, vRoot).first);
|
||||
|
||||
@@ -415,12 +420,14 @@ static void queryInstSources(EvalState & state,
|
||||
(import ./foo.nix)' = `(import ./foo.nix).bar'. */
|
||||
case srcNixExprs: {
|
||||
|
||||
Value vArg = loadSourceExpr(state, *instSource.nixExprPath);
|
||||
Value vArg;
|
||||
loadSourceExpr(state, *instSource.nixExprPath, vArg);
|
||||
|
||||
for (auto & i : args) {
|
||||
Expr & eFun = state.ctx.parseExprFromString(i, CanonPath::fromCwd());
|
||||
Value vFun = state.eval(eFun);
|
||||
Value vTmp = {NewValueAs::app, state.ctx.mem, vFun, vArg};
|
||||
Value vFun, vTmp;
|
||||
state.eval(eFun, vFun);
|
||||
vTmp = {NewValueAs::app, state.ctx.mem, vFun, vArg};
|
||||
getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true);
|
||||
}
|
||||
|
||||
@@ -472,7 +479,8 @@ static void queryInstSources(EvalState & state,
|
||||
}
|
||||
|
||||
case srcAttrPath: {
|
||||
Value vRoot = loadSourceExpr(state, *instSource.nixExprPath);
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, *instSource.nixExprPath, vRoot);
|
||||
for (auto & i : args) {
|
||||
Value v(findAlongAttrPath(state, i, *instSource.autoArgs, vRoot).first);
|
||||
getDerivations(state, v, "", *instSource.autoArgs, elems, true);
|
||||
@@ -509,7 +517,8 @@ static bool keep(EvalState & state, DrvInfo & drv)
|
||||
static void setMetaFlag(EvalState & state, DrvInfo & drv,
|
||||
const std::string & name, const std::string & value)
|
||||
{
|
||||
Value v = {NewValueAs::string, value};
|
||||
Value v;
|
||||
v.mkString(value);
|
||||
drv.setMeta(state, name, v);
|
||||
}
|
||||
|
||||
@@ -883,7 +892,8 @@ static bool cmpElemByName(EvalState & state, DrvInfo & a, DrvInfo & b)
|
||||
|
||||
typedef std::list<Strings> Table;
|
||||
|
||||
std::string formatTable(Table & table)
|
||||
|
||||
void printTable(Table & table)
|
||||
{
|
||||
auto nrColumns = table.size() > 0 ? table.front().size() : 0;
|
||||
|
||||
@@ -898,22 +908,18 @@ std::string formatTable(Table & table)
|
||||
if (j->size() > widths[column]) widths[column] = j->size();
|
||||
}
|
||||
|
||||
std::stringstream result;
|
||||
|
||||
for (auto & i : table) {
|
||||
Strings::iterator j;
|
||||
size_t column;
|
||||
for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) {
|
||||
std::string s = *j;
|
||||
replace(s.begin(), s.end(), '\n', ' ');
|
||||
result << s;
|
||||
cout << s;
|
||||
if (column < nrColumns - 1)
|
||||
result << std::string(widths[column] - s.size() + 2, ' ');
|
||||
cout << std::string(widths[column] - s.size() + 2, ' ');
|
||||
}
|
||||
result << std::endl;
|
||||
cout << std::endl;
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -1124,250 +1130,209 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
return;
|
||||
}
|
||||
|
||||
withPager([&](Pager & pager) {
|
||||
Table table;
|
||||
std::ostringstream xmlStream;
|
||||
XMLWriter xml(true, xmlStream);
|
||||
xml.openElement("items");
|
||||
RunPager pager;
|
||||
|
||||
for (auto & i : elems) {
|
||||
try {
|
||||
if (i.hasFailed()) {
|
||||
continue;
|
||||
}
|
||||
Table table;
|
||||
std::ostringstream dummy;
|
||||
XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
|
||||
XMLOpenElement xmlRoot(xml, "items");
|
||||
|
||||
// Activity act(*logger, lvlDebug, "outputting query result '%1%'", i.attrPath);
|
||||
for (auto & i : elems) {
|
||||
try {
|
||||
if (i.hasFailed()) continue;
|
||||
|
||||
if (globals.prebuiltOnly && !validPaths.count(i.queryOutPath(*state))
|
||||
&& !substitutablePaths.count(i.queryOutPath(*state)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//Activity act(*logger, lvlDebug, "outputting query result '%1%'", i.attrPath);
|
||||
|
||||
/* For table output. */
|
||||
Strings columns;
|
||||
if (globals.prebuiltOnly &&
|
||||
!validPaths.count(i.queryOutPath(*state)) &&
|
||||
!substitutablePaths.count(i.queryOutPath(*state)))
|
||||
continue;
|
||||
|
||||
/* For XML output. */
|
||||
XMLAttrs attrs;
|
||||
/* For table output. */
|
||||
Strings columns;
|
||||
|
||||
if (printStatus) {
|
||||
auto outPath = i.queryOutPath(*state);
|
||||
bool hasSubs = substitutablePaths.count(outPath);
|
||||
bool isInstalled = installed.count(outPath);
|
||||
bool isValid = validPaths.count(outPath);
|
||||
if (xmlOutput) {
|
||||
attrs["installed"] = isInstalled ? "1" : "0";
|
||||
attrs["valid"] = isValid ? "1" : "0";
|
||||
attrs["substitutable"] = hasSubs ? "1" : "0";
|
||||
} else {
|
||||
columns.push_back(
|
||||
(std::string) (isInstalled ? "I" : "-") + (isValid ? "P" : "-")
|
||||
+ (hasSubs ? "S" : "-")
|
||||
);
|
||||
}
|
||||
/* For XML output. */
|
||||
XMLAttrs attrs;
|
||||
|
||||
if (printStatus) {
|
||||
auto outPath = i.queryOutPath(*state);
|
||||
bool hasSubs = substitutablePaths.count(outPath);
|
||||
bool isInstalled = installed.count(outPath);
|
||||
bool isValid = validPaths.count(outPath);
|
||||
if (xmlOutput) {
|
||||
attrs["installed"] = isInstalled ? "1" : "0";
|
||||
attrs["valid"] = isValid ? "1" : "0";
|
||||
attrs["substitutable"] = hasSubs ? "1" : "0";
|
||||
} else
|
||||
columns.push_back(
|
||||
(std::string) (isInstalled ? "I" : "-")
|
||||
+ (isValid ? "P" : "-")
|
||||
+ (hasSubs ? "S" : "-"));
|
||||
}
|
||||
|
||||
if (xmlOutput)
|
||||
attrs["attrPath"] = i.attrPath;
|
||||
else if (printAttrPath)
|
||||
columns.push_back(i.attrPath);
|
||||
|
||||
if (xmlOutput) {
|
||||
auto drvName = DrvName(i.queryName(*state));
|
||||
attrs["name"] = drvName.fullName;
|
||||
attrs["pname"] = drvName.name;
|
||||
attrs["version"] = drvName.version;
|
||||
} else if (printName) {
|
||||
columns.push_back(i.queryName(*state));
|
||||
}
|
||||
|
||||
if (compareVersions) {
|
||||
/* Compare this element against the versions of the
|
||||
same named packages in either the set of available
|
||||
elements, or the set of installed elements. !!!
|
||||
This is O(N * M), should be O(N * lg M). */
|
||||
std::string version;
|
||||
VersionDiff diff = compareVersionAgainstSet(*state, i, otherElems, version);
|
||||
|
||||
char ch;
|
||||
switch (diff) {
|
||||
case cvLess: ch = '>'; break;
|
||||
case cvEqual: ch = '='; break;
|
||||
case cvGreater: ch = '<'; break;
|
||||
case cvUnavail: ch = '-'; break;
|
||||
default: abort();
|
||||
}
|
||||
|
||||
if (xmlOutput) {
|
||||
attrs["attrPath"] = i.attrPath;
|
||||
} else if (printAttrPath) {
|
||||
columns.push_back(i.attrPath);
|
||||
if (diff != cvUnavail) {
|
||||
attrs["versionDiff"] = ch;
|
||||
attrs["maxComparedVersion"] = version;
|
||||
}
|
||||
} else {
|
||||
auto column = (std::string) "" + ch + " " + version;
|
||||
if (diff == cvGreater && shouldANSI(StandardOutputStream::Stdout))
|
||||
column = ANSI_RED + column + ANSI_NORMAL;
|
||||
columns.push_back(column);
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlOutput) {
|
||||
if (i.querySystem(*state) != "") attrs["system"] = i.querySystem(*state);
|
||||
}
|
||||
else if (printSystem)
|
||||
columns.push_back(i.querySystem(*state));
|
||||
|
||||
if (printDrvPath) {
|
||||
auto drvPath = i.queryDrvPath(*state);
|
||||
if (xmlOutput) {
|
||||
auto drvName = DrvName(i.queryName(*state));
|
||||
attrs["name"] = drvName.fullName;
|
||||
attrs["pname"] = drvName.name;
|
||||
attrs["version"] = drvName.version;
|
||||
} else if (printName) {
|
||||
columns.push_back(i.queryName(*state));
|
||||
}
|
||||
if (drvPath) attrs["drvPath"] = store.printStorePath(*drvPath);
|
||||
} else
|
||||
columns.push_back(drvPath ? store.printStorePath(*drvPath) : "-");
|
||||
}
|
||||
|
||||
if (compareVersions) {
|
||||
/* Compare this element against the versions of the
|
||||
same named packages in either the set of available
|
||||
elements, or the set of installed elements. !!!
|
||||
This is O(N * M), should be O(N * lg M). */
|
||||
std::string version;
|
||||
VersionDiff diff = compareVersionAgainstSet(*state, i, otherElems, version);
|
||||
if (xmlOutput)
|
||||
attrs["outputName"] = i.queryOutputName(*state);
|
||||
|
||||
char ch;
|
||||
switch (diff) {
|
||||
case cvLess:
|
||||
ch = '>';
|
||||
break;
|
||||
case cvEqual:
|
||||
ch = '=';
|
||||
break;
|
||||
case cvGreater:
|
||||
ch = '<';
|
||||
break;
|
||||
case cvUnavail:
|
||||
ch = '-';
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (xmlOutput) {
|
||||
if (diff != cvUnavail) {
|
||||
attrs["versionDiff"] = ch;
|
||||
attrs["maxComparedVersion"] = version;
|
||||
}
|
||||
} else {
|
||||
auto column = (std::string) "" + ch + " " + version;
|
||||
if (diff == cvGreater && shouldANSI(StandardOutputStream::Stdout)) {
|
||||
column = ANSI_RED + column + ANSI_NORMAL;
|
||||
}
|
||||
columns.push_back(column);
|
||||
}
|
||||
if (printOutPath && !xmlOutput) {
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(*state);
|
||||
std::string s;
|
||||
for (auto & j : outputs) {
|
||||
if (!s.empty()) s += ';';
|
||||
if (j.first != "out") { s += j.first; s += "="; }
|
||||
s += store.printStorePath(*j.second);
|
||||
}
|
||||
columns.push_back(s);
|
||||
}
|
||||
|
||||
if (printDescription) {
|
||||
auto descr = i.queryMetaString(*state, "description");
|
||||
if (xmlOutput) {
|
||||
if (i.querySystem(*state) != "") {
|
||||
attrs["system"] = i.querySystem(*state);
|
||||
}
|
||||
} else if (printSystem) {
|
||||
columns.push_back(i.querySystem(*state));
|
||||
}
|
||||
if (descr != "") attrs["description"] = descr;
|
||||
} else
|
||||
columns.push_back(descr);
|
||||
}
|
||||
|
||||
if (printDrvPath) {
|
||||
auto drvPath = i.queryDrvPath(*state);
|
||||
if (xmlOutput) {
|
||||
if (drvPath) {
|
||||
attrs["drvPath"] = store.printStorePath(*drvPath);
|
||||
}
|
||||
} else {
|
||||
columns.push_back(drvPath ? store.printStorePath(*drvPath) : "-");
|
||||
}
|
||||
if (xmlOutput) {
|
||||
XMLOpenElement item(xml, "item", attrs);
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(*state, printOutPath);
|
||||
for (auto & j : outputs) {
|
||||
XMLAttrs attrs2;
|
||||
attrs2["name"] = j.first;
|
||||
if (j.second)
|
||||
attrs2["path"] = store.printStorePath(*j.second);
|
||||
xml.writeEmptyElement("output", attrs2);
|
||||
}
|
||||
|
||||
if (xmlOutput) {
|
||||
attrs["outputName"] = i.queryOutputName(*state);
|
||||
}
|
||||
|
||||
if (printOutPath && !xmlOutput) {
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(*state);
|
||||
std::string s;
|
||||
for (auto & j : outputs) {
|
||||
if (!s.empty()) {
|
||||
s += ';';
|
||||
}
|
||||
if (j.first != "out") {
|
||||
s += j.first;
|
||||
s += "=";
|
||||
}
|
||||
s += store.printStorePath(*j.second);
|
||||
}
|
||||
columns.push_back(s);
|
||||
}
|
||||
|
||||
if (printDescription) {
|
||||
auto descr = i.queryMetaString(*state, "description");
|
||||
if (xmlOutput) {
|
||||
if (descr != "") {
|
||||
attrs["description"] = descr;
|
||||
}
|
||||
} else {
|
||||
columns.push_back(descr);
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlOutput) {
|
||||
XMLOpenElement item(xml, "item", attrs);
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(*state, printOutPath);
|
||||
for (auto & j : outputs) {
|
||||
if (printMeta) {
|
||||
StringSet metaNames = i.queryMetaNames(*state);
|
||||
for (auto & j : metaNames) {
|
||||
XMLAttrs attrs2;
|
||||
attrs2["name"] = j.first;
|
||||
if (j.second) {
|
||||
attrs2["path"] = store.printStorePath(*j.second);
|
||||
}
|
||||
xml.writeEmptyElement("output", attrs2);
|
||||
}
|
||||
if (printMeta) {
|
||||
StringSet metaNames = i.queryMetaNames(*state);
|
||||
for (auto & j : metaNames) {
|
||||
XMLAttrs attrs2;
|
||||
attrs2["name"] = j;
|
||||
Value * v = i.queryMeta(*state, j);
|
||||
if (!v) {
|
||||
printError(
|
||||
"derivation '%s' has invalid meta attribute '%s'", i.queryName(*state), j
|
||||
);
|
||||
} else {
|
||||
if (v->type() == nString) {
|
||||
attrs2["type"] = "string";
|
||||
attrs2["value"] = v->str();
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nInt) {
|
||||
attrs2["type"] = "int";
|
||||
attrs2["value"] = fmt("%1%", v->integer());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nFloat) {
|
||||
attrs2["type"] = "float";
|
||||
attrs2["value"] = fmt("%1%", v->fpoint());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nBool) {
|
||||
attrs2["type"] = "bool";
|
||||
attrs2["value"] = v->boolean() ? "true" : "false";
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nList) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
for (auto & elem : v->listItems()) {
|
||||
if (elem.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["value"] = elem.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
} else if (v->type() == nAttrs) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
Bindings & attrs = *v->attrs();
|
||||
for (auto & i : attrs) {
|
||||
const Attr & a(*attrs.get(i.name));
|
||||
if (a.value.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["type"] = globals.state->symbols[i.name];
|
||||
attrs3["value"] = a.value.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
attrs2["name"] = j;
|
||||
Value * v = i.queryMeta(*state, j);
|
||||
if (!v)
|
||||
printError(
|
||||
"derivation '%s' has invalid meta attribute '%s'",
|
||||
i.queryName(*state), j);
|
||||
else {
|
||||
if (v->type() == nString) {
|
||||
attrs2["type"] = "string";
|
||||
attrs2["value"] = v->str();
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nInt) {
|
||||
attrs2["type"] = "int";
|
||||
attrs2["value"] = fmt("%1%", v->integer());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nFloat) {
|
||||
attrs2["type"] = "float";
|
||||
attrs2["value"] = fmt("%1%", v->fpoint());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nBool) {
|
||||
attrs2["type"] = "bool";
|
||||
attrs2["value"] = v->boolean() ? "true" : "false";
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nList) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
for (auto & elem : v->listItems()) {
|
||||
if (elem.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["value"] = elem.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
} else if (v->type() == nAttrs) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
Bindings & attrs = *v->attrs();
|
||||
for (auto &i : attrs) {
|
||||
const Attr & a(*attrs.get(i.name));
|
||||
if (a.value.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["type"] = globals.state->symbols[i.name];
|
||||
attrs3["value"] = a.value.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
table.push_back(columns);
|
||||
}
|
||||
} else
|
||||
table.push_back(columns);
|
||||
|
||||
cout.flush();
|
||||
cout.flush();
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
printMsg(
|
||||
lvlTalkative,
|
||||
"skipping derivation named '%1%' which gives an assertion failure",
|
||||
i.queryName(*state)
|
||||
);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName(*state));
|
||||
throw;
|
||||
}
|
||||
} catch (AssertionError & e) {
|
||||
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName(*state));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName(*state));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// </items>
|
||||
xml.closeElement();
|
||||
|
||||
if (!xmlOutput) {
|
||||
pager << formatTable(table);
|
||||
} else {
|
||||
pager << xmlStream.str();
|
||||
}
|
||||
});
|
||||
if (!xmlOutput) printTable(table);
|
||||
}
|
||||
|
||||
|
||||
static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (opFlags.size() > 0)
|
||||
@@ -1418,27 +1383,20 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs
|
||||
|
||||
auto [gens, curGen] = findGenerations(globals.profile);
|
||||
|
||||
withPager([&](Pager & pager) {
|
||||
for (auto & i : gens) {
|
||||
tm t;
|
||||
if (!localtime_r(&i.creationTime, &t)) {
|
||||
throw Error("cannot convert time");
|
||||
}
|
||||
pager << fmt(
|
||||
"%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||\n",
|
||||
i.number,
|
||||
t.tm_year + 1900,
|
||||
t.tm_mon + 1,
|
||||
t.tm_mday,
|
||||
t.tm_hour,
|
||||
t.tm_min,
|
||||
t.tm_sec,
|
||||
i.number == curGen ? "(current)" : ""
|
||||
);
|
||||
}
|
||||
});
|
||||
RunPager pager;
|
||||
|
||||
for (auto & i : gens) {
|
||||
tm t;
|
||||
if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time");
|
||||
logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||",
|
||||
i.number,
|
||||
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
|
||||
t.tm_hour, t.tm_min, t.tm_sec,
|
||||
i.number == curGen ? "(current)" : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (opFlags.size() > 0)
|
||||
|
||||
@@ -34,7 +34,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
return;
|
||||
}
|
||||
|
||||
Value vRoot = state.eval(e);
|
||||
Value vRoot;
|
||||
state.eval(e, vRoot);
|
||||
|
||||
for (auto & i : attrPaths) {
|
||||
Value v(findAlongAttrPath(state, i, autoArgs, vRoot).first);
|
||||
@@ -42,7 +43,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
|
||||
state.autoCallFunction(autoArgs, v, vRes, 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
|
||||
|
||||
+84
-103
@@ -25,9 +25,7 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ostream>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -290,7 +288,6 @@ try {
|
||||
graph. Topological sorting is used to keep the tree relatively
|
||||
flat. */
|
||||
static void printTree(
|
||||
std::ostream & ostream,
|
||||
std::shared_ptr<Store> store,
|
||||
AsyncIoRoot & aio,
|
||||
const StorePath & path,
|
||||
@@ -300,11 +297,11 @@ static void printTree(
|
||||
)
|
||||
{
|
||||
if (!done.insert(path).second) {
|
||||
ostream << fmt("%s%s [...]\n", firstPad, store->printStorePath(path));
|
||||
cout << fmt("%s%s [...]\n", firstPad, store->printStorePath(path));
|
||||
return;
|
||||
}
|
||||
|
||||
ostream << fmt("%s%s\n", firstPad, store->printStorePath(path));
|
||||
cout << fmt("%s%s\n", firstPad, store->printStorePath(path));
|
||||
|
||||
auto info = aio.blockOn(store->queryPathInfo(path));
|
||||
|
||||
@@ -318,7 +315,6 @@ static void printTree(
|
||||
for (const auto &[n, i] : enumerate(sorted)) {
|
||||
bool last = n + 1 == sorted.size();
|
||||
printTree(
|
||||
ostream,
|
||||
store,
|
||||
aio,
|
||||
i,
|
||||
@@ -377,15 +373,17 @@ opQuery(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, String
|
||||
|
||||
if (!query) query = qOutputs;
|
||||
|
||||
withPager([&](Pager & pager) {
|
||||
switch (*query) {
|
||||
RunPager pager;
|
||||
|
||||
switch (*query) {
|
||||
|
||||
case qOutputs: {
|
||||
for (auto & i : opArgs) {
|
||||
auto outputs =
|
||||
aio.blockOn(maybeUseOutputs(store, store->followLinksToStorePath(i), true, forceRealise));
|
||||
for (auto & outputPath : outputs) {
|
||||
pager << fmt("%1%\n", store->printStorePath(outputPath));
|
||||
}
|
||||
auto outputs = aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), true, forceRealise)
|
||||
);
|
||||
for (auto & outputPath : outputs)
|
||||
cout << fmt("%1%\n", store->printStorePath(outputPath));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -396,55 +394,54 @@ opQuery(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, String
|
||||
case qReferrersClosure: {
|
||||
StorePathSet paths;
|
||||
for (auto & i : opArgs) {
|
||||
auto ps = aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), useOutput, forceRealise)
|
||||
);
|
||||
auto ps = aio.blockOn(maybeUseOutputs(
|
||||
store, store->followLinksToStorePath(i), useOutput, forceRealise
|
||||
));
|
||||
for (auto & j : ps) {
|
||||
if (query == qRequisites) {
|
||||
aio.blockOn(store->computeFSClosure(j, paths, false, includeOutputs));
|
||||
} else if (query == qReferences) {
|
||||
for (auto & p : aio.blockOn(store->queryPathInfo(j))->references) {
|
||||
}
|
||||
else if (query == qReferences) {
|
||||
for (auto & p : aio.blockOn(store->queryPathInfo(j))->references)
|
||||
paths.insert(p);
|
||||
}
|
||||
} else if (query == qReferrers) {
|
||||
}
|
||||
else if (query == qReferrers) {
|
||||
StorePathSet tmp;
|
||||
aio.blockOn(store->queryReferrers(j, tmp));
|
||||
for (auto & i : tmp) {
|
||||
for (auto & i : tmp)
|
||||
paths.insert(i);
|
||||
}
|
||||
} else if (query == qReferrersClosure) {
|
||||
aio.blockOn(store->computeFSClosure(j, paths, true));
|
||||
}
|
||||
else if (query == qReferrersClosure)
|
||||
aio.blockOn(store->computeFSClosure(j, paths, true));
|
||||
}
|
||||
}
|
||||
auto sorted = aio.blockOn(store->topoSortPaths(paths));
|
||||
for (StorePaths::reverse_iterator i = sorted.rbegin(); i != sorted.rend(); ++i) {
|
||||
pager << fmt("%s\n", store->printStorePath(*i));
|
||||
}
|
||||
for (StorePaths::reverse_iterator i = sorted.rbegin();
|
||||
i != sorted.rend(); ++i)
|
||||
cout << fmt("%s\n", store->printStorePath(*i));
|
||||
break;
|
||||
}
|
||||
|
||||
case qDeriver:
|
||||
for (auto & i : opArgs) {
|
||||
auto info = aio.blockOn(store->queryPathInfo(store->followLinksToStorePath(i)));
|
||||
pager << fmt(
|
||||
"%s\n", info->deriver ? store->printStorePath(*info->deriver) : "unknown-deriver"
|
||||
);
|
||||
cout << fmt("%s\n", info->deriver ? store->printStorePath(*info->deriver) : "unknown-deriver");
|
||||
}
|
||||
break;
|
||||
|
||||
case qValidDerivers: {
|
||||
StorePathSet result;
|
||||
for (auto & i : opArgs) {
|
||||
auto derivers = aio.blockOn(store->queryValidDerivers(store->followLinksToStorePath(i)));
|
||||
auto derivers =
|
||||
aio.blockOn(store->queryValidDerivers(store->followLinksToStorePath(i)));
|
||||
for (const auto & i : derivers) {
|
||||
result.insert(i);
|
||||
}
|
||||
}
|
||||
auto sorted = aio.blockOn(store->topoSortPaths(result));
|
||||
for (StorePaths::reverse_iterator i = sorted.rbegin(); i != sorted.rend(); ++i) {
|
||||
pager << fmt("%s\n", store->printStorePath(*i));
|
||||
}
|
||||
for (StorePaths::reverse_iterator i = sorted.rbegin();
|
||||
i != sorted.rend(); ++i)
|
||||
cout << fmt("%s\n", store->printStorePath(*i));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -453,112 +450,95 @@ opQuery(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, String
|
||||
auto path = aio.blockOn(useDeriver(store, store->followLinksToStorePath(i)));
|
||||
Derivation drv = aio.blockOn(store->derivationFromPath(path));
|
||||
StringPairs::iterator j = drv.env.find(bindingName);
|
||||
if (j == drv.env.end()) {
|
||||
throw Error(
|
||||
"derivation '%s' has no environment binding named '%s'",
|
||||
store->printStorePath(path),
|
||||
bindingName
|
||||
);
|
||||
}
|
||||
pager << fmt("%s\n", j->second);
|
||||
if (j == drv.env.end())
|
||||
throw Error("derivation '%s' has no environment binding named '%s'",
|
||||
store->printStorePath(path), bindingName);
|
||||
cout << fmt("%s\n", j->second);
|
||||
}
|
||||
break;
|
||||
|
||||
case qHash:
|
||||
case qSize:
|
||||
for (auto & i : opArgs) {
|
||||
for (auto & j : aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), useOutput, forceRealise)
|
||||
))
|
||||
for (auto & j : aio.blockOn(maybeUseOutputs(
|
||||
store, store->followLinksToStorePath(i), useOutput, forceRealise
|
||||
)))
|
||||
{
|
||||
auto info = aio.blockOn(store->queryPathInfo(j));
|
||||
if (query == qHash) {
|
||||
assert(info->narHash.type == HashType::SHA256);
|
||||
pager << fmt("%s\n", info->narHash.to_string(HashFormat::Base32));
|
||||
} else if (query == qSize) {
|
||||
pager << fmt("%d\n", info->narSize);
|
||||
}
|
||||
cout << fmt("%s\n", info->narHash.to_string(Base::Base32, true));
|
||||
} else if (query == qSize)
|
||||
cout << fmt("%d\n", info->narSize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case qTree: {
|
||||
StorePathSet done;
|
||||
for (auto & i : opArgs) {
|
||||
std::stringstream tmp;
|
||||
printTree(tmp, store, aio, store->followLinksToStorePath(i), "", "", done);
|
||||
pager << tmp.str();
|
||||
}
|
||||
for (auto & i : opArgs)
|
||||
printTree(store, aio, store->followLinksToStorePath(i), "", "", done);
|
||||
break;
|
||||
}
|
||||
|
||||
case qGraph: {
|
||||
StorePathSet roots;
|
||||
for (auto & i : opArgs) {
|
||||
for (auto & j : aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), useOutput, forceRealise)
|
||||
))
|
||||
for (auto & i : opArgs)
|
||||
for (auto & j : aio.blockOn(maybeUseOutputs(
|
||||
store, store->followLinksToStorePath(i), useOutput, forceRealise
|
||||
)))
|
||||
{
|
||||
roots.insert(j);
|
||||
}
|
||||
}
|
||||
pager << aio.blockOn(formatDotGraph(ref<Store>::unsafeFromPtr(store), std::move(roots)));
|
||||
aio.blockOn(printDotGraph(ref<Store>::unsafeFromPtr(store), std::move(roots)));
|
||||
break;
|
||||
}
|
||||
|
||||
case qGraphML: {
|
||||
StorePathSet roots;
|
||||
for (auto & i : opArgs) {
|
||||
for (auto & j : aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), useOutput, forceRealise)
|
||||
))
|
||||
for (auto & i : opArgs)
|
||||
for (auto & j : aio.blockOn(maybeUseOutputs(
|
||||
store, store->followLinksToStorePath(i), useOutput, forceRealise
|
||||
)))
|
||||
{
|
||||
roots.insert(j);
|
||||
}
|
||||
}
|
||||
pager << aio.blockOn(formatGraphML(ref<Store>::unsafeFromPtr(store), std::move(roots)));
|
||||
aio.blockOn(printGraphML(ref<Store>::unsafeFromPtr(store), std::move(roots)));
|
||||
break;
|
||||
}
|
||||
|
||||
case qResolve: {
|
||||
for (auto & i : opArgs) {
|
||||
pager << fmt("%s\n", store->printStorePath(store->followLinksToStorePath(i)));
|
||||
}
|
||||
for (auto & i : opArgs)
|
||||
cout << fmt("%s\n", store->printStorePath(store->followLinksToStorePath(i)));
|
||||
break;
|
||||
}
|
||||
|
||||
case qRoots: {
|
||||
StorePathSet args;
|
||||
for (auto & i : opArgs) {
|
||||
for (auto & p : aio.blockOn(
|
||||
maybeUseOutputs(store, store->followLinksToStorePath(i), useOutput, forceRealise)
|
||||
))
|
||||
for (auto & i : opArgs)
|
||||
for (auto & p : aio.blockOn(maybeUseOutputs(
|
||||
store, store->followLinksToStorePath(i), useOutput, forceRealise
|
||||
)))
|
||||
{
|
||||
args.insert(p);
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet referrers;
|
||||
aio.blockOn(store->computeFSClosure(
|
||||
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations
|
||||
));
|
||||
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations));
|
||||
|
||||
auto & gcStore = require<GcStore>(*store);
|
||||
Roots roots = aio.blockOn(gcStore.findRoots(false));
|
||||
for (auto & [target, links] : roots) {
|
||||
if (referrers.find(target) != referrers.end()) {
|
||||
for (auto & link : links) {
|
||||
pager << fmt("%1% -> %2%\n", link, gcStore.printStorePath(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto & [target, links] : roots)
|
||||
if (referrers.find(target) != referrers.end())
|
||||
for (auto & link : links)
|
||||
cout << fmt("%1% -> %2%\n", link, gcStore.printStorePath(target));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -594,16 +574,15 @@ opReadLog(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, Stri
|
||||
|
||||
auto & logStore = require<LogStore>(*store);
|
||||
|
||||
withPager([&](Pager & pager) {
|
||||
for (auto & i : opArgs) {
|
||||
auto path = logStore.followLinksToStorePath(i);
|
||||
auto log = aio.blockOn(logStore.getBuildLog(path));
|
||||
if (!log) {
|
||||
throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path));
|
||||
}
|
||||
pager << *log;
|
||||
}
|
||||
});
|
||||
RunPager pager;
|
||||
|
||||
for (auto & i : opArgs) {
|
||||
auto path = logStore.followLinksToStorePath(i);
|
||||
auto log = aio.blockOn(logStore.getBuildLog(path));
|
||||
if (!log)
|
||||
throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path));
|
||||
std::cout << *log;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -732,8 +711,12 @@ static void opGC(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlag
|
||||
}
|
||||
|
||||
else {
|
||||
PrintFreed freed(options.action, results);
|
||||
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
|
||||
aio.blockOn(gcStore.collectGarbage(options, results));
|
||||
|
||||
if (options.action != GCOptions::gcDeleteDead)
|
||||
for (auto & i : results.paths)
|
||||
cout << i << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,7 +749,7 @@ opDelete(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, Strin
|
||||
auto & gcStore = require<GcStore>(*store);
|
||||
|
||||
GCResults results;
|
||||
PrintFreed freed(options.action, results);
|
||||
PrintFreed freed(true, results);
|
||||
aio.blockOn(gcStore.collectGarbage(options, results));
|
||||
}
|
||||
|
||||
@@ -877,12 +860,10 @@ opVerifyPath(std::shared_ptr<Store> store, AsyncIoRoot & aio, Strings opFlags, S
|
||||
aio.blockOn(aio.blockOn(store->narFromPath(path))->drainInto(sink));
|
||||
auto current = sink.finish();
|
||||
if (current.first != info->narHash) {
|
||||
printError(
|
||||
"path '%s' was modified! expected hash '%s', got '%s'",
|
||||
printError("path '%s' was modified! expected hash '%s', got '%s'",
|
||||
store->printStorePath(path),
|
||||
info->narHash.to_string(),
|
||||
current.first.to_string()
|
||||
);
|
||||
info->narHash.to_string(Base::SRI, true),
|
||||
current.first.to_string(Base::SRI, true));
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
@@ -948,7 +929,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));
|
||||
|
||||
+21
-27
@@ -46,31 +46,24 @@ 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.s.type).mkString("derivation");
|
||||
attrs.alloc(state.ctx.s.name).mkString(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.s.system).mkString(system);
|
||||
attrs.alloc(state.ctx.s.outPath).mkString(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.s.drvPath).mkString(state.ctx.store->printStorePath(*drvPath));
|
||||
|
||||
// Copy each output meant for installation.
|
||||
auto & vOutputs = attrs.alloc(state.ctx.s.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};
|
||||
outputsList->elems[m].mkString(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.s.outPath).mkString(state.ctx.store->printStorePath(*j.second));
|
||||
attrs.alloc(j.first).mkAttrs(outputAttrs);
|
||||
|
||||
/* This is only necessary when installing store paths, e.g.,
|
||||
`nix-env -i /nix/store/abcd...-foo'. */
|
||||
@@ -88,9 +81,9 @@ 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.s.meta).mkAttrs(meta);
|
||||
|
||||
manifest->elems[n++] = {NewValueAs::attrs, attrs};
|
||||
manifest->elems[n++].mkAttrs(attrs);
|
||||
|
||||
if (drvPath) references.insert(*drvPath);
|
||||
}
|
||||
@@ -104,17 +97,18 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
str.str(), references));
|
||||
|
||||
/* Get the environment builder expression. */
|
||||
Value envBuilder = state.eval(state.ctx.parseExprFromString(
|
||||
#include "buildenv.nix.gen.hh"
|
||||
, CanonPath::root
|
||||
));
|
||||
Value envBuilder;
|
||||
state.eval(state.ctx.parseExprFromString(
|
||||
#include "buildenv.nix.gen.hh"
|
||||
, CanonPath::root), envBuilder);
|
||||
|
||||
/* 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};
|
||||
Value args;
|
||||
args.mkAttrs(attrs);
|
||||
|
||||
Value topLevel{NewValueAs::app, state.ctx.mem, envBuilder, args};
|
||||
|
||||
@@ -122,9 +116,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
debug("evaluating user environment builder");
|
||||
state.forceValue(topLevel, noPos);
|
||||
NixStringContext context;
|
||||
const Attr & aDrvPath(*topLevel.attrs()->get(state.ctx.symbols.sym_drvPath));
|
||||
const Attr & aDrvPath(*topLevel.attrs()->get(state.ctx.s.drvPath));
|
||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, aDrvPath.value, context, "");
|
||||
const Attr & aOutPath(*topLevel.attrs()->get(state.ctx.symbols.sym_outPath));
|
||||
const Attr & aOutPath(*topLevel.attrs()->get(state.ctx.s.outPath));
|
||||
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, aOutPath.value, context, "");
|
||||
|
||||
/* Realise the resulting store expression. */
|
||||
|
||||
@@ -20,7 +20,8 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
|
||||
throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv);
|
||||
auto manifestFile = userEnv + "/manifest.nix";
|
||||
if (pathExists(manifestFile)) {
|
||||
Value v = state.evalFile(CanonPath(manifestFile));
|
||||
Value v;
|
||||
state.evalFile(CanonPath(manifestFile), v);
|
||||
Bindings & bindings(*state.ctx.mem.allocBindings(0));
|
||||
getDerivations(state, v, "", bindings, elems, false);
|
||||
}
|
||||
@@ -98,10 +99,6 @@ void ProfileElement::updateStorePaths(
|
||||
for (auto & output : bfd.outputs) {
|
||||
storePaths.insert(output.second);
|
||||
}
|
||||
|
||||
if (settings.envKeepDerivations) {
|
||||
storePaths.insert(bfd.drvPath.path);
|
||||
}
|
||||
},
|
||||
},
|
||||
buildable.raw()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "lix/libcmd/built-path.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/flake/flakeref.hh"
|
||||
#include "lix/libflakes/expr/flakeref.hh"
|
||||
#include "lix/libexpr/get-drvs.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libutil/json-fwd.hh"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libcmd/command.hh"
|
||||
#include "lix/libflakes/init.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/local-fs-store.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
@@ -75,15 +76,6 @@ CopyCommand::CopyCommand()
|
||||
});
|
||||
}
|
||||
|
||||
void CopyCommand::run()
|
||||
{
|
||||
if (requireStore && srcUri.empty() && dstUri.empty()) {
|
||||
throw UsageError("you must pass '--from' and/or '--to'");
|
||||
}
|
||||
|
||||
StoreCommand::run();
|
||||
}
|
||||
|
||||
ref<Store> CopyCommand::createStore(AsyncIoRoot & in)
|
||||
{
|
||||
return srcUri.empty() ? StoreCommand::createStore(in) : in.blockOn(openStore(srcUri));
|
||||
@@ -91,6 +83,9 @@ ref<Store> CopyCommand::createStore(AsyncIoRoot & in)
|
||||
|
||||
ref<Store> CopyCommand::getDstStore()
|
||||
{
|
||||
if (srcUri.empty() && dstUri.empty())
|
||||
throw UsageError("you must pass '--from' and/or '--to'");
|
||||
|
||||
return aio().blockOn(dstUri.empty() ? openStore() : openStore(dstUri));
|
||||
}
|
||||
|
||||
@@ -125,6 +120,8 @@ ref<eval_cache::CachingEvaluator> EvalCommand::getEvaluator()
|
||||
startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr
|
||||
);
|
||||
|
||||
flake::initFlakes(evalState.get());
|
||||
|
||||
evalState->repair = repair;
|
||||
}
|
||||
return ref<eval_cache::CachingEvaluator>::unsafeFromPtr(evalState);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "lix/libutil/args.hh"
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libstore/path.hh"
|
||||
#include "lix/libexpr/flake/lockfile.hh"
|
||||
#include "lix/libflakes/expr/lockfile.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
|
||||
#include <optional>
|
||||
@@ -15,6 +15,8 @@ namespace nix {
|
||||
|
||||
extern std::string programPath;
|
||||
|
||||
extern char * * savedArgv;
|
||||
|
||||
class EvalState;
|
||||
struct Pos;
|
||||
class Store;
|
||||
@@ -54,11 +56,9 @@ private:
|
||||
struct CopyCommand : virtual StoreCommand
|
||||
{
|
||||
std::string srcUri, dstUri;
|
||||
bool requireStore = true;
|
||||
|
||||
CopyCommand();
|
||||
|
||||
void run() override;
|
||||
ref<Store> createStore(AsyncIoRoot & in) override;
|
||||
|
||||
ref<Store> getDstStore();
|
||||
@@ -171,7 +171,7 @@ struct RawInstallablesCommand : virtual Args, SourceExprCommand
|
||||
|
||||
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
|
||||
std::vector<std::string> rawInstallables;
|
||||
};
|
||||
@@ -220,11 +220,13 @@ struct MixOperateOnOptions : virtual Args
|
||||
*/
|
||||
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
|
||||
bool recursive = false;
|
||||
bool all = false;
|
||||
|
||||
protected:
|
||||
|
||||
Realise realiseMode = Realise::Derivation;
|
||||
|
||||
public:
|
||||
|
||||
@@ -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"
|
||||
@@ -8,15 +5,36 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libfetchers/fetchers.hh"
|
||||
#include "lix/libfetchers/registry.hh"
|
||||
#include "lix/libexpr/flake/flakeref.hh"
|
||||
#include "lix/libflakes/expr/flakeref.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#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,20 @@ 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')
|
||||
state.evalLazily(
|
||||
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), v
|
||||
);
|
||||
else
|
||||
v.mkString(((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
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
|
||||
#include "lix/libutil/args.hh"
|
||||
|
||||
namespace nix::cli {
|
||||
template<typename Enum>
|
||||
struct enum_cli_traits;
|
||||
|
||||
template<typename Enum>
|
||||
constexpr std::string_view toString(Enum value)
|
||||
{
|
||||
for (const auto & [name, val] : enum_cli_traits<Enum>::values) {
|
||||
if (val == value) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
template<typename Enum>
|
||||
std::optional<Enum> fromString(std::string_view str)
|
||||
{
|
||||
for (const auto & [name, val] : enum_cli_traits<Enum>::values) {
|
||||
if (name == str) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename Enum>
|
||||
void completeAmongEnumChoices(AddCompletions & completions, size_t, std::string_view prefix)
|
||||
{
|
||||
for (const auto & [name, _] : enum_cli_traits<Enum>::values) {
|
||||
if (name.starts_with(prefix)) {
|
||||
completions.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Enum>
|
||||
Enum parseEnumArg(std::string text)
|
||||
{
|
||||
auto valueOpt = fromString<Enum>(text);
|
||||
|
||||
if (valueOpt) {
|
||||
return *valueOpt;
|
||||
} else {
|
||||
auto names = std::ranges::views::keys(enum_cli_traits<Enum>::values)
|
||||
| std::ranges::to<std::set<std::string>>();
|
||||
auto suggestions = Suggestions::bestMatches(names, text);
|
||||
throw UsageError(suggestions, "'%s' is not a recognised '%s'", text, enum_cli_traits<Enum>::typeName);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Enum>
|
||||
std::optional<Enum> parseOptionalEnumArg(std::string text)
|
||||
{
|
||||
auto target = fromString<Enum>(text);
|
||||
|
||||
if (!target && text != "") {
|
||||
auto names = std::ranges::views::keys(enum_cli_traits<Enum>::values) | std::ranges::to<std::set>();
|
||||
auto suggestions = Suggestions::bestMatches(names, text);
|
||||
throw UsageError(suggestions, "'%s' is not a recognised '%s'", text, enum_cli_traits<Enum>::typeName);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/get-drvs.hh"
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
#include "lix/libflakes/expr/flake.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
#include "lix/libflakes/expr/flake.hh"
|
||||
#include "lix/libexpr/eval-cache.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "lix/libcmd/installables.hh"
|
||||
#include "lix/libexpr/eval-cache.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
#include "lix/libflakes/expr/flake.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
+18
-15
@@ -14,7 +14,7 @@
|
||||
#include "lix/libexpr/eval-settings.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libmain/shared.hh"
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
#include "lix/libflakes/expr/flake.hh"
|
||||
#include "lix/libexpr/eval-cache.hh"
|
||||
#include "lix/libfetchers/registry.hh"
|
||||
#include "lix/libstore/build-result.hh"
|
||||
@@ -219,7 +219,8 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
|
||||
state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap()
|
||||
));
|
||||
|
||||
Value root = state.eval(e);
|
||||
Value root;
|
||||
state.eval(e, root);
|
||||
|
||||
auto autoArgs = getAutoArgs(*evaluator);
|
||||
|
||||
@@ -236,7 +237,8 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
|
||||
|
||||
auto [v1, pos] = findAlongAttrPath(state, prefix_, *autoArgs, root);
|
||||
state.forceValue(v1, pos);
|
||||
Value v2 = state.autoCallFunction(*autoArgs, v1, pos);
|
||||
Value v2;
|
||||
state.autoCallFunction(*autoArgs, v1, v2, pos);
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs()) {
|
||||
@@ -409,7 +411,8 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
|
||||
throw Error("not everything is cached, but evaluation is not allowed");
|
||||
|
||||
Value vFlake = flake::callFlake(state, *lockedFlake);
|
||||
Value vFlake;
|
||||
flake::callFlake(state, *lockedFlake, vFlake);
|
||||
|
||||
state.forceAttrs(vFlake, noPos, "while parsing cached flake data");
|
||||
|
||||
@@ -446,18 +449,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();
|
||||
state.eval(e, vFile);
|
||||
}
|
||||
else if (file)
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), vFile);
|
||||
else {
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
state.eval(e, vFile);
|
||||
}
|
||||
|
||||
for (auto & s : ss) {
|
||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||
|
||||
+3
-17
@@ -5,32 +5,18 @@
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
typedef std::function<int(AsyncIoRoot &, std::string, std::list<std::string>)> MainFunction;
|
||||
|
||||
struct LegacyCommandRegistry
|
||||
{
|
||||
typedef std::function<int(AsyncIoRoot &, std::string, std::list<std::string>)> MainFunction;
|
||||
typedef std::function<
|
||||
int(AsyncIoRoot &, std::string, std::list<std::string>, std::span<char *>)>
|
||||
RawMainFunction;
|
||||
|
||||
using LegacyCommandMap = std::map<std::string, RawMainFunction>;
|
||||
using LegacyCommandMap = std::map<std::string, MainFunction>;
|
||||
static LegacyCommandMap * commands;
|
||||
|
||||
static void add(const std::string & name, MainFunction fun)
|
||||
{
|
||||
addWithRaw(
|
||||
name,
|
||||
[fun](AsyncIoRoot & aio, std::string name, std::list<std::string> args, std::span<char *>) {
|
||||
return fun(aio, name, args);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static void addWithRaw(const std::string & name, RawMainFunction fun)
|
||||
{
|
||||
if (!commands) commands = new LegacyCommandMap;
|
||||
(*commands)[name] = fun;
|
||||
|
||||
@@ -5,4 +5,6 @@ includedir=@includedir@
|
||||
Name: Lix (libcmd)
|
||||
Description: Lix Package Manager (libcmd)
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: lix
|
||||
Requires: lix-base lix-util lix-store
|
||||
Requires.private: lix-fetchers lix-expr lix-main @BOEHM_IF_FOUND@ libeditline lowdown ncurses
|
||||
Libs: -L${libdir} @LIBLIX_DOC_IF_STATIC@ -llixcmd
|
||||
|
||||
@@ -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,13 +65,16 @@ 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,
|
||||
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
|
||||
#ifdef LOWDOWN_CONSOLIDATED_OFLAGS
|
||||
.oflags = LOWDOWN_NOLINK,
|
||||
#else
|
||||
.maxdepth = 20,
|
||||
.cols = lowdown_cols,
|
||||
.hmargin = 0,
|
||||
.vmargin = 0,
|
||||
#endif /* LOWDOWN_SEPARATE_TERM_OPTS */
|
||||
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
|
||||
.oflags = LOWDOWN_TERM_NOLINK,
|
||||
#endif /* LOWDOWN_CONSOLIDATED_OFLAGS */
|
||||
};
|
||||
if (!shouldANSI(fileno)) {
|
||||
opts.oflags |= LOWDOWN_TERM_NOANSI;
|
||||
|
||||
+64
-3
@@ -1,4 +1,4 @@
|
||||
liblix_sources += files(
|
||||
libcmd_sources = files(
|
||||
'built-path.cc',
|
||||
'cmd-profiles.cc',
|
||||
'command.cc',
|
||||
@@ -21,7 +21,6 @@ libcmd_headers = files(
|
||||
'command.hh',
|
||||
'common-eval-args.hh',
|
||||
'editor-for.hh',
|
||||
'enum-traits.hh',
|
||||
'installable-attr-path.hh',
|
||||
'installable-derived-path.hh',
|
||||
'installable-flake.hh',
|
||||
@@ -33,8 +32,70 @@ libcmd_headers = files(
|
||||
'repl.hh',
|
||||
)
|
||||
|
||||
liblix_generated_headers += [
|
||||
libcmd_generated_headers = [
|
||||
gen_header.process('repl-overlays.nix', preserve_path_from: meson.current_source_dir()),
|
||||
]
|
||||
|
||||
libcmd = library(
|
||||
'lixcmd',
|
||||
libcmd_generated_headers,
|
||||
libcmd_sources,
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixfetchers,
|
||||
liblixexpr,
|
||||
liblixflakes,
|
||||
liblixmain,
|
||||
liblix_doc,
|
||||
boehm,
|
||||
editline,
|
||||
kj,
|
||||
lowdown,
|
||||
ncurses,
|
||||
nlohmann_json,
|
||||
],
|
||||
# '../..' for self references like "lix/libcmd/*.hh"
|
||||
include_directories : [ '../..' ],
|
||||
cpp_pch : cpp_pch,
|
||||
install : true,
|
||||
# FIXME(Qyriad): is this right?
|
||||
install_rpath : libdir,
|
||||
)
|
||||
|
||||
install_headers(libcmd_headers, subdir : 'lix/libcmd', preserve_path : true)
|
||||
custom_target(
|
||||
command : [ 'cp', '@INPUT@', '@OUTPUT@' ],
|
||||
input : libcmd_generated_headers,
|
||||
output : '@PLAINNAME@',
|
||||
install : true,
|
||||
install_dir : includedir / 'lix/libcmd',
|
||||
)
|
||||
|
||||
liblixcmd = declare_dependency(
|
||||
include_directories : include_directories('../..'),
|
||||
dependencies : [
|
||||
liblixutil,
|
||||
liblixstore,
|
||||
liblixflakes,
|
||||
kj,
|
||||
],
|
||||
link_with : libcmd,
|
||||
)
|
||||
meson.override_dependency('lix-cmd', liblixcmd)
|
||||
|
||||
# FIXME: not using the pkg-config module because it creates way too many deps
|
||||
# while meson migration is in progress, and we want to not include boost here
|
||||
configure_file(
|
||||
input : 'lix-cmd.pc.in',
|
||||
output : 'lix-cmd.pc',
|
||||
install_dir : libdir / 'pkgconfig',
|
||||
configuration : {
|
||||
'prefix' : prefix,
|
||||
'libdir' : libdir,
|
||||
'includedir' : includedir,
|
||||
'PACKAGE_VERSION' : meson.project_version(),
|
||||
'BOEHM_IF_FOUND' : boehm.found() ? 'bdw-gc' : '',
|
||||
'LIBLIX_DOC_IF_STATIC' : is_static ? '-llix_doc' : '',
|
||||
},
|
||||
)
|
||||
|
||||
+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(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,18 +35,18 @@ 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.
|
||||
*
|
||||
* This function logs but ignores errors from readline's write_history().
|
||||
*/
|
||||
void writeHistory();
|
||||
virtual void writeHistory();
|
||||
virtual ~ReadlineLikeInteracter() override;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
+465
-1007
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user