Compare commits

...

57 commits

Author SHA1 Message Date
eldritch horrors c856b82c2e libstore: despecialcase protocol version check
protocol versions are sent as u64. on the peer we read them as uint64,
check that the upper half is 0, and throw an exception if not. we then
read an arbitrary amount of data from the peer and dump it to the user
terminal. this is a little bit ridiculous, can never happen in correct
implementation, and is severly untested. let us just drop it entirely.

Change-Id: Ibd2f53a765341ed6439d40d9d1eac11e79c6b5e3
2024-03-24 18:45:22 +00:00
eldritch horrors 3e428f2289 libstore: un-inline copyNAR expansions
these are copies of copyNAR with only some variables renamed.

Change-Id: I98ddd7a98250fa5d304e18e1debf417e9f7768dd
2024-03-24 15:24:02 +01:00
jade 946fc12e4e Revert "Merge pull request #9476 from alois31/restore-progress-bar"
Observed to regress nix repl attrset printing with narrow windows.

This reverts commit a2d5e803cf.

Fixes: lix-project/lix#168

Change-Id: I8e0031475b4ec26d6a71014357d973578b70815c
2024-03-23 18:04:29 -07:00
eldritch horrors 652f52f071 libutil: don't memset 64k in drainFD
this is not needed and introduces a bunch of memset calls, making up for
3% of valgrind cycle estimation *alone*. real-world impact is a lot
lower on our test machine, but we suspect that less powerful machines
would see an impact from dropping this.

Change-Id: Iad10e9d556e64fdeb0bee0059a4e52520058d11e
2024-03-23 22:17:46 +00:00
Qyriad b4d07656ff build: optionally build and install with meson
This commit adds several meson.build, which successfully build and
install Lix executables, libraries, and headers. Meson does not yet
build docs, Perl bindings, or run tests, which will be added in
following commits. As such, this commit does not remove the existing
build system, or make it the default, and also as such, this commit has
several FIXMEs and TODOs as notes for what should be done before the
existing autoconf + make buildsystem can be removed and Meson made the
default. This commit does not modify any source files.

A Meson-enabled build is also added as a Hydra job, and to
`nix flake check`.

Change-Id: I667c8685b13b7bab91e281053f807a11616ae3d4
2024-03-22 08:36:50 -06:00
jade a7161b6c0f Merge "clang-tidy check infrastructure" into main 2024-03-21 12:28:13 -06:00
Qyriad fab55aff0e flake: fix arm32 Linux cross devShell on macOS (fix nix flake check)
Change-Id: Iacac97de0b3d5f2df52c7bc985148624a351f45d
2024-03-21 08:22:38 -06:00
eldritch horrors 22e3f0e987 libexpr: unbreak PosTable performance
this was mostly an inconvenience for error reporting, but fully broke
the debugger (because the debugger does *a lot* of eager position
resolution). copying the line offsets into a local and filling that
local when empty without also storing the calculated offsets back does
kind of ... not cache anything.

fixes lix-project/lix#165

Change-Id: Iccb0ba193ce2f15c832978daecf7b9bebbbe8585
2024-03-20 13:45:36 +01:00
jade 5a28d70d1e Merge "un-ups your start" into main 2024-03-19 14:29:57 -06:00
eldritch horrors d9a83886f9 libutil: remove exception handling workingness check
within lix itself this problem is caught by the test suite. outside of
lix itself three cases can be had: either the problem is fully inside
lix libs, fully inside user code, or it exists at the boundary. the
first is caught by the test suite, the second isn't caught at all, and
the third is something lix should not be responsible for.

Change-Id: I95aa35d8cb6f0ef5816a2941c467bc0c15916063
2024-03-19 06:09:42 -06:00
jade 4050245faa Merge changes I72c945ca,I2138bb4d,Ib96749f3 into main
* changes:
  Release notes for builtins.nixVersion change
  un-nixes ur lix, a little
  issue importer: list issues that are *not* closed when finding existing issues
2024-03-18 20:19:53 -06:00
jade 985bd5eb9f un-ups your start
We do not need upstart, it is so thoroughly obsolete that we should not
care about supporting it.

Change-Id: Ie0ca084740845555fddffacc899cd129c9a4c1fe
2024-03-18 18:28:08 -07:00
jade 20b4a97af3 Release notes for builtins.nixVersion change
Change-Id: I72c945cab464d26d73f5594ef0a4bb2184545da4
2024-03-18 18:26:52 -07:00
jade 30233d87f9 un-nixes ur lix, a little
I didn't really go attack the docs because we need to pull a bunch of
PRs. I went looking for strings in the code that called lix nix.

Change-Id: I2138bb4dd239096bc530946b281db7f875195b39
2024-03-18 18:20:24 -07:00
jade 81be5eb7c6 issue importer: list issues that are *not* closed when finding existing issues
Turns out also, you cannot set the queue to 0 with any success. So we
really should just like, prevent notifications in forgejo itself.

Filed a bug for that:
lix-project/web-services#38

Change-Id: Ib96749f3159659182904963cab7b2ef88fc64442
2024-03-18 18:14:31 -07:00
jade 6b0020749d clang-tidy check infrastructure
This brings in infrastructure for developing new custom clang-tidy lints
and refactors for Lix.

Change-Id: I3df5f5855712ab4f97d4e84d771e5e818f81f881
2024-03-18 16:10:29 -07:00
eldritch horrors f38ae92a38 libutil: make AutoCloseFD a better resource
add a reset() method to close the wrapped fd instead of assigning magic
constants. also make the from-fd constructor explicit so you can't
accidentally assign the *wrong* magic constant, or even an unrelated
integer that also just happens to be an fd by pure chance.

Change-Id: I51311b0f6e040240886b5103d39d1794a6acc325
2024-03-18 15:42:52 -06:00
eldritch horrors 0f518f44e2 Merge "libexpr: associate let exprs with the correct StaticEnv" into main 2024-03-18 15:20:21 -06:00
eldritch horrors afb839a0c9 libexpr: associate let exprs with the correct StaticEnv
static env association is from expr to its enclosing scope, but let
exprs set their association to their *inner* scope. this skips one level
of envs and will cause segfaults if the parent is a with expr.

fixes #145

Change-Id: I1d22146110f071ede21b4eed7ed34b5850ef2ef3
2024-03-18 14:15:22 -07:00
jade 37c4b10c44 Add clang format configuration
Contemplate the configuration with: https://clang-format-configurator.site/

(cherry picked from commit 53fdcbca509b6c5dacaea3d3c465d86e49b0dd74)
Change-Id: I5446fd45de2bf644e34112f719afb3318a440b30
2024-03-18 13:31:39 -07:00
eldritch horrors b3599166ad libexpr: sort binding name in debugger
not doing this exposes the binding name order to the annoying
interference of parse order on symbol order, which wouldn't be so bad if
it didn't make the tests less reliable and, importantly, dependent on
linker behavior (due to primop initialization being done in static
initializer, and the order of static initializers being defined only
within a single translation unit).

fixes #143

Change-Id: I3cf417893fbcf19e9ad3ff8986deb7cbcf3ca511
2024-03-18 20:03:31 +01:00
jade 47a237f7ec Merge "Delete hasPrefix and hasSuffix from the codebase" into main 2024-03-18 12:01:39 -06:00
eldritch horrors 86a1121d16 use byte indexed locations for PosIdx
we now keep not a table of all positions, but a table of all origins and
their sizes. position indices are now direct pointers into the virtual
concatenation of all parsed contents. this slightly reduces memory usage
and time spent in the parser, at the cost of not being able to report
positions if the total input size exceeds 4GiB. this limit is not unique
to nix though, rustc and clang also limit their input to 4GiB (although
at least clang refuses to process inputs that are larger, we will not).

this new 4GiB limit probably will not cause any problems for quite a
while, all of nixpkgs together is less than 100MiB in size and already
needs over 700MiB of memory and multiple seconds just to parse. 4GiB
worth of input will easily take multiple minutes and over 30GiB of
memory without even evaluating anything. if problems *do* arise we can
probably recover the old table-based system by adding some tracking to
Pos::Origin (or increasing the size of PosIdx outright), but for time
being this looks like more complexity than it's worth.

since we now need to read the entire input again to determine the
line/column of a position we'll make unsafeGetAttrPos slightly lazy:
mostly the set it returns is only used to determine the file of origin
of an attribute, not its exact location. the thunks do not add
measurable runtime overhead.

notably this change is necessary to allow changing the parser since
apparently nothing supports nix's very idiosyncratic line ending choice
of "anything goes", making it very hard to calculate line/column
positions in the parser (while byte offsets are very easy).

(cherry picked from commit 5d9fdab3de0ee17c71369ad05806b9ea06dfceda)
Change-Id: Ie0b2430cb120c09097afa8c0101884d94f4bbf34
2024-03-18 16:12:46 +01:00
eldritch horrors c39150e6bb diagnose "unexpected EOF" at EOF
this needs a string comparison because there seems to be no other way to
get that information out of bison. usually the location info is going to
be correct (pointing at a bad token), but since EOF isn't a token as
such it'll be wrong in that this case.

this hasn't shown up much so far because a single line ending *is* a
token, so any file formatted in the usual manner (ie, ending in a line
ending) would have its EOF position reported correctly.

(cherry picked from commit 855fd5a1bb781e4f722c1d757ba43e866d370132)
Change-Id: I120c56a962f4286b1ae3b71da7b71ce8ec3e0535
2024-03-18 16:12:46 +01:00
eldritch horrors 4c072c7c5f match line endings used by parser and error reports
the parser treats a plain \r as a newline, error reports do not. this
can lead to interesting divergences if anything makes use of this
feature, with error reports pointing to wrong locations in the input (or
even outside the input altogether).

(cherry picked from commit 2be6b143289e5479cc4a2667bb84e879116c2447)
Change-Id: Ieb7f7655bac8cb0cf5734c60bd41723388f2973c
2024-03-18 16:12:46 +01:00
eldritch horrors 9cf92c012d report inherit attr errors at the duplicate name
previously we reported the error at the beginning of the binding
block (for plain inherits) or the beginning of the attr list (for
inherit-from), effectively hiding where exactly the error happened.

this also carries over to runtime positions of attributes in sets as
reported by unsafeGetAttrPos. we're not worried about this changing
observable eval behavior because it *is* marked unsafe, and the new
behavior is much more useful.

(cherry picked from commit 1edd6fada53553b89847ac3981ac28025857ca02)
Change-Id: I2f50eb9f3dc3977db4eb3e3da96f1cb37ccd5174
2024-03-18 16:12:45 +01:00
eldritch horrors d826427f02 normalize formal order on ExprLambda::show
we already normalize attr order to lexicographic, doing the same for
formals makes sense. doubly so because the order of formals would
otherwise depend on the context of the expression, which is not quite as
useful as one might expect.

(cherry picked from commit 4147ecfb1c51f3fe3b4adcbd4e753fd487dab645)
Change-Id: I3fd0dbdef3ac7447a3a03ff20bb514a0d0f23fb1
2024-03-18 07:56:34 -06:00
eldritch horrors 314f044c2b keep copies of parser inputs that are in-memory only
the parser modifies its inputs, which means that sharing them between
the error context reporting system and the parser itself can confuse the
reporting system. usually this led to early truncation of error context
reports which, while not dangerous, can be quite confusing.

(cherry picked from commit d384ecd553aa997270b79ee98d02f7cf7e1849e6)
Change-Id: I677646b5675b12b2faa787943646aa36dc6e6ee3
2024-03-18 07:56:23 -06:00
eldritch horrors 1f8b85786e libutil: remove vfork
vfork confers a large performance advantage over fork, measured locally
at 16µs per vfork agains 90µs per fork. however nix *almost always*
follows a vfork up with an execve-family call, melting the performance
advantage from 6x to only 15%. in most of those cases it's doing things
that are undefined behavior (like manipulating the heap, or even
throwing exceptions and trashing the parent process stack).

most notably the one place that could benefit from the vfork performance
improvement is linux derivation sandbox setup—which doesn't use vfork.

Change-Id: I2037b7384d5a4ca24da219a569e1b1f39531410e
2024-03-18 06:10:41 -06:00
jade 2890840b96 Merge "Forgejo issue importer" into main 2024-03-17 22:08:59 -06:00
jade 61e21b2557 Delete hasPrefix and hasSuffix from the codebase
These now have equivalents in the standard lib in C++20. This change was
performed with a custom clang-tidy check which I will submit later.
Executed like so:

ninja -C build && run-clang-tidy -checks='-*,nix-*' -load=build/libnix-clang-tidy.so -p .. -fix ../tests | tee -a clang-tidy-result

Change-Id: I62679e315ff9e7ce72a40b91b79c3e9fc01b27e9
2024-03-17 20:17:19 -07:00
jade 706cee5c49 Merge "builtins.nixVersion: return fixed fake version" into main 2024-03-17 12:13:21 -06:00
Qyriad 32d6e58069 flake: fix musl static stdenv devShell (fix nix flake check)
pkgs.pkgsStatic.glibcLocales is null, so the string coercion was failing
for devShells against static stdenvs

Change-Id: Iee8e1042a852133ce0432627d72a85e97c17055e
2024-03-17 10:05:18 -06:00
jade 886a418d23 builtins.nixVersion: return fixed fake version
This builtin is only going to cause us problems because we are not Nix,
so let's just falsify being in the 2.18 series, since that is the
closest target that has any meaning.

In future we might want to have a better feature detection mechanism,
for when we actually add stuff to some builtin's attr set argument. But
builtins.nixVersion is just going to be hopelessly broken and it should
be stubbed out.

Fixes lix-project/lix#144

Change-Id: Id7390b32a29c6147f2977737d81846320de5d67e
2024-03-17 00:32:19 -07:00
eldritch horrors 11f35afa6f diagnose duplicated attrs at correct path
diagnose attr duplication at the path the duplication was detected, not
at the path the current attribute wanted to place. doing the latter is
only correct if a leaf attribute was duplicated, not if an attrpath was
set to a non-attrset in one binding and a (potentially implied) attrset
in another binding.

fixes #124

Change-Id: Ic4aa9cc12a9874d4e7897c6f64408f10aa36fc82
2024-03-16 22:12:49 +01:00
jade 3392020710 Forgejo issue importer
We needed a script to go yoink all the real NixOS/Nix issues from our
mirror into the Lix repo.

Change-Id: If8c8ebfb58634c675eae450454c0189288c6b18a
2024-03-16 00:22:33 -07:00
Rebecca Turner e257ff10fd Merge "Fix gc-small-vector.hh includes" into main 2024-03-15 16:20:40 -06:00
eldritch horrors 3c52344300 out with the -O3, in with the -O2
-O3 does not measurably improve performance of the resulting binaries,
neither with lto enabled nor with lto disabled. what it does to however
is cause gcc warning spew in libstdc++ that we can't do anything
about (and that upon inspection of libstdc++ source looks like a gcc
bug).

with lto, -O3:

Benchmark 1: GC_INITIAL_HEAP_SIZE=10g nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
    Time (mean ± σ):      4.608 s ±  0.027 s    [User: 3.866 s, System: 0.522 s]
    Range (min … max):    4.579 s …  4.640 s    10 runs

Benchmark 2:  nix eval -f <nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix>
    Time (mean ± σ):     408.1 ms ±  25.5 ms    [User: 360.0 ms, System: 28.1 ms]
    Range (min … max):   387.6 ms … 439.0 ms    10 runs

with lto, -O2:

Benchmark 1: GC_INITIAL_HEAP_SIZE=10g nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
    Time (mean ± σ):      4.632 s ±  0.044 s    [User: 3.874 s, System: 0.544 s]
    Range (min … max):    4.563 s …  4.673 s    10 runs

Benchmark 2:  nix eval -f <nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix>
    Time (mean ± σ):     394.0 ms ±  23.9 ms    [User: 351.2 ms, System: 27.6 ms]
    Range (min … max):   377.8 ms … 429.3 ms    10 runs

without lto, -O3:

Benchmark 1: GC_INITIAL_HEAP_SIZE=10g nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
    Time (mean ± σ):      4.700 s ±  0.024 s    [User: 3.906 s, System: 0.559 s]
    Range (min … max):    4.663 s …  4.717 s    10 runs

Benchmark 2:  nix eval -f <nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix>
    Time (mean ± σ):     400.4 ms ±  25.6 ms    [User: 353.7 ms, System: 26.8 ms]
    Range (min … max):   379.8 ms … 430.6 ms    10 runs

without lto, -O2:

Benchmark 1: GC_INITIAL_HEAP_SIZE=10g nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
    Time (mean ± σ):      4.724 s ±  0.030 s    [User: 3.924 s, System: 0.570 s]
    Range (min … max):    4.687 s …  4.749 s    10 runs

Benchmark 2:  nix eval -f <nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix>
    Time (mean ± σ):     392.4 ms ±  24.3 ms    [User: 350.9 ms, System: 26.4 ms]
    Range (min … max):   376.9 ms … 428.0 ms    10 runs

fixes #46

Change-Id: Ib8afad8a07c278f57f2e3317d00cce4f9ec0f338
2024-03-15 15:31:49 -06:00
Rebecca Turner 7abbce500b
Fix gc-small-vector.hh includes
Change-Id: I4abc19029fb62712582761d4fc1895156b68803d
2024-03-15 13:39:32 -07:00
jade 0d85875c3a Allow dlopen of plugins to fail
It happens with some frequency that plugins that might be unimportant to
the evaluation at hand mismatch with the nix version, leading to
spurious load failures. Let's make these non fatal.

Change-Id: Iba10e951d171725ccf1a121bcd9be1e1d6ad69eb
2024-03-15 12:31:16 -07:00
jade 7d361f1a82 Test that :st does ... something
Change-Id: I97c00b5eb1288f68d8c2b484436cc185d040b8b2
2024-03-15 12:31:16 -07:00
jade af066af7f3 repl_characterization: Also verify the stack trace exists
Change-Id: I8b2d8211a24011fae1586a1182d7d0772a039cd7
2024-03-15 12:31:16 -07:00
jade 78513b1fc8 repl_characterization: eat newlines after commands and source-dir paths
This is because they are unrepresentable in the source files with
commentary but not in the output, so we should just eat them in
normalization. It's ok.

Change-Id: I2cb7e8b3fc7b00874885bb287cbaa200b41cb16b
2024-03-15 12:31:16 -07:00
jade 8a8715af89 Add regression tests for #9917, #9918
Change-Id: Ib0591e1499c5dba5e5a83ee75a899c9d16986827
2024-03-15 12:31:16 -07:00
jade 18ed6c3bdf Implement a repl characterization test system
This allows for automating using the repl without needing a PTY, with
very easy to write test files.

Change-Id: Ia8d7854edd91f93477638942cb6fc261354e6035
2024-03-15 12:31:16 -07:00
jade 38571c50e6 Implement a parser for a literate testing system for the repl
This parser can be reused for other purposes. It's inspired by
https://bitheap.org/cram/

Although eelco's impostor exists https://github.com/mobusoperandi/eelco,
it is not very nice to depend on out of tree testing frameworks with no
way to customize them.

Change-Id: Ifca50177e09730182baf0ebf829c3505bbb0274a
2024-03-14 14:30:38 -07:00
Cole Helbling 84727bebb4
Backport PR#9633 by cole-h: package: don't set sysconfdir in devShells
(cherry-picked from commit ba0087316acc2aba999cabe5e1a159da636b2569)

Change-Id: I5a50afb3b7b65516df798ee51b74f06727a91928
2024-03-14 14:06:31 -07:00
Rebecca Turner 9f242fae76
Only set LOCALE_ARCHIVE on Linux
`macOS` does not have `glibcLocales`:

    error:
       … while calling the 'derivationStrict' builtin

         at /derivation-internal.nix:9:12:

            8|
            9|   strict = derivationStrict drvAttrs;
             |            ^
           10|

       … while evaluating derivation 'nix-2.90.0'
         whose name attribute is located at /nix/store/y0c95bwyvs80pm69hdd4b11pyq2ghiwh-source
/pkgs/stdenv/generic/make-derivation.nix:348:7

       … while evaluating attribute 'LOCALE_ARCHIVE' of derivation 'nix-2.90.0'

         at /nix/store/ng5qzbyv4902b4pw7g35caqw5cnmryf9-source/flake.nix:331:15:

          330|               # Required to make non-NixOS Linux not complain about missing loc

Change-Id: I4464484a0eca12b5e073d49d900b6f25886245c1
2024-03-14 14:06:23 -07:00
puck caded0d55e Merge "Delete the existing installer" into main 2024-03-14 13:14:21 -06:00
jade c79b5dca2a Merge "flake: dev shells should include the LOCALE_ARCHIVE so configure does not complain" into main 2024-03-14 12:22:35 -06:00
jade cc55da201b Merge "un-thumbs-up ur github templates" into main 2024-03-14 12:22:09 -06:00
puck 93cc063344 Delete the existing installer
We're not going to use it.

Fixes: #31
Change-Id: Ib17a2eb6cae1ecbbf9ad1062e576ba6107a3c13b
2024-03-14 18:15:46 +00:00
eldritch horrors c26599b143 libexpr: fix elided value counting in printer
using the total-attrs-printed and total-list-items-printed counters to
calculate how many attrs were elided only works properly if no nesting
is involved. once things do nest the global counter can exceed the size
of the currently printed object, leading to unsigned wrapping and great
overestimation of elided counts. counting locally in addition to global
counts fixes this.

these are functional tests because creating these objects requires the
evaluator to not be a huge amount of code, and we also want defaults to
be tested for cli usage.

fixes #14

Change-Id: Icb9a0cb21b2f4bacbc5e9dcdd8c0b9055b4088a7
2024-03-14 01:52:19 -06:00
jade 0278c03de5 un-thumbs-up ur github templates
These are not the way that we want to do things.

Change-Id: I5f3706cf50d007a6659edb96a6230d52e18a769a
2024-03-13 23:10:25 -07:00
jade 2a8f579c53 Merge "Backport PR#10204 by 9999years: Replace foo with __NIX_STR in cxx-big-literal" into main 2024-03-14 00:07:21 -06:00
jade ab5ff86917 flake: dev shells should include the LOCALE_ARCHIVE so configure does not complain
Change-Id: Id661bd8c90696cb663a25503878a697f596d1e77
2024-03-13 12:25:23 -07:00
Rebecca Turner da2f165128 Backport PR#10204 by 9999years: Replace foo with __NIX_STR in cxx-big-literal
Looks a little nicer when you check the generated sources.

(cherry-picked from commit e65e9114d2797cc4380da218972979dda7395df6)

