Compare commits

...
Author SHA1 Message Date
Qyriad 9697754418 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-18 19:32:37 -06: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 horrorsandGerrit Code Review 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 Lovelace 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
jadeandGerrit Code Review 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
jadeandGerrit Code Review 2890840b96 Merge "Forgejo issue importer" into main 2024-03-17 22:08:59 -06:00
Jade Lovelace 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
jadeandGerrit Code Review 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 Lovelace 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 https://git.lix.systems/lix-project/lix/issues/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 Lovelace 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
rbtandGerrit Code Review 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 Lovelace 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 Lovelace 7d361f1a82 Test that :st does ... something
Change-Id: I97c00b5eb1288f68d8c2b484436cc185d040b8b2
2024-03-15 12:31:16 -07:00
Jade Lovelace af066af7f3 repl_characterization: Also verify the stack trace exists
Change-Id: I8b2d8211a24011fae1586a1182d7d0772a039cd7
2024-03-15 12:31:16 -07:00
Jade Lovelace 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 Lovelace 8a8715af89 Add regression tests for #9917, #9918
Change-Id: Ib0591e1499c5dba5e5a83ee75a899c9d16986827
2024-03-15 12:31:16 -07:00
Jade Lovelace 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 Lovelace 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 HelblingandRebecca Turner 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 MeerburgandGerrit Code Review caded0d55e Merge "Delete the existing installer" into main 2024-03-14 13:14:21 -06:00
jadeandGerrit Code Review 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
jadeandGerrit Code Review cc55da201b Merge "un-thumbs-up ur github templates" into main 2024-03-14 12:22:09 -06:00
Puck Meerburg 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 Lovelace 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
jadeandGerrit Code Review 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 Lovelace 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
eldritch horrors 06952cf7c4 support <program>_ENV variables
this lets us set per-test-program environment variables rather than only
a single, global default. this was supported in nix originally but
might've gone partially missing in the upstream backports process?

Change-Id: Iad0919841b1b6d11e0b7ebd3920449a62f544e77
2024-03-13 19:48:26 +01:00
Rebecca TurnerandJade Lovelace 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
Jade Lovelace 1b8662b85c import the revisions to the characterization test framework from cppnix
This has some Flaws for sure (like, it is going to be a bit stretched to
use for repl characterization), but it is a start.

Change-Id: I258c8beb3aee236f45818a03be83bcda858120c9
2024-03-11 14:14:43 -07:00
Jade Lovelace be2b87ed4d add automated usage mode to the repl
This is definitely not a stable thing, but it does feel slightly crimes
to put it as an experimental feature. Shrug, up for bikeshedding.

Change-Id: I6ef176e3dee6fb1cac9c0a7a60d553a2c63ea728
2024-03-11 14:14:43 -07:00
jadeandGerrit Code Review b06a392114 Merge "refactor: repl prompts are now the job of the interacter" into main 2024-03-11 15:12:09 -06:00
jadeandGerrit Code Review dd05106d1c Merge "refactor: move readline stuff into its own file" into main 2024-03-11 15:11:56 -06:00
jadeandGerrit Code Review df2723b972 Merge "finally.hh: delete copy constructor which is a bad idea" into main 2024-03-11 15:11:20 -06:00
jadeandGerrit Code Review d9367da027 Merge "Add box_ptr: nonnull unique_ptr with value semantics" into main 2024-03-11 15:11:15 -06:00
Qyriad SzekelyandGerrit Code Review f9701da8f6 Merge changes from topic "maint/flake-package.nix" into main
* changes:
  package: cleanup of all intermediaries
  package: migrate devShells
  package: migrate internal-api-docs
  package: migrate testNixVersions
  package: use pname, version, and dontBuild (first change with diff hash)
  package: refactor Nix out of flake.nix and into package.nix
2024-03-11 14:32:37 -06:00
jadeandGerrit Code Review 50c401b4c1 Merge "util.hh: split out signals stuff" into main 2024-03-11 11:14:24 -06:00
Qyriad 36a8d151e1 package: cleanup of all intermediaries
Change-Id: I0da5182de6b01c192cfcba407959d659d70c6dc9
2024-03-11 04:26:35 -06:00
Qyriad 529a01ade2 package: migrate devShells
Change-Id: Ic63721667edd4bef79aa699a0de8411639e5159b
2024-03-11 04:26:35 -06:00
Qyriad b072c069b7 package: migrate internal-api-docs
Change-Id: I344d73a412c2c6e4bb2eb14bd4859056324f1ba7
2024-03-11 04:26:35 -06:00
Qyriad 4ad3446311 package: migrate testNixVersions
Change-Id: I71845f8a6d7b77c3617d055e726ed4a28cd05fa3
2024-03-11 04:26:35 -06:00
Qyriad 875b76d0c7 package: use pname, version, and dontBuild (first change with diff hash)
The src fileset, preConfigure, and separateDebugInfo also respond to doBuild if its overridden

This commit is logically just a continuation of the previous commit's
refactor, but exists separately to delineate when the core Nix
derivation hash changed (this commit).

Change-Id: I67a61bc9608d91b6a833ebc5c3894b2d2e694050
2024-03-11 04:26:35 -06:00
Qyriad 15380b4c6e package: refactor Nix out of flake.nix and into package.nix
This series takes a somewhat different approach from the flake rework
done in NixOS/nix. The package.nix here does not provide callPackage
options for all the various settings in the build, and instead the other
places Nix derivations are used (like internal-api-docs) will .overrideAttrs
the normal Nix package derivation. This more closely matches how these
things were structured originally, and results in less churn and more
atomicity in these changes.

In the future, package.nix likely will migrate to have more build
options in the callPackage arguments, but we are also planning to
rewrite the build system anyway.

Change-Id: I170c4e5a4184bab62e1fd75e56db876d4ff116cf
2024-03-11 04:26:35 -06:00
Jade Lovelace 1758a6ef25 refactor: repl prompts are now the job of the interacter
Change-Id: I17c2873dfbbff303cdbdc7a8903deb8409ce3026
2024-03-11 01:04:52 -07:00
Jade Lovelace 95a87f2c2a refactor: move readline stuff into its own file
This is in direct preparation for an automation mode of nix repl.

Change-Id: I26e6ca88ef1c48aab11a2d1e939ff769f1770caa
2024-03-11 01:04:52 -07:00
Jade Lovelace 45f6e3521a finally.hh: delete copy constructor which is a bad idea
Change-Id: I6d0b5736893c44bddc6f5789b452b434f8671b9b
2024-03-11 01:04:52 -07:00
Jade Lovelace af515baf6e Add box_ptr: nonnull unique_ptr with value semantics
This solves the problem of collections of boxed subclasses with virtual
dispatch, which should still be treated as values, since the
indirection is only there due to the virtual dispatch.

Change-Id: I368daedd3f31298e99c6e56a15606337a55494c6
2024-03-11 01:04:52 -07:00
Jade Lovelace 8be7030299 util.hh: split out signals stuff
Copies part of the changes of ac89bb064aeea85a62b82a6daf0ecca7190a28b7

Change-Id: I9ce601875cd6d4db5eb1132d7835c5bab9f126d8
2024-03-11 00:52:09 -07:00
jadeandGerrit Code Review 6432bf9197 Merge "Print derivation paths in nix eval" into main 2024-03-10 16:12:32 -06:00
eldritch horrors 0a4737f519 add doc comment justifying ExprInheritFrom
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
(cherry picked from commit f24e445bc024cfd3c26be5f061280af549321c22)
Change-Id: I7acda5d5c34c0914a78adc2385d32782c4c275cd
2024-03-10 03:18:32 -06:00
eldritch horrors 06764118ea remove ExprAttrs::AttrDef::inherited
it's no longer widely used and has a rather confusing meaning now that
inherit-from is handled very differently.

(cherry picked from commit 1cd87b7042d14aae1fafa47b1c28db4c5bd20de7)
Change-Id: I90bbebddf06762960d8ca4f621cf042ce8ae83f9
2024-03-10 03:18:32 -06:00
eldritch horrors b667b4cded evaluate inherit (from) exprs only once per directive
desugaring inherit-from to syntactic duplication of the source expr also
duplicates side effects of the source expr (such as trace calls) and
expensive computations (such as derivationStrict).

(cherry picked from commit cefd0302b55b3360dbca59cfcb4bf6a750d6cdcf)
Change-Id: Iff519f991adef2e51683ba2c552d37a3df7a179e
2024-03-10 03:18:32 -06:00
eldritch horrors 71e0114708 remove getDerivations deduplication
deduplication does not currently work fully, showing derivations
multiple times if they have different underlying values. this can happen
by selecting the same derivation twice for two different attributes of a
set, using inherit-from (which reduces to the previous), importing
nixpkgs twice, or any other number of things.

since users already have to deal with duplicates for this reason it
won't hurt to add *more* duplicates. the alternative would be to
deduplicate fully, which would drop derivations that are currently
returned and those pose a regression risk.

Change-Id: I64b397351237e10375d270f1bddecb71f62aa131
2024-03-10 03:18:32 -06:00
eldritch horrors 2a84123631 group inherit by source during Expr::show
for plain inherits this is really just a stylistic choice, but for
inherit-from it actually fixes an exponential size increase problem
during expr printing (as may happen during assertion failure reporting,
on during duplicate attr detection in the parser)

(cherry picked from commit ecf8b12d60ad2929f9998666cf0966475b91e291)
Change-Id: Ie55f0cb01a37e766414c31f8d40f51c2c7d106b0
2024-03-10 03:18:32 -06:00
eldritch horrors bf19eebb9b use the same bindings print for ExprAttrs and ExprLet
this also has the effect of sorting let bindings lexicographically
rather than by symbol creation order as was previously done, giving a
better canonicalization in the process.