Change-Id: I91bd185bf12deef72d20fba36178ff42a686c518
Upstream-PR: https://github.com/NixOS/nix/pull/10204
2024-03-12 11:58:36 -07:00
190 changed files with 3484 additions and 3627 deletions

51
.clang-format Normal file
View file

@ -0,0 +1,51 @@
---
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: BlockIndent
AlignEscapedNewlines: Left
AlignOperands: DontAlign
AllowShortBlocksOnASingleLine: Always
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: None
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: MultiLine
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakAfterAttributes: Always
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
ColumnLimit: 100
EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Leave
FixNamespaceComments: false
IndentWidth: 4
InsertBraces: true
InsertTrailingCommas: Wrapped
LambdaBodyIndentation: Signature
PackConstructorInitializers: CurrentLine
PointerAlignment: Middle
SortIncludes: Never
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false

View file

@ -7,30 +7,26 @@ assignees: ''
---
**Describe the bug**
## Describe the bug
A clear and concise description of what the bug is.
If you have a problem with a specific package or NixOS,
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
**Steps To Reproduce**
## Steps To Reproduce
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
## Expected behavior
A clear and concise description of what you expected to happen.
**`nix-env --version` output**
## `nix-env --version` output
**Additional context**
## Additional context
Add any other context about the problem here.
**Priorities**
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -7,18 +7,18 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
## Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
## Describe the solution you'd like
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
## Additional context
Add any other context or screenshots about the feature request here.
**Priorities**
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -30,7 +30,3 @@ assignees: ''
```
</details>
## Priorities
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -25,7 +25,3 @@ assignees: ''
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
## Priorities
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -9,7 +9,3 @@
<!-- Invasive change: Discuss alternative designs or approaches you considered. -->
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
# Priorities
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -20,8 +20,7 @@ makefiles = \
misc/fish/local.mk \
misc/zsh/local.mk \
misc/systemd/local.mk \
misc/launchd/local.mk \
misc/upstart/local.mk
misc/launchd/local.mk
endif
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
@ -41,6 +40,7 @@ makefiles += \
tests/functional/ca/local.mk \
tests/functional/dyn-drv/local.mk \
tests/functional/test-libstoreconsumer/local.mk \
tests/functional/repl_characterization/local.mk \
tests/functional/plugins/local.mk
else
makefiles += \
@ -60,7 +60,7 @@ endif
OPTIMIZE = 1
ifeq ($(OPTIMIZE), 1)
GLOBAL_CXXFLAGS += -O3 $(CXXLTO)
GLOBAL_CXXFLAGS += -O2 $(CXXLTO)
GLOBAL_LDFLAGS += $(CXXLTO)
else
GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE

1
clang-tidy/.clang-format Normal file
View file

@ -0,0 +1 @@
BasedOnStyle: llvm

View file

@ -0,0 +1,80 @@
#include "HasPrefixSuffix.hh"
#include <clang/AST/ASTTypeTraits.h>
#include <clang/AST/Expr.h>
#include <clang/AST/PrettyPrinter.h>
#include <clang/AST/Type.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Frontend/FrontendPluginRegistry.h>
#include <clang/Tooling/Transformer/SourceCode.h>
#include <clang/Tooling/Transformer/SourceCodeBuilders.h>
#include <iostream>
namespace nix::clang_tidy {
using namespace clang::ast_matchers;
using namespace clang;
void HasPrefixSuffixCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
Finder->addMatcher(
traverse(clang::TK_AsIs,
callExpr(callee(functionDecl(anyOf(hasName("hasPrefix"),
hasName("hasSuffix")))
.bind("callee-decl")),
optionally(hasArgument(
0, cxxConstructExpr(
hasDeclaration(functionDecl(hasParameter(
0, parmVarDecl(hasType(
asString("const char *")))))))
.bind("implicit-cast"))))
.bind("call")),
this);
}
void HasPrefixSuffixCheck::check(
const ast_matchers::MatchFinder::MatchResult &Result) {
const auto *CalleeDecl = Result.Nodes.getNodeAs<FunctionDecl>("callee-decl");
auto FuncName = std::string(CalleeDecl->getName());
std::string NewName;
if (FuncName == "hasPrefix") {
NewName = "starts_with";
} else if (FuncName == "hasSuffix") {
NewName = "ends_with";
} else {
llvm_unreachable("nix-has-prefix: invalid callee");
}
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("call");
const auto *ImplicitConvertArg =
Result.Nodes.getNodeAs<CXXConstructExpr>("implicit-cast");
const auto *Lhs = MatchedDecl->getArg(0);
const auto *Rhs = MatchedDecl->getArg(1);
auto Diag = diag(MatchedDecl->getExprLoc(), FuncName + " is deprecated");
std::string Text = "";
// Form possible cast to string_view, or nothing.
if (ImplicitConvertArg) {
Text = "std::string_view(";
Text.append(tooling::getText(*Lhs, *Result.Context));
Text.append(").");
} else {
Text.append(*tooling::buildAccess(*Lhs, *Result.Context));
}
// Call .starts_with.
Text.append(NewName);
Text.push_back('(');
Text.append(tooling::getText(*Rhs, *Result.Context));
Text.push_back(')');
Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(), Text);
// for (const auto *arg : MatchedDecl->arguments()) {
// arg->dumpColor();
// arg->getType().dump();
// }
}
}; // namespace nix::clang_tidy

View file

@ -0,0 +1,25 @@
#pragma once
///@file
/// This is an example of a clang-tidy automated refactoring against the Nix
/// codebase. The refactoring has been completed in
/// https://gerrit.lix.systems/c/lix/+/565 so this code is around as
/// an example.
#include <clang-tidy/ClangTidyCheck.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <llvm/ADT/StringRef.h>
namespace nix::clang_tidy {
using namespace clang;
using namespace clang::tidy;
using namespace llvm;
class HasPrefixSuffixCheck : public ClangTidyCheck {
public:
HasPrefixSuffixCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
}; // namespace nix::clang_tidy

View file

@ -0,0 +1,17 @@
#include <clang-tidy/ClangTidyModule.h>
#include <clang-tidy/ClangTidyModuleRegistry.h>
#include "HasPrefixSuffix.hh"
namespace nix::clang_tidy {
using namespace clang;
using namespace clang::tidy;
class NixClangTidyChecks : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<HasPrefixSuffixCheck>("nix-hasprefixsuffix");
}
};
static ClangTidyModuleRegistry::Add<NixClangTidyChecks> X("nix-module", "Adds nix specific checks");
};

56
clang-tidy/README.md Normal file
View file

@ -0,0 +1,56 @@
# Clang tidy lints for Nix
This is a skeleton of a clang-tidy lints library for Nix.
Currently there is one check (which is already obsolete as it has served its
goal and is there as an example), `HasPrefixSuffixCheck`.
## Running fixes/checks
One file:
```
ninja -C build && clang-tidy --checks='-*,nix-*' --load=build/libnix-clang-tidy.so -p ../compile_commands.json --fix ../src/libcmd/installables.cc
```
Several files, in parallel:
```
ninja -C build && run-clang-tidy -checks='-*,nix-*' -load=build/libnix-clang-tidy.so -p .. -fix ../src | tee -a clang-tidy-result
```
## Resources
* https://firefox-source-docs.mozilla.org/code-quality/static-analysis/writing-new/clang-query.html
* https://clang.llvm.org/docs/LibASTMatchersReference.html
* https://devblogs.microsoft.com/cppblog/exploring-clang-tooling-part-3-rewriting-code-with-clang-tidy/
## Developing new checks
Put something like so in `myquery.txt`:
```
set traversal IgnoreUnlessSpelledInSource
# ^ Ignore implicit AST nodes. May need to use AsIs depending on how you are
# working.
set bind-root true
# ^ true unless you use any .bind("foo") commands
set print-matcher true
enable output dump
match callExpr(callee(functionDecl(hasName("hasPrefix"))), optionally(hasArgument( 0, cxxConstructExpr(hasDeclaration(functionDecl(hasParameter(0, parmVarDecl(hasType(asString("const char *"))).bind("meow2"))))))))
```
Then run, e.g. `clang-query --preload hasprefix.query -p compile_commands.json src/libcmd/installables.cc`.
With this you can iterate a query before writing it in C++ and suffering from
C++.
### Tips and tricks for the C++
There is a function `dump()` on many things that will dump to stderr. Also
`llvm::errs()` lets you print to stderr.
When I wrote `HasPrefixSuffixCheck`, I was not really able to figure out how
the structured replacement system was supposed to work. In principle you can
describe the replacement with a nice DSL. Look up the Stencil system in Clang
for details.

8
clang-tidy/meson.build Normal file
View file

@ -0,0 +1,8 @@
project('nix-clang-tidy', ['cpp', 'c'],
version : '0.1',
default_options : ['warning_level=3', 'cpp_std=c++20'])
llvm = dependency('Clang', version: '>= 14', modules: ['libclang'])
sources = ['HasPrefixSuffix.cc', 'NixClangTidyChecks.cc']
shared_module('nix-clang-tidy', sources,
dependencies: llvm)

View file

@ -0,0 +1,22 @@
---
synopsis: Duplicate attribute reports are more accurate
# prs: cl 557
---
Duplicate attribute errors are now more accurate, showing the path at which an error was detected rather than the full, possibly longer, path that caused the error.
Error reports are now
```ShellSession
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
error: attribute 'a.b' already defined at «string»:1:3
at «string»:1:12:
1| { a.b = 1; a.b.c.d = 1;
| ^
```
instead of
```ShellSession
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
error: attribute 'a.b.c.d' already defined at «string»:1:3
at «string»:1:12:
1| { a.b = 1; a.b.c.d = 1;
| ^
```

View file

@ -0,0 +1,7 @@
---
synopsis: consistent order of lambda formals in printed expressions
prs: 9874
---
Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order.
This makes printed formals independent of the context they appear in.

View file

@ -0,0 +1,6 @@
---
synopsis: fix duplicate attribute error positions for `inherit`
prs: 9874
---
When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name.

View file

@ -0,0 +1,11 @@
---
synopsis: "`builtins.nixVersion` now returns a fixed value \"2.18.3-lix\""
# prs: cl 558
---
`builtins.nixVersion` now returns a fixed value `"2.18.3-lix"`. This prevents
feature detection assuming that features that exist in Nix post-Lix-branch-off
might exist, even though the Lix version is greater than the Nix version.
In the future, check for builtins for feature detection. If a feature cannot be
detected by *those* means, please file a Lix bug.

View file

@ -0,0 +1,8 @@
---
synopsis: Upstart scripts removed
# prs: cl 574
---
Upstart scripts have been removed from Lix, since Upstart is obsolete and has
not been shipped by any major distributions for many years. If these are
necessary to your use case, please back port them to your packaging.

131
flake.nix
View file

@ -74,40 +74,6 @@
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
});
installScriptFor = systems:
with nixpkgsFor.x86_64-linux.native;
runCommand "installer-script"
{ buildInputs = [ nix ];
}
''
mkdir -p $out/nix-support
# Converts /nix/store/50p3qk8k...-nix-2.4pre20201102_550e11f/bin/nix to 50p3qk8k.../bin/nix.
tarballPath() {
# Remove the store prefix
local path=''${1#${builtins.storeDir}/}
# Get the path relative to the derivation root
local rest=''${path#*/}
# Get the derivation hash
local drvHash=''${path%%-*}
echo "$drvHash/$rest"
}
substitute ${./scripts/install.in} $out/install \
${pkgs.lib.concatMapStrings
(system: let
tarball = if builtins.elem system crossSystems then self.hydraJobs.binaryTarballCross.x86_64-linux.${system} else self.hydraJobs.binaryTarball.${system};
in '' \
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${tarball}/*.tar.xz) \
--replace '@tarballPath_${system}@' $(tarballPath ${tarball}/*.tar.xz) \
''
)
systems
} --replace '@nixVersion@' ${version}
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
'';
testNixVersions = pkgs: client: daemon: let
nix = pkgs.callPackage ./package.nix {
pname =
@ -146,8 +112,7 @@
binaryTarball = nix: pkgs:
let
inherit (pkgs) buildPackages;
inherit (pkgs) cacert;
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix ]; };
in
buildPackages.runCommand "nix-binary-tarball-${version}"
@ -156,45 +121,7 @@
}
''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
if type -p shellcheck; then
# SC1090: Don't worry about not being able to find
# $nix/etc/profile.d/nix.sh
shellcheck --exclude SC1090 $TMPDIR/install
shellcheck $TMPDIR/create-darwin-volume.sh
shellcheck $TMPDIR/install-darwin-multi-user.sh
shellcheck $TMPDIR/install-systemd-multi-user.sh
# SC1091: Don't panic about not being able to source
# /etc/profile
# SC2002: Ignore "useless cat" "error", when loading
# .reginfo, as the cat is a much cleaner
# implementation, even though it is "useless"
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
# root's home directory
shellcheck --external-sources \
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
fi
chmod +x $TMPDIR/install
chmod +x $TMPDIR/create-darwin-volume.sh
chmod +x $TMPDIR/install-darwin-multi-user.sh
chmod +x $TMPDIR/install-systemd-multi-user.sh
chmod +x $TMPDIR/install-multi-user
dir=nix-${version}-${pkgs.system}
fn=$out/$dir.tar.xz
mkdir -p $out/nix-support
@ -204,15 +131,8 @@
--mtime='1970-01-01' \
--absolute-names \
--hard-dereference \
--transform "s,$TMPDIR/install,$dir/install," \
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
--transform "s,$NIX_STORE,$dir/store,S" \
$TMPDIR/install \
$TMPDIR/create-darwin-volume.sh \
$TMPDIR/install-darwin-multi-user.sh \
$TMPDIR/install-systemd-multi-user.sh \
$TMPDIR/install-multi-user \
$TMPDIR/reginfo \
$(cat ${installerClosureInfo}/store-paths)
'';
@ -284,12 +204,21 @@
# Binary package for various platforms.
build = forAllSystems (system: self.packages.${system}.nix);
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
mesonBuild = forAllSystems (system: self.packages.${system}.nix.override {
buildWithMeson = true;
});
mesonBuildClang = forAllSystems (system:
nixpkgsFor.${system}.stdenvs.clangStdenvPackages.nix.override {
buildWithMeson = true;
}
);
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
# Binary tarball for various platforms, containing a Nix store
# with the closure of 'nix' package, and the second half of
# the installation script.
# with the closure of 'nix' package.
binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native);
# docker image with Nix inside
@ -340,26 +269,14 @@
}
);
};
installTests = forAllSystems (system:
let pkgs = nixpkgsFor.${system}.native; in
pkgs.runCommand "install-tests" {
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
againstCurrentUnstable =
# FIXME: temporarily disable this on macOS because of #3605.
if system == "x86_64-linux"
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
else null;
# Disabled because the latest stable version doesn't handle
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
} "touch $out");
};
checks = forAllSystems (system: {
# FIXME(Qyriad): remove this when the migration to Meson has been completed.
mesonBuild = self.hydraJobs.mesonBuild.${system};
mesonBuildClang = self.hydraJobs.mesonBuildClang.${system};
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
rl-next =
let pkgs = nixpkgsFor.${system}.native;
@ -409,14 +326,27 @@
inherit stdenv versionSuffix fileset;
boehmgc = pkgs.boehmgc-nix;
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
forDevShell = true;
};
in
nix.overrideAttrs (prev: {
# Required for clang-tidy checks
buildInputs = prev.buildInputs ++ lib.optionals (stdenv.cc.isClang) [ pkgs.llvmPackages.llvm pkgs.llvmPackages.clang-unwrapped.dev ];
nativeBuildInputs = prev.nativeBuildInputs
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
# Required for clang-tidy checks
++ lib.optionals (stdenv.cc.isClang) [ pkgs.buildPackages.cmake pkgs.buildPackages.ninja pkgs.buildPackages.llvmPackages.llvm.dev ]
++ lib.optional
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
pkgs.buildPackages.clang-tools;
# for some reason that seems accidental and was changed in
# NixOS 24.05-pre, clang-tools is pinned to LLVM 14 when
# default LLVM is newer.
(pkgs.buildPackages.clang-tools.override { inherit (pkgs.buildPackages) llvmPackages; })
++ [
# FIXME(Qyriad): remove once the migration to Meson is complete.
pkgs.buildPackages.meson
pkgs.buildPackages.ninja
];
src = null;
@ -431,6 +361,9 @@
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
'';
} // lib.optionalAttrs (stdenv.buildPlatform.isLinux && pkgs.glibcLocales != null) {
# Required to make non-NixOS Linux not complain about missing locale files during configure in a dev shell
LOCALE_ARCHIVE = "${lib.getLib pkgs.glibcLocales}/lib/locale/locale-archive";
});
in
forAllSystems (system:

152
maintainers/issue_import.py Normal file
View file

@ -0,0 +1,152 @@
import requests
import textwrap
import dataclasses
import logging
import re
import os
API_BASE = 'https://git.lix.systems/api/v1'
API_KEY = os.environ['FORGEJO_API_KEY']
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
fmt = logging.Formatter('{asctime} {levelname} {name}: {message}',
datefmt='%b %d %H:%M:%S',
style='{')
if not any(isinstance(h, logging.StreamHandler) for h in log.handlers):
hand = logging.StreamHandler()
hand.setFormatter(fmt)
log.addHandler(hand)
# These are erring in the direction of re-triage, rather than necessarily
# mapping all metadata of the issue
LABEL_MAPPING = {
'lix-import': 153, # 'imported',
'contributor-experience': 148, # 'devx',
'bug': 150, # 'bug',
'UX': 149, # 'ux',
'error-messages': 149, # 'ux',
'lix-stability': 146, # 'stability',
'performance': 147, # 'performance',
'tests': 121, # 'tests',
}
def api(method, endpoint: str, resp_json=True, **kwargs):
log.info('http %s %s', method, endpoint)
if not endpoint.startswith('https'):
endpoint = API_BASE + endpoint
resp = requests.request(method,
endpoint,
headers={'Authorization': f'Bearer {API_KEY}'},
**kwargs)
resp.raise_for_status()
if resp_json:
return resp.json()
else:
return resp
def paginate(method: str, url: str):
while True:
resp = api(method, url, resp_json=False)
yield from resp.json()
next_one = resp.links.get('next')
if not next_one:
return
url = next_one.get('url')
if not url:
return
class DataClassUnpack:
"""Taken from: https://stackoverflow.com/a/72164665"""
classFieldCache = {}
@classmethod
def instantiate(cls, classToInstantiate, argDict):
if classToInstantiate not in cls.classFieldCache:
cls.classFieldCache[classToInstantiate] = {
f.name
for f in getattr(classToInstantiate, dataclasses._FIELDS).values() if f._field_type is not dataclasses._FIELD_CLASSVAR # type: ignore
}
fieldSet = cls.classFieldCache[classToInstantiate]
filteredArgDict = {k: v for k, v in argDict.items() if k in fieldSet}
return classToInstantiate(**filteredArgDict)
@dataclasses.dataclass
class Label:
name: str
description: str
@dataclasses.dataclass
class Issue:
number: int
url: str
html_url: str
title: str
body: str
labels: dataclasses.InitVar[list[dict]]
labels_clean: list[Label] = dataclasses.field(init=False)
def __post_init__(self, labels):
self.labels_clean = [DataClassUnpack.instantiate(Label, l) for l in labels]
def issues_to_import():
yield from paginate('GET', '/repos/nixos/nix/issues?state=open&labels=lix-import')
def issues_already_imported():
yield from paginate('GET', '/repos/lix-project/lix/issues?state=all&labels=imported')
UPSTREAM_ISSUE_RE = re.compile(r'^Upstream-Issue: https://git\.lix\.systems/NixOS/nix/issues/(\d+)$', re.MULTILINE)
def make_already_imported():
d = {}
for issue in issues_already_imported():
iss = DataClassUnpack.instantiate(Issue, issue)
print(iss)
match = UPSTREAM_ISSUE_RE.search(iss.body)
if match:
d[int(match.group(1))] = iss
return d
def new_issue(title, body, labels):
api('POST', '/repos/lix-project/lix/issues', resp_json=True, json={
'labels': labels,
'body': body,
'title': title,
})
already_imported = make_already_imported()
def import_issue(iss: Issue):
if iss.number in already_imported:
log.info('Skipping already imported %d', iss.number)
return
new_body = textwrap.dedent('''
Upstream-Issue: {iss}
{original_body}
''').format(iss=iss.html_url, original_body=iss.body)
new_labels = [LABEL_MAPPING[l.name] for l in iss.labels_clean if l.name in LABEL_MAPPING]
new_title = '[Nix#{num}] {title}'.format(num=iss.number, title=iss.title)
log.info('%s', f'create issue with: {new_labels} {new_title} {new_body}')
new_issue(new_title, new_body, new_labels)
def go():
print('Have you turned off the forgejo mailer? Enter "We have" if so:')
answer = input('> ')
if answer != 'We have':
return
log.info('Importing issues!')
for issue in issues_to_import():
import_issue(DataClassUnpack.instantiate(Issue, issue))
if __name__ == '__main__':
go()

287
meson.build Normal file
View file

@ -0,0 +1,287 @@
#
# OUTLINE:
#
# The top-level meson.build file (this file) handles general logic for build options,
# generation of config.h (which is put in the build directory, not the source root
# like the previous, autoconf-based build system did), the mechanism for header
# generation, and the few global C++ compiler arguments that are added to all targets in Lix.
#
# src/meson.build coordinates each of Lix's subcomponents (the lib dirs in ./src),
# which each have their own meson.build. Lix's components depend on each other,
# so each of `src/lib{util,store,fetchers,expr,main,cmd}/meson.build` rely on variables
# set in earlier `meson.build` files. Each of these also defines the install targets for
# their headers.
#
# src/meson.build also collects the miscellaneous source files that are in further subdirectories
# that become part of the final Nix command (things like `src/nix-build/*.cc`).
#
# Finally, src/nix/meson.build defines the Nix command itself, relying on all prior meson files.
project('lix', 'cpp',
version : run_command('bash', '-c', 'echo -n $(cat ./.version)$VERSION_SUFFIX', check : true).stdout().strip(),
default_options : [
'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level
'warning_level=1',
'debug=true',
'optimization=2',
],
)
fs = import('fs')
prefix = get_option('prefix')
# For each of these paths, assume that it is relative to the prefix unless
# it is already an absolute path (which is the default for store-dir, state-dir, and log-dir).
path_opts = [
# Meson built-ins.
'datadir',
'sysconfdir',
'bindir',
'mandir',
'libdir',
'includedir',
# Homecooked Lix directories.
'store-dir',
'state-dir',
'log-dir',
]
foreach optname : path_opts
varname = optname.replace('-', '_')
path = get_option(optname)
if fs.is_absolute(path)
set_variable(varname, path)
else
set_variable(varname, prefix / path)
endif
endforeach
cxx = meson.get_compiler('cpp')
host_system = host_machine.cpu_family() + '-' + host_machine.system()
message('canonical Nix system name:', host_system)
is_linux = host_machine.system() == 'linux'
is_x64 = host_machine.cpu_family() == 'x86_64'
deps = [ ]
configdata = { }
#
# Dependencies
#
boehm = dependency('bdw-gc', required : get_option('gc'))
if boehm.found()
deps += boehm
endif
configdata += {
'HAVE_BOEHMGC': boehm.found().to_int(),
}
boost = dependency('boost', required : true, modules : ['context', 'coroutine', 'container'])
deps += boost
# cpuid only makes sense on x86_64
cpuid_required = is_x64 ? get_option('cpuid') : false
cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required)
configdata += {
'HAVE_LIBCPUID': cpuid.found().to_int(),
}
deps += cpuid
# seccomp only makes sense on Linux
seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false
seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required)
configdata += {
'HAVE_SECCOMP': seccomp.found().to_int(),
}
libarchive = dependency('libarchive', required : true)
deps += libarchive
brotli = [
dependency('libbrotlicommon', required : true),
dependency('libbrotlidec', required : true),
dependency('libbrotlienc', required : true),
]
deps += brotli
openssl = dependency('libcrypto', 'openssl', required : true)
deps += openssl
aws_sdk = dependency('aws-cpp-sdk-core', required : false)
if aws_sdk.found()
# The AWS pkg-config adds -std=c++11.
# https://github.com/aws/aws-sdk-cpp/issues/2673
aws_sdk = aws_sdk.partial_dependency(
compile_args : false,
includes : true,
link_args : true,
links : true,
sources : true,
)
deps += aws_sdk
s = aws_sdk.version().split('.')
configdata += {
'AWS_VERSION_MAJOR': s[0].to_int(),
'AWS_VERSION_MINOR': s[1].to_int(),
'AWS_VERSION_PATCH': s[2].to_int(),
}
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : true).partial_dependency(
compile_args : false,
includes : true,
link_args : true,
links : true,
sources : true,
)
endif
aws_s3 = dependency('aws-cpp-sdk-s3', required : false)
if aws_s3.found()
# The AWS pkg-config adds -std=c++11.
# https://github.com/aws/aws-sdk-cpp/issues/2673
aws_s3 = aws_s3.partial_dependency(
compile_args : false,
includes : true,
link_args : true,
links : true,
sources : true,
)
deps += aws_s3
endif
configdata += {
'ENABLE_S3': aws_s3.found().to_int(),
}
sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19', required : true)
deps += sqlite
sodium = dependency('libsodium', 'sodium', required : true)
deps += sodium
curl = dependency('libcurl', 'curl', required : true)
deps += curl
editline = dependency('libeditline', 'editline', version : '>=1.14', required : true)
deps += editline
lowdown = dependency('lowdown', version : '>=0.9.0', required : true)
deps += lowdown
rapidcheck = dependency('rapidcheck', required : false)
deps += rapidcheck
gtest = dependency('gtest', required : false)
deps += gtest
#
# Build-time tools
#
bash = find_program('bash')
# Used to workaround https://github.com/mesonbuild/meson/issues/2320 in src/nix/meson.build.
installcmd = find_program('install')
sandbox_shell = get_option('sandbox-shell')
# Consider it required if we're on Linux and the user explicitly specified a non-default value.
sandbox_shell_required = sandbox_shell != 'busybox' and host_machine.system() == 'linux'
# NOTE(Qyriad): package.nix puts busybox in buildInputs for Linux.
# Most builds should not require setting this.
busybox = find_program(sandbox_shell, required : sandbox_shell_required, native : false)
if not busybox.found() and host_machine.system() == 'linux' and sandbox_shell_required
warning('busybox not found and other sandbox shell was specified')
warning('a sandbox shell is recommended on Linux -- configure with -Dsandbox-shell=/path/to/shell to set')
endif
# FIXME(Qyriad): the autoconf system checks that busybox has the "standalone" feature, indicating
# that busybox sh won't run busybox applets as builtins (which would break our sandbox).
lsof = find_program('lsof')
bison = find_program('bison')
flex = find_program('flex')
# This is how Nix does generated headers...
# FIXME(Qyriad): do we really need to use the shell for this?
gen_header = generator(
bash,
arguments : [
'-c',
'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'',
],
capture : true,
output : '@PLAINNAME@.gen.hh',
)
#
# Configuration
#
run_command('ln', '-s',
meson.project_build_root() / '__nothing_link_target',
meson.project_build_root() / '__nothing_symlink',
check : true,
)
can_link_symlink = run_command('ln',
meson.project_build_root() / '__nothing_symlink',
meson.project_build_root() / '__nothing_hardlink',
check : false,
).returncode() == 0
run_command('rm', '-f',
meson.project_build_root() / '__nothing_symlink',
meson.project_build_root() / '__nothing_hardlink',
check : true,
)
summary('can hardlink to symlink', can_link_symlink, bool_yn : true)
configdata += { 'CAN_LINK_SYMLINK': can_link_symlink.to_int() }
# Check for each of these functions, and create a define like `#define HAVE_LCHOWN 1`.
check_funcs = [
'lchown',
'lutimes',
'pipe2',
'posix_fallocate',
'statvfs',
'strsignal',
'sysconf',
]
foreach funcspec : check_funcs
define_name = 'HAVE_' + funcspec.underscorify().to_upper()
define_value = cxx.has_function(funcspec).to_int()
configdata += {
define_name: define_value,
}
endforeach
config_h = configure_file(
configuration : {
'PACKAGE_NAME': '"' + meson.project_name() + '"',
'PACKAGE_VERSION': '"' + meson.project_version() + '"',
'PACKAGE_TARNAME': '"' + meson.project_name() + '"',
'PACKAGE_STRING': '"' + meson.project_name() + ' ' + meson.project_version() + '"',
'HAVE_STRUCT_DIRENT_D_TYPE': 1, # FIXME: actually check this for solaris
'SYSTEM': '"' + host_system + '"',
} + configdata,
output : 'config.h',
)
install_headers(config_h, subdir : 'nix')
add_project_arguments(
# TODO(Qyriad): Yes this is how the autoconf+Make system did it.
# It would be nice for our headers to be idempotent instead.
'-include', 'config.h',
'-Wno-deprecated-declarations',
'-Wimplicit-fallthrough',
'-Werror=switch',
'-Werror=switch-enum',
language : 'cpp',
)
add_project_link_arguments('-pthread', language : 'cpp')
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
endif
subdir('src')

32
meson.options Normal file
View file

@ -0,0 +1,32 @@
# vim: filetype=meson
option('gc', type : 'feature',
description : 'enable garbage collection in the Nix expression evaluator (requires Boehm GC)',
)
# TODO(Qyriad): is this feature maintained?
option('embedded-sandbox-shell', type : 'feature',
description : 'include the sandbox shell in the Nix binary',
)
option('cpuid', type : 'feature',
description : 'determine microarchitecture levels with libcpuid (only relevant on x86_64)',
)
option('seccomp-sandboxing', type : 'feature',
description : 'build support for seccomp sandboxing (recommended unless your arch doesn\'t support libseccomp, only relevant on Linux)',
)
option('sandbox-shell', type : 'string', value : 'busybox',
description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)',
)
option('store-dir', type : 'string', value : '/nix/store',
description : 'path of the Nix store',
)
option('state-dir', type : 'string', value : '/nix/var/nix',
description : 'path to store state in for Nix',
)
option('log-dir', type : 'string', value : '/nix/var/log',
description : 'path to store logs in for Nix',
)

50
meson/cleanup-install.bash Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Meson will call this with an absolute path to Bash.
# The shebang is just for convenience.
# The parser and lexer tab are generated via custom Meson targets in src/libexpr/meson.build,
# but Meson doesn't support marking only part of a target for install. The generation creates
# both headers (parser-tab.hh, lexer-tab.hh) and source files (parser-tab.cc, lexer-tab.cc),
# and we definitely want the former installed, but not the latter. This script is added to
# Meson's install steps to correct this, as the logic for it is just complex enough to
# warrant separate and careful handling, because both Meson's configured include directory
# may or may not be an absolute path, and DESTDIR may or may not be set at all, but can't be
# manipulated in Meson logic.
set -euo pipefail
echo "cleanup-install: removing Meson-placed C++ sources from dest includedir"
if [[ "${1/--help/}" != "$1" ]]; then
echo "cleanup-install: this script should only be called from the Meson build system"
exit 1
fi
# Ensure the includedir was passed as the first argument
# (set -u will make this fail otherwise).
includedir="$1"
# And then ensure that first argument is a directory that exists.
if ! [[ -d "$1" ]]; then
echo "cleanup-install: this script should only be called from the Meson build system"
echo "argv[1] (${1@Q}) is not a directory"
exit 2
fi
# If DESTDIR environment variable is set, prepend it to the include dir.
# Unfortunately, we cannot do this on the Meson side. We do have an environment variable
# `MESON_INSTALL_DESTDIR_PREFIX`, but that will not refer to the include directory if
# includedir has been set separately, which Lix's split-output derivation does.
# We also cannot simply do an inline bash conditional like "${DESTDIR:=}" or similar,
# because we need to specifically *join* DESTDIR and includedir with a slash, and *not*
# have a slash if DESTDIR isn't set at all, since $includedir could be a relative directory.
# Finally, DESTDIR is only available to us as an environment variable in these install scripts,
# not in Meson logic.
# Therefore, our best option is to have Meson pass this script the configured includedir,
# and perform this dance with it and $DESTDIR.
if [[ -n "${DESTDIR:-}" ]]; then
includedir="$DESTDIR/$includedir"
fi
# Intentionally not using -f.
# If these files don't exist then our assumptions have been violated and we should fail.
rm -v "$includedir/nix/parser-tab.cc" "$includedir/nix/lexer-tab.cc"

View file

@ -1,7 +0,0 @@
ifdef HOST_LINUX
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
clean-files += $(d)/nix-daemon.conf
endif

View file

@ -1,5 +0,0 @@
description "Nix Daemon"
start on filesystem
stop on shutdown
respawn
exec @bindir@/nix-daemon --daemon

View file

@ -1,5 +1,5 @@
%.gen.hh: %
@echo 'R"foo(' >> $@.tmp
@echo 'R"__NIX_STR(' >> $@.tmp
$(trace-gen) cat $< >> $@.tmp
@echo ')foo"' >> $@.tmp
@echo ')__NIX_STR"' >> $@.tmp
@mv $@.tmp $@

View file

@ -24,10 +24,13 @@
libcpuid,
libseccomp,
libsodium,
lsof,
lowdown,
mdbook,
mdbook-linkcheck,
mercurial,
meson,
ninja,
openssl,
pkg-config,
rapidcheck,
@ -44,6 +47,12 @@
# Set to true to build the release notes for the next release.
buildUnreleasedNotes ? false,
internalApiDocs ? false,
# Avoid setting things that would interfere with a functioning devShell
forDevShell ? false,
# FIXME(Qyriad): build Lix using Meson instead of autoconf and make.
# This flag will be removed when the migration to Meson is complete.
buildWithMeson ? false,
# Not a real argument, just the only way to approximate let-binding some
# stuff for argument defaults.
@ -84,12 +93,16 @@
./README.md
];
topLevelBuildFiles = fileset.unions [
topLevelBuildFiles = fileset.unions ([
./local.mk
./Makefile
./Makefile.config.in
./mk
];
] ++ lib.optionals buildWithMeson [
./meson.build
./meson.options
./meson/cleanup-install.bash
]);
functionalTestFiles = fileset.unions [
./tests/functional
@ -124,6 +137,11 @@ in stdenv.mkDerivation (finalAttrs: {
dontBuild = false;
# FIXME(Qyriad): see if this is still needed once the migration to Meson is completed.
mesonFlags = lib.optionals (buildWithMeson && stdenv.hostPlatform.isLinux) [
"-Dsandbox-shell=${lib.getBin busybox-sandbox-shell}/bin/busybox"
];
nativeBuildInputs = [
bison
flex
@ -132,17 +150,21 @@ in stdenv.mkDerivation (finalAttrs: {
mdbook
mdbook-linkcheck
autoconf-archive
autoreconfHook
] ++ lib.optional (!buildWithMeson) autoreconfHook ++ [
pkg-config
# Tests
git
mercurial
jq
lsof
] ++ lib.optional stdenv.hostPlatform.isLinux util-linuxMinimal
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
++ lib.optional internalApiDocs doxygen
;
++ lib.optionals buildWithMeson [
meson
ninja
];
buildInputs = [
curl
@ -157,7 +179,7 @@ in stdenv.mkDerivation (finalAttrs: {
lowdown
libsodium
]
++ lib.optionals stdenv.isLinux [ libseccomp ]
++ lib.optionals stdenv.hostPlatform.isLinux [ libseccomp busybox-sandbox-shell ]
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) aws-sdk-cpp-nix
@ -175,6 +197,13 @@ in stdenv.mkDerivation (finalAttrs: {
boost
];
# Needed for Meson to find Boost.
# https://github.com/NixOS/nixpkgs/issues/86131.
env = lib.optionalAttrs (buildWithMeson || forDevShell) {
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
};
preConfigure = lib.optionalString (!finalAttrs.dontBuild && !stdenv.hostPlatform.isStatic) ''
# Copy libboost_context so we don't get all of Boost in our closure.
# https://github.com/NixOS/nixpkgs/issues/45462
@ -205,12 +234,14 @@ in stdenv.mkDerivation (finalAttrs: {
"--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
] ++ lib.optionals (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) [
"LDFLAGS=-fuse-ld=gold"
] ++ [ "--sysconfdir=/etc" ]
]
++ lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell"
++ lib.optionals (finalAttrs.doCheck || internalApiDocs) testConfigureFlags
++ lib.optional (!canRunInstalled) "--disable-doc-gen"
++ [ (lib.enableFeature internalApiDocs "internal-api-docs") ]
;
++ lib.optional (!forDevShell) "--sysconfdir=/etc";
mesonBuildType = lib.optional (buildWithMeson || forDevShell) "debugoptimized";
installTargets = lib.optional internalApiDocs "internal-api-html";
@ -229,10 +260,12 @@ in stdenv.mkDerivation (finalAttrs: {
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
'' + lib.optionalString stdenv.isDarwin ''
install_name_tool \
-change ${boost}/lib/libboost_context.dylib \
$out/lib/libboost_context.dylib \
$out/lib/libnixutil.dylib
for lib in libnixutil.dylib libnixexpr.dylib; do
install_name_tool \
-change "${lib.getLib boost}/lib/libboost_context.dylib" \
"$out/lib/libboost_context.dylib" \
"$out/lib/$lib"
done
'' + lib.optionalString internalApiDocs ''
mkdir -p $out/nix-support
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> "$out/nix-support/hydra-build-products"

View file

@ -1,46 +0,0 @@
#!/usr/bin/env bash
((NEW_NIX_FIRST_BUILD_UID=301))
id_available(){
dscl . list /Users UniqueID | grep -E '\b'$1'\b' >/dev/null
}
change_nixbld_names_and_ids(){
local name uid next_id
((next_id=NEW_NIX_FIRST_BUILD_UID))
echo "Attempting to migrate nixbld users."
echo "Each user should change from nixbld# to _nixbld#"
echo "and their IDs relocated to $next_id+"
while read -r name uid; do
echo " Checking $name (uid: $uid)"
# iterate for a clean ID
while id_available "$next_id"; do
((next_id++))
if ((next_id >= 400)); then
echo "We've hit UID 400 without placing all of your users :("
echo "You should use the commands in this script as a starting"
echo "point to review your UID-space and manually move the"
echo "remaining users (or delete them, if you don't need them)."
exit 1
fi
done
if [[ $name == _* ]]; then
echo " It looks like $name has already been renamed--skipping."
else
# first 3 are cleanup, it's OK if they aren't here
sudo dscl . delete /Users/$name dsAttrTypeNative:_writers_passwd &>/dev/null || true
sudo dscl . change /Users/$name NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true
# remove existing user from group
sudo dseditgroup -o edit -t user -d $name nixbld || true
sudo dscl . change /Users/$name UniqueID $uid $next_id
sudo dscl . change /Users/$name RecordName $name _$name
# add renamed user to group
sudo dseditgroup -o edit -t user -a _$name nixbld
echo " $name migrated to _$name (uid: $next_id)"
fi
done < <(dscl . list /Users UniqueID | grep nixbld | sort -n -k2)
}
change_nixbld_names_and_ids

View file

@ -1,33 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# set -x
# mapfile BUILDS_FOR_LATEST_EVAL < <(
# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
# jq -r '.evals[0].builds[] | @sh')
BUILDS_FOR_LATEST_EVAL=$(
curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
jq -r '.evals[0].builds[]')
someBuildFailed=0
for buildId in $BUILDS_FOR_LATEST_EVAL; do
buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
finished=$(echo "$buildInfo" | jq -r '.finished')
if [[ $finished = 0 ]]; then
continue
fi
buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
if [[ $buildStatus != 0 ]]; then
someBuildFailed=1
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
fi
done
exit "$someBuildFailed"

View file

@ -1,875 +0,0 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
# I'm a little agnostic on the choices, but supporting a wide
# slate of uses for now, including:
# - import-only: `. create-darwin-volume.sh no-main[ ...]`
# - legacy: `./create-darwin-volume.sh` or `. create-darwin-volume.sh`
# (both will run main())
# - external alt-routine: `./create-darwin-volume.sh no-main func[ ...]`
if [ "${1-}" = "no-main" ]; then
shift
readonly _CREATE_VOLUME_NO_MAIN=1
else
readonly _CREATE_VOLUME_NO_MAIN=0
# declare some things we expect to inherit from install-multi-user
# I don't love this (because it's a bit of a kludge).
#
# CAUTION: (Dec 19 2020)
# This is a stopgap. It doesn't cover the full slate of
# identifiers we inherit--just those necessary to:
# - avoid breaking direct invocations of this script (here/now)
# - avoid hard-to-reverse structural changes before the call to rm
# single-user support is verified
#
# In the near-mid term, I (personally) think we should:
# - decide to deprecate the direct call and add a notice
# - fold all of this into install-darwin-multi-user.sh
# - intentionally remove the old direct-invocation form (kill the
# routine, replace this script w/ deprecation notice and a note
# on the remove-after date)
#
readonly NIX_ROOT="${NIX_ROOT:-/nix}"
_sudo() {
shift # throw away the 'explanation'
/usr/bin/sudo "$@"
}
failure() {
if [ "$*" = "" ]; then
cat
else
echo "$@"
fi
exit 1
}
task() {
echo "$@"
}
fi
# usually "disk1"
root_disk_identifier() {
# For performance (~10ms vs 280ms) I'm parsing 'diskX' from stat output
# (~diskXsY)--but I'm retaining the more-semantic approach since
# it documents intent better.
# /usr/sbin/diskutil info -plist / | xmllint --xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" -
#
local special_device
special_device="$(/usr/bin/stat -f "%Sd" /)"
echo "${special_device%s[0-9]*}"
}
# make it easy to play w/ 'Case-sensitive APFS'
readonly NIX_VOLUME_FS="${NIX_VOLUME_FS:-APFS}"
readonly NIX_VOLUME_LABEL="${NIX_VOLUME_LABEL:-Nix Store}"
# Strongly assuming we'll make a volume on the device / is on
# But you can override NIX_VOLUME_USE_DISK to create it on some other device
readonly NIX_VOLUME_USE_DISK="${NIX_VOLUME_USE_DISK:-$(root_disk_identifier)}"
NIX_VOLUME_USE_SPECIAL="${NIX_VOLUME_USE_SPECIAL:-}"
NIX_VOLUME_USE_UUID="${NIX_VOLUME_USE_UUID:-}"
readonly NIX_VOLUME_MOUNTD_DEST="${NIX_VOLUME_MOUNTD_DEST:-/Library/LaunchDaemons/org.nixos.darwin-store.plist}"
if /usr/bin/fdesetup isactive >/dev/null; then
test_filevault_in_use() { return 0; }
# no readonly; we may modify if user refuses from cure_volume
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-1}"
else
test_filevault_in_use() { return 1; }
NIX_VOLUME_DO_ENCRYPT="${NIX_VOLUME_DO_ENCRYPT:-0}"
fi
should_encrypt_volume() {
test_filevault_in_use && (( NIX_VOLUME_DO_ENCRYPT == 1 ))
}
substep() {
printf " %s\n" "" "- $1" "" "${@:2}"
}
volumes_labeled() {
local label="$1"
xsltproc --novalid --stringparam label "$label" - <(/usr/sbin/ioreg -ra -c "AppleAPFSVolume") <<'EOF'
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="/plist/array/dict/key[text()='IORegistryEntryName']/following-sibling::*[1][text()=$label]/.."/>
</xsl:template>
<xsl:template match="dict">
<xsl:apply-templates match="string" select="key[text()='BSD Name']/following-sibling::*[1]"/>
<xsl:text>=</xsl:text>
<xsl:apply-templates match="string" select="key[text()='UUID']/following-sibling::*[1]"/>
<xsl:text>&#xA;</xsl:text>
</xsl:template>
</xsl:stylesheet>
EOF
# I cut label out of the extracted values, but here it is for reference:
# <xsl:apply-templates match="string" select="key[text()='IORegistryEntryName']/following-sibling::*[1]"/>
# <xsl:text>=</xsl:text>
}
right_disk() {
local volume_special="$1" # (i.e., disk1s7)
[[ "$volume_special" == "$NIX_VOLUME_USE_DISK"s* ]]
}
right_volume() {
local volume_special="$1" # (i.e., disk1s7)
# if set, it must match; otherwise ensure it's on the right disk
if [ -z "$NIX_VOLUME_USE_SPECIAL" ]; then
if right_disk "$volume_special"; then
NIX_VOLUME_USE_SPECIAL="$volume_special" # latch on
return 0
else
return 1
fi
else
[ "$volume_special" = "$NIX_VOLUME_USE_SPECIAL" ]
fi
}
right_uuid() {
local volume_uuid="$1"
# if set, it must match; otherwise allow
if [ -z "$NIX_VOLUME_USE_UUID" ]; then
NIX_VOLUME_USE_UUID="$volume_uuid" # latch on
return 0
else
[ "$volume_uuid" = "$NIX_VOLUME_USE_UUID" ]
fi
}
cure_volumes() {
local found volume special uuid
# loop just in case they have more than one volume
# (nothing stops you from doing this)
for volume in $(volumes_labeled "$NIX_VOLUME_LABEL"); do
# CAUTION: this could (maybe) be a more normal read
# loop like:
# while IFS== read -r special uuid; do
# # ...
# done <<<"$(volumes_labeled "$NIX_VOLUME_LABEL")"
#
# I did it with for to skirt a problem with the obvious
# pattern replacing stdin and causing user prompts
# inside (which also use read and access stdin) to skip
#
# If there's an existing encrypted volume we can't find
# in keychain, the user never gets prompted to delete
# the volume, and the install fails.
#
# If you change this, a human needs to test a very
# specific scenario: you already have an encrypted
# Nix Store volume, and have deleted its credential
# from keychain. Ensure the script asks you if it can
# delete the volume, and then prompts for your sudo
# password to confirm.
#
# shellcheck disable=SC1097
IFS== read -r special uuid <<< "$volume"
# take the first one that's on the right disk
if [ -z "${found:-}" ]; then
if right_volume "$special" && right_uuid "$uuid"; then
cure_volume "$special" "$uuid"
found="${special} (${uuid})"
else
warning <<EOF
Ignoring ${special} (${uuid}) because I am looking for:
disk=${NIX_VOLUME_USE_DISK} special=${NIX_VOLUME_USE_SPECIAL:-${NIX_VOLUME_USE_DISK}sX} uuid=${NIX_VOLUME_USE_UUID:-any}
EOF
# TODO: give chance to delete if ! headless?
fi
else
warning <<EOF
Ignoring ${special} (${uuid}), already found target: $found
EOF
# TODO reminder? I feel like I want one
# idiom that reminds some warnings, or warns
# some reminders?
# TODO: if ! headless, chance to delete?
fi
done
if [ -z "${found:-}" ]; then
readonly NIX_VOLUME_USE_SPECIAL NIX_VOLUME_USE_UUID
fi
}
volume_encrypted() {
local volume_special="$1" # (i.e., disk1s7)
# Trying to match the first line of output; known first lines:
# No cryptographic users for <special>
# Cryptographic user for <special> (1 found)
# Cryptographic users for <special> (2 found)
/usr/sbin/diskutil apfs listCryptoUsers -plist "$volume_special" | /usr/bin/grep -q APFSCryptoUserUUID
}
test_fstab() {
/usr/bin/grep -q "$NIX_ROOT apfs rw" /etc/fstab 2>/dev/null
}
test_nix_root_is_symlink() {
[ -L "$NIX_ROOT" ]
}
test_synthetic_conf_either(){
/usr/bin/grep -qE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf 2>/dev/null
}
test_synthetic_conf_mountable() {
/usr/bin/grep -q "^${NIX_ROOT:1}$" /etc/synthetic.conf 2>/dev/null
}
test_synthetic_conf_symlinked() {
/usr/bin/grep -qE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf 2>/dev/null
}
test_nix_volume_mountd_installed() {
test -e "$NIX_VOLUME_MOUNTD_DEST"
}
# current volume password
test_keychain_by_uuid() {
local volume_uuid="$1"
# Note: doesn't need sudo just to check; doesn't output pw
security find-generic-password -s "$volume_uuid" &>/dev/null
}
get_volume_pass() {
local volume_uuid="$1"
_sudo \
"to confirm keychain has a password that unlocks this volume" \
security find-generic-password -s "$volume_uuid" -w
}
verify_volume_pass() {
local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2"
_sudo "to confirm the password actually unlocks the volume" \
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
}
volume_pass_works() {
local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2"
get_volume_pass "$volume_uuid" | verify_volume_pass "$volume_special" "$volume_uuid"
}
# Create the paths defined in synthetic.conf, saving us a reboot.
create_synthetic_objects() {
# Big Sur takes away the -B flag we were using and replaces it
# with a -t flag that appears to do the same thing (but they
# don't behave exactly the same way in terms of return values).
# This feels a little dirty, but as far as I can tell the
# simplest way to get the right one is to just throw away stderr
# and call both... :]
{
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true # Big Sur
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true # Catalina
} >/dev/null 2>&1
}
test_nix() {
test -d "$NIX_ROOT"
}
test_voldaemon() {
test -f "$NIX_VOLUME_MOUNTD_DEST"
}
generate_mount_command() {
local cmd_type="$1" # encrypted|unencrypted
local volume_uuid mountpoint cmd=()
printf -v volume_uuid "%q" "$2"
printf -v mountpoint "%q" "$NIX_ROOT"
case "$cmd_type" in
encrypted)
cmd=(/bin/sh -c "/usr/bin/security find-generic-password -s '$volume_uuid' -w | /usr/sbin/diskutil apfs unlockVolume '$volume_uuid' -mountpoint '$mountpoint' -stdinpassphrase");;
unencrypted)
cmd=(/usr/sbin/diskutil mount -mountPoint "$mountpoint" "$volume_uuid");;
*)
failure "Invalid first arg $cmd_type to generate_mount_command";;
esac
printf " <string>%s</string>\n" "${cmd[@]}"
}
generate_mount_daemon() {
local cmd_type="$1" # encrypted|unencrypted
local volume_uuid="$2"
cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>Label</key>
<string>org.nixos.darwin-store</string>
<key>ProgramArguments</key>
<array>
$(generate_mount_command "$cmd_type" "$volume_uuid")
</array>
</dict>
</plist>
EOF
}
_eat_bootout_err() {
/usr/bin/grep -v "Boot-out failed: 36: Operation now in progress"
}
# TODO: remove with --uninstall?
uninstall_launch_daemon_directions() {
local daemon_label="$1" # i.e., org.nixos.blah-blah
local daemon_plist="$2" # abspath
substep "Uninstall LaunchDaemon $daemon_label" \
" sudo launchctl bootout system/$daemon_label" \
" sudo rm $daemon_plist"
}
uninstall_launch_daemon_prompt() {
local daemon_label="$1" # i.e., org.nixos.blah-blah
local daemon_plist="$2" # abspath
local reason_for_daemon="$3"
cat <<EOF
The installer adds a LaunchDaemon to $reason_for_daemon: $daemon_label
EOF
if ui_confirm "Can I remove it?"; then
_sudo "to terminate the daemon" \
launchctl bootout "system/$daemon_label" 2> >(_eat_bootout_err >&2) || true
# this can "fail" with a message like:
# Boot-out failed: 36: Operation now in progress
_sudo "to remove the daemon definition" rm "$daemon_plist"
fi
}
nix_volume_mountd_uninstall_directions() {
uninstall_launch_daemon_directions "org.nixos.darwin-store" \
"$NIX_VOLUME_MOUNTD_DEST"
}
nix_volume_mountd_uninstall_prompt() {
uninstall_launch_daemon_prompt "org.nixos.darwin-store" \
"$NIX_VOLUME_MOUNTD_DEST" \
"mount your Nix volume"
}
# TODO: move nix_daemon to install-darwin-multi-user if/when uninstall_launch_daemon_prompt moves up to install-multi-user
nix_daemon_uninstall_prompt() {
uninstall_launch_daemon_prompt "org.nixos.nix-daemon" \
"$NIX_DAEMON_DEST" \
"run the nix-daemon"
}
# TODO: remove with --uninstall?
nix_daemon_uninstall_directions() {
uninstall_launch_daemon_directions "org.nixos.nix-daemon" \
"$NIX_DAEMON_DEST"
}
# TODO: remove with --uninstall?
synthetic_conf_uninstall_directions() {
# :1 to strip leading slash
substep "Remove ${NIX_ROOT:1} from /etc/synthetic.conf" \
" If nix is the only entry: sudo rm /etc/synthetic.conf" \
" Otherwise: sudo /usr/bin/sed -i '' -e '/^${NIX_ROOT:1}$/d' /etc/synthetic.conf"
}
synthetic_conf_uninstall_prompt() {
cat <<EOF
During install, I add '${NIX_ROOT:1}' to /etc/synthetic.conf, which instructs
macOS to create an empty root directory for mounting the Nix volume.
EOF
# make the edit to a copy
/usr/bin/grep -vE "^${NIX_ROOT:1}($|\t.{3,}$)" /etc/synthetic.conf > "$SCRATCH/synthetic.conf.edit"
if test_synthetic_conf_symlinked; then
warning <<EOF
/etc/synthetic.conf already contains a line instructing your system
to make '${NIX_ROOT}' as a symlink:
$(/usr/bin/grep -nE "^${NIX_ROOT:1}\t.{3,}$" /etc/synthetic.conf)
This may mean your system has/had a non-standard Nix install.
The volume-creation process in this installer is *not* compatible
with a symlinked store, so I'll have to remove this instruction to
continue.
If you want/need to keep this instruction, answer 'n' to abort.
EOF
fi
# ask to rm if this left the file empty aside from comments, else edit
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/synthetic.conf.edit") &>/dev/null; then
if confirm_rm "/etc/synthetic.conf"; then
if test_nix_root_is_symlink; then
failure >&2 <<EOF
I removed /etc/synthetic.conf, but $NIX_ROOT is already a symlink
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
Once you've rebooted, run the installer again.
EOF
fi
return 0
fi
else
if confirm_edit "$SCRATCH/synthetic.conf.edit" "/etc/synthetic.conf"; then
if test_nix_root_is_symlink; then
failure >&2 <<EOF
I edited Nix out of /etc/synthetic.conf, but $NIX_ROOT is already a symlink
(-> $(readlink "$NIX_ROOT")). The system should remove it when you reboot.
Once you've rebooted, run the installer again.
EOF
fi
return 0
fi
fi
# fallback instructions
echo "Manually remove nix from /etc/synthetic.conf"
return 1
}
add_nix_vol_fstab_line() {
local uuid="$1"
# shellcheck disable=SC1003,SC2026
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
shift
# wrap `ex` to work around problems w/ vim features breaking exit codes
# - plugins (see github.com/NixOS/nix/issues/5468): -u NONE
# - swap file: -n
#
# the first draft used `--noplugin`, but github.com/NixOS/nix/issues/6462
# suggests we need the less-semantic `-u NONE`
#
# we'd prefer EDITOR="/usr/bin/ex -u NONE" but vifs doesn't word-split
# the EDITOR env.
#
# TODO: at some point we should switch to `--clean`, but it wasn't added
# until https://github.com/vim/vim/releases/tag/v8.0.1554 while the macOS
# minver 10.12.6 seems to have released with vim 7.4
cat > "$SCRATCH/ex_cleanroom_wrapper" <<EOF
#!/bin/sh
/usr/bin/ex -u NONE -n "\$@"
EOF
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
:a
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
.
:x
EOF
# TODO: preserving my notes on suid,owners above until resolved
# There *may* be some issue regarding volume ownership, see nix#3156
#
# It seems like the cheapest fix is adding "suid,owners" to fstab, but:
# - We don't have much info on this condition yet
# - I'm not certain if these cause other problems?
# - There's a "chown" component some people claim to need to fix this
# that I don't understand yet
# (Note however that I've had to add a chown step to handle
# single->multi-user reinstalls, which may cover this)
#
# I'm not sure if it's safe to approach this way?
#
# I think I think the most-proper way to test for it is:
# diskutil info -plist "$NIX_VOLUME_LABEL" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1][name()='true']" -; echo $?
#
# There's also `sudo /usr/sbin/vsdbutil -c /path` (which is much faster, but is also
# deprecated and needs minor parsing).
#
# If no one finds a problem with doing so, I think the simplest approach
# is to just eagerly set this. I found a few imperative approaches:
# (diskutil enableOwnership, ~100ms), a cheap one (/usr/sbin/vsdbutil -a, ~40-50ms),
# a very cheap one (append the internal format to /var/db/volinfo.database).
#
# But vsdbutil's deprecation notice suggests using fstab, so I want to
# give that a whirl first.
#
# TODO: when this is workable, poke infinisil about reproducing the issue
# and confirming this fix?
}
delete_nix_vol_fstab_line() {
# TODO: I'm scaffolding this to handle the new nix volumes
# but it might be nice to generalize a smidge further to
# go ahead and set up a pattern for curing "old" things
# we no longer do?
EDITOR="/usr/bin/patch" _sudo "to cut nix from fstab" "$@" < <(/usr/bin/diff /etc/fstab <(/usr/bin/grep -v "$NIX_ROOT apfs rw" /etc/fstab))
# leaving some parts out of the grep; people may fiddle this a little?
}
# TODO: hope to remove with --uninstall
fstab_uninstall_directions() {
substep "Remove ${NIX_ROOT} from /etc/fstab" \
" If nix is the only entry: sudo rm /etc/fstab" \
" Otherwise, run 'sudo /usr/sbin/vifs' to remove the nix line"
}
fstab_uninstall_prompt() {
cat <<EOF
During install, I add '${NIX_ROOT}' to /etc/fstab so that macOS knows what
mount options to use for the Nix volume.
EOF
cp /etc/fstab "$SCRATCH/fstab.edit"
# technically doesn't need the _sudo path, but throwing away the
# output is probably better than mostly-duplicating the code...
delete_nix_vol_fstab_line patch "$SCRATCH/fstab.edit" &>/dev/null
# if the patch test edit, minus comment lines, is equal to empty (:)
if /usr/bin/diff -q <(:) <(/usr/bin/grep -v "^#" "$SCRATCH/fstab.edit") &>/dev/null; then
# this edit would leave it empty; propose deleting it
if confirm_rm "/etc/fstab"; then
return 0
else
echo "Remove nix from /etc/fstab (or remove the file)"
fi
else
echo "I might be able to help you make this edit. Here's the diff:"
if ! _diff "/etc/fstab" "$SCRATCH/fstab.edit" && ui_confirm "Does the change above look right?"; then
delete_nix_vol_fstab_line /usr/sbin/vifs
else
echo "Remove nix from /etc/fstab (or remove the file)"
fi
fi
}
remove_volume() {
local volume_special="$1" # (i.e., disk1s7)
_sudo "to unmount the Nix volume" \
/usr/sbin/diskutil unmount force "$volume_special" || true # might not be mounted
_sudo "to delete the Nix volume" \
/usr/sbin/diskutil apfs deleteVolume "$volume_special"
}
# aspiration: robust enough to both fix problems
# *and* update older darwin volumes
cure_volume() {
local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2"
header "Found existing Nix volume"
row " special" "$volume_special"
row " uuid" "$volume_uuid"
if volume_encrypted "$volume_special"; then
row "encrypted" "yes"
if volume_pass_works "$volume_special" "$volume_uuid"; then
NIX_VOLUME_DO_ENCRYPT=0
ok "Found a working decryption password in keychain :)"
echo ""
else
# - this is a volume we made, and
# - the user encrypted it on their own
# - something deleted the credential
# - this is an old or BYO volume and the pw
# just isn't somewhere we can find it.
#
# We're going to explain why we're freaking out
# and prompt them to either delete the volume
# (requiring a sudo auth), or abort to fix
warning <<EOF
This volume is encrypted, but I don't see a password to decrypt it.
The quick fix is to let me delete this volume and make you a new one.
If that's okay, enter your (sudo) password to continue. If not, you
can ensure the decryption password is in your system keychain with a
"Where" (service) field set to this volume's UUID:
$volume_uuid
EOF
if password_confirm "delete this volume"; then
remove_volume "$volume_special"
else
# TODO: this is a good design case for a warn-and
# remind idiom...
failure <<EOF
Your Nix volume is encrypted, but I couldn't find its password. Either:
- Delete or rename the volume out of the way
- Ensure its decryption password is in the system keychain with a
"Where" (service) field set to this volume's UUID:
$volume_uuid
EOF
fi
fi
elif test_filevault_in_use; then
row "encrypted" "no"
warning <<EOF
FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted.
EOF
# if we're interactive, give them a chance to
# encrypt the volume. If not, /shrug
if ! headless && (( NIX_VOLUME_DO_ENCRYPT == 1 )); then
if ui_confirm "Should I encrypt it and add the decryption key to your keychain?"; then
encrypt_volume "$volume_uuid" "$NIX_VOLUME_LABEL"
NIX_VOLUME_DO_ENCRYPT=0
else
NIX_VOLUME_DO_ENCRYPT=0
reminder "FileVault is on, but your $NIX_VOLUME_LABEL volume isn't encrypted."
fi
fi
else
row "encrypted" "no"
fi
}
remove_volume_artifacts() {
if test_synthetic_conf_either; then
# NIX_ROOT is in synthetic.conf
if synthetic_conf_uninstall_prompt; then
# TODO: moot until we tackle uninstall, but when we're
# actually uninstalling, we should issue:
# reminder "macOS will clean up the empty mount-point directory at $NIX_ROOT on reboot."
:
fi
fi
if test_fstab; then
fstab_uninstall_prompt
fi
if test_nix_volume_mountd_installed; then
nix_volume_mountd_uninstall_prompt
fi
}
setup_synthetic_conf() {
if test_nix_root_is_symlink; then
if ! test_synthetic_conf_symlinked; then
failure >&2 <<EOF
error: $NIX_ROOT is a symlink (-> $(readlink "$NIX_ROOT")).
Please remove it. If nix is in /etc/synthetic.conf, remove it and reboot.
EOF
fi
fi
if ! test_synthetic_conf_mountable; then
task "Configuring /etc/synthetic.conf to make a mount-point at $NIX_ROOT" >&2
# technically /etc/synthetic.d/nix is supported in Big Sur+
# but handling both takes even more code...
# See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
_sudo "to add Nix to /etc/synthetic.conf" \
/usr/bin/ex -u NONE -n /etc/synthetic.conf <<EOF
:a
${NIX_ROOT:1}
.
:x
EOF
if ! test_synthetic_conf_mountable; then
failure "error: failed to configure synthetic.conf" >&2
fi
create_synthetic_objects
if ! test_nix; then
failure >&2 <<EOF
error: failed to bootstrap $NIX_ROOT
If you enabled FileVault after booting, this is likely a known issue
with macOS that you'll have to reboot to fix. If you didn't enable FV,
though, please open an issue describing how the system that you see
this error on was set up.
EOF
fi
fi
}
setup_fstab() {
local volume_uuid="$1"
# fstab used to be responsible for mounting the volume. Now the last
# step adds a LaunchDaemon responsible for mounting. This is technically
# redundant for mounting, but diskutil appears to pick up mount options
# from fstab (and diskutil's support for specifying them directly is not
# consistent across versions/subcommands).
if ! test_fstab; then
task "Configuring /etc/fstab to specify volume mount options" >&2
add_nix_vol_fstab_line "$volume_uuid" /usr/sbin/vifs
fi
}
encrypt_volume() {
local volume_uuid="$1"
local volume_label="$2"
local password
task "Encrypt the Nix volume" >&2
# Note: mount/unmount are late additions to support the right order
# of operations for creating the volume and then baking its uuid into
# other artifacts; not as well-trod wrt to potential errors, race
# conditions, etc.
_sudo "to mount your Nix volume for encrypting" \
/usr/sbin/diskutil mount "$volume_label"
password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)"
_sudo "to add your Nix volume's password to Keychain" \
/usr/bin/security -i <<EOF
add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain"
EOF
builtin printf "%s" "$password" | _sudo "to actually encrypt your Nix volume" \
/usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase
_sudo "to unmount the encrypted volume" \
/usr/sbin/diskutil unmount force "$volume_label"
}
create_volume() {
# Notes:
# 1) using `-nomount` instead of `-mountpoint "$NIX_ROOT"` to get
# its UUID and set mount opts in fstab before first mount
#
# 2) system is in some sense less secure than user keychain... (it's
# possible to read the password for decrypting the keychain) but
# the user keychain appears to be available too late. As far as I
# can tell, the file with this password (/var/db/SystemKey) is
# inside the FileVault envelope. If that isn't true, it may make
# sense to store the password inside the envelope?
#
# 3) At some point it would be ideal to have a small binary to serve
# as the daemon itself, and for it to replace /usr/bin/security here.
#
# 4) *UserAgent exemptions should let the system seamlessly supply the
# password if noauto is removed from fstab entry. This is intentional;
# the user will hopefully look for help if the volume stops mounting,
# rather than failing over into subtle race-condition problems.
#
# 5) If we ever get users griping about not having space to do
# anything useful with Nix, it is possibly to specify
# `-reserve 10g` or something, which will fail w/o that much
#
# 6) getting special w/ awk may be fragile, but doing it to:
# - save time over running slow diskutil commands
# - skirt risk we grab wrong volume if multiple match
_sudo "to create a new APFS volume '$NIX_VOLUME_LABEL' on $NIX_VOLUME_USE_DISK" \
/usr/sbin/diskutil apfs addVolume "$NIX_VOLUME_USE_DISK" "$NIX_VOLUME_FS" "$NIX_VOLUME_LABEL" -nomount | /usr/bin/awk '/Created new APFS Volume/ {print $5}'
}
volume_uuid_from_special() {
local volume_special="$1" # (i.e., disk1s7)
# For reasons I won't pretend to fathom, this returns 253 when it works
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -k "$volume_special" || true
}
# this sometimes clears immediately, and AFAIK clears
# within about 1s. diskutil info on an unmounted path
# fails in around 50-100ms and a match takes about
# 250-300ms. I suspect it's usually ~250-750ms
await_volume() {
# caution: this could, in theory, get stuck
until /usr/sbin/diskutil info "$NIX_ROOT" &>/dev/null; do
:
done
}
setup_volume() {
local use_special use_uuid profile_packages
task "Creating a Nix volume" >&2
use_special="${NIX_VOLUME_USE_SPECIAL:-$(create_volume)}"
_sudo "to ensure the Nix volume is not mounted" \
/usr/sbin/diskutil unmount force "$use_special" || true # might not be mounted
use_uuid=${NIX_VOLUME_USE_UUID:-$(volume_uuid_from_special "$use_special")}
setup_fstab "$use_uuid"
if should_encrypt_volume; then
encrypt_volume "$use_uuid" "$NIX_VOLUME_LABEL"
setup_volume_daemon "encrypted" "$use_uuid"
# TODO: might be able to save ~60ms by caching or setting
# this somewhere rather than re-checking here.
elif volume_encrypted "$use_special"; then
setup_volume_daemon "encrypted" "$use_uuid"
else
setup_volume_daemon "unencrypted" "$use_uuid"
fi
await_volume
if [ "$(/usr/sbin/diskutil info -plist "$NIX_ROOT" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
_sudo "to set enableOwnership (enabling users to own files)" \
/usr/sbin/diskutil enableOwnership "$NIX_ROOT"
fi
# TODO: below is a vague kludge for now; I just don't know
# what if any safe action there is to take here. Also, the
# reminder isn't very helpful.
# I'm less sure where this belongs, but it also wants mounted, pre-install
if type -p nix-env; then
profile_packages="$(nix-env --query --installed)"
# TODO: can probably do below faster w/ read
# intentionally unquoted string to eat whitespace in wc output
# shellcheck disable=SC2046,SC2059
if ! [ $(printf "$profile_packages" | /usr/bin/wc -l) = "0" ]; then
reminder <<EOF
Nix now supports only multi-user installs on Darwin/macOS, and your user's
Nix profile has some packages in it. These packages may obscure those in the
default profile, including the Nix this installer will add. You should
review these packages:
$profile_packages
EOF
fi
fi
}
setup_volume_daemon() {
local cmd_type="$1" # encrypted|unencrypted
local volume_uuid="$2"
if ! test_voldaemon; then
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
# See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
_sudo "to install the Nix volume mounter" /usr/bin/ex -u NONE -n "$NIX_VOLUME_MOUNTD_DEST" <<EOF
:a
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
.
:x
EOF
# TODO: should probably alert the user if this is disabled?
_sudo "to launch the Nix volume mounter" \
launchctl bootstrap system "$NIX_VOLUME_MOUNTD_DEST" || true
# TODO: confirm whether kickstart is necessesary?
# I feel a little superstitous, but it can guard
# against multiple problems (doesn't start, old
# version still running for some reason...)
_sudo "to launch the Nix volume mounter" \
launchctl kickstart -k system/org.nixos.darwin-store
fi
}
setup_darwin_volume() {
setup_synthetic_conf
setup_volume
}
if [ "$_CREATE_VOLUME_NO_MAIN" = 1 ]; then
if [ -n "$*" ]; then
"$@" # expose functions in case we want multiple routines?
fi
else
# no reason to pay for bash to process this
main() {
{
echo ""
echo " ------------------------------------------------------------------ "
echo " | This installer will create a volume for the nix store and |"
echo " | configure it to mount at $NIX_ROOT. Follow these steps to uninstall. |"
echo " ------------------------------------------------------------------ "
echo ""
echo " 1. Remove the entry from fstab using 'sudo /usr/sbin/vifs'"
echo " 2. Run 'sudo launchctl bootout system/org.nixos.darwin-store'"
echo " 3. Remove $NIX_VOLUME_MOUNTD_DEST"
echo " 4. Destroy the data volume using '/usr/sbin/diskutil apfs deleteVolume'"
echo " 5. Remove the 'nix' line from /etc/synthetic.conf (or the file)"
echo ""
} >&2
setup_darwin_volume
}
main "$@"
fi

View file

@ -1,226 +0,0 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
# System specific settings
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
# create by default; set 0 to DIY, use a symlink, etc.
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
# caution: may update times on / if not run as normal non-root user
read_only_root() {
# this touch command ~should~ always produce an error
# as of this change I confirmed /usr/bin/touch emits:
# "touch: /: Operation not permitted" Monterey
# "touch: /: Read-only file system" Catalina+ and Big Sur
# "touch: /: Permission denied" Mojave
# (not matching prefix for compat w/ coreutils touch in case using
# an explicit path causes problems; its prefix differs)
case "$(/usr/bin/touch / 2>&1)" in
*"Read-only file system") # Catalina, Big Sur
return 0
;;
*"Operation not permitted") # Monterey
return 0
;;
*)
return 1
;;
esac
# Avoiding the slow semantic way to get this information (~330ms vs ~8ms)
# unless using touch causes problems. Just in case, that approach is:
# diskutil info -plist / | <find the Writable or WritableVolume keys>, i.e.
# diskutil info -plist / | xmllint --xpath "name(/plist/dict/key[text()='Writable']/following-sibling::*[1])" -
}
if read_only_root && [ "$NIX_VOLUME_CREATE" = 1 ]; then
should_create_volume() { return 0; }
else
should_create_volume() { return 1; }
fi
# shellcheck source=./create-darwin-volume.sh
. "$EXTRACTED_NIX_PATH/create-darwin-volume.sh" "no-main"
dsclattr() {
/usr/bin/dscl . -read "$1" \
| /usr/bin/awk "/$2/ { print \$2 }"
}
test_nix_daemon_installed() {
test -e "$NIX_DAEMON_DEST"
}
poly_cure_artifacts() {
if should_create_volume; then
task "Fixing any leftover Nix volume state"
cat <<EOF
Before I try to install, I'll check for any existing Nix volume config
and ask for your permission to remove it (so that the installer can
start fresh). I'll also ask for permission to fix any issues I spot.
EOF
cure_volumes
remove_volume_artifacts
fi
}
poly_service_installed_check() {
if should_create_volume; then
test_nix_daemon_installed || test_nix_volume_mountd_installed
else
test_nix_daemon_installed
fi
}
poly_service_uninstall_directions() {
echo "$1. Remove macOS-specific components:"
if should_create_volume && test_nix_volume_mountd_installed; then
nix_volume_mountd_uninstall_directions
fi
if test_nix_daemon_installed; then
nix_daemon_uninstall_directions
fi
}
poly_service_setup_note() {
if should_create_volume; then
echo " - create a Nix volume and a LaunchDaemon to mount it"
fi
echo " - create a LaunchDaemon (at $NIX_DAEMON_DEST) for nix-daemon"
echo ""
}
poly_extra_try_me_commands() {
:
}
poly_configure_nix_daemon_service() {
task "Setting up the nix-daemon LaunchDaemon"
_sudo "to set up the nix-daemon as a LaunchDaemon" \
/usr/bin/install -m "u=rw,go=r" "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
_sudo "to load the LaunchDaemon plist for nix-daemon" \
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
_sudo "to start the nix-daemon" \
launchctl kickstart -k system/org.nixos.nix-daemon
}
poly_group_exists() {
/usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
}
poly_group_id_get() {
dsclattr "/Groups/$1" "PrimaryGroupID"
}
poly_create_build_group() {
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
/usr/sbin/dseditgroup -o create \
-r "Nix build group for nix-daemon" \
-i "$NIX_BUILD_GROUP_ID" \
"$NIX_BUILD_GROUP_NAME" >&2
}
poly_user_exists() {
/usr/bin/dscl . -read "/Users/$1" > /dev/null 2>&1
}
poly_user_id_get() {
dsclattr "/Users/$1" "UniqueID"
}
poly_user_hidden_get() {
dsclattr "/Users/$1" "IsHidden"
}
poly_user_hidden_set() {
_sudo "in order to make $1 a hidden user" \
/usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
}
poly_user_home_get() {
dsclattr "/Users/$1" "NFSHomeDirectory"
}
poly_user_home_set() {
# This can trigger a permission prompt now:
# "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings.
_sudo "in order to give $1 a safe home directory" \
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
}
poly_user_note_get() {
dsclattr "/Users/$1" "RealName"
}
poly_user_note_set() {
_sudo "in order to give $username a useful note" \
/usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
}
poly_user_shell_get() {
dsclattr "/Users/$1" "UserShell"
}
poly_user_shell_set() {
_sudo "in order to give $1 a safe shell" \
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
}
poly_user_in_group_check() {
username=$1
group=$2
/usr/sbin/dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
}
poly_user_in_group_set() {
username=$1
group=$2
_sudo "Add $username to the $group group"\
/usr/sbin/dseditgroup -o edit -t user \
-a "$username" "$group"
}
poly_user_primary_group_get() {
dsclattr "/Users/$1" "PrimaryGroupID"
}
poly_user_primary_group_set() {
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
/usr/bin/dscl . -create "/Users/$1" "PrimaryGroupID" "$2"
}
poly_create_build_user() {
username=$1
uid=$2
builder_num=$3
_sudo "Creating the Nix build user (#$builder_num), $username" \
/usr/bin/dscl . create "/Users/$username" \
UniqueID "${uid}"
}
poly_prepare_to_install() {
if should_create_volume; then
header "Preparing a Nix volume"
# intentional indent below to match task indent
cat <<EOF
Nix traditionally stores its data in the root directory $NIX_ROOT, but
macOS now (starting in 10.15 Catalina) has a read-only root directory.
To support Nix, I will create a volume and configure macOS to mount it
at $NIX_ROOT.
EOF
setup_darwin_volume
fi
if [ "$(/usr/sbin/diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
failure "This script needs a /nix volume with global permissions! This may require running sudo /usr/sbin/diskutil enableOwnership /nix."
fi
}

File diff suppressed because it is too large Load diff

View file

@ -1,284 +0,0 @@
#!/bin/sh
set -e
umask 0022
dest="/nix"
self="$(dirname "$0")"
nix="@nix@"
cacert="@cacert@"
if ! [ -e "$self/.reginfo" ]; then
echo "$0: incomplete installer (.reginfo is missing)" >&2
fi
if [ -z "$USER" ] && ! USER=$(id -u -n); then
echo "$0: \$USER is not set" >&2
exit 1
fi
if [ -z "$HOME" ]; then
echo "$0: \$HOME is not set" >&2
exit 1
fi
# macOS support for 10.12.6 or higher
if [ "$(uname -s)" = "Darwin" ]; then
IFS='.' read -r macos_major macos_minor macos_patch << EOF
$(sw_vers -productVersion)
EOF
if [ "$macos_major" -lt 10 ] || { [ "$macos_major" -eq 10 ] && [ "$macos_minor" -lt 12 ]; } || { [ "$macos_minor" -eq 12 ] && [ "$macos_patch" -lt 6 ]; }; then
# patch may not be present; command substitution for simplicity
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.12.6 or higher"
exit 1
fi
fi
# Determine if we could use the multi-user installer or not
if [ "$(uname -s)" = "Linux" ]; then
echo "Note: a multi-user installation is possible. See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation" >&2
fi
case "$(uname -s)" in
"Darwin")
INSTALL_MODE=daemon;;
*)
INSTALL_MODE=no-daemon;;
esac
# space-separated string
ACTIONS=
# handle the command line flags
while [ $# -gt 0 ]; do
case $1 in
--daemon)
INSTALL_MODE=daemon
ACTIONS="${ACTIONS}install "
;;
--no-daemon)
if [ "$(uname -s)" = "Darwin" ]; then
printf '\e[1;31mError: --no-daemon installs are no-longer supported on Darwin/macOS!\e[0m\n' >&2
exit 1
fi
INSTALL_MODE=no-daemon
# intentional tail space
ACTIONS="${ACTIONS}install "
;;
# --uninstall)
# # intentional tail space
# ACTIONS="${ACTIONS}uninstall "
# ;;
--yes)
export NIX_INSTALLER_YES=1;;
--no-channel-add)
export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
--daemon-user-count)
export NIX_USER_COUNT=$2
shift;;
--no-modify-profile)
NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
--darwin-use-unencrypted-nix-store-volume)
{
echo "Warning: the flag --darwin-use-unencrypted-nix-store-volume"
echo " is no longer needed and will be removed in the future."
echo ""
} >&2;;
--nix-extra-conf-file)
# shellcheck disable=SC2155
export NIX_EXTRA_CONF="$(cat "$2")"
shift;;
*)
{
echo "Nix Installer [--daemon|--no-daemon] [--daemon-user-count INT] [--yes] [--no-channel-add] [--no-modify-profile] [--nix-extra-conf-file FILE]"
echo "Choose installation method."
echo ""
echo " --daemon: Installs and configures a background daemon that manages the store,"
echo " providing multi-user support and better isolation for local builds."
echo " Both for security and reproducibility, this method is recommended if"
echo " supported on your platform."
echo " See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation"
echo ""
echo " --no-daemon: Simple, single-user installation that does not require root and is"
echo " trivial to uninstall."
echo " (default)"
echo ""
echo " --yes: Run the script non-interactively, accepting all prompts."
echo ""
echo " --no-channel-add: Don't add any channels. nixpkgs-unstable is installed by default."
echo ""
echo " --no-modify-profile: Don't modify the user profile to automatically load nix."
echo ""
echo " --daemon-user-count: Number of build users to create. Defaults to 32."
echo ""
echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix/nix.conf"
echo ""
if [ -n "${INVOKED_FROM_INSTALL_IN:-}" ]; then
echo " --tarball-url-prefix URL: Base URL to download the Nix tarball from."
fi
} >&2
exit;;
esac
shift
done
if [ "$INSTALL_MODE" = "daemon" ]; then
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split
exit 0
fi
if [ "$(id -u)" -eq 0 ]; then
printf '\e[1;31mwarning: installing Nix as root is not supported by this script!\e[0m\n'
fi
echo "performing a single-user installation of Nix..." >&2
if ! [ -e "$dest" ]; then
cmd="mkdir -m 0755 $dest && chown $USER $dest"
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
if ! sudo sh -c "$cmd"; then
echo "$0: please manually run '$cmd' as root to create $dest" >&2
exit 1
fi
fi
if ! [ -w "$dest" ]; then
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/manual/nix/stable/installation/multi-user.html. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
exit 1
fi
# The auto-chroot code in openFromNonUri() checks for the
# non-existence of /nix/var/nix, so we need to create it here.
mkdir -p "$dest/store" "$dest/var/nix"
printf "copying Nix to %s..." "${dest}/store" >&2
# Insert a newline if no progress is shown.
if [ ! -t 0 ]; then
echo ""
fi
for i in $(cd "$self/store" >/dev/null && echo ./*); do
if [ -t 0 ]; then
printf "." >&2
fi
i_tmp="$dest/store/$i.$$"
if [ -e "$i_tmp" ]; then
rm -rf "$i_tmp"
fi
if ! [ -e "$dest/store/$i" ]; then
cp -RPp "$self/store/$i" "$i_tmp"
chmod -R a-w "$i_tmp"
chmod +w "$i_tmp"
mv "$i_tmp" "$dest/store/$i"
chmod -w "$dest/store/$i"
fi
done
echo "" >&2
if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then
echo "$0: unable to register valid paths" >&2
exit 1
fi
# shellcheck source=./nix-profile.sh.in
. "$nix/etc/profile.d/nix.sh"
NIX_LINK="$HOME/.nix-profile"
if ! "$nix/bin/nix-env" -i "$nix"; then
echo "$0: unable to install Nix into your default profile" >&2
exit 1
fi
# Install an SSL certificate bundle.
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
"$nix/bin/nix-env" -i "$cacert"
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
fi
# Subscribe the user to the Nixpkgs channel and fetch it.
if [ -z "$NIX_INSTALLER_NO_CHANNEL_ADD" ]; then
if ! "$nix/bin/nix-channel" --list | grep -q "^nixpkgs "; then
"$nix/bin/nix-channel" --add https://nixos.org/channels/nixpkgs-unstable
fi
if [ -z "$_NIX_INSTALLER_TEST" ]; then
if ! "$nix/bin/nix-channel" --update nixpkgs; then
echo "Fetching the nixpkgs channel failed. (Are you offline?)"
echo "To try again later, run \"nix-channel --update nixpkgs\"."
fi
fi
fi
added=
p=
p_sh=$NIX_LINK/etc/profile.d/nix.sh
p_fish=$NIX_LINK/etc/profile.d/nix.fish
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
# Make the shell source nix.sh during login.
for i in .bash_profile .bash_login .profile; do
fn="$HOME/$i"
if [ -w "$fn" ]; then
if ! grep -q "$p_sh" "$fn"; then
echo "modifying $fn..." >&2
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
fi
added=1
p=${p_sh}
break
fi
done
for i in .zshenv .zshrc; do
fn="$HOME/$i"
if [ -w "$fn" ]; then
if ! grep -q "$p_sh" "$fn"; then
echo "modifying $fn..." >&2
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p_sh" "$p_sh" >> "$fn"
fi
added=1
p=${p_sh}
break
fi
done
if [ -d "$HOME/.config/fish" ]; then
fishdir=$HOME/.config/fish/conf.d
if [ ! -d "$fishdir" ]; then
mkdir -p "$fishdir"
fi
fn="$fishdir/nix.fish"
echo "placing $fn..." >&2
printf '\nif test -e %s; . %s; end # added by Nix installer\n' "$p_fish" "$p_fish" > "$fn"
added=1
p=${p_fish}
fi
else
p=${p_sh}
fi
if [ -z "$added" ]; then
cat >&2 <<EOF
Installation finished! To ensure that the necessary environment
variables are set, please add the line
. $p
to your shell profile (e.g. ~/.profile).
EOF
else
cat >&2 <<EOF
Installation finished! To ensure that the necessary environment
variables are set, either log in again, or type
. $p
in your shell.
EOF
fi

View file

@ -1,222 +0,0 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
# System specific settings
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket
readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket
readonly TMPFILES_SRC=/lib/tmpfiles.d/nix-daemon.conf
readonly TMPFILES_DEST=/etc/tmpfiles.d/nix-daemon.conf
# Path for the systemd override unit file to contain the proxy settings
readonly SERVICE_OVERRIDE=${SERVICE_DEST}.d/override.conf
create_systemd_override() {
header "Configuring proxy for the nix-daemon service"
_sudo "create directory for systemd unit override" mkdir -p "$(dirname "$SERVICE_OVERRIDE")"
cat <<EOF | _sudo "create systemd unit override" tee "$SERVICE_OVERRIDE"
[Service]
$1
EOF
}
escape_systemd_env() {
temp_var="${1//\'/\\\'}"
echo "${temp_var//\%/%%}"
}
# Gather all non-empty proxy environment variables into a string
create_systemd_proxy_env() {
vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY"
for v in $vars; do
if [ "x${!v:-}" != "x" ]; then
echo "Environment=${v}=$(escape_systemd_env ${!v})"
fi
done
}
handle_network_proxy() {
# Create a systemd unit override with proxy environment variables
# if any proxy environment variables are not empty.
PROXY_ENV_STRING=$(create_systemd_proxy_env)
if [ -n "${PROXY_ENV_STRING}" ]; then
create_systemd_override "${PROXY_ENV_STRING}"
fi
}
poly_cure_artifacts() {
:
}
poly_service_installed_check() {
[ "$(systemctl is-enabled nix-daemon.service)" = "linked" ] \
|| [ "$(systemctl is-enabled nix-daemon.socket)" = "enabled" ]
}
poly_service_uninstall_directions() {
cat <<EOF
$1. Delete the systemd service and socket units
sudo systemctl stop nix-daemon.socket
sudo systemctl stop nix-daemon.service
sudo systemctl disable nix-daemon.socket
sudo systemctl disable nix-daemon.service
sudo systemctl daemon-reload
EOF
}
poly_service_setup_note() {
cat <<EOF
- load and start a service (at $SERVICE_DEST
and $SOCKET_DEST) for nix-daemon
EOF
}
poly_extra_try_me_commands() {
if [ -e /run/systemd/system ]; then
:
else
cat <<EOF
$ sudo nix-daemon
EOF
fi
}
poly_configure_nix_daemon_service() {
if [ -e /run/systemd/system ]; then
task "Setting up the nix-daemon systemd service"
_sudo "to create the nix-daemon tmpfiles config" \
ln -sfn "/nix/var/nix/profiles/default$TMPFILES_SRC" "$TMPFILES_DEST"
_sudo "to run systemd-tmpfiles once to pick that path up" \
systemd-tmpfiles --create --prefix=/nix/var/nix
_sudo "to set up the nix-daemon service" \
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
_sudo "to set up the nix-daemon socket service" \
systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC"
handle_network_proxy
_sudo "to load the systemd unit for nix-daemon" \
systemctl daemon-reload
_sudo "to start the nix-daemon.socket" \
systemctl start nix-daemon.socket
_sudo "to start the nix-daemon.service" \
systemctl restart nix-daemon.service
else
reminder "I don't support your init system yet; you may want to add nix-daemon manually."
fi
}
poly_group_exists() {
getent group "$1" > /dev/null 2>&1
}
poly_group_id_get() {
getent group "$1" | cut -d: -f3
}
poly_create_build_group() {
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
groupadd -g "$NIX_BUILD_GROUP_ID" --system \
"$NIX_BUILD_GROUP_NAME" >&2
}
poly_user_exists() {
getent passwd "$1" > /dev/null 2>&1
}
poly_user_id_get() {
getent passwd "$1" | cut -d: -f3
}
poly_user_hidden_get() {
echo "1"
}
poly_user_hidden_set() {
true
}
poly_user_home_get() {
getent passwd "$1" | cut -d: -f6
}
poly_user_home_set() {
_sudo "in order to give $1 a safe home directory" \
usermod --home "$2" "$1"
}
poly_user_note_get() {
getent passwd "$1" | cut -d: -f5
}
poly_user_note_set() {
_sudo "in order to give $1 a useful comment" \
usermod --comment "$2" "$1"
}
poly_user_shell_get() {
getent passwd "$1" | cut -d: -f7
}
poly_user_shell_set() {
_sudo "in order to prevent $1 from logging in" \
usermod --shell "$2" "$1"
}
poly_user_in_group_check() {
groups "$1" | grep -q "$2" > /dev/null 2>&1
}
poly_user_in_group_set() {
_sudo "Add $1 to the $2 group"\
usermod --append --groups "$2" "$1"
}
poly_user_primary_group_get() {
getent passwd "$1" | cut -d: -f4
}
poly_user_primary_group_set() {
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
usermod --gid "$2" "$1"
}
poly_create_build_user() {
username=$1
uid=$2
builder_num=$3
_sudo "Creating the Nix build user, $username" \
useradd \
--home-dir /var/empty \
--comment "Nix build user $builder_num" \
--gid "$NIX_BUILD_GROUP_ID" \
--groups "$NIX_BUILD_GROUP_NAME" \
--no-user-group \
--system \
--shell /sbin/nologin \
--uid "$uid" \
--password "!" \
"$username"
}
poly_prepare_to_install() {
:
}

View file

@ -1,119 +0,0 @@
#!/bin/sh
# This script installs the Nix package manager on your system by
# downloading a binary distribution and running its installer script
# (which in turn creates and populates /nix).
{ # Prevent execution if this script was only partially downloaded
oops() {
echo "$0:" "$@" >&2
exit 1
}
umask 0022
tmpDir="$(mktemp -d -t nix-binary-tarball-unpack.XXXXXXXXXX || \
oops "Can't create temporary directory for downloading the Nix binary tarball")"
cleanup() {
rm -rf "$tmpDir"
}
trap cleanup EXIT INT QUIT TERM
require_util() {
command -v "$1" > /dev/null 2>&1 ||
oops "you do not have '$1' installed, which I need to $2"
}
case "$(uname -s).$(uname -m)" in
Linux.x86_64)
hash=@tarballHash_x86_64-linux@
path=@tarballPath_x86_64-linux@
system=x86_64-linux
;;
Linux.i?86)
hash=@tarballHash_i686-linux@
path=@tarballPath_i686-linux@
system=i686-linux
;;
Linux.aarch64)
hash=@tarballHash_aarch64-linux@
path=@tarballPath_aarch64-linux@
system=aarch64-linux
;;
Linux.armv6l)
hash=@tarballHash_armv6l-linux@
path=@tarballPath_armv6l-linux@
system=armv6l-linux
;;
Linux.armv7l)
hash=@tarballHash_armv7l-linux@
path=@tarballPath_armv7l-linux@
system=armv7l-linux
;;
Darwin.x86_64)
hash=@tarballHash_x86_64-darwin@
path=@tarballPath_x86_64-darwin@
system=x86_64-darwin
;;
Darwin.arm64|Darwin.aarch64)
hash=@tarballHash_aarch64-darwin@
path=@tarballPath_aarch64-darwin@
system=aarch64-darwin
;;
*) oops "sorry, there is no binary distribution of Nix for your platform";;
esac
# Use this command-line option to fetch the tarballs using nar-serve or Cachix
if [ "${1:-}" = "--tarball-url-prefix" ]; then
if [ -z "${2:-}" ]; then
oops "missing argument for --tarball-url-prefix"
fi
url=${2}/${path}
shift 2
else
url=https://releases.nixos.org/nix/nix-@nixVersion@/nix-@nixVersion@-$system.tar.xz
fi
tarball=$tmpDir/nix-@nixVersion@-$system.tar.xz
require_util tar "unpack the binary tarball"
if [ "$(uname -s)" != "Darwin" ]; then
require_util xz "unpack the binary tarball"
fi
if command -v curl > /dev/null 2>&1; then
fetch() { curl --fail -L "$1" -o "$2"; }
elif command -v wget > /dev/null 2>&1; then
fetch() { wget "$1" -O "$2"; }
else
oops "you don't have wget or curl installed, which I need to download the binary tarball"
fi
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
fetch "$url" "$tarball" || oops "failed to download '$url'"
if command -v sha256sum > /dev/null 2>&1; then
hash2="$(sha256sum -b "$tarball" | cut -c1-64)"
elif command -v shasum > /dev/null 2>&1; then
hash2="$(shasum -a 256 -b "$tarball" | cut -c1-64)"
elif command -v openssl > /dev/null 2>&1; then
hash2="$(openssl dgst -r -sha256 "$tarball" | cut -c1-64)"
else
oops "cannot verify the SHA-256 hash of '$url'; you need one of 'shasum', 'sha256sum', or 'openssl'"
fi
if [ "$hash" != "$hash2" ]; then
oops "SHA-256 hash mismatch in '$url'; expected $hash, got $hash2"
fi
unpack=$tmpDir/unpack
mkdir -p "$unpack"
tar -xJf "$tarball" -C "$unpack" || oops "failed to unpack '$url'"
script=$(echo "$unpack"/*/install)
[ -e "$script" ] || oops "installation script is missing from the binary tarball!"
export INVOKED_FROM_INSTALL_IN=1
"$script" "$@"
} # End of wrapping

View file

@ -7,7 +7,5 @@ profiledir = $(sysconfdir)/profile.d
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
$(eval $(call install-file-as, $(d)/nix-profile.fish, $(profiledir)/nix.fish, 0644))
$(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
$(eval $(call install-file-as, $(d)/nix-profile-daemon.fish, $(profiledir)/nix-daemon.fish, 0644))
clean-files += $(nix_noinst_scripts)

View file

@ -1,57 +0,0 @@
function add_path --argument-names new_path
if type -q fish_add_path
# fish 3.2.0 or newer
fish_add_path --prepend --global $new_path
else
# older versions of fish
if not contains $new_path $fish_user_paths
set --global fish_user_paths $new_path $fish_user_paths
end
end
end
# Only execute this file once per shell.
if test -n "$__ETC_PROFILE_NIX_SOURCED"
exit
end
set __ETC_PROFILE_NIX_SOURCED 1
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Populate bash completions, .desktop files, etc
if test -z "$XDG_DATA_DIRS"
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
else
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
end
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE
else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
else if test -e /etc/ssl/ca-bundle.pem # openSUSE Tumbleweed
set --export NIX_SSL_CERT_FILE /etc/ssl/ca-bundle.pem
else if test -e /etc/ssl/certs/ca-bundle.crt # Old NixOS
set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-bundle.crt
else if test -e /etc/pki/tls/certs/ca-bundle.crt # Fedora, CentOS
set --export NIX_SSL_CERT_FILE /etc/pki/tls/certs/ca-bundle.crt
else if test -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" # fall back to cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile
set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt"
else
# Fall back to what is in the nix profiles, favouring whatever is defined last.
for i in $NIX_PROFILES
if test -e "$i/etc/ssl/certs/ca-bundle.crt"
set --export NIX_SSL_CERT_FILE "$i/etc/ssl/certs/ca-bundle.crt"
end
end
end
add_path "@localstatedir@/nix/profiles/default/bin"
add_path "$HOME/.nix-profile/bin"
functions -e add_path

View file

@ -1,72 +0,0 @@
# Only execute this file once per shell.
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
__ETC_PROFILE_NIX_SOURCED=1
NIX_LINK=$HOME/.nix-profile
if [ -n "${XDG_STATE_HOME-}" ]; then
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
else
NIX_LINK_NEW=$HOME/.local/state/nix/profile
fi
if [ -e "$NIX_LINK_NEW" ]; then
NIX_LINK="$NIX_LINK_NEW"
else
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
warning="\033[1;35mwarning:\033[0m"
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
else
# This should be an exceptionally rare occasion: the only way to get it would be to
# 1. Update to newer Nix;
# 2. Remove .nix-profile;
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
# 4. Roll back to older Nix.
# If someone did all that, they can probably figure out how to migrate the profile.
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
fi
fi
fi
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
# Populate bash completions, .desktop files, etc
if [ -z "${XDG_DATA_DIRS-}" ]; then
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
else
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
fi
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
: # Allow users to override the NIX_SSL_CERT_FILE
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
elif [ -e /etc/ssl/ca-bundle.pem ]; then # openSUSE Tumbleweed
export NIX_SSL_CERT_FILE=/etc/ssl/ca-bundle.pem
elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
export NIX_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
else
# Fall back to what is in the nix profiles, favouring whatever is defined last.
check_nix_profiles() {
if [ -n "$ZSH_VERSION" ]; then
# Zsh by default doesn't split words in unquoted parameter expansion.
# Set local_options for these options to be reverted at the end of the function
# and shwordsplit to force splitting words in $NIX_PROFILES below.
setopt local_options shwordsplit
fi
for i in $NIX_PROFILES; do
if [ -e "$i/etc/ssl/certs/ca-bundle.crt" ]; then
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
fi
done
}
check_nix_profiles
unset -f check_nix_profiles
fi
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
unset NIX_LINK

View file

@ -1,10 +0,0 @@
#!/usr/bin/env bash
set -e
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
installerHash=$(echo "$script" | cut -b12-43 -)
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
echo "::set-output name=installerURL::$installerURL"

View file

@ -126,7 +126,7 @@ static int main_build_remote(int argc, char * * argv)
mkdir(currentLoad.c_str(), 0777);
while (true) {
bestSlotLock = -1;
bestSlotLock.reset();
AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true);
lockFile(lock.get(), ltWrite, true);
@ -229,7 +229,7 @@ static int main_build_remote(int argc, char * * argv)
futimens(bestSlotLock.get(), NULL);
#endif
lock = -1;
lock.reset();
try {
@ -282,7 +282,7 @@ connected:
copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
}
uploadLock = -1;
uploadLock.reset();
auto drv = store->readDerivation(*drvPath);

View file

@ -172,7 +172,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s)
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}
else if (hasPrefix(s, "flake:")) {
else if (s.starts_with("flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;

View file

@ -159,7 +159,7 @@ void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
auto flake = flake::getFlake(*evalState, flakeRef, true);
for (auto & input : flake.inputs)
if (hasPrefix(input.first, prefix))
if (input.first.starts_with(prefix))
completions->add(input.first);
}
}
@ -320,7 +320,7 @@ void completeFlakeRefWithFragment(
auto attrPath = parseAttrPath(*evalState, attrPathS);
std::string lastAttr;
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
if (!attrPath.empty() && !attrPathS.ends_with(".")) {
lastAttr = evalState->symbols[attrPath.back()];
attrPath.pop_back();
}
@ -329,7 +329,7 @@ void completeFlakeRefWithFragment(
if (!attr) continue;
for (auto & attr2 : (*attr)->getAttrs()) {
if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
if (std::string_view(evalState->symbols[attr2]).starts_with(lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(attr2);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
@ -367,12 +367,12 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
for (auto & registry : fetchers::getRegistries(store)) {
for (auto & entry : registry->entries) {
auto from = entry.from.to_string();
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
if (!prefix.starts_with("flake:") && from.starts_with("flake:")) {
std::string from2(from, 6);
if (hasPrefix(from2, prefix))
if (from2.starts_with(prefix))
completions->add(from2);
} else {
if (hasPrefix(from, prefix))
if (from.starts_with(prefix))
completions->add(from);
}
}

58
src/libcmd/meson.build Normal file
View file

@ -0,0 +1,58 @@
libcmd_sources = files(
'built-path.cc',
'command-installable-value.cc',
'command.cc',
'common-eval-args.cc',
'editor-for.cc',
'installable-attr-path.cc',
'installable-derived-path.cc',
'installable-flake.cc',
'installable-value.cc',
'installables.cc',
'legacy.cc',
'markdown.cc',
'repl.cc',
'repl-interacter.cc',
)
libcmd_headers = files(
'built-path.hh',
'command-installable-value.hh',
'command.hh',
'common-eval-args.hh',
'editor-for.hh',
'installable-attr-path.hh',
'installable-derived-path.hh',
'installable-flake.hh',
'installable-value.hh',
'installables.hh',
'legacy.hh',
'markdown.hh',
'repl-interacter.hh',
'repl.hh',
)
libcmd = library(
'nixcmd',
libcmd_sources,
dependencies : [
liblixutil,
liblixstore,
liblixexpr,
liblixfetchers,
liblixmain,
boehm,
editline,
lowdown,
],
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
install_headers(libcmd_headers, subdir : 'nix', preserve_path : true)
liblixcmd = declare_dependency(
include_directories : '.',
link_with : libcmd,
)

View file

@ -29,6 +29,7 @@
#include "local-fs-store.hh"
#include "signals.hh"
#include "print.hh"
#include "progress-bar.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
@ -186,7 +187,7 @@ ReplExitStatus NixRepl::mainLoop()
if (state->debugRepl) {
debuggerNotice = " debugger";
}
notice("Nix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
}
isFirstRepl = false;
@ -195,11 +196,13 @@ ReplExitStatus NixRepl::mainLoop()
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
/* Stop the progress bar because it interferes with the display of
the repl. */
stopProgressBar();
std::string input;
while (true) {
// Hide the progress bar while waiting for user input, so that it won't interfere.
logger->pause();
// When continuing input from previous lines, don't print a prompt, just align to the same
// number of chars as the prompt.
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
@ -210,7 +213,6 @@ ReplExitStatus NixRepl::mainLoop()
// the entire program?
return ReplExitStatus::QuitAll;
}
logger->resume();
try {
switch (processLine(input)) {
case ProcessLineResult::Quit:
@ -272,7 +274,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
auto dir = std::string(cur, 0, slash);
auto prefix2 = std::string(cur, slash + 1);
for (auto & entry : readDirectory(dir == "" ? "/" : dir)) {
if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2))
if (entry.name[0] != '.' && entry.name.starts_with(prefix2))
completions.insert(prev + dir + "/" + entry.name);
}
} catch (Error &) {

View file

@ -29,7 +29,7 @@ static Strings parseNixPath(const std::string & s)
if (*p == ':') {
auto prefix = std::string(start2, s.end());
if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) {
if (EvalSettings::isPseudoUrl(prefix) || prefix.starts_with("flake:")) {
++p;
while (p != s.end() && *p != ':') ++p;
}
@ -82,7 +82,7 @@ bool EvalSettings::isPseudoUrl(std::string_view s)
std::string EvalSettings::resolvePseudoUrl(std::string_view url)
{
if (hasPrefix(url, "channel:"))
if (url.starts_with("channel:"))
return "https://nixos.org/channels/" + std::string(url.substr(8)) + "/nixexprs.tar.xz";
else
return std::string(url);

View file

@ -484,7 +484,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
*/
Path abspath = canonPath(path_.path.abs());
if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
if (abspath.starts_with(corepkgsPrefix)) return CanonPath(abspath);
for (auto & i : *allowedPaths) {
if (isDirOrInDir(abspath, i)) {
@ -527,18 +527,18 @@ void EvalState::checkURI(const std::string & uri)
if (uri == prefix ||
(uri.size() > prefix.size()
&& prefix.size() > 0
&& hasPrefix(uri, prefix)
&& uri.starts_with(prefix)
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
return;
/* If the URI is a path, then check it against allowedPaths as
well. */
if (hasPrefix(uri, "/")) {
if (uri.starts_with("/")) {
checkSourcePath(CanonPath(uri));
return;
}
if (hasPrefix(uri, "file://")) {
if (uri.starts_with("file://")) {
checkSourcePath(CanonPath(std::string(uri, 7)));
return;
}
@ -642,7 +642,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
}
auto envName = symbols.create(primOp.name);
if (hasPrefix(primOp.name, "__"))
if (primOp.name.starts_with("__"))
primOp.name = primOp.name.substr(2);
Value * v = allocValue();
@ -677,12 +677,21 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
}
static std::set<std::string_view> sortedBindingNames(const SymbolTable & st, const StaticEnv & se)
{
std::set<std::string_view> bindings;
for (auto [symbol, displ] : se.vars)
bindings.emplace(st[symbol]);
return bindings;
}
// just for the current level of StaticEnv, not the whole chain.
void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
{
std::cout << ANSI_MAGENTA;
for (auto & i : se.vars)
std::cout << st[i.first] << " ";
for (auto & i : sortedBindingNames(st, se))
std::cout << i << " ";
std::cout << ANSI_NORMAL;
std::cout << std::endl;
}
@ -691,13 +700,14 @@ void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
void printWithBindings(const SymbolTable & st, const Env & env)
{
if (!env.values[0]->isThunk()) {
std::set<std::string_view> bindings;
for (const auto & attr : *env.values[0]->attrs)
bindings.emplace(st[attr.name]);
std::cout << "with: ";
std::cout << ANSI_MAGENTA;
Bindings::iterator j = env.values[0]->attrs->begin();
while (j != env.values[0]->attrs->end()) {
std::cout << st[j->name] << " ";
++j;
}
for (auto & i : bindings)
std::cout << i << " ";
std::cout << ANSI_NORMAL;
std::cout << std::endl;
}
@ -718,9 +728,9 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env &
std::cout << ANSI_MAGENTA;
// for the top level, don't print the double underscore ones;
// they are in builtins.
for (auto & i : se.vars)
if (!hasPrefix(st[i.first], "__"))
std::cout << st[i.first] << " ";
for (auto & i : sortedBindingNames(st, se))
if (!i.starts_with("__"))
std::cout << i << " ";
std::cout << ANSI_NORMAL;
std::cout << std::endl;
if (se.isWith)
@ -940,12 +950,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
void EvalState::mkPos(Value & v, PosIdx p)
{
auto pos = positions[p];
if (auto path = std::get_if<SourcePath>(&pos.origin)) {
auto origin = positions.originOf(p);
if (auto path = std::get_if<SourcePath>(&origin)) {
auto attrs = buildBindings(3);
attrs.alloc(sFile).mkString(path->path.abs());
attrs.alloc(sLine).mkInt(pos.line);
attrs.alloc(sColumn).mkInt(pos.column);
makePositionThunks(*this, p, attrs.alloc(sLine), attrs.alloc(sColumn));
v.mkAttrs(attrs);
} else
v.mkNull();
@ -2745,9 +2754,12 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta
Expr * EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
{
auto s = make_ref<std::string>(std::move(s_));
s->append("\0\0", 2);
return parse(s->data(), s->size(), Pos::String{.source = s}, basePath, staticEnv);
// NOTE this method (and parseStdin) must take care to *fully copy* their input
// into their respective Pos::Origin until the parser stops overwriting its input
// data.
auto s = make_ref<std::string>(s_);
s_.append("\0\0", 2);
return parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
}
@ -2759,12 +2771,15 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath
Expr * EvalState::parseStdin()
{
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
// input into their respective Pos::Origin until the parser stops overwriting its
// input data.
//Activity act(*logger, lvlTalkative, "parsing standard input");
auto buffer = drainFD(0);
// drainFD should have left some extra space for terminators
auto s = make_ref<std::string>(buffer);
buffer.append("\0\0", 2);
auto s = make_ref<std::string>(std::move(buffer));
return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
return parse(buffer.data(), buffer.size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
}
@ -2790,7 +2805,7 @@ SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_
if (pathExists(res)) return CanonPath(canonPath(res));
}
if (hasPrefix(path, "nix/"))
if (path.starts_with("nix/"))
return CanonPath(concatStrings(corepkgsPrefix, path.substr(4)));
error<ThrownError>(
@ -2823,7 +2838,7 @@ std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Pa
}
}
else if (hasPrefix(value, "flake:")) {
else if (value.starts_with("flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false);
debug("fetching flake search path element '%s''", value);

View file

@ -35,7 +35,7 @@ void ConfigFile::apply()
for (auto & [name, value] : settings) {
auto baseName = hasPrefix(name, "extra-") ? std::string(name, 6) : name;
auto baseName = name.starts_with("extra-") ? std::string(name, 6) : name;
// FIXME: Move into libutil/config.cc.
std::string valueS;

View file

@ -227,11 +227,10 @@ static Flake getFlake(
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
};
// NOTE evalFile forces vInfo to be an attrset because mustBeTrivial is true.
Value vInfo;
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, nString, *description->value, description->pos);
flake.description = description->value->string.s;

View file

@ -186,7 +186,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
}
} else {
if (!hasPrefix(path, "/"))
if (!path.starts_with("/"))
throw BadURL("flake reference '%s' is not an absolute path", url);
auto query = decodeQuery(match[2]);
path = canonPath(path + "/" + getOr(query, "dir", ""));

View file

@ -4,6 +4,7 @@
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#include <gc/gc_allocator.h>
@ -39,4 +40,4 @@ using SmallValueVector = SmallVector<Value *, nItems>;
template <size_t nItems>
using SmallTemporaryValueVector = SmallVector<Value, nItems>;
}
}

View file

@ -39,33 +39,16 @@ namespace nix {
static void initLoc(YYLTYPE * loc)
{
loc->first_line = loc->last_line = 1;
loc->first_column = loc->last_column = 1;
loc->first_line = loc->last_line = 0;
loc->first_column = loc->last_column = 0;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
loc->stash();
loc->first_line = loc->last_line;
loc->first_column = loc->last_column;
for (size_t i = 0; i < len; i++) {
switch (*s++) {
case '\r':
if (*s == '\n') { /* cr/lf */
i++;
s++;
}
/* fall through */
case '\n':
++loc->last_line;
loc->last_column = 1;
break;
default:
++loc->last_column;
}
}
loc->last_column += len;
}

153
src/libexpr/meson.build Normal file
View file

@ -0,0 +1,153 @@
parser_tab = custom_target(
input : 'parser.y',
output : [
'parser-tab.cc',
'parser-tab.hh',
],
command : [
'bison',
'-v',
'-o',
'@OUTPUT0@',
'@INPUT@',
'-d',
],
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
# an install script below which removes parser-tab.cc.
install : true,
install_dir : includedir / 'nix',
)
lexer_tab = custom_target(
input : [
'lexer.l',
parser_tab,
],
output : [
'lexer-tab.cc',
'lexer-tab.hh',
],
command : [
'flex',
'--outfile',
'@OUTPUT0@',
'--header-file=' + '@OUTPUT1@',
'@INPUT0@',
],
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
# an install script below which removes lexer-tab.cc.
install : true,
install_dir : includedir / 'nix',
)
# TODO(Qyriad): When the parser and lexer are rewritten this should be removed.
# NOTE(Qyriad): We do this this way instead of an inline bash or rm command
# due to subtleties in Meson. Check the comments in cleanup-install.bash for details.
meson.add_install_script(
bash,
meson.project_source_root() / 'meson/cleanup-install.bash',
'@0@'.format(includedir),
)
imported_drv_to_derivation_gen = gen_header.process('imported-drv-to-derivation.nix')
fetchurl_gen = gen_header.process('fetchurl.nix')
derivation_gen = gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir())
call_flake_gen = gen_header.process('flake/call-flake.nix')
libexpr_sources = files(
'attr-path.cc',
'attr-set.cc',
'eval-cache.cc',
'eval-error.cc',
'eval-settings.cc',
'eval.cc',
'function-trace.cc',
'get-drvs.cc',
'json-to-value.cc',
'nixexpr.cc',
'paths.cc',
'primops.cc',
'print-ambiguous.cc',
'print.cc',
'search-path.cc',
'value-to-json.cc',
'value-to-xml.cc',
'flake/config.cc',
'flake/flake.cc',
'flake/flakeref.cc',
'flake/lockfile.cc',
'primops/context.cc',
'primops/fetchClosure.cc',
'primops/fetchMercurial.cc',
'primops/fetchTree.cc',
'primops/fromTOML.cc',
'value/context.cc',
)
libexpr_headers = files(
'attr-path.hh',
'attr-set.hh',
'eval-cache.hh',
'eval-error.hh',
'eval-inline.hh',
'eval-settings.hh',
'eval.hh',
'flake/flake.hh',
'flake/flakeref.hh',
'flake/lockfile.hh',
'function-trace.hh',
'gc-small-vector.hh',
'get-drvs.hh',
'json-to-value.hh',
'nixexpr.hh',
'parser-state.hh',
'pos-idx.hh',
'pos-table.hh',
'primops.hh',
'print-ambiguous.hh',
'print-options.hh',
'print.hh',
'repl-exit-status.hh',
'search-path.hh',
'symbol-table.hh',
'value/context.hh',
'value-to-json.hh',
'value-to-xml.hh',
'value.hh',
)
libexpr = library(
'nixexpr',
libexpr_sources,
parser_tab,
lexer_tab,
imported_drv_to_derivation_gen,
fetchurl_gen,
derivation_gen,
call_flake_gen,
dependencies : [
liblixutil,
liblixstore,
liblixfetchers,
boehm,
boost,
],
# for shared.hh
include_directories : [
'../libmain',
],
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
install_headers(
libexpr_headers,
subdir : 'nix',
preserve_path : true,
)
liblixexpr = declare_dependency(
include_directories : include_directories('.'),
link_with : libexpr,
)

View file

@ -147,7 +147,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
if (hasFormals()) {
str << "{ ";
bool first = true;
for (auto & i : formals->formals) {
// the natural Symbol ordering is by creation time, which can lead to the
// same expression being printed in two different ways depending on its
// context. always use lexicographic ordering to avoid this.
for (auto & i : formals->lexicographicOrder(symbols)) {
if (first) first = false; else str << ", ";
str << symbols[i.name];
if (i.def) {
@ -480,7 +483,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, newEnv));
es.exprEnvs.insert(std::make_pair(this, env));
body->bindVars(es, newEnv);
}
@ -578,6 +581,39 @@ std::string ExprLambda::showNamePos(const EvalState & state) const
/* Position table. */
Pos PosTable::operator[](PosIdx p) const
{
auto origin = resolve(p);
if (!origin)
return {};
const auto offset = origin->offsetOf(p);
Pos result{0, 0, origin->origin};
auto lines = this->lines.lock();
auto & linesForInput = (*lines)[origin->offset];
if (linesForInput.empty()) {
auto source = result.getSource().value_or("");
const char * begin = source.data();
for (Pos::LinesIterator it(source), end; it != end; it++)
linesForInput.push_back(it->data() - begin);
if (linesForInput.empty())
linesForInput.push_back(0);
}
// as above: the first line starts at byte 0 and is always present
auto lineStartOffset = std::prev(
std::upper_bound(linesForInput.begin(), linesForInput.end(), offset));
result.line = 1 + (lineStartOffset - linesForInput.begin());
result.column = 1 + (offset - *lineStartOffset);
return result;
}
/* Symbol table. */
size_t SymbolTable::totalSize() const

View file

@ -7,7 +7,6 @@
#include "value.hh"
#include "symbol-table.hh"
#include "error.hh"
#include "chunked-vector.hh"
#include "position.hh"
#include "eval-error.hh"
#include "pos-idx.hh"

View file

@ -24,20 +24,15 @@ struct ParserLocation
int last_line, last_column;
// backup to recover from yyless(0)
int stashed_first_line, stashed_first_column;
int stashed_last_line, stashed_last_column;
int stashed_first_column, stashed_last_column;
void stash() {
stashed_first_line = first_line;
stashed_first_column = first_column;
stashed_last_line = last_line;
stashed_last_column = last_column;
}
void unstash() {
first_line = stashed_first_line;
first_column = stashed_first_column;
last_line = stashed_last_line;
last_column = stashed_last_column;
}
};
@ -90,10 +85,10 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
if (j != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
attrs = attrs2;
} else
dupAttr(attrPath, pos, j->second.pos);
dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
} else {
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
@ -275,7 +270,7 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
inline PosIdx ParserState::at(const ParserLocation & loc)
{
return positions.add(origin, loc.first_line, loc.first_column);
return positions.add(origin, loc.first_column);
}
}

View file

@ -62,6 +62,10 @@ using namespace nix;
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
{
if (std::string_view(error).starts_with("syntax error, unexpected end of file")) {
loc->first_column = loc->last_column;
loc->first_line = loc->last_line;
}
throw ParseError({
.msg = HintFmt(error),
.pos = state->positions[state->at(*loc)]
@ -85,6 +89,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
nix::StringToken uri;
nix::StringToken str;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
}
@ -95,7 +100,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
%type <attrs> binds
%type <formals> formals
%type <formal> formal
%type <attrNames> attrs attrpath
%type <attrNames> attrpath
%type <inheritAttrs> attrs
%type <string_parts> string_parts_interpolated
%type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr
@ -307,13 +313,12 @@ binds
: binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; }
| binds INHERIT attrs ';'
{ $$ = $1;
for (auto & i : *$3) {
for (auto & [i, iPos] : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
state->dupAttr(i.symbol, state->at(@3), $$->attrs[i.symbol].pos);
auto pos = state->at(@3);
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, ExprAttrs::AttrDef::Kind::Inherited));
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
}
delete $3;
}
@ -323,14 +328,14 @@ binds
$$->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
$$->inheritFromExprs->push_back($4);
auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
for (auto & i : *$6) {
for (auto & [i, iPos] : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos);
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(
new ExprSelect(CUR_POS, from, i.symbol),
state->at(@6),
new ExprSelect(iPos, from, i.symbol),
iPos,
ExprAttrs::AttrDef::Kind::InheritedFrom));
}
delete $6;
@ -339,12 +344,12 @@ binds
;
attrs
: attrs attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($2))); }
: attrs attr { $$ = $1; $1->emplace_back(AttrName(state->symbols.create($2)), state->at(@2)); }
| attrs string_attr
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($2);
if (str) {
$$->push_back(AttrName(state->symbols.create(str->s)));
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
delete str;
} else
throw ParseError({
@ -352,7 +357,7 @@ attrs
.pos = state->positions[state->at(@2)]
});
}
| { $$ = new AttrPath; }
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
;
attrpath
@ -430,7 +435,7 @@ Expr * parseExprFromBuf(
.symbols = symbols,
.positions = positions,
.basePath = basePath,
.origin = {origin},
.origin = positions.addOrigin(origin, length),
.s = astSymbols,
};

View file

@ -6,6 +6,7 @@ namespace nix {
class PosIdx
{
friend struct LazyPosAcessors;
friend class PosTable;
private:

View file

@ -7,6 +7,7 @@
#include "chunked-vector.hh"
#include "pos-idx.hh"
#include "position.hh"
#include "sync.hh"
namespace nix {
@ -17,66 +18,68 @@ public:
{
friend PosTable;
private:
// must always be invalid by default, add() replaces this with the actual value.
// subsequent add() calls use this index as a token to quickly check whether the
// current origins.back() can be reused or not.
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
uint32_t offset;
// Used for searching in PosTable::[].
explicit Origin(uint32_t idx)
: idx(idx)
, origin{std::monostate()}
{
}
Origin(Pos::Origin origin, uint32_t offset, size_t size):
offset(offset), origin(origin), size(size)
{}
public:
const Pos::Origin origin;
const size_t size;
Origin(Pos::Origin origin)
: origin(origin)
uint32_t offsetOf(PosIdx p) const
{
return p.id - 1 - offset;
}
};
struct Offset
{
uint32_t line, column;
};
private:
std::vector<Origin> origins;
ChunkedVector<Offset, 8192> offsets;
using Lines = std::vector<uint32_t>;
public:
PosTable()
: offsets(1024)
{
origins.reserve(1024);
}
std::map<uint32_t, Origin> origins;
mutable Sync<std::map<uint32_t, Lines>> lines;
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
const Origin * resolve(PosIdx p) const
{
const auto idx = offsets.add({line, column}).second;
if (origins.empty() || origins.back().idx != origin.idx) {
origin.idx = idx;
origins.push_back(origin);
}
return PosIdx(idx + 1);
}
if (p.id == 0)
return nullptr;
Pos operator[](PosIdx p) const
{
if (p.id == 0 || p.id > offsets.size())
return {};
const auto idx = p.id - 1;
/* we want the last key <= idx, so we'll take prev(first key > idx).
this is guaranteed to never rewind origin.begin because the first
key is always 0. */
const auto pastOrigin = std::upper_bound(
origins.begin(), origins.end(), Origin(idx), [](const auto & a, const auto & b) { return a.idx < b.idx; });
const auto origin = *std::prev(pastOrigin);
const auto offset = offsets[idx];
return {offset.line, offset.column, origin.origin};
this is guaranteed to never rewind origin.begin because the first
key is always 0. */
const auto pastOrigin = origins.upper_bound(idx);
return &std::prev(pastOrigin)->second;
}
public:
Origin addOrigin(Pos::Origin origin, size_t size)
{
uint32_t offset = 0;
if (auto it = origins.rbegin(); it != origins.rend())
offset = it->first + it->second.size;
// +1 because all PosIdx are offset by 1 to begin with (because noPos == 0), and
// another +1 to ensure that all origins can point to EOF, eg on (invalid) empty inputs.
if (2 + offset + size < offset)
return Origin{origin, offset, 0};
return origins.emplace(offset, Origin{origin, offset, size}).first->second;
}
PosIdx add(const Origin & origin, size_t offset)
{
if (offset > origin.size)
return PosIdx();
return PosIdx(1 + origin.offset + offset);
}
Pos operator[](PosIdx p) const;
Pos::Origin originOf(PosIdx p) const
{
if (auto o = resolve(p))
return o->origin;
return std::monostate{};
}
};

View file

@ -2492,6 +2492,54 @@ static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
.fun = prim_unsafeGetAttrPos,
});
// access to exact position information (ie, line and colum numbers) is deferred
// due to the cost associated with calculating that information and how rarely
// it is used in practice. this is achieved by creating thunks to otherwise
// inaccessible primops that are not exposed as __op or under builtins to turn
// the internal PosIdx back into a line and column number, respectively. exposing
// these primops in any way would at best be not useful and at worst create wildly
// indeterministic eval results depending on parse order of files.
//
// in a simpler world this would instead be implemented as another kind of thunk,
// but each type of thunk has an associated runtime cost in the current evaluator.
// as with black holes this cost is too high to justify another thunk type to check
// for in the very hot path that is forceValue.
static struct LazyPosAcessors {
PrimOp primop_lineOfPos{
.arity = 1,
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer)].line);
}
};
PrimOp primop_columnOfPos{
.arity = 1,
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer)].column);
}
};
Value lineOfPos, columnOfPos;
LazyPosAcessors()
{
lineOfPos.mkPrimOp(&primop_lineOfPos);
columnOfPos.mkPrimOp(&primop_columnOfPos);
}
void operator()(EvalState & state, const PosIdx pos, Value & line, Value & column)
{
Value * posV = state.allocValue();
posV->mkInt(pos.id);
line.mkApp(&lineOfPos, posV);
column.mkApp(&columnOfPos, posV);
}
} makeLazyPosAccessors;
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column)
{
makeLazyPosAccessors(state, pos, line, column);
}
/* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
@ -4299,25 +4347,16 @@ void EvalState::createBaseEnv()
.impureOnly = true,
});
v.mkString(nixVersion);
v.mkString("2.18.3-lix");
addConstant("__nixVersion", v, {
.type = nString,
.doc = R"(
The version of Nix.
Legacy version of Nix. Always returns "2.18.3-lix" on Lix.
For example, where the command line returns the current Nix version,
```shell-session
$ nix --version
nix (Nix) 2.16.0
```
the Nix language evaluator returns the same value:
```nix-repl
nix-repl> builtins.nixVersion
"2.16.0"
```
To determine if features exist, Nix scripts should instead use direct
means of feature detection, such as checking for existence of
builtins they want to use. Doing so allows for much better compatibility
across implementations.
)",
});

View file

@ -51,4 +51,6 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
*/
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
}

View file

@ -93,7 +93,7 @@ void printAmbiguous(
str << v.fpoint;
break;
default:
printError("Nix evaluator internal error: printAmbiguous: invalid value type");
printError("Lix evaluator internal error: printAmbiguous: invalid value type");
abort();
}
}