(cherry picked from commit 6c08fba533ef31cad2bdc03ba72ecf58dc8ee5a0)
Change-Id: Ia887f629305645bb8a165fbbc0d32e620912595a
2024-03-10 03:18:32 -06:00
eldritch horrors 1cf0fa0633 add ExprAttrs::AttrDef::chooseByKind
in place of inherited() — not quite useful yet since we don't
distinguish plain and inheritFrom attr kinds so far.

(cherry picked from commit 1f542adb3e18e7078e6a589182a53a47d971748a)
Change-Id: If948c9d43e875de18f213a73a06a36f7c335b536
2024-03-10 03:18:32 -06:00
eldritch horrors 03f852b2c6 preserve information about whether/how an attribute was inherited
(cherry picked from commit c66ee57edc6cac3571bfbf77d0c0ea4d25b4e805)
Change-Id: Ie8606a8b2f5946c87dd4d16b7b46203e199a4cc1
2024-03-10 03:18:32 -06:00
eldritch horrors 3e43f4aeff add test for inherit expr printing
(cherry picked from commit 73065a400d176b21f518c1f4ece90c31318b218d)
Change-Id: I9356d8084d241a7904b66554d7c4194f8433edf7
2024-03-10 03:18:32 -06:00
eldritch horrors 0c19e6b56c add test for inherit-from semantics
(cherry picked from commit 8669c02468994887be91072ac58b1ee43380d354)
Change-Id: If8513316bf4b4b559c5bb63842c856f016816802
2024-03-10 03:18:32 -06:00
eldritch horrorsandGerrit Code Review 7c882a5075 Merge "make the multi-node vm tests a bit more reliable" into main 2024-03-10 03:18:08 -06:00
eldritch horrors 34fb7a7e9d make the multi-node vm tests a bit more reliable
without these changes the tests will very repeatably (although not very
reliably) wedge in our runs. the ssh command starts, opens a sessions,
does something, the session closes again, but the test does not move on.
adding *just* the redirect and not the unit waits is not sufficient
either, it needs both. this feels like a bug in the nixos testing
framework somewhere, but digging that far is not in the cards right now.

Change-Id: Idab577b83a36cc4899bb5ffbb3d9adc04e83e51c
2024-03-10 10:10:52 +01:00
eldritch horrors a9b813cc3b Merge pull request #10066 from 9999years/print-all-frames
Do not skip any stack frames when `--show-trace` is given

(cherry picked from commit 0b47783d0a879875d558f0b56e49584f25ceb2d0)
Change-Id: Ia0f18266dbcf97543110110c655c219c7a3e3270
2024-03-09 10:17:26 -07:00
eldritch horrors f2e11ddce1 Merge pull request #9914 from 9999years/debugger-on-trace
Enter debugger on `builtins.trace` with an option

(cherry picked from commit 774e7ca5847ebc392eac2a124a8f12b24da4f65a)
Change-Id: If01e2110b3a128e639b05143227e365227d149f1
2024-03-09 10:17:26 -07:00
eldritch horrors 030c8aa833 Rename ProcessLineResult variants
(cherry picked from commit 8e71883e3f59100479e96aa1883ef52dbaa03fd3)
Change-Id: If7d8b75eaec623dac106ce2363fa148af37d150c
2024-03-09 10:17:26 -07:00
eldritch horrors 992d99592f :quit in the debugger should quit the whole program
(cherry picked from commit 2a8fe9a93837733e9dd9ed5c078734a35b203e14)
Change-Id: I71dadfef6b24d9272b206e9e2c408040559d8a1c
2024-03-09 10:17:26 -07:00
eldritch horrors 6b11c2cd70 Extract printSpace helper
(cherry picked from commit 403c90ddf58a3f16a44dfe1f20004b6baa4e5ce2)
Change-Id: I53c9824e6b1c4c619b4dfd8346d39e5289d92265
2024-03-09 07:20:23 -07:00
eldritch horrors 73cdaf44cf prettyPrint -> shouldPrettyPrint
(cherry picked from commit 1c5f5d4291df7bf80806e57c75d2ec67bced8616)
Change-Id: I7a517490e7baa5cef00716f6d6cfcbcbcdde11bf
2024-03-09 07:20:23 -07:00
eldritch horrors 4dabde0485 Add assertion for decreasing the indent
Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>
(cherry picked from commit a27651908fc1b5ef73a81e46434a408c5868fa7b)
Change-Id: I2ec78e234c1c6e982f7b05f81d8b8356daf6c274
2024-03-09 07:20:23 -07:00
eldritch horrors 1958152d14 Pretty-print values in the REPL
Pretty-print values in the REPL by printing each item in a list or
attrset on a separate line. When possible, single-item lists and
attrsets are printed on one line, as long as they don't contain a nested
list, attrset, or thunk.

Before:
```
{ attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
```

After:
```
{
  attrs = {
    a = {
      b = {
        c = { };
      };
    };
  };
  list = [ 1 ];
  list' = [
    1
    2
    3
  ];
}
```

(cherry picked from commit c0a15fb7d03dfb8f53bc6726c414bc88aa362592)
Change-Id: Ia2b41849165a5ddb63f7a8c272a2476b3e4292df
2024-03-09 07:20:23 -07:00
Rebecca TurnerandJade Lovelace 6ef9d8efba Print derivation paths in nix eval
`nix eval` forces values and prints derivations as attribute sets, so
commands that print derivations (e.g. `nix eval nixpkgs#bash`) will
infinitely loop and segfault.

Printing derivations as `.drv` paths makes `nix eval` complete as
expected. Further work is needed, but this is better than a segfault.

(cherry picked from commit 4910d74086a85876e093136a0e8ebc547b467af7)