View file

@ -337,11 +337,12 @@ private:
auto prettyPrint = shouldPrettyPrintAttrs(sorted);
size_t printedHere = 0;
for (auto & i : sorted) {
printSpace(prettyPrint);
if (attrsPrinted >= options.maxAttrs) {
printElided(sorted.size() - attrsPrinted, "attribute", "attributes");
printElided(sorted.size() - printedHere, "attribute", "attributes");
break;
}
@ -350,6 +351,7 @@ private:
print(*i.second, depth + 1);
output << ";";
attrsPrinted++;
printedHere++;
}
decreaseIndent();
@ -394,11 +396,12 @@ private:
output << "[";
auto listItems = v.listItems();
auto prettyPrint = shouldPrettyPrintList(listItems);
size_t printedHere = 0;
for (auto elem : listItems) {
printSpace(prettyPrint);
if (listItemsPrinted >= options.maxListItems) {
printElided(v.listSize() - listItemsPrinted, "item", "items");
printElided(v.listSize() - printedHere, "item", "items");
break;
}
@ -408,6 +411,7 @@ private:
printNullptr();
}
listItemsPrinted++;
printedHere++;
}
decreaseIndent();

View file

@ -223,7 +223,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
Path actualPath(absPath(workdir));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath));
assert(p.starts_with(actualPath));
std::string file(p, actualPath.size() + 1);
auto st = lstat(p);
@ -231,7 +231,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
if (S_ISDIR(st.st_mode)) {
auto prefix = file + "/";
auto i = files.lower_bound(prefix);
return i != files.end() && hasPrefix(*i, prefix);
return i != files.end() && (*i).starts_with(prefix);
}
return files.count(file);
@ -267,7 +267,7 @@ struct GitInputScheme : InputScheme
url.scheme != "git+file") return {};
auto url2(url);
if (hasPrefix(url2.scheme, "git+")) url2.scheme = std::string(url2.scheme, 4);
if (url2.scheme.starts_with("git+")) url2.scheme = std::string(url2.scheme, 4);
url2.query.clear();
Attrs attrs;

View file

@ -181,7 +181,7 @@ struct MercurialInputScheme : InputScheme
Path actualPath(absPath(actualUrl));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath));
assert(p.starts_with(actualPath));
std::string file(p, actualPath.size() + 1);
auto st = lstat(p);
@ -189,7 +189,7 @@ struct MercurialInputScheme : InputScheme
if (S_ISDIR(st.st_mode)) {
auto prefix = file + "/";
auto i = files.lower_bound(prefix);
return i != files.end() && hasPrefix(*i, prefix);
return i != files.end() && (*i).starts_with(prefix);
}
return files.count(file);

View file

@ -0,0 +1,42 @@
libfetchers_sources = files(
'attrs.cc',
'cache.cc',
'fetch-settings.cc',
'fetch-to-store.cc',
'fetchers.cc',
'git.cc',
'github.cc',
'indirect.cc',
'mercurial.cc',
'path.cc',
'registry.cc',
'tarball.cc',
)
libfetchers_headers = files(
'attrs.hh',
'cache.hh',
'fetch-settings.hh',
'fetch-to-store.hh',
'fetchers.hh',
'registry.hh',
)
libfetchers = library(
'nixfetchers',
libfetchers_sources,
dependencies : [
liblixstore,
liblixutil,
],
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
install_headers(libfetchers_headers, subdir : 'nix', preserve_path : true)
liblixfetchers = declare_dependency(
include_directories : include_directories('.'),
link_with : libfetchers,
)

View file

@ -157,7 +157,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
return std::make_shared<Registry>(Registry::Global); // empty registry
}
if (!hasPrefix(path, "/")) {
if (!path.starts_with("/")) {
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json");

View file

@ -188,10 +188,10 @@ struct CurlInputScheme : InputScheme
const bool hasTarballExtension(std::string_view path) const
{
return hasSuffix(path, ".zip") || hasSuffix(path, ".tar")
|| hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz")
|| hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2")
|| hasSuffix(path, ".tar.zst");
return path.ends_with(".zip") || path.ends_with(".tar")
|| path.ends_with(".tgz") || path.ends_with(".tar.gz")
|| path.ends_with(".tar.xz") || path.ends_with(".tar.bz2")
|| path.ends_with(".tar.zst");
}
virtual bool isValidURL(const ParsedURL & url, bool requireTree) const = 0;

View file

@ -47,7 +47,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
std::map<std::string, Config::SettingInfo> settings;
globalConfig.getSettings(settings);
for (auto & s : settings)
if (hasPrefix(s.first, prefix))
if (s.first.starts_with(prefix))
completions->add(s.first, fmt("Set the `%s` setting.", s.first));
}
}