Change-Id: I8e1cb39c05db812080759ec183ee7a131760e6ea
2024-03-08 23:46:16 -08:00
290 changed files with 5111 additions and 4477 deletions
+51
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
+6 -10
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).
+8 -8
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).
-4
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).
@@ -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).
-4
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).
+2 -1
View File
@@ -41,6 +41,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 +61,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
+9
View File
@@ -0,0 +1,9 @@
---
synopsis: Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set
prs: 9914
---
If the `debugger-on-trace` option is set and `--debugger` is given,
`builtins.trace` calls will behave similarly to `builtins.break` and will enter
the debug REPL. This is useful for determining where warnings are being emitted
from.
+22
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;
| ^
```
+7
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.
@@ -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.
@@ -0,0 +1,7 @@
---
synopsis: "`inherit (x) ...` evaluates `x` only once"
prs: 9847
---
`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
+119 -434
View File
@@ -48,50 +48,6 @@
})
stdenvs);
baseFiles =
# .gitignore has already been processed, so any changes in it are irrelevant
# at this point. It is not represented verbatim for test purposes because
# that would interfere with repo semantics.
fileset.fileFilter (f: f.name != ".gitignore") ./.;
configureFiles = fileset.unions [
./.version
./configure.ac
./m4
# TODO: do we really need README.md? It doesn't seem used in the build.
./README.md
];
topLevelBuildFiles = fileset.unions [
./local.mk
./Makefile
./Makefile.config.in
./mk
];
functionalTestFiles = fileset.unions [
./tests/functional
./tests/unit
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
];
nixSrc = fileset.toSource {
root = ./.;
fileset = fileset.intersection baseFiles (fileset.unions [
configureFiles
topLevelBuildFiles
./boehmgc-coroutine-sp-fallback.diff
./doc
./misc
./precompiled-headers.h
./src
./unit-test-data
./COPYING
./scripts/local.mk
functionalTestFiles
]);
};
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor = forAllSystems
(system: let
@@ -118,205 +74,45 @@
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
});
commonDeps =
{ pkgs
, isStatic ? pkgs.stdenv.hostPlatform.isStatic
}:
with pkgs; rec {
# Use "busybox-sandbox-shell" if present,
# if not (legacy) fallback and hope it's sufficient.
sh = pkgs.busybox-sandbox-shell or (busybox.override {
useMusl = true;
enableStatic = true;
enableMinimal = true;
extraConfig = ''
CONFIG_FEATURE_FANCY_ECHO y
CONFIG_FEATURE_SH_MATH y
CONFIG_FEATURE_SH_MATH_64 y
testNixVersions = pkgs: client: daemon: let
nix = pkgs.callPackage ./package.nix {
pname =
"nix-tests"
+ lib.optionalString
(lib.versionAtLeast daemon.version "2.4pre20211005" &&
lib.versionAtLeast client.version "2.4pre20211005")
"-${client.version}-against-${daemon.version}";
CONFIG_ASH y
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
CONFIG_ASH_ALIAS y
CONFIG_ASH_BASH_COMPAT y
CONFIG_ASH_CMDCMD y
CONFIG_ASH_ECHO y
CONFIG_ASH_GETOPTS y
CONFIG_ASH_INTERNAL_GLOB y
CONFIG_ASH_JOB_CONTROL y
CONFIG_ASH_PRINTF y
CONFIG_ASH_TEST y
'';
});
configureFlags =
lib.optionals stdenv.isLinux [
"--with-boost=${boost}/lib"
"--with-sandbox-shell=${sh}/bin/busybox"
]
++ lib.optionals (stdenv.isLinux && !(isStatic && stdenv.system == "aarch64-linux")) [
"LDFLAGS=-fuse-ld=gold"
];
testConfigureFlags = [
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
];
internalApiDocsConfigureFlags = [
"--enable-internal-api-docs"
];
changelog-d = pkgs.buildPackages.callPackage ./misc/changelog-d.nix { };
nativeBuildDeps =
[
buildPackages.bison
buildPackages.flex
(lib.getBin buildPackages.lowdown)
buildPackages.mdbook
buildPackages.mdbook-linkcheck
buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
# Tests
buildPackages.git
buildPackages.mercurial # FIXME: remove? only needed for tests
buildPackages.jq # Also for custom mdBook preprocessor.
]
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)]
# Official releases don't have rl-next, so we don't need to compile a changelog
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
;
buildDeps =
[ curl
bzip2 xz brotli editline
openssl sqlite
libarchive
boost
lowdown
libsodium
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
checkDeps = [
gtest
rapidcheck
];
internalApiDocsDeps = [
buildPackages.doxygen
];
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
(aws-sdk-cpp.override {
apis = ["s3" "transfer"];
customMemoryManagement = false;
});
propagatedDeps =
[ ((boehmgc.override {
enableLargeConfig = true;
}).overrideAttrs(o: {
patches = (o.patches or []) ++ [
./boehmgc-coroutine-sp-fallback.diff
# https://github.com/ivmai/bdwgc/pull/586
./boehmgc-traceable_allocator-public.diff
];
})
)
nlohmann_json
];
};
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: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation {
inherit fileset;
};
in nix.overrideAttrs (prevAttrs: {
NIX_DAEMON_PACKAGE = daemon;
NIX_CLIENT_PACKAGE = client;
name =
"nix-tests"
+ optionalString
(versionAtLeast daemon.version "2.4pre20211005" &&
versionAtLeast client.version "2.4pre20211005")
"-${client.version}-against-${daemon.version}";
inherit version;
src = fileset.toSource {
root = ./.;
fileset = fileset.intersection baseFiles (fileset.unions [
configureFiles
topLevelBuildFiles
functionalTestFiles
]);
};
VERSION_SUFFIX = versionSuffix;
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ awsDeps ++ checkDeps;
propagatedBuildInputs = propagatedDeps;
enableParallelBuilding = true;
configureFlags =
testConfigureFlags # otherwise configure fails
++ [ "--disable-build" ];
dontBuild = true;
doInstallCheck = true;
configureFlags = prevAttrs.configureFlags ++ [
# We don't need the actual build here.
"--disable-build"
];
installPhase = ''
mkdir -p $out
'';
installCheckPhase = (optionalString pkgs.stdenv.hostPlatform.isDarwin ''
installCheckPhase = lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
'') + ''
'' + ''
mkdir -p src/nix-channel
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
'';
};
});
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}"
@@ -325,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
@@ -373,123 +131,67 @@
--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)
'';
overlayFor = getStdenv: final: prev:
let currentStdenv = getStdenv final; in
{
nixStable = prev.nix;
nix =
with final;
with commonDeps {
let
currentStdenv = getStdenv final;
comDeps = with final; commonDeps {
inherit pkgs;
inherit (currentStdenv.hostPlatform) isStatic;
};
let
canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
in currentStdenv.mkDerivation (finalAttrs: {
name = "nix-${version}";
inherit version;
in {
nixStable = prev.nix;
src = nixSrc;
VERSION_SUFFIX = versionSuffix;
# Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
outputs = [ "out" "dev" "doc" ];
changelog-d = final.buildPackages.callPackage ./misc/changelog-d.nix { };
boehmgc-nix = (final.boehmgc.override {
enableLargeConfig = true;
}).overrideAttrs (o: {
patches = (o.patches or [ ]) ++ [
./boehmgc-coroutine-sp-fallback.diff
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps
# There have been issues building these dependencies
++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps
++ lib.optionals finalAttrs.doCheck checkDeps;
propagatedBuildInputs = propagatedDeps;
disallowedReferences = [ boost ];
preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
''
# Copy libboost_context so we don't get all of Boost in our closure.
# https://github.com/NixOS/nixpkgs/issues/45462
mkdir -p $out/lib
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
rm -f $out/lib/*.a
${lib.optionalString currentStdenv.hostPlatform.isLinux ''
chmod u+w $out/lib/*.so.*
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
''}
${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
for LIB in $out/lib/*.dylib; do
chmod u+w $LIB
install_name_tool -id $LIB $LIB
install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
done
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
''}
'';
configureFlags = configureFlags ++
[ "--sysconfdir=/etc" ] ++
lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
[ (lib.enableFeature finalAttrs.doCheck "tests") ] ++
lib.optionals finalAttrs.doCheck testConfigureFlags ++
lib.optional (!canRunInstalled) "--disable-doc-gen";
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
doCheck = true;
installFlags = "sysconfdir=$(out)/etc";
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
${lib.optionalString currentStdenv.hostPlatform.isStatic ''
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
''}
${lib.optionalString currentStdenv.isDarwin ''
install_name_tool \
-change ${boost}/lib/libboost_context.dylib \
$out/lib/libboost_context.dylib \
$out/lib/libnixutil.dylib
''}
'';
doInstallCheck = finalAttrs.doCheck;
installCheckFlags = "sysconfdir=$(out)/etc";
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
'';
separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
strictDeps = true;
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
passthru.perl-bindings = final.callPackage ./perl {
inherit fileset;
stdenv = currentStdenv;
};
meta.platforms = lib.platforms.unix;
# https://github.com/ivmai/bdwgc/pull/586
./boehmgc-traceable_allocator-public.diff
];
});
default-busybox-sandbox-shell = final.busybox.override {
useMusl = true;
enableStatic = true;
enableMinimal = true;
extraConfig = ''
CONFIG_FEATURE_FANCY_ECHO y
CONFIG_FEATURE_SH_MATH y
CONFIG_FEATURE_SH_MATH_64 y
CONFIG_ASH y
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
CONFIG_ASH_ALIAS y
CONFIG_ASH_BASH_COMPAT y
CONFIG_ASH_CMDCMD y
CONFIG_ASH_ECHO y
CONFIG_ASH_GETOPTS y
CONFIG_ASH_INTERNAL_GLOB y
CONFIG_ASH_JOB_CONTROL y
CONFIG_ASH_PRINTF y
CONFIG_ASH_TEST y
'';
};
nix = final.callPackage ./package.nix {
inherit versionSuffix fileset;
stdenv = currentStdenv;
boehmgc = final.boehmgc-nix;
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
};
};
in {
@@ -502,43 +204,41 @@
# 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;
});
# 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
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
# API docs for Nix's unstable internal C++ interfaces.
internal-api-docs =
with nixpkgsFor.x86_64-linux.native;
with commonDeps { inherit pkgs; };
internal-api-docs = let
nixpkgs = nixpkgsFor.x86_64-linux.native;
inherit (nixpkgs) pkgs;
stdenv.mkDerivation {
pname = "nix-internal-api-docs";
inherit version;
src = nixSrc;
configureFlags = testConfigureFlags ++ internalApiDocsConfigureFlags;
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
dontBuild = true;
installTargets = [ "internal-api-html" ];
postInstall = ''
mkdir -p $out/nix-support
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> $out/nix-support/hydra-build-products
'';
nix = pkgs.callPackage ./package.nix {
inherit versionSuffix fileset officialRelease buildUnreleasedNotes;
inherit (pkgs) changelog-d;
internalApiDocs = true;
boehmgc = pkgs.boehmgc-nix;
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
};
in
nix.overrideAttrs (prev: {
# This Hydra job is just for the internal API docs.
# We don't need the build artifacts here.
dontBuild = true;
doCheck = false;
doInstallCheck = false;
});
# System tests.
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
@@ -552,7 +252,7 @@
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
mkdir $out
'';
@@ -564,31 +264,18 @@
}
);
};
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};
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;
in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } ''
LANG=C.UTF-8 ${(commonDeps { inherit pkgs; }).changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
'';
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
@@ -629,36 +316,31 @@
devShells = let
makeShell = pkgs: stdenv:
let
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
nix = pkgs.callPackage ./package.nix {
inherit stdenv versionSuffix fileset;
boehmgc = pkgs.boehmgc-nix;
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
forDevShell = true;
};
in
with commonDeps { inherit pkgs; };
stdenv.mkDerivation {
name = "nix";
nix.overrideAttrs (prev: {
nativeBuildInputs = prev.nativeBuildInputs
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
++ lib.optional
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
pkgs.buildPackages.clang-tools
++ [
# FIXME(Qyriad): remove once the migration to Meson is complete.
pkgs.buildPackages.meson
pkgs.buildPackages.ninja
];
outputs = [ "out" "dev" "doc" ];
src = null;
nativeBuildInputs = nativeBuildDeps
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
++ lib.optional
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
pkgs.buildPackages.clang-tools
# We want changelog-d in the shell even if the current build doesn't need it
++ lib.optional (officialRelease || ! buildUnreleasedNotes) changelog-d
;
installFlags = "sysconfdir=$(out)/etc";
strictDeps = false;
buildInputs = buildDeps ++ propagatedDeps
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
configureFlags = configureFlags
++ testConfigureFlags ++ internalApiDocsConfigureFlags
++ lib.optional (!canRunInstalled) "--disable-doc-gen";
enableParallelBuilding = true;
installFlags = "sysconfdir=$(out)/etc";
shellHook =
''
shellHook = ''
PATH=$prefix/bin:$PATH
unset PYTHONPATH
export MANPATH=$out/share/man:$MANPATH
@@ -666,7 +348,10 @@
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
'';
};
} // lib.optionalAttrs (stdenv.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:
let
+152
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=open&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 or limited the queue workers to 0 (assuming that works)? 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()
+294
View File
@@ -0,0 +1,294 @@
#
# 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++20',
# 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)
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 = dependency('libcpuid', 'cpuid', required : get_option('cpuid'))
configdata += {
'HAVE_LIBCPUID': cpuid.found().to_int(),
}
deps += cpuid
seccomp = dependency('libseccomp', 'seccomp', required : get_option('seccomp-sandboxing'))
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 : true)
if aws_sdk.found()
# The AWS pkg-config adds -std=c++11.
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 : true)
if aws_s3.found()
# The AWS pkg-config adds -std=c++11.
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
# extern "C++" functions can't be checked with the normal mechanism (much less member functions),
# so we have to do this one ourselves.
has_pubsetbuf = cxx.compiles('''
#include <iostream>
using namespace std;
static char buf[1024];
decltype(cerr.rdbuf()->pubsetbuf(buf, sizeof(buf))) _;
''')
summary('have std::basic_streambuf::pubsetbuf', has_pubsetbuf, bool_yn : true)
configdata += {
'HAVE_PUBSETBUF': has_pubsetbuf.to_int(),
}
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',
# FIXME(Qyriad): autoconf did this only if not Darwin, Solaris, or FreeBSD
# (...so only if Linux?)
'-Wl,--no-copy-dt-needed-entries',
language : 'cpp',
)
subdir('src')
+32
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 bindary',
)
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',
)
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Meson will call this with an absolute path to Bash.
# The shebang is just for convenience.
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"
+2 -2
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 $@
+4 -1
View File
@@ -6,6 +6,9 @@ programs-list :=
# - $(1)_NAME: the name of the program (e.g. foo); defaults to
# $(1).
#
# - $(1)_ENV: environment variables to set when running the program
# from the Makefile using the $(1)_RUN target.
#
# - $(1)_DIR: the directory where the (non-installed) program will be
# placed.
#
@@ -87,6 +90,6 @@ define build-program
# Phony target to run this program (typically as a dependency of 'check').
.PHONY: $(1)_RUN
$(1)_RUN: $$($(1)_PATH)
$(trace-test) $$(UNIT_TEST_ENV) $$($(1)_PATH)
$(trace-test) $$($(1)_ENV) $$($(1)_PATH)
endef
+289
View File
@@ -0,0 +1,289 @@
{
pkgs,
lib,
stdenv,
autoconf-archive,
autoreconfHook,
aws-sdk-cpp,
boehmgc,
nlohmann_json,
bison,
changelog-d,
boost,
brotli,
bzip2,
curl,
doxygen,
editline,
fileset,
flex,
git,
gtest,
jq,
libarchive,
libcpuid,
libseccomp,
libsodium,
lsof,
lowdown,
mdbook,
mdbook-linkcheck,
mercurial,
meson,
ninja,
openssl,
pkg-config,
rapidcheck,
sqlite,
util-linuxMinimal ? utillinuxMinimal,
utillinuxMinimal ? null,
xz,
busybox-sandbox-shell,
pname ? "nix",
versionSuffix ? "",
officialRelease ? true,
# 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.
__forDefaults ? {
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
},
}: let
inherit (__forDefaults) canRunInstalled;
version = lib.fileContents ./.version + versionSuffix;
aws-sdk-cpp-nix = aws-sdk-cpp.override {
apis = [ "s3" "transfer" ];
customMemoryManagement = false;
};
testConfigureFlags = [
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
];
# The internal API docs need these for the build, but if we're not building
# Nix itself, then these don't need to be propagated.
maybePropagatedInputs = [
boehmgc
nlohmann_json
];
# .gitignore has already been processed, so any changes in it are irrelevant
# at this point. It is not represented verbatim for test purposes because
# that would interfere with repo semantics.
baseFiles = fileset.fileFilter (f: f.name != ".gitignore") ./.;
configureFiles = fileset.unions [
./.version
./configure.ac
./m4
# TODO: do we really need README.md? It doesn't seem used in the build.
./README.md
];
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
./tests/unit
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
];
in stdenv.mkDerivation (finalAttrs: {
inherit pname version;
src = fileset.toSource {
root = ./.;
fileset = fileset.intersection baseFiles (fileset.unions ([
configureFiles
topLevelBuildFiles
functionalTestFiles
] ++ lib.optionals (!finalAttrs.dontBuild || internalApiDocs) [
./boehmgc-coroutine-sp-fallback.diff
./doc
./misc
./precompiled-headers.h
./src
./COPYING
./scripts/local.mk
]));
};
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" ]
++ lib.optionals (!finalAttrs.dontBuild) [ "dev" "doc" ];
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
] ++ [
(lib.getBin lowdown)
mdbook
mdbook-linkcheck
autoconf-archive
] ++ 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
bzip2
xz
brotli
editline
openssl
sqlite
libarchive
boost
lowdown
libsodium
]
++ 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
++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs
;
checkInputs = [
gtest
rapidcheck
];
propagatedBuildInputs = lib.optionals (!finalAttrs.dontBuild) maybePropagatedInputs;
disallowedReferences = [
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
mkdir -p $out/lib
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
rm -f $out/lib/*.a
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isLinux) ''
chmod u+w $out/lib/*.so.*
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isDarwin) ''
for LIB in $out/lib/*.dylib; do
chmod u+w $LIB
install_name_tool -id $LIB $LIB
install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
done
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
'' + ''
# Workaround https://github.com/NixOS/nixpkgs/issues/294890.
if [[ -n "''${doCheck:-}" ]]; then
appendToVar configureFlags "--enable-tests"
else
appendToVar configureFlags "--disable-tests"
fi
'';
configureFlags = lib.optionals stdenv.isLinux [
"--with-boost=${boost}/lib"
"--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
] ++ lib.optionals (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) [
"LDFLAGS=-fuse-ld=gold"
]
++ 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";
installTargets = lib.optional internalApiDocs "internal-api-html";
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
doCheck = true;
installFlags = "sysconfdir=$(out)/etc";
postInstall = lib.optionalString (!finalAttrs.dontBuild) ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
'' + lib.optionalString stdenv.hostPlatform.isStatic ''
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
'' + 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"
'';
doInstallCheck = finalAttrs.doCheck;
installCheckFlags = "sysconfdir=$(out)/etc";
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
'';
separateDebugInfo = !stdenv.hostPlatform.isStatic && !finalAttrs.dontBuild;
strictDeps = true;
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
meta.platforms = lib.platforms.unix;
passthru.perl-bindings = pkgs.callPackage ./perl {
inherit fileset stdenv;
};
})
-46
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
-33
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"
-875
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
-226
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
-284
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
-222
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() {
:
}
-119
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
-2
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)
-57
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
-72
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
@@ -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"
+3 -3
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);
+1 -1
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;
+6 -6
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
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,
)
+208
View File
@@ -0,0 +1,208 @@
#include <cstdio>
#include <iostream>
#include <string>
#ifdef READLINE
#include <readline/history.h>
#include <readline/readline.h>
#else
// editline < 1.15.2 don't wrap their API for C++ usage
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
// This results in linker errors due to to name-mangling of editline C symbols.
// For compatibility with these versions, we wrap the API here
// (wrapping multiple times on newer versions is no problem).
extern "C" {
#include <editline.h>
}
#endif
#include "signals.hh"
#include "finally.hh"
#include "repl-interacter.hh"
#include "util.hh"
#include "repl.hh"
namespace nix {
namespace {
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
volatile sig_atomic_t g_signal_received = 0;
void sigintHandler(int signo)
{
g_signal_received = signo;
}
};
static detail::ReplCompleterMixin * curRepl; // ugly
static char * completionCallback(char * s, int * match)
{
auto possible = curRepl->completePrefix(s);
if (possible.size() == 1) {
*match = 1;
auto * res = strdup(possible.begin()->c_str() + strlen(s));
if (!res)
throw Error("allocation failure");
return res;
} else if (possible.size() > 1) {
auto checkAllHaveSameAt = [&](size_t pos) {
auto & first = *possible.begin();
for (auto & p : possible) {
if (p.size() <= pos || p[pos] != first[pos])
return false;
}
return true;
};
size_t start = strlen(s);
size_t len = 0;
while (checkAllHaveSameAt(start + len))
++len;
if (len > 0) {
*match = 1;
auto * res = strdup(std::string(*possible.begin(), start, len).c_str());
if (!res)
throw Error("allocation failure");
return res;
}
}
*match = 0;
return nullptr;
}
static int listPossibleCallback(char * s, char *** avp)
{
auto possible = curRepl->completePrefix(s);
if (possible.size() > (INT_MAX / sizeof(char *)))
throw Error("too many completions");
int ac = 0;
char ** vp = nullptr;
auto check = [&](auto * p) {
if (!p) {
if (vp) {
while (--ac >= 0)
free(vp[ac]);
free(vp);
}
throw Error("allocation failure");
}
return p;
};
vp = check((char **) malloc(possible.size() * sizeof(char *)));
for (auto & p : possible)
vp[ac++] = check(strdup(p.c_str()));
*avp = vp;
return ac;
}
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
{
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
try {
createDirs(dirOf(historyFile));
} catch (SysError & e) {
logWarning(e.info());
}
#ifndef READLINE
el_hist_size = 1000;
#endif
read_history(historyFile.c_str());
auto oldRepl = curRepl;
curRepl = repl;
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
#ifndef READLINE
rl_set_complete_func(completionCallback);
rl_set_list_possib_func(listPossibleCallback);
#endif
return restoreRepl;
}
static constexpr const char * promptForType(ReplPromptType promptType)
{
switch (promptType) {
case ReplPromptType::ReplPrompt:
return "nix-repl> ";
case ReplPromptType::ContinuationPrompt:
return " ";
}
assert(false);
}
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
{
struct sigaction act, old;
sigset_t savedSignalMask, set;
auto setupSignals = [&]() {
act.sa_handler = sigintHandler;
sigfillset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGINT, &act, &old))
throw SysError("installing handler for SIGINT");
sigemptyset(&set);
sigaddset(&set, SIGINT);
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
throw SysError("unblocking SIGINT");
};
auto restoreSignals = [&]() {
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
throw SysError("restoring signals");
if (sigaction(SIGINT, &old, 0))
throw SysError("restoring handler for SIGINT");
};
setupSignals();
char * s = readline(promptForType(promptType));
Finally doFree([&]() { free(s); });
restoreSignals();
if (g_signal_received) {
g_signal_received = 0;
input.clear();
return true;
}
if (!s)
return false;
input += s;
input += '\n';
return true;
}
ReadlineLikeInteracter::~ReadlineLikeInteracter()
{
write_history(historyFile.c_str());
}
AutomationInteracter::Guard AutomationInteracter::init(detail::ReplCompleterMixin *)
{
return Guard([] {});
}
// ASCII ENQ character
constexpr const char * automationPrompt = "\x05";
bool AutomationInteracter::getLine(std::string & input, ReplPromptType promptType)
{
std::cout << std::unitbuf;
std::cout << automationPrompt;
if (!std::getline(std::cin, input)) {
// reset failure bits on EOF
std::cin.clear();
return false;
}
return true;
}
};
+57
View File
@@ -0,0 +1,57 @@
#pragma once
/// @file
#include "finally.hh"
#include "types.hh"
#include <functional>
#include <string>
namespace nix {
namespace detail {
/** Provides the completion hooks for the repl, without exposing its complete
* internals. */
struct ReplCompleterMixin {
virtual StringSet completePrefix(const std::string & prefix) = 0;
};
};
enum class ReplPromptType {
ReplPrompt,
ContinuationPrompt,
};
class ReplInteracter
{
public:
using Guard = Finally<std::function<void()>>;
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
/** Returns a boolean of whether the interacter got EOF */
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
virtual ~ReplInteracter(){};
};
class ReadlineLikeInteracter : public virtual ReplInteracter
{
std::string historyFile;
public:
ReadlineLikeInteracter(std::string historyFile)
: historyFile(historyFile)
{
}
virtual Guard init(detail::ReplCompleterMixin * repl) override;
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
virtual ~ReadlineLikeInteracter() override;
};
class AutomationInteracter : public virtual ReplInteracter
{
public:
AutomationInteracter() = default;
virtual Guard init(detail::ReplCompleterMixin * repl) override;
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
virtual ~AutomationInteracter() override = default;
};
};
+67 -182
View File
@@ -3,22 +3,8 @@
#include <cstring>
#include <climits>
#include <setjmp.h>
#ifdef READLINE
#include <readline/history.h>
#include <readline/readline.h>
#else
// editline < 1.15.2 don't wrap their API for C++ usage
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
// This results in linker errors due to to name-mangling of editline C symbols.
// For compatibility with these versions, we wrap the API here
// (wrapping multiple times on newer versions is no problem).
extern "C" {
#include <editline.h>
}
#endif
#include "box_ptr.hh"
#include "repl-interacter.hh"
#include "repl.hh"
#include "ansicolor.hh"
@@ -28,6 +14,7 @@ extern "C" {
#include "eval-inline.hh"
#include "eval-settings.hh"
#include "attr-path.hh"
#include "signals.hh"
#include "store-api.hh"
#include "log-store.hh"
#include "common-eval-args.hh"
@@ -40,6 +27,7 @@ extern "C" {
#include "finally.hh"
#include "markdown.hh"
#include "local-fs-store.hh"
#include "signals.hh"
#include "print.hh"
#if HAVE_BOEHMGC
@@ -49,8 +37,30 @@ extern "C" {
namespace nix {
/**
* Returned by `NixRepl::processLine`.
*/
enum class ProcessLineResult {
/**
* The user exited with `:quit`. The REPL should exit. The surrounding
* program or evaluation (e.g., if the REPL was acting as the debugger)
* should also exit.
*/
Quit,
/**
* The user exited with `:continue`. The REPL should exit, but the program
* should continue running.
*/
Continue,
/**
* The user did not exit. The REPL should request another line of input.
*/
PromptAgain,
};
struct NixRepl
: AbstractNixRepl
, detail::ReplCompleterMixin
#if HAVE_BOEHMGC
, gc
#endif
@@ -66,19 +76,18 @@ struct NixRepl
int displ;
StringSet varNames;
const Path historyFile;
box_ptr<ReplInteracter> interacter;
NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
virtual ~NixRepl();
virtual ~NixRepl() = default;
void mainLoop() override;
ReplExitStatus mainLoop() override;
void initEnv() override;
StringSet completePrefix(const std::string & prefix);
bool getLine(std::string & input, const std::string & prompt);
virtual StringSet completePrefix(const std::string & prefix) override;
StorePath getDerivationPath(Value & v);
bool processLine(std::string line);
ProcessLineResult processLine(std::string line);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
@@ -98,7 +107,8 @@ struct NixRepl
.ansiColors = true,
.force = true,
.derivationPaths = true,
.maxDepth = maxDepth
.maxDepth = maxDepth,
.prettyIndent = 2
});
}
};
@@ -111,6 +121,12 @@ std::string removeWhitespace(std::string s)
return s;
}
static box_ptr<ReplInteracter> makeInteracter() {
if (experimentalFeatureSettings.isEnabled(Xp::ReplAutomation))
return make_box_ptr<AutomationInteracter>();
else
return make_box_ptr<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history");
}
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues)
@@ -118,16 +134,10 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalS
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
, historyFile(getDataDir() + "/nix/repl-history")
, interacter(makeInteracter())
{
}
NixRepl::~NixRepl()
{
write_history(historyFile.c_str());
}
void runNix(Path program, const Strings & args,
const std::optional<std::string> & input = {})
{
@@ -144,79 +154,6 @@ void runNix(Path program, const Strings & args,
return;
}
static NixRepl * curRepl; // ugly
static char * completionCallback(char * s, int *match) {
auto possible = curRepl->completePrefix(s);
if (possible.size() == 1) {
*match = 1;
auto *res = strdup(possible.begin()->c_str() + strlen(s));
if (!res) throw Error("allocation failure");
return res;
} else if (possible.size() > 1) {
auto checkAllHaveSameAt = [&](size_t pos) {
auto &first = *possible.begin();
for (auto &p : possible) {
if (p.size() <= pos || p[pos] != first[pos])
return false;
}
return true;
};
size_t start = strlen(s);
size_t len = 0;
while (checkAllHaveSameAt(start + len)) ++len;
if (len > 0) {
*match = 1;
auto *res = strdup(std::string(*possible.begin(), start, len).c_str());
if (!res) throw Error("allocation failure");
return res;
}
}
*match = 0;
return nullptr;
}
static int listPossibleCallback(char *s, char ***avp) {
auto possible = curRepl->completePrefix(s);
if (possible.size() > (INT_MAX / sizeof(char*)))
throw Error("too many completions");
int ac = 0;
char **vp = nullptr;
auto check = [&](auto *p) {
if (!p) {
if (vp) {
while (--ac >= 0)
free(vp[ac]);
free(vp);
}
throw Error("allocation failure");
}
return p;
};
vp = check((char **)malloc(possible.size() * sizeof(char*)));
for (auto & p : possible)
vp[ac++] = check(strdup(p.c_str()));
*avp = vp;
return ac;
}
namespace {
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
volatile sig_atomic_t g_signal_received = 0;
void sigintHandler(int signo) {
g_signal_received = signo;
}
}
static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
{
if (dt.isError)
@@ -242,7 +179,7 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
static bool isFirstRepl = true;
void NixRepl::mainLoop()
ReplExitStatus NixRepl::mainLoop()
{
if (isFirstRepl) {
std::string_view debuggerNotice = "";
@@ -256,24 +193,7 @@ void NixRepl::mainLoop()
loadFiles();
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
try {
createDirs(dirOf(historyFile));
} catch (SysError & e) {
logWarning(e.info());
}
#ifndef READLINE
el_hist_size = 1000;
#endif
read_history(historyFile.c_str());
auto oldRepl = curRepl;
curRepl = this;
Finally restoreRepl([&] { curRepl = oldRepl; });
#ifndef READLINE
rl_set_complete_func(completionCallback);
rl_set_list_possib_func(listPossibleCallback);
#endif
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
std::string input;
@@ -282,16 +202,26 @@ void NixRepl::mainLoop()
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 (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
// ctrl-D should exit the debugger.
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
// Ctrl-D should exit the debugger.
state->debugStop = false;
state->debugQuit = true;
logger->cout("");
break;
// TODO: Should Ctrl-D exit just the current debugger session or
// the entire program?
return ReplExitStatus::QuitAll;
}
logger->resume();
try {
if (!removeWhitespace(input).empty() && !processLine(input)) return;
switch (processLine(input)) {
case ProcessLineResult::Quit:
return ReplExitStatus::QuitAll;
case ProcessLineResult::Continue:
return ReplExitStatus::Continue;
case ProcessLineResult::PromptAgain:
break;
default:
abort();
}
} catch (ParseError & e) {
if (e.msg().find("unexpected end of file") != std::string::npos) {
// For parse errors on incomplete input, we continue waiting for the next line of
@@ -321,51 +251,6 @@ void NixRepl::mainLoop()
}
}
bool NixRepl::getLine(std::string & input, const std::string & prompt)
{
struct sigaction act, old;
sigset_t savedSignalMask, set;
auto setupSignals = [&]() {
act.sa_handler = sigintHandler;
sigfillset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGINT, &act, &old))
throw SysError("installing handler for SIGINT");
sigemptyset(&set);
sigaddset(&set, SIGINT);
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
throw SysError("unblocking SIGINT");
};
auto restoreSignals = [&]() {
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
throw SysError("restoring signals");
if (sigaction(SIGINT, &old, 0))
throw SysError("restoring handler for SIGINT");
};
setupSignals();
char * s = readline(prompt.c_str());
Finally doFree([&]() { free(s); });
restoreSignals();
if (g_signal_received) {
g_signal_received = 0;
input.clear();
return true;
}
if (!s)
return false;
input += s;
input += '\n';
return true;
}
StringSet NixRepl::completePrefix(const std::string & prefix)
{
StringSet completions;
@@ -387,7 +272,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 &) {
@@ -478,10 +363,11 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
}
}
bool NixRepl::processLine(std::string line)
ProcessLineResult NixRepl::processLine(std::string line)
{
line = trim(line);
if (line == "") return true;
if (line.empty())
return ProcessLineResult::PromptAgain;
_isInterrupted = false;
@@ -576,13 +462,13 @@ bool NixRepl::processLine(std::string line)
else if (state->debugRepl && (command == ":s" || command == ":step")) {
// set flag to stop at next DebugTrace; exit repl.
state->debugStop = true;
return false;
return ProcessLineResult::Continue;
}
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
// set flag to run to next breakpoint or end of program; exit repl.
state->debugStop = false;
return false;
return ProcessLineResult::Continue;
}
else if (command == ":a" || command == ":add") {
@@ -725,8 +611,7 @@ bool NixRepl::processLine(std::string line)
else if (command == ":q" || command == ":quit") {
state->debugStop = false;
state->debugQuit = true;
return false;
return ProcessLineResult::Quit;
}
else if (command == ":doc") {
@@ -787,7 +672,7 @@ bool NixRepl::processLine(std::string line)
}
}
return true;
return ProcessLineResult::PromptAgain;
}
void NixRepl::loadFile(const Path & path)
@@ -918,7 +803,7 @@ std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
}
void AbstractNixRepl::runSimple(
ReplExitStatus AbstractNixRepl::runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv)
{
@@ -940,7 +825,7 @@ void AbstractNixRepl::runSimple(
for (auto & [name, value] : extraEnv)
repl->addVarToScope(repl->state->symbols.create(name), *value);
repl->mainLoop();
return repl->mainLoop();
}
}
+2 -7
View File
@@ -3,11 +3,6 @@
#include "eval.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
#include <gc/gc_cpp.h>
#endif
namespace nix {
struct AbstractNixRepl
@@ -28,13 +23,13 @@ struct AbstractNixRepl
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
static void runSimple(
static ReplExitStatus runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0;
virtual void mainLoop() = 0;
virtual ReplExitStatus mainLoop() = 0;
};
}
+3 -11
View File
@@ -28,15 +28,7 @@ template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
{
error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = false});
return *this;
}
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrameTrace(PosIdx pos, const std::string_view text)
{
error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = true});
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text))});
return *this;
}
@@ -63,9 +55,9 @@ EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr
}
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint, bool frame)
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint)
{
error.addTrace(error.state.positions[pos], hint, frame);
error.addTrace(error.state.positions[pos], hint);
return *this;
}
+1 -1
View File
@@ -90,7 +90,7 @@ public:
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint, bool frame = false);
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint);
template<typename... Args>
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
+2 -2
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);
+10
View File
@@ -114,6 +114,16 @@ struct EvalSettings : Config
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth",
"The maximum function call depth to allow before erroring."};
Setting<bool> builtinsTraceDebugger{this, false, "debugger-on-trace",
R"(
If set to true and the `--debugger` flag is given,
[`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will
enter the debugger like
[`builtins.break`](@docroot@/language/builtins.md#builtins-break).
This is useful for debugging warnings in third-party Nix code.
)"};
};
extern EvalSettings evalSettings;
+93 -43
View File
@@ -3,6 +3,7 @@
#include "hash.hh"
#include "primops.hh"
#include "print-options.hh"
#include "shared.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
@@ -391,7 +392,6 @@ EvalState::EvalState(
, buildStore(buildStore ? buildStore : store)
, debugRepl(nullptr)
, debugStop(false)
, debugQuit(false)
, trylevel(0)
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
@@ -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)
@@ -798,18 +808,30 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
auto se = getStaticEnv(expr);
if (se) {
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
(debugRepl)(ref<EvalState>(shared_from_this()), *vm);
auto exitStatus = (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
switch (exitStatus) {
case ReplExitStatus::QuitAll:
if (error)
throw *error;
throw Exit(0);
case ReplExitStatus::Continue:
break;
default:
abort();
}
}
}
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
template<typename... Args>
void EvalState::addErrorTrace(Error & e, const Args & ... formatArgs) const
{
e.addTrace(nullptr, s, s2);
e.addTrace(nullptr, HintFmt(formatArgs...));
}
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
template<typename... Args>
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const
{
e.addTrace(positions[pos], HintFmt(s, s2), frame);
e.addTrace(positions[pos], HintFmt(formatArgs...));
}
template<typename... Args>
@@ -928,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();
@@ -1205,6 +1226,18 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
}
Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
{
Env & inheritEnv = state.allocEnv(inheritFromExprs->size());
inheritEnv.up = &up;
Displacement displ = 0;
for (auto from : *inheritFromExprs)
inheritEnv.values[displ++] = from->maybeThunk(state, up);
return &inheritEnv;
}
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
@@ -1216,6 +1249,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Env & env2(state.allocEnv(attrs.size()));
env2.up = &env;
dynamicEnv = &env2;
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end();
@@ -1226,11 +1260,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Displacement displ = 0;
for (auto & i : attrs) {
Value * vAttr;
if (hasOverrides && !i.second.inherited) {
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
vAttr = state.allocValue();
mkThunk(*vAttr, env2, i.second.e);
mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), i.second.e);
} else
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
env2.values[displ++] = vAttr;
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
}
@@ -1262,9 +1296,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
}
}
else
for (auto & i : attrs)
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
else {
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
for (auto & i : attrs) {
v.attrs->push_back(Attr(
i.first,
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
i.second.pos));
}
}
/* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) {
@@ -1296,12 +1336,17 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
Env & env2(state.allocEnv(attrs->attrs.size()));
env2.up = &env;
Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
/* The recursive attributes are evaluated in the new environment,
while the inherited attributes are evaluated in the original
environment. */
Displacement displ = 0;
for (auto & i : attrs->attrs)
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
for (auto & i : attrs->attrs) {
env2.values[displ++] = i.second.e->maybeThunk(
state,
*i.second.chooseByKind(&env2, &env, inheritEnv));
}
auto dts = state.debugRepl
? makeDebugTraceStacker(
@@ -1596,9 +1641,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
"while calling %s",
lambda.name
? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda",
true);
if (pos) addErrorTrace(e, pos, "from call site%s", "", true);
: "anonymous lambda");
if (pos) addErrorTrace(e, pos, "from call site");
}
throw;
}
@@ -2710,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);
}
@@ -2724,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);
}
@@ -2755,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>(
@@ -2788,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);
+6 -5
View File
@@ -11,6 +11,7 @@
#include "experimental-features.hh"
#include "input-accessor.hh"
#include "search-path.hh"
#include "repl-exit-status.hh"
#include <map>
#include <optional>
@@ -207,9 +208,8 @@ public:
/**
* Debugger
*/
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop;
bool debugQuit;
int trylevel;
std::list<DebugTrace> debugTraces;
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
@@ -434,10 +434,12 @@ public:
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
template<typename... Args>
[[gnu::noinline]]
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
void addErrorTrace(Error & e, const Args & ... formatArgs) const;
template<typename... Args>
[[gnu::noinline]]
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
void addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const;
public:
/**
@@ -753,7 +755,6 @@ struct DebugTraceStacker {
DebugTraceStacker(EvalState & evalState, DebugTrace t);
~DebugTraceStacker()
{
// assert(evalState.debugTraces.front() == trace);
evalState.debugTraces.pop_front();
}
EvalState & evalState;
+1 -1
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;
+1 -2
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;
+1 -1
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", ""));
+2 -1
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>;
}
}
+15 -13
View File
@@ -296,22 +296,16 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
typedef std::set<Bindings *> Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
then put information about it in `drvs' (unless it's already in `done').
The result boolean indicates whether it makes sense
/* The result boolean indicates whether it makes sense
for the caller to recursively search for derivations in `v'. */
static bool getDerivation(EvalState & state, Value & v,
const std::string & attrPath, DrvInfos & drvs, Done & done,
const std::string & attrPath, DrvInfos & drvs,
bool ignoreAssertionFailures)
{
try {
state.forceValue(v, v.determinePos(noPos));
if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., a set like `rec { x =
derivation {...}; y = x;}'. */
if (!done.insert(v.attrs).second) return false;
DrvInfo drv(state, attrPath, v.attrs);
drv.queryName();
@@ -330,9 +324,8 @@ static bool getDerivation(EvalState & state, Value & v,
std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
bool ignoreAssertionFailures)
{
Done done;
DrvInfos drvs;
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
getDerivation(state, v, "", drvs, ignoreAssertionFailures);
if (drvs.size() != 1) return {};
return std::move(drvs.front());
}
@@ -347,6 +340,9 @@ static std::string addToPath(const std::string & s1, const std::string & s2)
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
then put information about it in `drvs'. If it evaluates to a different
kind of set recurse (unless it's already in `done'). */
static void getDerivations(EvalState & state, Value & vIn,
const std::string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
@@ -356,10 +352,14 @@ static void getDerivations(EvalState & state, Value & vIn,
state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
if (!getDerivation(state, v, pathPrefix, drvs, ignoreAssertionFailures)) ;
else if (v.type() == nAttrs) {
/* Dont consider sets we've already seen, e.g. y in
`rec { x.d = derivation {...}; y = x; }`. */
if (!done.insert(v.attrs).second) return;
/* !!! undocumented hackery to support combining channels in
nix-env.cc. */
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
@@ -376,7 +376,7 @@ static void getDerivations(EvalState & state, Value & vIn,
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
else if (getDerivation(state, *i->value, pathPrefix2, drvs, ignoreAssertionFailures)) {
/* If the value of this attribute is itself a set,
should we recurse into it? => Only if it has a
`recurseForDerivations = true' attribute. */
@@ -390,9 +390,11 @@ static void getDerivations(EvalState & state, Value & vIn,
}
else if (v.type() == nList) {
// NOTE we can't really deduplicate here because small lists don't have stable addresses
// and can cause spurious duplicate detections due to v being on the stack.
for (auto [n, elem] : enumerate(v.listItems())) {
std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
if (getDerivation(state, *elem, pathPrefix2, drvs, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
+3 -20
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;
}
+157
View File
@@ -0,0 +1,157 @@
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',
cpp_args : [
# FIXME(Qyriad): can we please fix this. toml11 pls.
# Technically this only applies to fromTOML.cc, but, well
# https://github.com/mesonbuild/meson/issues/1367
'-Wno-error=switch-enum',
],
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,
)
+125 -28
View File
@@ -68,10 +68,8 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
}
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i);
@@ -79,10 +77,37 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
std::string_view sa = symbols[a->first], sb = symbols[b->first];
return sa < sb;
});
std::vector<Symbol> inherits;
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
for (auto & i : sorted) {
if (i->second.inherited)
str << "inherit " << symbols[i->first] << " " << "; ";
else {
switch (i->second.kind) {
case AttrDef::Kind::Plain:
break;
case AttrDef::Kind::Inherited:
inherits.push_back(i->first);
break;
case AttrDef::Kind::InheritedFrom: {
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
inheritsFrom[&from].push_back(i->first);
break;
}
}
}
if (!inherits.empty()) {
str << "inherit";
for (auto sym : inherits) str << " " << symbols[sym];
str << "; ";
}
for (const auto & [from, syms] : inheritsFrom) {
str << "inherit (";
(*inheritFromExprs)[from->displ]->show(symbols, str);
str << ")";
for (auto sym : syms) str << " " << symbols[sym];
str << "; ";
}
for (auto & i : sorted) {
if (i->second.kind == AttrDef::Kind::Plain) {
str << symbols[i->first] << " = ";
i->second.e->show(symbols, str);
str << "; ";
@@ -95,6 +120,13 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
i.valueExpr->show(symbols, str);
str << "; ";
}
}
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
showBindings(symbols, str);
str << "}";
}
@@ -115,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) {
@@ -150,15 +185,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(let ";
for (auto & i : attrs->attrs)
if (i.second.inherited) {
str << "inherit " << symbols[i.first] << "; ";
}
else {
str << symbols[i.first] << " = ";
i.second.e->show(symbols, str);
str << "; ";
}
attrs->showBindings(symbols, str);
str << "in ";
body->show(symbols, str);
str << ")";
@@ -303,6 +330,12 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
this->level = withLevel;
}
void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
@@ -326,22 +359,47 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
i.expr->bindVars(es, env);
}
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (!inheritFromExprs)
return nullptr;
// the inherit (from) source values are inserted into an env of its own, which
// does not introduce any variable names.
// analysis must see an empty env, or an env that contains only entries with
// otherwise unused names to not interfere with regular names. the parser
// has already filled all exprs that access this env with appropriate level
// and displacement, and nothing else is allowed to access it. ideally we'd
// not even *have* an expr that grabs anything from this env since it's fully
// invisible, but the evaluator does not allow for this yet.
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
for (auto from : *inheritFromExprs)
from->bindVars(es, env);
return inner;
}
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), recursive ? attrs.size() : 0);
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
Displacement displ = 0;
for (auto & i : attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
Displacement displ = 0;
for (auto & i : attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
// No need to sort newEnv since attrs is in sorted order.
auto inheritFromEnv = bindInheritSources(es, newEnv);
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, newEnv);
@@ -349,8 +407,10 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
}
}
else {
auto inheritFromEnv = bindInheritSources(es, env);
for (auto & i : attrs)
i.second.e->bindVars(es, env);
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, env);
@@ -407,19 +467,23 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
Displacement displ = 0;
for (auto & i : attrs->attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
Displacement displ = 0;
for (auto & i : attrs->attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
// No need to sort newEnv since attrs->attrs is in sorted order.
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
for (auto & i : attrs->attrs)
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
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);
}
@@ -517,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
+49 -4
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"
@@ -129,6 +128,23 @@ struct ExprVar : Expr
COMMON_METHODS
};
/**
* A pseudo-expression for the purpose of evaluating the `from` expression in `inherit (from)` syntax.
* Unlike normal variable references, the displacement is set during parsing, and always refers to
* `ExprAttrs::inheritFromExprs` (by itself or in `ExprLet`), whose values are put into their own `Env`.
*/
struct ExprInheritFrom : ExprVar
{
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
{
this->level = 0;
this->displ = displ;
this->fromWith = nullptr;
}
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
};
struct ExprSelect : Expr
{
PosIdx pos;
@@ -154,16 +170,40 @@ struct ExprAttrs : Expr
bool recursive;
PosIdx pos;
struct AttrDef {
bool inherited;
enum class Kind {
/** `attr = expr;` */
Plain,
/** `inherit attr1 attrn;` */
Inherited,
/** `inherit (expr) attr1 attrn;` */
InheritedFrom,
};
Kind kind;
Expr * e;
PosIdx pos;
Displacement displ; // displacement
AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
: inherited(inherited), e(e), pos(pos) { };
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
: kind(kind), e(e), pos(pos) { };
AttrDef() { };
template<typename T>
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
{
switch (kind) {
case Kind::Plain:
return plain;
case Kind::Inherited:
return inherited;
default:
case Kind::InheritedFrom:
return inheritedFrom;
}
}
};
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef {
Expr * nameExpr, * valueExpr;
PosIdx pos;
@@ -176,6 +216,11 @@ struct ExprAttrs : Expr
ExprAttrs() : recursive(false) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
std::shared_ptr<const StaticEnv> bindInheritSources(
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
Env * buildInheritFromEnv(EvalState & state, Env & up);
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
};
struct ExprList : Expr
+16 -10
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;
}
};
@@ -88,12 +83,12 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
if (!j->second.inherited) {
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);
@@ -117,13 +112,24 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
auto ae = dynamic_cast<ExprAttrs *>(e);
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
from.displ += jAttrs->inheritFromExprs->size();
}
}
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
}
} else {
dupAttr(attrPath, pos, j->second.pos);
}
@@ -264,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);
}
}
+28 -13
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,21 +313,30 @@ 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);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
}
delete $3;
}
| binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
for (auto & i : *$6) {
if (!$$->inheritFromExprs)
$$->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, iPos] : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), state->at(@6)));
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(
new ExprSelect(iPos, from, i.symbol),
iPos,
ExprAttrs::AttrDef::Kind::InheritedFrom));
}
delete $6;
}
@@ -329,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({
@@ -342,7 +357,7 @@ attrs
.pos = state->positions[state->at(@2)]
});
}
| { $$ = new AttrPath; }
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
;
attrpath
@@ -420,7 +435,7 @@ Expr * parseExprFromBuf(
.symbols = symbols,
.positions = positions,
.basePath = basePath,
.origin = {origin},
.origin = positions.addOrigin(origin, length),
.s = astSymbols,
};
+1
View File
@@ -6,6 +6,7 @@ namespace nix {
class PosIdx
{
friend struct LazyPosAcessors;
friend class PosTable;
private:
+47 -44
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{};
}
};
+68 -29
View File
@@ -754,15 +754,6 @@ static RegisterPrimOp primop_break({
auto & dt = state.debugTraces.front();
state.runDebugRepl(&error, dt.env, dt.expr);
if (state.debugQuit) {
// If the user elects to quit the repl, throw an exception.
throw Error(ErrorInfo{
.level = lvlInfo,
.msg = HintFmt("quit the debugger"),
.pos = nullptr,
});
}
}
// Return the value we were passed.
@@ -814,7 +805,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned();
e.addTrace(nullptr, HintFmt(message), true);
e.addTrace(nullptr, HintFmt(message));
throw;
}
}
@@ -873,7 +864,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
/* increment state.trylevel, and decrement it when this function returns. */
MaintainCount trylevel(state.trylevel);
void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
ReplExitStatus (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
{
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
@@ -989,6 +980,10 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu
printError("trace: %1%", args[0]->string.s);
else
printError("trace: %1%", ValuePrinter(state, *args[0]));
if (evalSettings.builtinsTraceDebugger && state.debugRepl && !state.debugTraces.empty()) {
const DebugTrace & last = state.debugTraces.front();
state.runDebugRepl(nullptr, last.env, last.expr);
}
state.forceValue(*args[1], pos);
v = *args[1];
}
@@ -1000,6 +995,12 @@ static RegisterPrimOp primop_trace({
Evaluate *e1* and print its abstract syntax representation on
standard error. Then return *e2*. This function is useful for
debugging.
If the
[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace)
option is set to `true` and the `--debugger` flag is given, the
interactive debugger will be started when `trace` is called (like
[`break`](@docroot@/language/builtins.md#builtins-break)).
)",
.fun = prim_trace,
});
@@ -1068,7 +1069,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
e.addTrace(nullptr, HintFmt(
"while evaluating derivation '%s'\n"
" whose name attribute is located at %s",
drvName, pos), true);
drvName, pos));
throw;
}
}
@@ -1226,8 +1227,7 @@ drvName, Bindings * attrs, Value & v)
} catch (Error & e) {
e.addTrace(state.positions[i->pos],
HintFmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName),
true);
HintFmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName));
throw;
}
}
@@ -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.
)",
});
+2
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);
}
+1
View File
@@ -1,6 +1,7 @@
#include "print-ambiguous.hh"
#include "print.hh"
#include "eval.hh"
#include "signals.hh"
namespace nix {
+22
View File
@@ -17,24 +17,29 @@ struct PrintOptions
* If true, output ANSI color sequences.
*/
bool ansiColors = false;
/**
* If true, force values.
*/
bool force = false;
/**
* If true and `force` is set, print derivations as
* `«derivation /nix/store/...»` instead of as attribute sets.
*/
bool derivationPaths = false;
/**
* If true, track which values have been printed and skip them on
* subsequent encounters. Useful for self-referential values.
*/
bool trackRepeated = true;
/**
* Maximum depth to evaluate to.
*/
size_t maxDepth = std::numeric_limits<size_t>::max();
/**
* Maximum number of attributes in attribute sets to print.
*
@@ -42,6 +47,7 @@ struct PrintOptions
* attribute set encountered.
*/
size_t maxAttrs = std::numeric_limits<size_t>::max();
/**
* Maximum number of list items to print.
*
@@ -49,10 +55,26 @@ struct PrintOptions
* list encountered.
*/
size_t maxListItems = std::numeric_limits<size_t>::max();
/**
* Maximum string length to print.
*/
size_t maxStringLength = std::numeric_limits<size_t>::max();
/**
* Indentation width for pretty-printing.
*
* If set to 0 (the default), values are not pretty-printed.
*/
size_t prettyIndent = 0;
/**
* True if pretty-printing is enabled.
*/
inline bool shouldPrettyPrint()
{
return prettyIndent > 0;
}
};
/**
+109 -12
View File
@@ -5,6 +5,7 @@
#include "ansicolor.hh"
#include "store-api.hh"
#include "english.hh"
#include "signals.hh"
#include "eval.hh"
namespace nix {
@@ -151,6 +152,7 @@ struct ImportantFirstAttrNameCmp
};
typedef std::set<const void *> ValuesSeen;
typedef std::vector<std::pair<std::string, Value *>> AttrVec;
class Printer
{
@@ -161,6 +163,37 @@ private:
std::optional<ValuesSeen> seen;
size_t attrsPrinted = 0;
size_t listItemsPrinted = 0;
std::string indent;
void increaseIndent()
{
if (options.shouldPrettyPrint()) {
indent.append(options.prettyIndent, ' ');
}
}
void decreaseIndent()
{
if (options.shouldPrettyPrint()) {
assert(indent.size() >= options.prettyIndent);
indent.resize(indent.size() - options.prettyIndent);
}
}
/**
* Print a space (for separating items or attributes).
*
* If pretty-printing is enabled, a newline and the current `indent` is
* printed instead.
*/
void printSpace(bool prettyPrint)
{
if (prettyPrint) {
output << "\n" << indent;
} else {
output << " ";
}
}
void printRepeated()
{
@@ -258,6 +291,28 @@ private:
}
}
bool shouldPrettyPrintAttrs(AttrVec & v)
{
if (!options.shouldPrettyPrint() || v.empty()) {
return false;
}
// Pretty-print attrsets with more than one item.
if (v.size() > 1) {
return true;
}
auto item = v[0].second;
if (!item) {
return true;
}
// Pretty-print single-item attrsets only if they contain nested
// structures.
auto itemType = item->type();
return itemType == nList || itemType == nAttrs || itemType == nThunk;
}
void printAttrs(Value & v, size_t depth)
{
if (seen && !seen->insert(v.attrs).second) {
@@ -268,9 +323,10 @@ private:
if (options.force && options.derivationPaths && state.isDerivation(v)) {
printDerivation(v);
} else if (depth < options.maxDepth) {
output << "{ ";
increaseIndent();
output << "{";
std::vector<std::pair<std::string, Value *>> sorted;
AttrVec sorted;
for (auto & i : *v.attrs)
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
@@ -279,22 +335,53 @@ private:
else
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
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;
}
printAttributeName(output, i.first);
output << " = ";
print(*i.second, depth + 1);
output << "; ";
output << ";";
attrsPrinted++;
printedHere++;
}
decreaseIndent();
printSpace(prettyPrint);
output << "}";
} else
} else {
output << "{ ... }";
}
}
bool shouldPrettyPrintList(std::span<Value * const> list)
{
if (!options.shouldPrettyPrint() || list.empty()) {
return false;
}
// Pretty-print lists with more than one item.
if (list.size() > 1) {
return true;
}
auto item = list[0];
if (!item) {
return true;
}
// Pretty-print single-item lists only if they contain nested
// structures.
auto itemType = item->type();
return itemType == nList || itemType == nAttrs || itemType == nThunk;
}
void printList(Value & v, size_t depth)
@@ -304,11 +391,17 @@ private:
return;
}
output << "[ ";
if (depth < options.maxDepth) {
for (auto elem : v.listItems()) {
increaseIndent();
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;
}
@@ -317,13 +410,16 @@ private:
} else {
printNullptr();
}
output << " ";
listItemsPrinted++;
printedHere++;
}
decreaseIndent();
printSpace(prettyPrint);
output << "]";
} else {
output << "[ ... ]";
}
else
output << "... ";
output << "]";
}
void printFunction(Value & v)
@@ -486,6 +582,7 @@ public:
{
attrsPrinted = 0;
listItemsPrinted = 0;
indent.clear();
if (options.trackRepeated) {
seen.emplace();
+20
View File
@@ -0,0 +1,20 @@
#pragma once
namespace nix {
/**
* Exit status returned from the REPL.
*/
enum class ReplExitStatus {
/**
* The user exited with `:quit`. The program (e.g., if the REPL was acting
* as the debugger) should exit.
*/
QuitAll,
/**
* The user exited with `:continue`. The program should continue running.
*/
Continue,
};
}
+1
View File
@@ -1,6 +1,7 @@
#include "value-to-json.hh"
#include "eval-inline.hh"
#include "util.hh"
#include "signals.hh"
#include "store-api.hh"
#include <cstdlib>
+1
View File
@@ -2,6 +2,7 @@
#include "xml-writer.hh"
#include "eval-inline.hh"
#include "util.hh"
#include "signals.hh"
#include <cstdlib>
+3 -3
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;
+2 -2
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);
+42
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,
)
+1 -1
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");
+4 -4
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;
+1 -1
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
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,
)
+3 -3
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;
+1 -2
View File
@@ -3,6 +3,7 @@
#include "store-api.hh"
#include "gc-store.hh"
#include "util.hh"
#include "signals.hh"
#include "loggers.hh"
#include "progress-bar.hh"
@@ -407,6 +408,4 @@ PrintFreed::~PrintFreed()
showBytes(results.bytesFreed));
}
Exit::~Exit() { }
}
+1 -9
View File
@@ -6,6 +6,7 @@
#include "common-args.hh"
#include "path.hh"
#include "derived-path.hh"
#include "exit.hh"
#include <signal.h>
@@ -14,15 +15,6 @@
namespace nix {
class Exit : public std::exception
{
public:
int status;
Exit() : status(0) { }
Exit(int status) : status(status) { }
virtual ~Exit();
};
int handleExceptions(const std::string & programName, std::function<void()> fun);
/**
+2 -1
View File
@@ -10,6 +10,7 @@
#include "nar-info-disk-cache.hh"
#include "nar-accessor.hh"
#include "thread-pool.hh"
#include "signals.hh"
#include "callback.hh"
#include <chrono>
@@ -123,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;
+5 -5
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();
}
+3 -3
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();
+13 -13
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) {
+1
View File
@@ -1,6 +1,7 @@
#include "worker.hh"
#include "substitution-goal.hh"
#include "nar-info.hh"
#include "signals.hh"
#include "finally.hh"
namespace nix {
+1
View File
@@ -3,6 +3,7 @@
#include "substitution-goal.hh"
#include "drv-output-substitution-goal.hh"
#include "local-derivation-goal.hh"
#include "signals.hh"
#include "hook-instance.hh"
#include <poll.h>
+7 -7
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)) {
+2 -2
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));
+1 -1
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"
+2 -2
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;
}
+4 -3
View File
@@ -3,6 +3,7 @@
#include "globals.hh"
#include "store-api.hh"
#include "s3.hh"
#include "signals.hh"
#include "compression.hh"
#include "finally.hh"
#include "callback.hh"
@@ -669,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);
{
@@ -702,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
+3 -2
View File
@@ -1,6 +1,7 @@
#include "derivations.hh"
#include "globals.hh"
#include "local-store.hh"
#include "signals.hh"
#include "finally.hh"
#include <functional>
@@ -548,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");
@@ -646,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;
+3 -3
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());
}
}
@@ -415,7 +415,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
+1 -1
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);
+1 -1
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)
+5 -4
View File
@@ -8,6 +8,7 @@
#include "references.hh"
#include "callback.hh"
#include "topo-sort.hh"
#include "signals.hh"
#include "finally.hh"
#include "compression.hh"
@@ -262,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);
@@ -452,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;
@@ -477,7 +478,7 @@ LocalStore::~LocalStore()
try {
auto fdTempRoots(_fdTempRoots.lock());
if (*fdTempRoots) {
*fdTempRoots = -1;
fdTempRoots->reset();
unlink(fnTempRoots.c_str());
}
} catch (...) {
@@ -1483,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;
}
+2 -2
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);
+6 -6
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
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,
)
+1
View File
@@ -1,6 +1,7 @@
#include "util.hh"
#include "local-store.hh"
#include "globals.hh"
#include "signals.hh"
#include <cstdlib>
#include <cstring>
+1 -1
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");
+2 -3
View File
@@ -1,5 +1,6 @@
#include "pathlocks.hh"
#include "util.hh"
#include "signals.hh"
#include "sync.hh"
#include <cerrno>
@@ -16,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);
+1 -1
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);

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