33
src/libmain/meson.build Normal file
View file

@ -0,0 +1,33 @@
libmain_sources = files(
'common-args.cc',
'loggers.cc',
'progress-bar.cc',
'shared.cc',
'stack.cc',
)
libmain_headers = files(
'common-args.hh',
'loggers.hh',
'progress-bar.hh',
'shared.hh',
)
libmain = library(
'nixmain',
libmain_sources,
dependencies : [
liblixutil,
liblixstore,
],
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
install_headers(libmain_headers, subdir : 'nix', preserve_path : true)
liblixmain = declare_dependency(
include_directories : include_directories('.'),
link_with : libmain,
)

View file

@ -188,7 +188,7 @@ public:
if (type == actBuild) {
std::string name(storePathToName(getS(fields, 0)));
if (hasSuffix(name, ".drv"))
if (name.ends_with(".drv"))
name = name.substr(0, name.size() - 4);
i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
auto machineName = getS(fields, 1);
@ -207,7 +207,7 @@ public:
auto name = storePathToName(getS(fields, 0));
auto sub = getS(fields, 1);
i->s = fmt(
hasPrefix(sub, "local")
sub.starts_with("local")
? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
: "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
name, sub);
@ -215,7 +215,7 @@ public:
if (type == actPostBuildHook) {
auto name = storePathToName(getS(fields, 0));
if (hasSuffix(name, ".drv"))
if (name.ends_with(".drv"))
name = name.substr(0, name.size() - 4);
i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name);
i->name = DrvName(name).name;

View file

@ -114,10 +114,8 @@ static void sigHandler(int signo) { }
void initNix()
{
/* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
static char buf[1024];
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
#endif
initLibStore();

View file

@ -124,7 +124,7 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
AutoCloseFD openFile(const Path & path)
{
auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
if (!fd)
throw SysError("opening file '%1%'", path);
return fd;

View file

@ -846,8 +846,8 @@ int DerivationGoal::getChildStatus()
void DerivationGoal::closeReadPipes()
{
hook->builderOut.readSide = -1;
hook->fromHook.readSide = -1;
hook->builderOut.readSide.reset();
hook->fromHook.readSide.reset();
}
@ -1227,7 +1227,7 @@ HookReply DerivationGoal::tryBuildHook()
}
hook->sink = FdSink();
hook->toHook.writeSide = -1;
hook->toHook.writeSide.reset();
/* Create the log file and pipe. */
Path logFile = openLogFile();
@ -1273,7 +1273,7 @@ Path DerivationGoal::openLogFile()
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
settings.compressLog ? ".bz2" : "");
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
fdLogFile = AutoCloseFD{open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666)};
if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName);
logFileSink = std::make_shared<FdSink>(fdLogFile.get());
@ -1293,7 +1293,7 @@ void DerivationGoal::closeLogFile()
if (logSink2) logSink2->finish();
if (logFileSink) logFileSink->flush();
logSink = logFileSink = 0;
fdLogFile = -1;
fdLogFile.reset();
}

View file

@ -61,8 +61,8 @@ HookInstance::HookInstance()
});
pid.setSeparatePG(true);
fromHook.writeSide = -1;
toHook.readSide = -1;
fromHook.writeSide.reset();
toHook.readSide.reset();
sink = FdSink(toHook.writeSide.get());
std::map<std::string, Config::SettingInfo> settings;
@ -76,7 +76,7 @@ HookInstance::HookInstance()
HookInstance::~HookInstance()
{
try {
toHook.writeSide = -1;
toHook.writeSide.reset();
if (pid != -1) pid.kill();
} catch (...) {
ignoreException();

View file

@ -309,8 +309,8 @@ void LocalDerivationGoal::cleanupHookFinally()
void LocalDerivationGoal::cleanupPreChildKill()
{
sandboxMountNamespace = -1;
sandboxUserNamespace = -1;
sandboxMountNamespace.reset();
sandboxUserNamespace.reset();
}
@ -596,7 +596,7 @@ void LocalDerivationGoal::startBuilder()
else
pathsInChroot[i.substr(0, p)] = {i.substr(p + 1), optional};
}
if (hasPrefix(worker.store.storeDir, tmpDirInSandbox))
if (worker.store.storeDir.starts_with(tmpDirInSandbox))
{
throw Error("`sandbox-build-dir` must not contain the storeDir");
}
@ -807,7 +807,7 @@ void LocalDerivationGoal::startBuilder()
Path logFile = openLogFile();
/* Create a pseudoterminal to get the output of the builder. */
builderOut = posix_openpt(O_RDWR | O_NOCTTY);
builderOut = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)};
if (!builderOut)
throw SysError("opening pseudoterminal master");
@ -834,7 +834,7 @@ void LocalDerivationGoal::startBuilder()
/* Open the slave side of the pseudoterminal and use it as stderr. */
auto openSlave = [&]()
{
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
AutoCloseFD builderOut{open(slaveName.c_str(), O_RDWR | O_NOCTTY)};
if (!builderOut)
throw SysError("opening pseudoterminal slave");
@ -937,12 +937,12 @@ void LocalDerivationGoal::startBuilder()
if (helper.wait() != 0)
throw Error("unable to start build process");
userNamespaceSync.readSide = -1;
userNamespaceSync.readSide.reset();
/* Close the write side to prevent runChild() from hanging
reading from this. */
Finally cleanup([&]() {
userNamespaceSync.writeSide = -1;
userNamespaceSync.writeSide.reset();
});
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
@ -981,12 +981,12 @@ void LocalDerivationGoal::startBuilder()
/* Save the mount- and user namespace of the child. We have to do this
*before* the child does a chroot. */
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)};
if (sandboxMountNamespace.get() == -1)
throw SysError("getting sandbox mount namespace");
if (usingUserNamespace) {
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY)};
if (sandboxUserNamespace.get() == -1)
throw SysError("getting sandbox user namespace");
}
@ -1471,8 +1471,8 @@ void LocalDerivationGoal::startDaemon()
struct sockaddr_un remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
AutoCloseFD remote = accept(daemonSocket.get(),
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
AutoCloseFD remote{accept(daemonSocket.get(),
(struct sockaddr *) &remoteAddr, &remoteAddrLen)};
if (!remote) {
if (errno == EINTR || errno == EAGAIN) continue;
if (errno == EINVAL || errno == ECONNABORTED) break;
@ -1705,12 +1705,12 @@ void LocalDerivationGoal::runChild()
#if __linux__
if (useChroot) {
userNamespaceSync.writeSide = -1;
userNamespaceSync.writeSide.reset();
if (drainFD(userNamespaceSync.readSide.get()) != "1")
throw Error("user namespace initialisation failed");
userNamespaceSync.readSide = -1;
userNamespaceSync.readSide.reset();
if (privateNetwork) {

View file

@ -53,13 +53,13 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
* Python package brings its own
* `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
*/
if (hasSuffix(srcFile, "/propagated-build-inputs") ||
hasSuffix(srcFile, "/nix-support") ||
hasSuffix(srcFile, "/perllocal.pod") ||
hasSuffix(srcFile, "/info/dir") ||
hasSuffix(srcFile, "/log") ||
hasSuffix(srcFile, "/manifest.nix") ||
hasSuffix(srcFile, "/manifest.json"))
if (srcFile.ends_with("/propagated-build-inputs") ||
srcFile.ends_with("/nix-support") ||
srcFile.ends_with("/perllocal.pod") ||
srcFile.ends_with("/info/dir") ||
srcFile.ends_with("/log") ||
srcFile.ends_with("/manifest.nix") ||
srcFile.ends_with("/manifest.json"))
continue;
else if (S_ISDIR(srcSt.st_mode)) {

View file

@ -41,7 +41,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
request.decompress = false;
auto decompressor = makeDecompressionSink(
unpack && hasSuffix(mainUrl, ".xz") ? "xz" : "none", sink);
unpack && mainUrl.ends_with(".xz") ? "xz" : "none", sink);
fileTransfer->download(std::move(request), *decompressor);
decompressor->finish();
});
@ -62,7 +62,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
if (getAttr("outputHashMode") == "flat")
for (auto hashedMirror : settings.hashedMirrors.get())
try {
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
if (!hashedMirror.ends_with("/")) hashedMirror += '/';
std::optional<HashType> ht = parseHashTypeOpt(getAttr("outputHashAlgo"));
Hash h = newHashAllowEmpty(getAttr("outputHash"), ht);
fetch(hashedMirror + printHashType(h.type) + "/" + h.to_string(Base16, false));

View file

@ -221,7 +221,7 @@ struct ClientSettings
for (auto & s : ss)
if (trusted.count(s))
subs.push_back(s);
else if (!hasSuffix(s, "/") && trusted.count(s + "/"))
else if (!s.ends_with("/") && trusted.count(s + "/"))
subs.push_back(s + "/");
else
warn("ignoring untrusted substituter '%s', you are not a trusted user.\n"
@ -453,9 +453,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
command. (We don't trust `addToStoreFromDump` to not
eagerly consume the entire stream it's given, past the
length of the Nar. */
TeeSource savedNARSource(from, saved);
ParseSink sink; /* null sink; just parse the NAR */
parseDump(sink, savedNARSource);
copyNAR(from, saved);
} else {
/* Incrementally parse the NAR file, stripping the
metadata, and streaming the sole file we expect into
@ -907,9 +905,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
source = std::make_unique<TunnelSource>(from, to);
else {
TeeSource tee { from, saved };
ParseSink ether;
parseDump(ether, tee);
copyNAR(from, saved);
source = std::make_unique<StringSource>(saved.s);
}
@ -1090,7 +1086,7 @@ void processConnection(
tunnelLogger->stopWork(&e);
if (!errorAllowed) throw;
} catch (std::bad_alloc & e) {
auto ex = Error("Nix daemon out of memory");
auto ex = Error("Lix daemon out of memory");
tunnelLogger->stopWork(&ex);
throw;
}

View file

@ -658,7 +658,7 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
// FIXME: remove
bool isDerivation(std::string_view fileName)
{
return hasSuffix(fileName, drvExtension);
return fileName.ends_with(drvExtension);
}
@ -927,7 +927,7 @@ std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath)
{
auto nameWithSuffix = drvPath.name();
constexpr std::string_view extension = ".drv";
assert(hasSuffix(nameWithSuffix, extension));
assert(nameWithSuffix.ends_with(extension));
nameWithSuffix.remove_suffix(extension.size());
return nameWithSuffix;
}

View file

@ -64,9 +64,7 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
/* Extract the NAR from the source. */
StringSink saved;
TeeSource tee { source, saved };
ParseSink ether;
parseDump(ether, tee);
copyNAR(source, saved);
uint32_t magic = readInt(source);
if (magic != exportMagic)

View file

@ -670,8 +670,8 @@ struct curlFileTransfer : public FileTransfer
void enqueueItem(std::shared_ptr<TransferItem> item)
{
if (item->request.data
&& !hasPrefix(item->request.uri, "http://")
&& !hasPrefix(item->request.uri, "https://"))
&& !item->request.uri.starts_with("http://")
&& !item->request.uri.starts_with("https://"))
throw nix::Error("uploading to '%s' is not supported", item->request.uri);
{
@ -703,7 +703,7 @@ struct curlFileTransfer : public FileTransfer
Callback<FileTransferResult> callback) override
{
/* Ugly hack to support s3:// URIs. */
if (hasPrefix(request.uri, "s3://")) {
if (request.uri.starts_with("s3://")) {
// FIXME: do this on a worker thread
try {
#if ENABLE_S3

View file

@ -549,7 +549,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (fds[1].revents) {
/* Accept a new connection. */
assert(fds[1].revents & POLLIN);
AutoCloseFD fdClient = accept(fdServer.get(), nullptr, nullptr);
AutoCloseFD fdClient{accept(fdServer.get(), nullptr, nullptr)};
if (!fdClient) continue;
debug("GC roots server accepted new client");
@ -647,7 +647,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
by another process. We need to be sure that we can acquire an
exclusive lock before deleting them. */
if (baseName.find("tmp-", 0) == 0) {
AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY);
AutoCloseFD tmpDirFd{open(realPath.c_str(), O_RDONLY | O_DIRECTORY)};
if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) {
debug("skipping locked tempdir '%s'", realPath);
return;

View file

@ -241,7 +241,7 @@ bool Settings::isWSL1()
uname(&utsbuf);
// WSL1 uses -Microsoft suffix
// WSL2 uses -microsoft-standard suffix
return hasSuffix(utsbuf.release, "-Microsoft");
return std::string_view(utsbuf.release).ends_with("-Microsoft");
}
Path Settings::getDefaultSSLCertFile()
@ -342,7 +342,7 @@ void initPlugins()
void *handle =
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
throw Error("could not dynamically open plugin file '%s': %s", file, dlerror());
warn("could not dynamically open plugin file '%s': %s", file, dlerror());
}
}
@ -402,8 +402,6 @@ void assertLibStoreInitialized() {
void initLibStore() {
initLibUtil();
if (sodium_init() == -1)
throw Error("could not initialise libsodium");
@ -415,7 +413,7 @@ void initLibStore() {
sshd). This breaks build users because they don't have access
to the TMPDIR, in particular in nix-store --serve. */
#if __APPLE__
if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
if (getEnv("TMPDIR").value_or("/tmp").starts_with("/var/folders/"))
unsetenv("TMPDIR");
#endif

View file

@ -145,7 +145,7 @@ protected:
FileTransferRequest makeRequest(const std::string & path)
{
return FileTransferRequest(
hasPrefix(path, "https://") || hasPrefix(path, "http://") || hasPrefix(path, "file://")
path.starts_with("https://") || path.starts_with("http://") || path.starts_with("file://")
? path
: cacheUri + "/" + path);

View file

@ -124,20 +124,9 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
conn->to << SERVE_MAGIC_1 << SERVE_PROTOCOL_VERSION;
conn->to.flush();
StringSink saved;
try {
TeeSource tee(conn->from, saved);
unsigned int magic = readInt(tee);
if (magic != SERVE_MAGIC_2)
throw Error("'nix-store --serve' protocol mismatch from '%s'", host);
} catch (SerialisationError & e) {
/* In case the other side is waiting for our input,
close it. */
conn->sshConn->in.close();
auto msg = conn->from.drain();
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
host, chomp(saved.s + msg));
}
uint64_t magic = readLongLong(conn->from);
if (magic != SERVE_MAGIC_2)
throw Error("'nix-store --serve' protocol mismatch from '%s'", host);
conn->remoteVersion = readInt(conn->from);
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host);

View file

@ -85,7 +85,7 @@ protected:
for (auto & entry : readDirectory(binaryCacheDir)) {
if (entry.name.size() != 40 ||
!hasSuffix(entry.name, ".narinfo"))
!entry.name.ends_with(".narinfo"))
continue;
paths.insert(parseStorePath(
storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)

View file

@ -263,7 +263,7 @@ LocalStore::LocalStore(const Params & params)
if (stat(reservedPath.c_str(), &st) == -1 ||
st.st_size != settings.reservedSize)
{
AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
AutoCloseFD fd{open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600)};
int res = -1;
#if HAVE_POSIX_FALLOCATE
res = posix_fallocate(fd.get(), 0, settings.reservedSize);
@ -453,7 +453,7 @@ LocalStore::LocalStore(std::string scheme, std::string path, const Params & para
AutoCloseFD LocalStore::openGCLock()
{
Path fnGCLock = stateDir + "/gc.lock";
auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
AutoCloseFD fdGCLock{open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
if (!fdGCLock)
throw SysError("opening global GC lock '%1%'", fnGCLock);
return fdGCLock;
@ -478,7 +478,7 @@ LocalStore::~LocalStore()
try {
auto fdTempRoots(_fdTempRoots.lock());
if (*fdTempRoots) {
*fdTempRoots = -1;
fdTempRoots->reset();
unlink(fnTempRoots.c_str());
}
} catch (...) {
@ -1484,7 +1484,7 @@ std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
the GC between createTempDir() and when we acquire a lock on it.
We'll repeat until 'tmpDir' exists and we've locked it. */
tmpDirFn = createTempDir(realStoreDir, "tmp");
tmpDirFd = open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY);
tmpDirFd = AutoCloseFD{open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY)};
if (tmpDirFd.get() < 0) {
continue;
}

View file

@ -76,7 +76,7 @@ struct SimpleUserLock : UserLock
auto fnUserLock = fmt("%s/userpool/%s", settings.nixStateDir,pw->pw_uid);
AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
if (!fd)
throw SysError("opening user lock '%s'", fnUserLock);
@ -148,7 +148,7 @@ struct AutoUserLock : UserLock
auto fnUserLock = fmt("%s/userpool2/slot-%d", settings.nixStateDir, i);
AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
if (!fd)
throw SysError("opening user lock '%s'", fnUserLock);

View file

@ -24,10 +24,10 @@ Machine::Machine(decltype(storeUri) storeUri,
|| storeUri == "auto"
|| storeUri == "daemon"
|| storeUri == "local"
|| hasPrefix(storeUri, "auto?")
|| hasPrefix(storeUri, "daemon?")
|| hasPrefix(storeUri, "local?")
|| hasPrefix(storeUri, "?")
|| storeUri.starts_with("auto?")
|| storeUri.starts_with("daemon?")
|| storeUri.starts_with("local?")
|| storeUri.starts_with("?")
? storeUri
: "ssh://" + storeUri),
systemTypes(systemTypes),
@ -67,12 +67,12 @@ bool Machine::mandatoryMet(const std::set<std::string> & features) const
ref<Store> Machine::openStore() const
{
Store::Params storeParams;
if (hasPrefix(storeUri, "ssh://")) {
if (storeUri.starts_with("ssh://")) {
storeParams["max-connections"] = "1";
storeParams["log-fd"] = "4";
}
if (hasPrefix(storeUri, "ssh://") || hasPrefix(storeUri, "ssh-ng://")) {
if (storeUri.starts_with("ssh://") || storeUri.starts_with("ssh-ng://")) {
if (sshKey != "")
storeParams["ssh-key"] = sshKey;
if (sshPublicHostKey != "")

188
src/libstore/meson.build Normal file
View file

@ -0,0 +1,188 @@
schema_sql_gen = gen_header.process('schema.sql')
ca_specific_schema_gen = gen_header.process('ca-specific-schema.sql')
libstore_sources = files(
'binary-cache-store.cc',
'build-result.cc',
'common-protocol.cc',
'content-address.cc',
'crypto.cc',
'daemon.cc',
'derivations.cc',
'derived-path-map.cc',
'derived-path.cc',
'downstream-placeholder.cc',
'dummy-store.cc',
'export-import.cc',
'filetransfer.cc',
'gc.cc',
'globals.cc',
'http-binary-cache-store.cc',
'legacy-ssh-store.cc',
'local-binary-cache-store.cc',
'local-fs-store.cc',
'local-store.cc',
'lock.cc',
'log-store.cc',
'machines.cc',
'make-content-addressed.cc',
'misc.cc',
'names.cc',
'nar-accessor.cc',
'nar-info-disk-cache.cc',
'nar-info.cc',
'optimise-store.cc',
'outputs-spec.cc',
'parsed-derivations.cc',
'path-info.cc',
'path-references.cc',
'path-with-outputs.cc',
'path.cc',
'pathlocks.cc',
'profiles.cc',
'realisation.cc',
'remote-fs-accessor.cc',
'remote-store.cc',
's3-binary-cache-store.cc',
'serve-protocol.cc',
'sqlite.cc',
'ssh-store.cc',
'ssh.cc',
'store-api.cc',
'uds-remote-store.cc',
'worker-protocol.cc',
'build/derivation-goal.cc',
'build/drv-output-substitution-goal.cc',
'build/entry-points.cc',
'build/goal.cc',
'build/hook-instance.cc',
'build/local-derivation-goal.cc',
'build/personality.cc',
'build/substitution-goal.cc',
'build/worker.cc',
'builtins/buildenv.cc',
'builtins/fetchurl.cc',
'builtins/unpack-channel.cc',
)
libstore_headers = files(
'binary-cache-store.hh',
'build/derivation-goal.hh',
'build/drv-output-substitution-goal.hh',
'build/goal.hh',
'build/hook-instance.hh',
'build/local-derivation-goal.hh',
'build/personality.hh',
'build/substitution-goal.hh',
'build/worker.hh',
'build-result.hh',
'builtins/buildenv.hh',
'builtins.hh',
'common-protocol-impl.hh',
'common-protocol.hh',
'content-address.hh',
'crypto.hh',
'daemon.hh',
'derivations.hh',
'derived-path-map.hh',
'derived-path.hh',
'downstream-placeholder.hh',
'filetransfer.hh',
'fs-accessor.hh',
'gc-store.hh',
'globals.hh',
'indirect-root-store.hh',
'length-prefixed-protocol-helper.hh',
'local-fs-store.hh',
'local-store.hh',
'lock.hh',
'log-store.hh',
'machines.hh',
'make-content-addressed.hh',
'names.hh',
'nar-accessor.hh',
'nar-info-disk-cache.hh',
'nar-info.hh',
'outputs-spec.hh',
'parsed-derivations.hh',
'path-info.hh',
'path-references.hh',
'path-regex.hh',
'path-with-outputs.hh',
'path.hh',
'pathlocks.hh',
'profiles.hh',
'realisation.hh',
'remote-fs-accessor.hh',
'remote-store-connection.hh',
'remote-store.hh',
's3-binary-cache-store.hh',
's3.hh',
'serve-protocol-impl.hh',
'serve-protocol.hh',
'sqlite.hh',
'ssh-store-config.hh',
'ssh.hh',
'store-api.hh',
'store-cast.hh',
'uds-remote-store.hh',
'worker-protocol-impl.hh',
'worker-protocol.hh',
)
# These variables (aside from LSOF) are created pseudo-dynamically, near the beginning of
# the top-level meson.build. Aside from prefix itself, each of these was
# made into an absolute path by joining it with prefix, unless it was already
# an absolute path (which is the default for store-dir, state-dir, and log-dir).
cpp_str_defines = {
'LSOF': lsof.full_path(),
'NIX_PREFIX': prefix,
'NIX_STORE_DIR': store_dir,
'NIX_DATA_DIR': datadir,
'NIX_STATE_DIR': state_dir,
'NIX_LOG_DIR': log_dir,
'NIX_CONF_DIR': sysconfdir,
'NIX_BIN_DIR': bindir,
'NIX_MAN_DIR': mandir,
}
cpp_args = []
foreach name, value : cpp_str_defines
cpp_args += [
'-D' + name + '=' + '"' + value + '"'
]
endforeach
libstore = library(
'nixstore',
schema_sql_gen,
ca_specific_schema_gen,
libstore_sources,
dependencies : [
libarchive,
liblixutil, # Internal.
seccomp,
sqlite,
sodium,
seccomp,
curl,
openssl,
aws_sdk,
aws_s3,
aws_sdk_transfer,
],
cpp_args : cpp_args,
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
install_headers(libstore_headers, subdir : 'nix', preserve_path : true)
# Used by libfetchers.
liblixstore = declare_dependency(
include_directories : include_directories('.'),
link_with : libstore,
)

View file

@ -42,7 +42,7 @@ StorePath::StorePath(const Hash & hash, std::string_view _name)
bool StorePath::isDerivation() const
{
return hasSuffix(name(), drvExtension);
return name().ends_with(drvExtension);
}
StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");

View file

@ -17,9 +17,7 @@ namespace nix {
AutoCloseFD openLockFile(const Path & path, bool create)
{
AutoCloseFD fd;
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
AutoCloseFD fd{open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600)};
if (!fd && (create || errno != ENOENT))
throw SysError("opening lock file '%1%'", path);

View file

@ -71,7 +71,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
auto narAccessor = makeLazyNarAccessor(listing,
[cacheFile](uint64_t offset, uint64_t length) {
AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC);
AutoCloseFD fd{open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC)};
if (!fd)
throw SysError("opening NAR cache file '%s'", cacheFile);

View file

@ -69,19 +69,10 @@ void RemoteStore::initConnection(Connection & conn)
conn.from.endOfFileError = "Nix daemon disconnected unexpectedly (maybe it crashed?)";
conn.to << WORKER_MAGIC_1;
conn.to.flush();
StringSink saved;
try {
TeeSource tee(conn.from, saved);
unsigned int magic = readInt(tee);
if (magic != WORKER_MAGIC_2)
throw Error("protocol mismatch");
} catch (SerialisationError & e) {
/* In case the other side is waiting for our input, close
it. */
conn.closeWrite();
auto msg = conn.from.drain();
throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg));
}
uint64_t magic = readLongLong(conn.from);
if (magic != WORKER_MAGIC_2)
throw Error("protocol mismatch");
conn.from >> conn.daemonVersion;
if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))

View file

@ -213,7 +213,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
support it.
> **Note**
>
>
> HTTPS should be used if the cache might contain sensitive
> information.
)"};
@ -224,7 +224,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
Do not specify this setting if you're using Amazon S3.
> **Note**
>
>
> This endpoint must support HTTPS and will use path-based
> addressing instead of virtual host based addressing.
)"};
@ -448,11 +448,11 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
return std::make_shared<std::stringstream>(std::move(compressed));
};
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
if (narinfoCompression != "" && path.ends_with(".narinfo"))
uploadFile(path, compress(narinfoCompression), mimeType, narinfoCompression);
else if (lsCompression != "" && hasSuffix(path, ".ls"))
else if (lsCompression != "" && path.ends_with(".ls"))
uploadFile(path, compress(lsCompression), mimeType, lsCompression);
else if (logCompression != "" && hasPrefix(path, "log/"))
else if (logCompression != "" && path.starts_with("log/"))
uploadFile(path, compress(logCompression), mimeType, logCompression);
else
uploadFile(path, istream, mimeType, "");
@ -499,7 +499,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
for (auto object : contents) {
auto & key = object.GetKey();
if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue;
if (key.size() != 40 || !key.ends_with(".narinfo")) continue;
paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-" + MissingName));
}

View file

@ -12,7 +12,7 @@ SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, cons
, compress(compress)
, logFD(logFD)
{
if (host == "" || hasPrefix(host, "-"))
if (host == "" || host.starts_with("-"))
throw Error("invalid SSH host name '%s'", host);
auto state(state_.lock());
@ -100,8 +100,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string
}, options);
in.readSide = -1;
out.writeSide = -1;
in.readSide.reset();
out.writeSide.reset();
// Wait for the SSH connection to be established,
// So that we don't overwrite the password prompt with our progress bar.
@ -162,7 +162,7 @@ Path SSHMaster::startMaster()
throw SysError("unable to execute '%s'", args.front());
}, options);
out.writeSide = -1;
out.writeSide.reset();
std::string reply;
try {

View file

@ -44,7 +44,7 @@ static void dumpContents(const Path & path, off_t size,
{
sink << "contents" << size;
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
if (!fd) throw SysError("opening file '%1%'", path);
std::vector<char> buf(65536);
@ -318,7 +318,7 @@ struct RestoreSink : ParseSink
void createRegularFile(const Path & path) override
{
Path p = dstPath + path;
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
fd = AutoCloseFD{open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)};
if (!fd) throw SysError("creating file '%1%'", p);
}

View file

@ -162,7 +162,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
if (auto prefix = needsCompletion(*pos)) {
for (auto & [name, flag] : longFlags) {
if (!hiddenCategories.count(flag->category)
&& hasPrefix(name, std::string(*prefix, 2)))
&& name.starts_with(std::string(*prefix, 2)))
{
if (auto & f = flag->experimentalFeature)
flagExperimentalFeatures.insert(*f);
@ -274,7 +274,7 @@ nlohmann::json Args::toJSON()
static void hashTypeCompleter(size_t index, std::string_view prefix)
{
for (auto & type : hashTypes)
if (hasPrefix(type, prefix))
if (type.starts_with(prefix))
completions->add(type);
}
@ -370,7 +370,7 @@ MultiCommand::MultiCommand(const Commands & commands_)
}},
.completer = {[&](size_t, std::string_view prefix) {
for (auto & [name, command] : commands)
if (hasPrefix(name, prefix))
if (name.starts_with(prefix))
completions->add(name);
}}
});

View file

@ -41,7 +41,7 @@ std::map<std::string, std::string> getCgroups(const Path & cgroupFile)
if (!std::regex_match(line, match, regex))
throw Error("invalid line '%s' in '%s'", line, cgroupFile);
std::string name = hasPrefix(std::string(match[2]), "name=") ? std::string(match[2], 5) : match[2];
std::string name = std::string(match[2]).starts_with("name=") ? std::string(match[2], 5) : match[2];
cgroups.insert_or_assign(name, match[3]);
}
@ -117,13 +117,13 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats)
if (pathExists(cpustatPath)) {
for (auto & line : tokenizeString<std::vector<std::string>>(readFile(cpustatPath), "\n")) {
std::string_view userPrefix = "user_usec ";
if (hasPrefix(line, userPrefix)) {
if (line.starts_with(userPrefix)) {
auto n = string2Int<uint64_t>(line.substr(userPrefix.size()));
if (n) stats.cpuUser = std::chrono::microseconds(*n);
}
std::string_view systemPrefix = "system_usec ";
if (hasPrefix(line, systemPrefix)) {
if (line.starts_with(systemPrefix)) {
auto n = string2Int<uint64_t>(line.substr(systemPrefix.size()));
if (n) stats.cpuSystem = std::chrono::microseconds(*n);
}

View file

@ -18,7 +18,7 @@ bool Config::set(const std::string & name, const std::string & value)
bool append = false;
auto i = _settings.find(name);
if (i == _settings.end()) {
if (hasPrefix(name, "extra-")) {
if (name.starts_with("extra-")) {
i = _settings.find(std::string(name, 6));
if (i == _settings.end() || !i->second.setting->isAppendable())
return false;

Some files were not shown because too many files have changed in this diff Show more