Compare commits

..

2 Commits

Author SHA1 Message Date
Eelco Dolstra
5253cb4b68 Parse '(f x) y' the same as 'f x y' 2021-11-05 13:05:14 +01:00
Eelco Dolstra
904d0ec5c0 Merge environments of nested functions
Previously an expression like 'x: y: ...' would create two
environments with one value. Now it creates one environment with two
values. This reduces the number of allocations and the distance in the
environment chain that variable lookups need to traverse.

On

  $ nix-instantiate --dry-run '<nixpkgs/nixos/release-combined.nix>' -A nixos.tests.simple.x86_64-linux

this gives a ~30% reduction in the number of Env allocations.
2021-11-05 13:05:03 +01:00
267 changed files with 6105 additions and 17320 deletions

View File

@@ -1,7 +0,0 @@
**Release Notes**
Please include relevant [release notes](https://github.com/NixOS/nix/blob/master/doc/manual/src/release-notes/rl-next.md) as needed.
**Testing**
If this issue is a regression or something that should block release, please consider including a test either in the [testsuite](https://github.com/NixOS/nix/tree/master/tests) or as a [hydraJob]( https://github.com/NixOS/nix/blob/master/flake.nix#L396) so that it can be part of the [automatic checks](https://hydra.nixos.org/jobset/nix/master).

View File

@@ -1,26 +0,0 @@
name: Backport
on:
pull_request_target:
types: [closed, labeled]
jobs:
backport:
name: Backport Pull Request
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
uses: zeebe-io/backport-action@v0.0.7
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}
pull_description: |-
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
# should be kept in sync with `uses`
version: v0.0.5

View File

@@ -1,16 +0,0 @@
name: Hydra status
on:
schedule:
- cron: "12,42 * * * *"
workflow_dispatch:
jobs:
check_hydra_status:
name: Check Hydra status
if: github.repository_owner == 'NixOS'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- run: bash scripts/check-hydra-status.sh

View File

@@ -1,4 +1,4 @@
name: "CI"
name: "Test"
on:
pull_request:
@@ -14,10 +14,10 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v2.3.5
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v16
- uses: cachix/install-nix-action@v14.1
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
@@ -46,11 +46,11 @@ jobs:
outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v2.3.5
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v16
- uses: cachix/install-nix-action@v14.1
- uses: cachix/cachix-action@v10
with:
name: '${{ env.CACHIX_NAME }}'
@@ -67,42 +67,10 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v2.3.5
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v16
- uses: cachix/install-nix-action@v14.1
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
- run: nix-instantiate -E 'builtins.currentTime' --eval
docker_push_image:
needs: [check_cachix, tests]
if: >-
github.event_name == 'push' &&
github.ref_name == 'master' &&
needs.check_cachix.outputs.secret == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v16
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix-build -A checks.$(nix-instantiate --eval -E 'builtins.currentSystem' --json).dockerImage
- run: docker load -i ./result/image.tar.gz
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
- run: docker tag nix:$NIX_VERSION nixos/nix:master
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: docker push nixos/nix:$NIX_VERSION
- run: docker push nixos/nix:master

2
.gitignore vendored
View File

@@ -26,6 +26,8 @@ perl/Makefile.config
# /scripts/
/scripts/nix-profile.sh
/scripts/nix-reduce-build
/scripts/nix-http-export.cgi
/scripts/nix-profile-daemon.sh
# /src/libexpr/

View File

@@ -1 +1 @@
2.6.1
2.5

View File

@@ -10,7 +10,6 @@ makefiles = \
src/libexpr/local.mk \
src/libcmd/local.mk \
src/nix/local.mk \
src/nlohmann/local.mk \
src/resolve-system-dependencies/local.mk \
scripts/local.mk \
misc/bash/local.mk \

View File

@@ -188,24 +188,17 @@ PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLA
[AC_MSG_ERROR([Nix requires libeditline; it was not found via pkg-config, but via its header, but required functions do not work. Maybe it is too old? >= 1.14 is required.])])
])
# Look for libsodium.
# Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
# Look for libbrotli{enc,dec}.
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
# Look for libcpuid.
have_libcpuid=
if test "$machine_name" = "x86_64"; then
AC_ARG_ENABLE([cpuid],
AS_HELP_STRING([--disable-cpuid], [Do not determine microarchitecture levels with libcpuid (relevant to x86_64 only)]))
if test "x$enable_cpuid" != "xno"; then
PKG_CHECK_MODULES([LIBCPUID], [libcpuid],
[CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"
have_libcpuid=1
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])]
)
fi
PKG_CHECK_MODULES([LIBCPUID], [libcpuid], [CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"])
have_libcpuid=1
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])
fi
AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])

View File

@@ -8,19 +8,17 @@ concatStrings (map
let option = options.${name}; in
" - `${name}` \n\n"
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
+ (if option.documentDefault
then " **Default:** " + (
if option.value == "" || option.value == []
then "*empty*"
else if isBool option.value
then (if option.value then "`true`" else "`false`")
else
# n.b. a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is "null" in
# JSON, but that converts to "{ }" here.
(if isAttrs option.value then "`\"\"`"
else "`" + toString option.value + "`")) + "\n\n"
else " **Default:** *machine-specific*")
+ " **Default:** " + (
if option.value == "" || option.value == []
then "*empty*"
else if isBool option.value
then (if option.value then "`true`" else "`false`")
else
# n.b. a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is "null" in
# JSON, but that converts to "{ }" here.
(if isAttrs option.value then "`\"\"`"
else "`" + toString option.value + "`")) + "\n\n"
+ (if option.aliases != []
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
else "")

View File

@@ -12,13 +12,11 @@ man-pages := $(foreach n, \
clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8
# Provide a dummy environment for nix, so that it will not access files outside the macOS sandbox.
# Set cores to 0 because otherwise nix show-config resolves the cores based on the current machine
dummy-env = env -i \
HOME=/dummy \
NIX_CONF_DIR=/dummy \
NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt \
NIX_STATE_DIR=/dummy \
NIX_CONFIG='cores = 0'
NIX_STATE_DIR=/dummy
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw

View File

@@ -9,7 +9,6 @@
- [Prerequisites](installation/prerequisites-source.md)
- [Obtaining a Source Distribution](installation/obtaining-source.md)
- [Building Nix from Source](installation/building-source.md)
- [Using Nix within Docker](installation/installing-docker.md)
- [Security](installation/nix-security.md)
- [Single-User Mode](installation/single-user.md)
- [Multi-User Mode](installation/multi-user.md)
@@ -71,8 +70,7 @@
- [Hacking](contributing/hacking.md)
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)

View File

@@ -53,8 +53,8 @@ example, the following command allows you to build a derivation for
$ uname
Linux
$ nix build --impure \
--expr '(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
$ nix build \
'(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
--builders 'ssh://mac x86_64-darwin'
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac

View File

@@ -16,9 +16,8 @@ By default Nix reads settings from the following places:
will be loaded in reverse order.
Otherwise it will look for `nix/nix.conf` files in `XDG_CONFIG_DIRS`
and `XDG_CONFIG_HOME`. If unset, `XDG_CONFIG_DIRS` defaults to
`/etc/xdg`, and `XDG_CONFIG_HOME` defaults to `$HOME/.config`
as per [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
and `XDG_CONFIG_HOME`. If these are unset, it will look in
`$HOME/.config/nix/nix.conf`.
- If `NIX_CONFIG` is set, its contents is treated as the contents of
a configuration file.

View File

@@ -238,16 +238,7 @@ a number of possible ways:
## Examples
To install a package using a specific attribute path from the active Nix expression:
```console
$ nix-env -iA gcc40mips
installing `gcc-4.0.2'
$ nix-env -iA xorg.xorgserver
installing `xorg-server-1.2.0'
```
To install a specific version of `gcc` using the derivation name:
To install a specific version of `gcc` from the active Nix expression:
```console
$ nix-env --install gcc-3.3.2
@@ -255,9 +246,6 @@ installing `gcc-3.3.2'
uninstalling `gcc-3.1'
```
Using attribute path for selecting a package is preferred,
as it is much faster and there will not be multiple matches.
Note the previously installed version is removed, since
`--preserve-installed` was not specified.
@@ -268,6 +256,13 @@ $ nix-env --install gcc
installing `gcc-3.3.2'
```
To install using a specific attribute:
```console
$ nix-env -i -A gcc40mips
$ nix-env -i -A xorg.xorgserver
```
To install all derivations in the Nix expression `foo.nix`:
```console
@@ -379,29 +374,22 @@ For the other flags, see `--install`.
## Examples
```console
$ nix-env --upgrade -A nixpkgs.gcc
$ nix-env --upgrade gcc
upgrading `gcc-3.3.1' to `gcc-3.4'
```
When there are no updates available, nothing will happen:
```console
$ nix-env --upgrade -A nixpkgs.pan
```
Using `-A` is preferred when possible, as it is faster and unambiguous but
it is also possible to upgrade to a specific version by matching the derivation name:
```console
$ nix-env -u gcc-3.3.2 --always
$ nix-env -u gcc-3.3.2 --always (switch to a specific version)
upgrading `gcc-3.4' to `gcc-3.3.2'
```
To try to upgrade everything
(matching packages based on the part of the derivation name without version):
```console
$ nix-env --upgrade pan
(no upgrades available, so nothing happens)
```
```console
$ nix-env -u
$ nix-env -u (try to upgrade everything)
upgrading `hello-2.1.2' to `hello-2.1.3'
upgrading `mozilla-1.2' to `mozilla-1.4'
```

View File

@@ -101,8 +101,7 @@ The following common options are supported:
- `NIX_BUILD_SHELL`\
Shell used to start the interactive environment. Defaults to the
`bash` found in `<nixpkgs>`, falling back to the `bash` found in
`PATH` if not found.
`bash` found in `PATH`.
# Examples

View File

@@ -162,11 +162,11 @@ Most Nix commands accept the following command-line options:
}: ...
```
So if you call this Nix expression (e.g., when you do `nix-env -iA
So if you call this Nix expression (e.g., when you do `nix-env -i
pkgname`), the function will be called automatically using the
value [`builtins.currentSystem`](../expressions/builtins.md) for
the `system` argument. You can override this using `--arg`, e.g.,
`nix-env -iA pkgname --arg system \"i686-freebsd\"`. (Note that
`nix-env -i pkgname --arg system \"i686-freebsd\"`. (Note that
since the argument is a Nix string literal, you have to escape the
quotes.)

View File

@@ -103,7 +103,7 @@ impacted the most by bad user experience.
# Help is essential
Help should be built into your command line so that new users can gradually
discover new features when they need them.
discover new features when they need them.
## Looking for help
@@ -176,7 +176,7 @@ $ nix init --template=template#pyton
------------------------------------------------------------------------
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> template#python
|> template#pyton
template#python-pip
template#python-poetry
```
@@ -237,10 +237,10 @@ love, but if not done perfectly it will annoy users and leave bad impression.
# Input
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
`ARGUMENTS` represent a required input for a function. When choosing to use
`ARGUMENTS` over `OPTIONS` please be aware of the downsides that come with it:
`ARGUMENT` over function please be aware of the downsides that come with it:
- User will need to remember the order of `ARGUMENTS`. This is not a problem if
there is only one `ARGUMENT`.
@@ -253,7 +253,7 @@ developer consider the downsides and choose wisely.
## Naming the `OPTIONS`
The only naming convention - apart from the ones mentioned in Naming the
Then only naming convention - apart from the ones mentioned in Naming the
`COMMANDS` section is how flags are named.
Flags are a type of `OPTION` that represent an option that can be turned ON of
@@ -271,7 +271,7 @@ to improve the discoverability of possible input. A new user will most likely
not know which `ARGUMENTS` and `OPTIONS` are required or which values are
possible for those options.
In case the user does not provide the input or they provide wrong input,
In cases, the user might not provide the input or they provide wrong input,
rather than show the error, prompt a user with an option to find and select
correct input (see examples).
@@ -302,7 +302,7 @@ $ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question needs to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
@@ -342,7 +342,7 @@ also allowing them to redirect content to a file. For example:
```shell
$ nix build > build.txt
------------------------------------------------------------------------
Error! Attribute `bin` missing at (1:94) from string.
Error! Atrribute `bin` missing at (1:94) from string.
------------------------------------------------------------------------
1| with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (surge.bin) ]; } ""
@@ -408,7 +408,7 @@ Above command clearly states that command successfully completed. And in case
of `nix build`, which is a command that might take some time to complete, it is
equally important to also show that a command started.
## Text alignment
## Text alignment
Text alignment is the number one design element that will present all of the
Nix commands as a family and not as separate tools glued together.
@@ -419,7 +419,7 @@ The format we should follow is:
$ nix COMMAND
VERB_1 NOUN and other words
VERB__1 NOUN and other words
|> Some details
|> Some details
```
Few rules that we can extract from above example:
@@ -444,13 +444,13 @@ is not even notable, therefore relying on it wouldn’t make much sense.
**The bright text is much better supported** across terminals and color
schemes. Most of the time the difference is perceived as if the bright text
would be bold.
would be bold.
## Colors
Humans are already conditioned by society to attach certain meaning to certain
colors. While the meaning is not universal, a simple collection of colors is
used to represent basic emotions.
used to represent basic emotions.
Colors that can be used in output
@@ -555,7 +555,7 @@ $ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question needs to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
@@ -566,7 +566,7 @@ $ nix build --option substitutors https://cache.example.org
There are many ways that you can control verbosity.
Verbosity levels are:
Verbosity levels are:
- `ERROR` (level 0)
- `WARN` (level 1)
@@ -586,4 +586,4 @@ There are also two shortcuts, `--debug` to run in `DEBUG` verbosity level and
# Appendix 1: Commands naming exceptions
`nix init` and `nix repl` are well established
`nix init` and `nix repl` are well established

View File

@@ -35,25 +35,6 @@ variables are set up so that those dependencies can be found:
$ nix-shell
```
or if you have a flake-enabled nix:
```console
$ nix develop
```
To get a shell with a different compilation environment (e.g. stdenv,
gccStdenv, clangStdenv, clang11Stdenv):
```console
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
```
or if you have a flake-enabled nix:
```console
$ nix develop .#clang11StdenvPackages
```
To build Nix itself in this shell:
```console

View File

@@ -12,5 +12,5 @@ For instance, `derivation` is also available as `builtins.derivation`.
<dl>
<dt><code>derivation <var>attrs</var></code>;
<code>builtins.derivation <var>attrs</var></code></dt>
<dd><p><var>derivation</var> is described in
<dd><p><var>derivation</var> in described in
<a href="derivations.md">its own section</a>.</p></dd>

View File

@@ -284,10 +284,6 @@ The points of interest are:
function is called with the `localServer` argument set to `true` but
the `db4` argument set to `null`, then the evaluation fails.
Note that `->` is the [logical
implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication)
Boolean operation.
2. This is a more subtle condition: if Subversion is built with Apache
(`httpServer`) support, then the Expat library (an XML library) used
by Subversion should be same as the one used by Apache. This is

View File

@@ -17,12 +17,12 @@ order of precedence (from strongest to weakest binding).
| String Concatenation | *string1* `+` *string2* | left | String concatenation. | 7 |
| Not | `!` *e* | none | Boolean negation. | 8 |
| Update | *e1* `//` *e2* | right | Return a set consisting of the attributes in *e1* and *e2* (with the latter taking precedence over the former in case of equally named attributes). | 9 |
| Less Than | *e1* `<` *e2*, | none | Arithmetic/lexicographic comparison. | 10 |
| Less Than or Equal To | *e1* `<=` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
| Greater Than | *e1* `>` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
| Greater Than or Equal To | *e1* `>=` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
| Less Than | *e1* `<` *e2*, | none | Arithmetic comparison. | 10 |
| Less Than or Equal To | *e1* `<=` *e2* | none | Arithmetic comparison. | 10 |
| Greater Than | *e1* `>` *e2* | none | Arithmetic comparison. | 10 |
| Greater Than or Equal To | *e1* `>=` *e2* | none | Arithmetic comparison. | 10 |
| Equality | *e1* `==` *e2* | none | Equality. | 11 |
| Inequality | *e1* `!=` *e2* | none | Inequality. | 11 |
| Logical AND | *e1* `&&` *e2* | left | Logical AND. | 12 |
| Logical OR | *e1* <code>&#124;&#124;</code> *e2* | left | Logical OR. | 13 |
| Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to <code>!e1 &#124;&#124; e2</code>). | 14 |
| Logical OR | *e1* `\|\|` *e2* | left | Logical OR. | 13 |
| Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to `!e1 \|\| e2`). | 14 |

View File

@@ -1,6 +1,6 @@
# Building and Testing
You can now try to build Hello. Of course, you could do `nix-env -f . -iA
You can now try to build Hello. Of course, you could do `nix-env -i
hello`, but you may not want to install a possibly broken package just
yet. The best way to test the package is by using the command
`nix-build`, which builds a Nix expression and creates a symlink named

View File

@@ -47,7 +47,7 @@
the store object at `P` contains the path `Q` somewhere. The
*references* of a store path are the set of store paths to which it
has a reference.
A derivation can reference other derivations and sources (but not
output paths), whereas an output path only references other output
paths.
@@ -66,7 +66,7 @@
is necessary to deploy whole closures, since otherwise at runtime
files could be missing. The command `nix-store -qR` prints out
closures of store paths.
As an example, if the store object at path `P` contains a reference
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
references `R` then `R` is also in the closure of `P`.
@@ -98,7 +98,3 @@
store. It can contain regular files, directories and symbolic
links. NARs are generated and unpacked using `nix-store --dump`
and `nix-store --restore`.
- `∅` \
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
- `ε` \
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.

View File

@@ -119,30 +119,6 @@ this to run the installer, but it may help if you run into trouble:
- update `/etc/synthetic.conf` to direct macOS to create a "synthetic"
empty root directory to mount your volume
- specify mount options for the volume in `/etc/fstab`
- `rw`: read-write
- `noauto`: prevent the system from auto-mounting the volume (so the
LaunchDaemon mentioned below can control mounting it, and to avoid
masking problems with that mounting service).
- `nobrowse`: prevent the Nix Store volume from showing up on your
desktop; also keeps Spotlight from spending resources to index
this volume
<!-- TODO:
- `suid`: honor setuid? surely not? ...
- `owners`: honor file ownership on the volume
For now I'll avoid pretending to understand suid/owners more
than I do. There've been some vague reports of file-ownership
and permission issues, particularly in cloud/VM/headless setups.
My pet theory is that this has something to do with these setups
not having a token that gets delegated to initial/admin accounts
on macOS. See scripts/create-darwin-volume.sh for a little more.
In any case, by Dec 4 2021, it _seems_ like some combination of
suid, owners, and calling diskutil enableOwnership have stopped
new reports from coming in. But I hesitate to celebrate because we
haven't really named and catalogued the behavior, understood what
we're fixing, and validated that all 3 components are essential.
-->
- if you have FileVault enabled
- generate an encryption password
- put it in your system Keychain

View File

@@ -1,59 +0,0 @@
# Using Nix within Docker
To run the latest stable release of Nix with Docker run the following command:
```console
$ docker run -ti nixos/nix
Unable to find image 'nixos/nix:latest' locally
latest: Pulling from nixos/nix
5843afab3874: Pull complete
b52bf13f109c: Pull complete
1e2415612aa3: Pull complete
Digest: sha256:27f6e7f60227e959ee7ece361f75d4844a40e1cc6878b6868fe30140420031ff
Status: Downloaded newer image for nixos/nix:latest
35ca4ada6e96:/# nix --version
nix (Nix) 2.3.12
35ca4ada6e96:/# exit
```
# What is included in Nix's Docker image?
The official Docker image is created using `pkgs.dockerTools.buildLayeredImage`
(and not with `Dockerfile` as it is usual with Docker images). You can still
base your custom Docker image on it as you would do with any other Docker
image.
The Docker image is also not based on any other image and includes minimal set
of runtime dependencies that are required to use Nix:
- pkgs.nix
- pkgs.bashInteractive
- pkgs.coreutils-full
- pkgs.gnutar
- pkgs.gzip
- pkgs.gnugrep
- pkgs.which
- pkgs.curl
- pkgs.less
- pkgs.wget
- pkgs.man
- pkgs.cacert.out
- pkgs.findutils
# Docker image with the latest development version of Nix
To get the latest image that was built by [Hydra](https://hydra.nixos.org) run
the following command:
```console
$ curl -L https://hydra.nixos.org/job/nix/master/dockerImage.x86_64-linux/latest/download/1 | docker load
$ docker run -ti nix:2.5pre20211105
```
You can also build a Docker image from source yourself:
```console
$ nix build ./\#hydraJobs.dockerImage.x86_64-linux
$ docker load -i ./result/image.tar.gz
$ docker run -ti nix:2.5pre20211105
```

View File

@@ -44,11 +44,6 @@
obtained from the its repository
<https://github.com/troglobit/editline>.
- The `libsodium` library for verifying cryptographic signatures
of contents fetched from binary caches.
It can be obtained from the official web site
<https://libsodium.org>.
- Recent versions of Bison and Flex to build the parser. (This is
because Nix needs GLR support in Bison and reentrancy support in
Flex.) For Bison, you need version 2.6, which can be obtained from
@@ -63,11 +58,3 @@
`--disable-seccomp-sandboxing` option to the `configure` script (Not
recommended unless your system doesn't support `libseccomp`). To get
the library, visit <https://github.com/seccomp/libseccomp>.
- On 64-bit x86 machines only, `libcpuid` library
is used to determine which microarchitecture levels are supported
(e.g., as whether to have `x86_64-v2-linux` among additional system types).
The library is available from its homepage
<http://libcpuid.sourceforge.net>.
This is an optional dependency and can be disabled
by providing a `--disable-cpuid` to the `configure` script.

View File

@@ -4,4 +4,4 @@ Nix is currently supported on the following platforms:
- Linux (i686, x86\_64, aarch64).
- macOS (x86\_64, aarch64).
- macOS (x86\_64).

View File

@@ -76,7 +76,7 @@ there after an upgrade. This means that you can _roll back_ to the
old version:
```console
$ nix-env --upgrade -A nixpkgs.some-package
$ nix-env --upgrade some-packages
$ nix-env --rollback
```
@@ -122,7 +122,7 @@ Nix expressions generally describe how to build a package from
source, so an installation action like
```console
$ nix-env --install -A nixpkgs.firefox
$ nix-env --install firefox
```
_could_ cause quite a bit of build activity, as not only Firefox but

View File

@@ -24,7 +24,7 @@ collection; you could write your own Nix expressions based on Nixpkgs,
or completely new ones.)
You can manually download the latest version of Nixpkgs from
<https://github.com/NixOS/nixpkgs>. However, it’s much more
<http://nixos.org/nixpkgs/download.html>. However, it’s much more
convenient to use the Nixpkgs [*channel*](channels.md), since it makes
it easy to stay up to date with new versions of Nixpkgs. Nixpkgs is
automatically added to your list of “subscribed” channels when you
@@ -40,52 +40,48 @@ $ nix-channel --update
>
> On NixOS, you’re automatically subscribed to a NixOS channel
> corresponding to your NixOS major release (e.g.
> <http://nixos.org/channels/nixos-21.11>). A NixOS channel is identical
> <http://nixos.org/channels/nixos-14.12>). A NixOS channel is identical
> to the Nixpkgs channel, except that it contains only Linux binaries
> and is updated only if a set of regression tests succeed.
You can view the set of available packages in Nixpkgs:
```console
$ nix-env -qaP
nixpkgs.aterm aterm-2.2
nixpkgs.bash bash-3.0
nixpkgs.binutils binutils-2.15
nixpkgs.bison bison-1.875d
nixpkgs.blackdown blackdown-1.4.2
nixpkgs.bzip2 bzip2-1.0.2
$ nix-env -qa
aterm-2.2
bash-3.0
binutils-2.15
bison-1.875d
blackdown-1.4.2
bzip2-1.0.2
…
```
The flag `-q` specifies a query operation, `-a` means that you want
The flag `-q` specifies a query operation, and `-a` means that you want
to show the “available” (i.e., installable) packages, as opposed to the
installed packages, and `-P` prints the attribute paths that can be used
to unambiguously select a package for installation (listed in the first column).
If you downloaded Nixpkgs yourself, or if you checked it out from GitHub,
then you need to pass the path to your Nixpkgs tree using the `-f` flag:
installed packages. If you downloaded Nixpkgs yourself, or if you
checked it out from GitHub, then you need to pass the path to your
Nixpkgs tree using the `-f` flag:
```console
$ nix-env -qaPf /path/to/nixpkgs
aterm aterm-2.2
bash bash-3.0
…
$ nix-env -qaf /path/to/nixpkgs
```
where */path/to/nixpkgs* is where you’ve unpacked or checked out
Nixpkgs.
You can filter the packages by name:
You can select specific packages by name:
```console
$ nix-env -qaP firefox
nixpkgs.firefox-esr firefox-91.3.0esr
nixpkgs.firefox firefox-94.0.1
$ nix-env -qa firefox
firefox-34.0.5
firefox-with-plugins-34.0.5
```
and using regular expressions:
```console
$ nix-env -qaP 'firefox.*'
$ nix-env -qa 'firefox.*'
```
It is also possible to see the *status* of available packages, i.e.,
@@ -93,11 +89,11 @@ whether they are installed into the user environment and/or present in
the system:
```console
$ nix-env -qaPs
$ nix-env -qas
…
-PS nixpkgs.bash bash-3.0
--S nixpkgs.binutils binutils-2.15
IPS nixpkgs.bison bison-1.875d
-PS bash-3.0
--S binutils-2.15
IPS bison-1.875d
…
```
@@ -110,13 +106,13 @@ which is Nix’s mechanism for doing binary deployment. It just means that
Nix knows that it can fetch a pre-built package from somewhere
(typically a network server) instead of building it locally.
You can install a package using `nix-env -iA`. For instance,
You can install a package using `nix-env -i`. For instance,
```console
$ nix-env -iA nixpkgs.subversion
$ nix-env -i subversion
```
will install the package called `subversion` from `nixpkgs` channel (which is, of course, the
will install the package called `subversion` (which is, of course, the
[Subversion version management system](http://subversion.tigris.org/)).
> **Note**
@@ -126,7 +122,7 @@ will install the package called `subversion` from `nixpkgs` channel (which is, o
> binary cache <https://cache.nixos.org>; it contains binaries for most
> packages in Nixpkgs. Only if no binary is available in the binary
> cache, Nix will build the package from source. So if `nix-env
> -iA nixpkgs.subversion` results in Nix building stuff from source, then either
> -i subversion` results in Nix building stuff from source, then either
> the package is not built for your platform by the Nixpkgs build
> servers, or your version of Nixpkgs is too old or too new. For
> instance, if you have a very recent checkout of Nixpkgs, then the
@@ -137,10 +133,7 @@ will install the package called `subversion` from `nixpkgs` channel (which is, o
> using a Git checkout of the Nixpkgs tree), you will get binaries for
> most packages.
Naturally, packages can also be uninstalled. Unlike when installing, you will
need to use the derivation name (though the version part can be omitted),
instead of the attribute path, as `nix-env` does not record which attribute
was used for installing:
Naturally, packages can also be uninstalled:
```console
$ nix-env -e subversion
@@ -150,7 +143,7 @@ Upgrading to a new version is just as easy. If you have a new release of
Nix Packages, you can do:
```console
$ nix-env -uA nixpkgs.subversion
$ nix-env -u subversion
```
This will *only* upgrade Subversion if there is a “newer” version in the

View File

@@ -9,7 +9,7 @@ The daemon that handles binary cache requests via HTTP, `nix-serve`, is
not part of the Nix distribution, but you can install it from Nixpkgs:
```console
$ nix-env -iA nixpkgs.nix-serve
$ nix-env -i nix-serve
```
You can then start the server, listening for HTTP connections on
@@ -35,7 +35,7 @@ On the client side, you can tell Nix to use your binary cache using
`--option extra-binary-caches`, e.g.:
```console
$ nix-env -iA nixpkgs.firefox --option extra-binary-caches http://avalon:8080/
$ nix-env -i firefox --option extra-binary-caches http://avalon:8080/
```
The option `extra-binary-caches` tells Nix to use this binary cache in

View File

@@ -44,7 +44,7 @@ collector as follows:
$ nix-store --gc
```
The behaviour of the garbage collector is affected by the
The behaviour of the gargage collector is affected by the
`keep-derivations` (default: true) and `keep-outputs` (default: false)
options in the Nix configuration file. The defaults will ensure that all
derivations that are build-time dependencies of garbage collector roots

View File

@@ -39,7 +39,7 @@ just Subversion 1.1.2 (arrows in the figure indicate symlinks). This
would be what we would obtain if we had done
```console
$ nix-env -iA nixpkgs.subversion
$ nix-env -i subversion
```
on a set of Nix expressions that contained Subversion 1.1.2.
@@ -54,7 +54,7 @@ environment is generated based on the current one. For instance,
generation 43 was created from generation 42 when we did
```console
$ nix-env -iA nixpkgs.subversion nixpkgs.firefox
$ nix-env -i subversion firefox
```
on a set of Nix expressions that contained Firefox and a new version of
@@ -127,7 +127,7 @@ All `nix-env` operations work on the profile pointed to by
(abbreviation `-p`):
```console
$ nix-env -p /nix/var/nix/profiles/other-profile -iA nixpkgs.subversion
$ nix-env -p /nix/var/nix/profiles/other-profile -i subversion
```
This will *not* change the `~/.nix-profile` symlink.

View File

@@ -6,7 +6,7 @@ automatically fetching any store paths in Firefox’s closure if they are
available on the server `avalon`:
```console
$ nix-env -iA nixpkgs.firefox --substituters ssh://alice@avalon
$ nix-env -i firefox --substituters ssh://alice@avalon
```
This works similar to the binary cache substituter that Nix usually

View File

@@ -19,19 +19,19 @@ to subsequent chapters.
channel:
```console
$ nix-env -qaP
nixpkgs.docbook_xml_dtd_43 docbook-xml-4.3
nixpkgs.docbook_xml_dtd_45 docbook-xml-4.5
nixpkgs.firefox firefox-33.0.2
nixpkgs.hello hello-2.9
nixpkgs.libxslt libxslt-1.1.28
$ nix-env -qa
docbook-xml-4.3
docbook-xml-4.5
firefox-33.0.2
hello-2.9
libxslt-1.1.28
…
```
1. Install some packages from the channel:
```console
$ nix-env -iA nixpkgs.hello
$ nix-env -i hello
```
This should download pre-built packages; it should not build them

View File

@@ -276,9 +276,6 @@ more than 2800 commits from 195 contributors since release 2.3.
* Plugins can now register `nix` subcommands.
* The `--indirect` flag to `nix-store --add-root` has become a no-op.
`--add-root` will always generate indirect GC roots from now on.
## Incompatible changes
* The `nix` command is now marked as an experimental feature. This
@@ -398,7 +395,6 @@ dramforever,
Dustin DeWeese,
edef,
Eelco Dolstra,
Ellie Hermaszewska,
Emilio Karakey,
Emily,
Eric Culp,
@@ -409,7 +405,7 @@ Federico Pellegrin,
Finn Behrens,
Florian Franzen,
Félix Baylac-Jacqué,
Gabriella Gonzalez,
Gabriel Gonzalez,
Geoff Reedy,
Georges Dubus,
Graham Christensen,
@@ -432,6 +428,7 @@ Jaroslavas Pocepko,
Jarrett Keifer,
Jeremy Schlatter,
Joachim Breitner,
Joe Hermaszewski,
Joe Pea,
John Ericson,
Jonathan Ringer,

View File

@@ -1,16 +0,0 @@
# Release 2.5 (2021-12-13)
* The garbage collector no longer blocks new builds, so the message
`waiting for the big garbage collector lock...` is a thing of the
past.
* Binary cache stores now have a setting `compression-level`.
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
* Lists can now be compared lexicographically using the `<` operator.
* New built-in function: `builtins.groupBy`, with the same functionality as
Nixpkgs' `lib.groupBy`, but faster.
* `nix repl` now has a `:log` command.

View File

@@ -1,21 +0,0 @@
# Release 2.6 (2022-01-24)
* The Nix CLI now searches for a `flake.nix` up until the root of the current
Git repository or a filesystem boundary rather than just in the current
directory.
* The TOML parser used by `builtins.fromTOML` has been replaced by [a
more compliant one](https://github.com/ToruNiina/toml11).
* Added `:st`/`:show-trace` commands to `nix repl`, which are used to
set or toggle display of error traces.
* New builtin function `builtins.zipAttrsWith` with the same
functionality as `lib.zipAttrsWith` from Nixpkgs, but much more
efficient.
* New command `nix store copy-log` to copy build logs from one store
to another.
* The `commit-lockfile-summary` option can be set to a non-empty
string to override the commit summary used when commiting an updated
lockfile. This may be used in conjunction with the `nixConfig`
attribute in `flake.nix` to better conform to repository
conventions.
* `docker run -ti nixos/nix:master` will place you in the Docker
container with the latest version of Nix from the `master` branch.

View File

@@ -1,2 +1,5 @@
# Release X.Y (202?-??-??)
# Release 2.5 (2021-XX-XX)
* Binary cache stores now have a setting `compression-level`.
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.

View File

@@ -1,264 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, lib ? pkgs.lib
, name ? "nix"
, tag ? "latest"
, channelName ? "nixpkgs"
, channelURL ? "https://nixos.org/channels/nixpkgs-unstable"
}:
let
defaultPkgs = with pkgs; [
nix
bashInteractive
coreutils-full
gnutar
gzip
gnugrep
which
curl
less
wget
man
cacert.out
findutils
iana-etc
git
];
users = {
root = {
uid = 0;
shell = "/bin/bash";
home = "/root";
gid = 0;
};
} // lib.listToAttrs (
map
(
n: {
name = "nixbld${toString n}";
value = {
uid = 30000 + n;
gid = 30000;
groups = [ "nixbld" ];
description = "Nix build user ${toString n}";
};
}
)
(lib.lists.range 1 32)
);
groups = {
root.gid = 0;
nixbld.gid = 30000;
};
userToPasswd = (
k:
{ uid
, gid ? 65534
, home ? "/var/empty"
, description ? ""
, shell ? "/bin/false"
, groups ? [ ]
}: "${k}:x:${toString uid}:${toString gid}:${description}:${home}:${shell}"
);
passwdContents = (
lib.concatStringsSep "\n"
(lib.attrValues (lib.mapAttrs userToPasswd users))
);
userToShadow = k: { ... }: "${k}:!:1::::::";
shadowContents = (
lib.concatStringsSep "\n"
(lib.attrValues (lib.mapAttrs userToShadow users))
);
# Map groups to members
# {
# group = [ "user1" "user2" ];
# }
groupMemberMap = (
let
# Create a flat list of user/group mappings
mappings = (
builtins.foldl'
(
acc: user:
let
groups = users.${user}.groups or [ ];
in
acc ++ map
(group: {
inherit user group;
})
groups
)
[ ]
(lib.attrNames users)
);
in
(
builtins.foldl'
(
acc: v: acc // {
${v.group} = acc.${v.group} or [ ] ++ [ v.user ];
}
)
{ }
mappings)
);
groupToGroup = k: { gid }:
let
members = groupMemberMap.${k} or [ ];
in
"${k}:x:${toString gid}:${lib.concatStringsSep "," members}";
groupContents = (
lib.concatStringsSep "\n"
(lib.attrValues (lib.mapAttrs groupToGroup groups))
);
nixConf = {
sandbox = "false";
build-users-group = "nixbld";
trusted-public-keys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=";
};
nixConfContents = (lib.concatStringsSep "\n" (lib.mapAttrsFlatten (n: v: "${n} = ${v}") nixConf)) + "\n";
baseSystem =
let
nixpkgs = pkgs.path;
channel = pkgs.runCommand "channel-nixos" { } ''
mkdir $out
ln -s ${nixpkgs} $out/nixpkgs
echo "[]" > $out/manifest.nix
'';
rootEnv = pkgs.buildPackages.buildEnv {
name = "root-profile-env";
paths = defaultPkgs;
};
manifest = pkgs.buildPackages.runCommand "manifest.nix" { } ''
cat > $out <<EOF
[
${lib.concatStringsSep "\n" (builtins.map (drv: let
outputs = drv.outputsToInstall or [ "out" ];
in ''
{
${lib.concatStringsSep "\n" (builtins.map (output: ''
${output} = { outPath = "${lib.getOutput output drv}"; };
'') outputs)}
outputs = [ ${lib.concatStringsSep " " (builtins.map (x: "\"${x}\"") outputs)} ];
name = "${drv.name}";
outPath = "${drv}";
system = "${drv.system}";
type = "derivation";
meta = { };
}
'') defaultPkgs)}
]
EOF
'';
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
mkdir $out
cp -a ${rootEnv}/* $out/
ln -s ${manifest} $out/manifest.nix
'';
in
pkgs.runCommand "base-system"
{
inherit passwdContents groupContents shadowContents nixConfContents;
passAsFile = [
"passwdContents"
"groupContents"
"shadowContents"
"nixConfContents"
];
allowSubstitutes = false;
preferLocalBuild = true;
} ''
env
set -x
mkdir -p $out/etc
mkdir -p $out/etc/ssl/certs
ln -s /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs
cat $passwdContentsPath > $out/etc/passwd
echo "" >> $out/etc/passwd
cat $groupContentsPath > $out/etc/group
echo "" >> $out/etc/group
cat $shadowContentsPath > $out/etc/shadow
echo "" >> $out/etc/shadow
mkdir -p $out/usr
ln -s /nix/var/nix/profiles/share $out/usr/
mkdir -p $out/nix/var/nix/gcroots
mkdir $out/tmp
mkdir -p $out/var/tmp
mkdir -p $out/etc/nix
cat $nixConfContentsPath > $out/etc/nix/nix.conf
mkdir -p $out/root
mkdir -p $out/nix/var/nix/profiles/per-user/root
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
ln -s $out/nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default
ln -s /nix/var/nix/profiles/default $out/root/.nix-profile
ln -s ${channel} $out/nix/var/nix/profiles/per-user/root/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/root/channels-1-link $out/nix/var/nix/profiles/per-user/root/channels
mkdir -p $out/root/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/root/channels $out/root/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out/root/.nix-channels
mkdir -p $out/bin $out/usr/bin
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
'';
in
pkgs.dockerTools.buildLayeredImageWithNixDb {
inherit name tag;
contents = [ baseSystem ];
extraCommands = ''
rm -rf nix-support
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
'';
fakeRootCommands = ''
chmod 1777 tmp
chmod 1777 var/tmp
'';
config = {
Cmd = [ "/root/.nix-profile/bin/bash" ];
Env = [
"USER=root"
"PATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/bin"
"/nix/var/nix/profiles/default/bin"
"/nix/var/nix/profiles/default/sbin"
]}"
"MANPATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/share/man"
"/nix/var/nix/profiles/default/share/man"
]}"
"SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_PATH=/nix/var/nix/profiles/per-user/root/channels:/root/.nix-defexpr/channels"
];
};
}

18
flake.lock generated
View File

@@ -31,26 +31,10 @@
"type": "indirect"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"id": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "indirect"
}
},
"root": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
"nixpkgs": "nixpkgs"
}
}
},

156
flake.nix
View File

@@ -2,10 +2,9 @@
description = "The purely functional package manager";
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
inputs.nixpkgs-regression.url = "nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
outputs = { self, nixpkgs, lowdown-src }:
let
@@ -15,7 +14,7 @@
then ""
else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}";
officialRelease = true;
officialRelease = false;
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
@@ -23,36 +22,15 @@
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
forAllSystemsAndStdenvs = f: forAllSystems (system:
nixpkgs.lib.listToAttrs
(map
(n:
nixpkgs.lib.nameValuePair "${n}Packages" (
f system n
)) stdenvs
)
);
forAllStdenvs = stdenvs: f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor =
let stdenvsPackages = forAllSystemsAndStdenvs
(system: stdenv:
import nixpkgs {
inherit system;
overlays = [
(overlayFor (p: p.${stdenv}))
];
}
);
in
# Add the `stdenvPackages` at toplevel, both because these are the ones
# we want most of the time and for backwards compatibility
forAllSystems (system: stdenvsPackages.${system} // stdenvsPackages.${system}.stdenvPackages);
nixpkgsFor = forAllSystems (system:
import nixpkgs {
inherit system;
overlays = [ self.overlay ];
}
);
commonDeps = pkgs: with pkgs; rec {
# Use "busybox-sandbox-shell" if present,
@@ -97,7 +75,7 @@
buildPackages.mdbook
buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
buildPackages.pkgconfig
# Tests
buildPackages.git
@@ -113,7 +91,7 @@
libarchive
boost
lowdown-nix
gtest
gmock
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
@@ -277,15 +255,18 @@
$(cat ${installerClosureInfo}/store-paths)
'';
overlayFor = getStdenv: final: prev:
let currentStdenv = getStdenv final; in
{
in {
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
overlay = final: prev: {
nixStable = prev.nix;
# Forward from the previous stage as we don’t want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
name = "nix-${version}";
inherit version;
@@ -300,8 +281,6 @@
propagatedBuildInputs = propagatedDeps;
disallowedReferences = [ boost ];
preConfigure =
''
# Copy libboost_context so we don't get all of Boost in our closure.
@@ -309,16 +288,9 @@
mkdir -p $out/lib
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
rm -f $out/lib/*.a
${lib.optionalString currentStdenv.isLinux ''
${lib.optionalString stdenv.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.isDarwin ''
for LIB in $out/lib/*.dylib; do
chmod u+w $LIB
install_name_tool -id $LIB $LIB
done
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
''}
'';
@@ -336,12 +308,6 @@
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/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 = true;
@@ -351,7 +317,7 @@
strictDeps = true;
passthru.perl-bindings = with final; currentStdenv.mkDerivation {
passthru.perl-bindings = with final; stdenv.mkDerivation {
name = "nix-perl-${version}";
src = self;
@@ -359,7 +325,7 @@
nativeBuildInputs =
[ buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
buildPackages.pkgconfig
];
buildInputs =
@@ -370,8 +336,8 @@
pkgs.perl
boost
]
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
configureFlags = ''
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
@@ -385,7 +351,7 @@
};
lowdown-nix = with final; currentStdenv.mkDerivation rec {
lowdown-nix = with final; stdenv.mkDerivation rec {
name = "lowdown-0.9.0";
src = lowdown-src;
@@ -395,20 +361,15 @@
nativeBuildInputs = [ buildPackages.which ];
configurePhase = ''
${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
./configure \
PREFIX=${placeholder "dev"} \
BINDIR=${placeholder "bin"}/bin
'';
'';
};
};
in {
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
overlay = overlayFor (p: p.stdenv);
hydraJobs = {
# Binary package for various platforms.
@@ -444,21 +405,6 @@
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "armv6l-linux" "armv7l-linux" ];
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
# docker image with Nix inside
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system:
let
pkgs = nixpkgsFor.${system};
image = import ./docker.nix { inherit pkgs; tag = version; };
in pkgs.runCommand "docker-image-tarball-${version}"
{ meta.description = "Docker image with Nix for ${system}";
}
''
mkdir -p $out/nix-support
image=$out/image.tar.gz
ln -s ${image} $image
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
'');
# Line coverage analysis.
coverage =
with nixpkgsFor.x86_64-linux;
@@ -519,23 +465,29 @@
inherit (self) overlay;
});
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
/*
# Check whether we can still evaluate all of Nixpkgs.
tests.evalNixpkgs =
import (nixpkgs + "/pkgs/top-level/make-tarball.nix") {
# FIXME: fix pkgs/top-level/make-tarball.nix in NixOS to not require a revCount.
inherit nixpkgs;
pkgs = nixpkgsFor.x86_64-linux;
officialRelease = false;
};
# Check whether we can still evaluate NixOS.
tests.evalNixOS =
with nixpkgsFor.x86_64-linux;
runCommand "eval-nixos" { buildInputs = [ nix ]; }
''
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
mkdir $out
'';
export NIX_STATE_DIR=$TMPDIR
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
pkgs = nixpkgsFor.x86_64-linux;
nixpkgs = nixpkgs-regression;
};
nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \
--arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }'
touch $out
'';
*/
installTests = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
@@ -557,8 +509,6 @@
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
});
packages = forAllSystems (system: {
@@ -643,22 +593,15 @@
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
};
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
nixpkgsFor.${system}.lib.nameValuePair
"nix-${stdenvName}"
nixpkgsFor.${system}."${stdenvName}Packages".nix
) stdenvs))
);
}) crossSystems)));
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
devShell = forAllSystems (system: self.devShells.${system}.stdenvPackages);
devShells = forAllSystemsAndStdenvs (system: stdenv:
devShell = forAllSystems (system:
with nixpkgsFor.${system};
with commonDeps pkgs;
nixpkgsFor.${system}.${stdenv}.mkDerivation {
stdenv.mkDerivation {
name = "nix";
outputs = [ "out" "dev" "doc" ];
@@ -677,9 +620,6 @@
PATH=$prefix/bin:$PATH
unset PYTHONPATH
export MANPATH=$out/share/man:$MANPATH
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
'';
});

View File

@@ -7,15 +7,13 @@ function _complete_nix {
local completion=${line%% *}
if [[ -z $have_type ]]; then
have_type=1
if [[ $completion == filenames ]]; then
if [[ $completion = filenames ]]; then
compopt -o filenames
elif [[ $completion == attrs ]]; then
compopt -o nospace
fi
else
COMPREPLY+=("$completion")
fi
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}")
__ltrim_colon_completions "$cur"
}

View File

@@ -19,6 +19,7 @@ end
function _nix_accepts_files
set -l response (_nix_complete)
# First line is either filenames or no-filenames.
test $response[1] = 'filenames'
end

View File

@@ -25,10 +25,5 @@
<string>/var/log/nix-daemon.log</string>
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>4096</integer>
</dict>
</dict>
</plist>

View File

@@ -41,7 +41,7 @@ perlarchname=$($perl -e 'use Config; print $Config{archname};')
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
AC_MSG_RESULT($perllibdir)
# Look for libsodium.
# Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
# Check for the required Perl dependencies (DBI and DBD::SQLite).

View File

@@ -1,7 +1,6 @@
package Nix::Config;
use MIME::Base64;
use Nix::Store;
$version = "@PACKAGE_VERSION@";

View File

@@ -1,28 +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 -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
buildStatus=$(echo "$buildInfo" | \
jq -r '.buildstatus')
if [[ "$buildStatus" -ne 0 ]]; then
someBuildFailed=1
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
fi
done
exit "$someBuildFailed"

View File

@@ -440,22 +440,7 @@ add_nix_vol_fstab_line() {
# shellcheck disable=SC1003,SC2026
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
shift
# wrap `ex` to work around a problem with vim plugins breaking exit codes;
# (see https://github.com/NixOS/nix/issues/5468)
# we'd prefer EDITOR="/usr/bin/ex --noplugin" 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 --noplugin "\$@"
EOF
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
EDITOR="/usr/bin/ex" _sudo "to add nix to fstab" "$@" <<EOF
:a
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
.
@@ -646,7 +631,7 @@ EOF
# technically /etc/synthetic.d/nix is supported in Big Sur+
# but handling both takes even more code...
_sudo "to add Nix to /etc/synthetic.conf" \
/usr/bin/ex --noplugin /etc/synthetic.conf <<EOF
/usr/bin/ex /etc/synthetic.conf <<EOF
:a
${NIX_ROOT:1}
.
@@ -757,9 +742,6 @@ setup_volume() {
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"
@@ -809,7 +791,7 @@ setup_volume_daemon() {
local volume_uuid="$2"
if ! test_voldaemon; then
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
_sudo "to install the Nix volume mounter" /usr/bin/ex --noplugin "$NIX_VOLUME_MOUNTD_DEST" <<EOF
_sudo "to install the Nix volume mounter" /usr/bin/ex "$NIX_VOLUME_MOUNTD_DEST" <<EOF
:a
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
.

View File

@@ -218,7 +218,7 @@ 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."
if [ "$(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 diskutil enableOwnership /nix."
fi
}

View File

@@ -377,11 +377,6 @@ cure_artifacts() {
}
validate_starting_assumptions() {
task "Checking for artifacts of previous installs"
cat <<EOF
Before I try to install, I'll check for signs Nix already is or has
been installed on this system.
EOF
if type nix-env 2> /dev/null >&2; then
warning <<EOF
Nix already appears to be installed. This installer may run into issues.
@@ -391,34 +386,20 @@ $(uninstall_directions)
EOF
fi
# TODO: I think it would be good for this step to accumulate more
# knowledge of older obsolete artifacts, if there are any.
# We could issue a "reminder" here that the user might want
# to clean them up?
for profile_target in "${PROFILE_TARGETS[@]}"; do
# TODO: I think it would be good to accumulate a list of all
# of the copies so that people don't hit this 2 or 3x in
# a row for different files.
if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
# this backup process first released in Nix 2.1
failure <<EOF
I back up shell profile/rc scripts before I add Nix to them.
I need to back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX,
but the latter already exists.
When this script runs, it backs up the current $profile_target to
$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
Here's how to clean up the old backup file:
Please follow these instructions to clean up the old backup file:
1. Back up (copy) $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX
to another location, just in case.
1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
in case.
2. Ensure $profile_target$PROFILE_BACKUP_SUFFIX does not have anything
Nix-related in it. If it does, something is probably quite
wrong. Please open an issue or get in touch immediately.
3. Once you confirm $profile_target is backed up and
$profile_target$PROFILE_BACKUP_SUFFIX doesn't mention Nix, run:
mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
it has anything nix-related in it. If it does, something is probably
quite wrong. Please open an issue or get in touch immediately.
EOF
fi
done
@@ -590,7 +571,7 @@ create_directories() {
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
fi
_sudo "to make the basic directory structure of Nix (part 1)" \
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool,/daemon-socket} /nix/var/nix/{gcroots,profiles}/per-user
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
_sudo "to make the basic directory structure of Nix (part 2)" \
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store
@@ -828,7 +809,7 @@ main() {
# can fail faster in this case. Sourcing install-darwin... now runs
# `touch /` to detect Read-only root, but it could update times on
# pre-Catalina macOS if run as root user.
if [ "$EUID" -eq 0 ]; then
if [ $EUID -eq 0 ]; then
failure <<EOF
Please do not run this script with root privileges. I will call sudo
when I need to.

View File

@@ -38,7 +38,7 @@ 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
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
fi
case "$(uname -s)" in
@@ -98,7 +98,7 @@ while [ $# -gt 0 ]; do
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 " See https://nixos.org/nix/manual/#sect-multi-user-installation"
echo ""
echo " --no-daemon: Simple, single-user installation that does not require root and is"
echo " trivial to uninstall."
@@ -134,7 +134,7 @@ fi
echo "performing a single-user installation of Nix..." >&2
if ! [ -e "$dest" ]; then
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
@@ -143,12 +143,12 @@ if ! [ -e "$dest" ]; then
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
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/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
exit 1
fi
mkdir -p "$dest/store"
mkdir -p $dest/store
printf "copying Nix to %s..." "${dest}/store" >&2
# Insert a newline if no progress is shown.
@@ -189,17 +189,17 @@ fi
# Install an SSL certificate bundle.
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
"$nix/bin/nix-env" -i "$cacert"
$nix/bin/nix-env -i "$cacert"
export NIX_SSL_CERT_FILE="$HOME/.nix-profile/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
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
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

View File

@@ -15,7 +15,7 @@ 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")"
_sudo "create directory for systemd unit override" mkdir -p "$(dirname $SERVICE_OVERRIDE)"
cat <<EOF | _sudo "create systemd unit override" tee "$SERVICE_OVERRIDE"
[Service]
$1

View File

@@ -81,10 +81,10 @@ if [ "$(uname -s)" != "Darwin" ]; then
require_util xz "unpack the binary tarball"
fi
if command -v curl > /dev/null 2>&1; then
fetch() { curl -L "$1" -o "$2"; }
elif command -v wget > /dev/null 2>&1; then
if command -v wget > /dev/null 2>&1; then
fetch() { wget "$1" -O "$2"; }
elif command -v curl > /dev/null 2>&1; then
fetch() { curl -L "$1" -o "$2"; }
else
oops "you don't have wget or curl installed, which I need to download the binary tarball"
fi

View File

@@ -1,5 +1,7 @@
nix_noinst_scripts := \
$(d)/nix-profile.sh
$(d)/nix-http-export.cgi \
$(d)/nix-profile.sh \
$(d)/nix-reduce-build
noinst-scripts += $(nix_noinst_scripts)

51
scripts/nix-http-export.cgi.in Executable file
View File

@@ -0,0 +1,51 @@
#! /bin/sh
export HOME=/tmp
export NIX_REMOTE=daemon
TMP_DIR="${TMP_DIR:-/tmp/nix-export}"
@coreutils@/mkdir -p "$TMP_DIR" || true
@coreutils@/chmod a+r "$TMP_DIR"
needed_path="?$QUERY_STRING"
needed_path="${needed_path#*[?&]needed_path=}"
needed_path="${needed_path%%&*}"
#needed_path="$(echo $needed_path | ./unhttp)"
needed_path="${needed_path//%2B/+}"
needed_path="${needed_path//%3D/=}"
echo needed_path: "$needed_path" >&2
NIX_STORE="${NIX_STORE_DIR:-/nix/store}"
echo NIX_STORE: "${NIX_STORE}" >&2
full_path="${NIX_STORE}"/"$needed_path"
if [ "$needed_path" != "${needed_path%.drv}" ]; then
echo "Status: 403 You should create the derivation file yourself"
echo "Content-Type: text/plain"
echo
echo "Refusing to disclose derivation contents"
exit
fi
if @bindir@/nix-store --check-validity "$full_path"; then
if ! [ -e nix-export/"$needed_path".nar.gz ]; then
@bindir@/nix-store --export "$full_path" | @gzip@ > "$TMP_DIR"/"$needed_path".nar.gz
@coreutils@/ln -fs "$TMP_DIR"/"$needed_path".nar.gz nix-export/"$needed_path".nar.gz
fi;
echo "Status: 301 Moved"
echo "Location: nix-export/"$needed_path".nar.gz"
echo
else
echo "Status: 404 No such path found"
echo "Content-Type: text/plain"
echo
echo "Path not found:"
echo "$needed_path"
echo "checked:"
echo "$full_path"
fi

View File

@@ -5,7 +5,7 @@ __ETC_PROFILE_NIX_SOURCED=1
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
if [ ! -z "${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
@@ -18,14 +18,14 @@ elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
else
# Fall back to what is in the nix profiles, favouring whatever is defined last.
check_nix_profiles() {
if [ -n "$ZSH_VERSION" ]; then
if [ "$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
if [ -e $i/etc/ssl/certs/ca-bundle.crt ]; then
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
fi
done

171
scripts/nix-reduce-build.in Executable file
View File

@@ -0,0 +1,171 @@
#! @bash@
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
cd "$WORKING_DIRECTORY";
if test -z "$1" || test "a--help" = "a$1" ; then
echo 'nix-reduce-build (paths or Nix expressions) -- (package sources)' >&2
echo As in: >&2
echo nix-reduce-build /etc/nixos/nixos -- ssh://user@somewhere.nowhere.example.org >&2
echo nix-reduce-build /etc/nixos/nixos -- \\
echo " " \''http://somewhere.nowhere.example.org/nix/nix-http-export.cgi?needed_path='\' >&2
echo " store path name will be added into the end of the URL" >&2
echo nix-reduce-build /etc/nixos/nixos -- file://home/user/nar/ >&2
echo " that should be a directory where gzipped 'nix-store --export' ">&2
echo " files are located (they should have .nar.gz extension)" >&2
echo " Or all together: " >&2
echo -e nix-reduce-build /expr.nix /e2.nix -- \\\\\\\n\
" ssh://a@b.example.com http://n.example.com/get-nar?q= file://nar/" >&2
echo " Also supports best-effort local builds of failing expression set:" >&2
echo "nix-reduce-build /e.nix -- nix-daemon:// nix-self://" >&2
echo " nix-daemon:// builds using daemon"
echo " nix-self:// builds directly using nix-store from current installation" >&2
echo " nix-daemon-fixed:// and nix-self-fixed:// do the same, but only for" >&2;
echo "derivations with specified output hash (sha256, sha1 or md5)." >&2
echo " nix-daemon-substitute:// and nix-self-substitute:// try to substitute" >&2;
echo "maximum amount of paths" >&2;
echo " nix-daemon-build:// and nix-self-build:// try to build (not substitute)" >&2;
echo "maximum amount of paths" >&2;
echo " If no package sources are specified, required paths are listed." >&2;
exit;
fi;
while ! test "$1" = "--" || test "$1" = "" ; do
echo "$1" >> initial; >&2
shift;
done
shift;
echo Will work on $(cat initial | wc -l) targets. >&2
while read ; do
case "$REPLY" in
${NIX_STORE_DIR:-/nix/store}/*)
echo "$REPLY" >> paths; >&2
;;
*)
(
IFS=: ;
nix-instantiate $REPLY >> paths;
);
;;
esac;
done < initial;
echo Proceeding $(cat paths | wc -l) paths. >&2
while read; do
case "$REPLY" in
*.drv)
echo "$REPLY" >> derivers; >&2
;;
*)
nix-store --query --deriver "$REPLY" >>derivers;
;;
esac;
done < paths;
echo Found $(cat derivers | wc -l) derivers. >&2
cat derivers | xargs nix-store --query -R > derivers-closure;
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We need $(cat needed-paths | wc -l) paths. >&2
egrep '[.]drv$' derivers-closure > critical-derivers;
if test -z "$1" ; then
cat needed-paths;
fi;
refresh_critical_derivers() {
echo "Finding needed derivers..." >&2;
cat critical-derivers | while read; do
if ! (nix-store --query --outputs "$REPLY" | xargs nix-store --check-validity &> /dev/null;); then
echo "$REPLY";
fi;
done > new-critical-derivers;
mv new-critical-derivers critical-derivers;
echo The needed paths are realized by $(cat critical-derivers | wc -l) derivers. >&2
}
build_here() {
cat critical-derivers | while read; do
echo "Realising $REPLY using nix-daemon" >&2
@bindir@/nix-store -r "${REPLY}"
done;
}
try_to_substitute(){
cat needed-paths | while read ; do
echo "Building $REPLY using nix-daemon" >&2
@bindir@/nix-store -r "${NIX_STORE_DIR:-/nix/store}/${REPLY##*/}"
done;
}
for i in "$@"; do
sshHost="${i#ssh://}";
httpHost="${i#http://}";
httpsHost="${i#https://}";
filePath="${i#file:/}";
if [ "$i" != "$sshHost" ]; then
cat needed-paths | while read; do
echo "Getting $REPLY and its closure over ssh" >&2
nix-copy-closure --from "$sshHost" --gzip "$REPLY" </dev/null || true;
done;
elif [ "$i" != "$httpHost" ] || [ "$i" != "$httpsHost" ]; then
cat needed-paths | while read; do
echo "Getting $REPLY over http/https" >&2
curl ${BAD_CERTIFICATE:+-k} -L "$i${REPLY##*/}" | gunzip | nix-store --import;
done;
elif [ "$i" != "$filePath" ] ; then
cat needed-paths | while read; do
echo "Installing $REPLY from file" >&2
gunzip < "$filePath/${REPLY##*/}".nar.gz | nix-store --import;
done;
elif [ "$i" = "nix-daemon://" ] ; then
NIX_REMOTE=daemon try_to_substitute;
refresh_critical_derivers;
NIX_REMOTE=daemon build_here;
elif [ "$i" = "nix-self://" ] ; then
NIX_REMOTE= try_to_substitute;
refresh_critical_derivers;
NIX_REMOTE= build_here;
elif [ "$i" = "nix-daemon-fixed://" ] ; then
refresh_critical_derivers;
cat critical-derivers | while read; do
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
echo "Realising $REPLY using nix-daemon" >&2
NIX_REMOTE=daemon @bindir@/nix-store -r "${REPLY}"
fi;
done;
elif [ "$i" = "nix-self-fixed://" ] ; then
refresh_critical_derivers;
cat critical-derivers | while read; do
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
echo "Realising $REPLY using direct Nix build" >&2
NIX_REMOTE= @bindir@/nix-store -r "${REPLY}"
fi;
done;
elif [ "$i" = "nix-daemon-substitute://" ] ; then
NIX_REMOTE=daemon try_to_substitute;
elif [ "$i" = "nix-self-substitute://" ] ; then
NIX_REMOTE= try_to_substitute;
elif [ "$i" = "nix-daemon-build://" ] ; then
refresh_critical_derivers;
NIX_REMOTE=daemon build_here;
elif [ "$i" = "nix-self-build://" ] ; then
refresh_critical_derivers;
NIX_REMOTE= build_here;
fi;
mv needed-paths wanted-paths;
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We still need $(cat needed-paths | wc -l) paths. >&2
done;
cd /
rm -r "$WORKING_DIRECTORY"

View File

@@ -3,7 +3,7 @@
set -e
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
installerHash=$(echo "$script" | cut -b12-43 -)
installerHash=$(echo $script | cut -b12-43 -)
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install

18
src/cpptoml/LICENSE Normal file
View File

@@ -0,0 +1,18 @@
Copyright (c) 2014 Chase Geigle
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3668
src/cpptoml/cpptoml.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -54,36 +54,6 @@ void StoreCommand::run()
run(getStore());
}
CopyCommand::CopyCommand()
{
addFlag({
.longName = "from",
.description = "URL of the source Nix store.",
.labels = {"store-uri"},
.handler = {&srcUri},
});
addFlag({
.longName = "to",
.description = "URL of the destination Nix store.",
.labels = {"store-uri"},
.handler = {&dstUri},
});
}
ref<Store> CopyCommand::createStore()
{
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
}
ref<Store> CopyCommand::getDstStore()
{
if (srcUri.empty() && dstUri.empty())
throw UsageError("you must pass '--from' and/or '--to'");
return dstUri.empty() ? openStore() : openStore(dstUri);
}
EvalCommand::EvalCommand()
{
}
@@ -104,15 +74,7 @@ ref<Store> EvalCommand::getEvalStore()
ref<EvalState> EvalCommand::getEvalState()
{
if (!evalState)
evalState =
#if HAVE_BOEHMGC
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
searchPath, getEvalStore(), getStore())
#else
std::make_shared<EvalState>(
searchPath, getEvalStore(), getStore())
#endif
;
evalState = std::make_shared<EvalState>(searchPath, getEvalStore(), getStore());
return ref<EvalState>(evalState);
}

View File

@@ -43,19 +43,6 @@ private:
std::shared_ptr<Store> _store;
};
/* A command that copies something between `--from` and `--to`
stores. */
struct CopyCommand : virtual StoreCommand
{
std::string srcUri, dstUri;
CopyCommand();
ref<Store> createStore() override;
ref<Store> getDstStore();
};
struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
EvalCommand();

View File

@@ -191,7 +191,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
auto sep = prefix_.rfind('.');
std::string searchWord;
if (sep != std::string::npos) {
searchWord = prefix_.substr(sep + 1, std::string::npos);
searchWord = prefix_.substr(sep, std::string::npos);
prefix_ = prefix_.substr(0, sep);
} else {
searchWord = prefix_;
@@ -203,8 +203,6 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
completionType = ctAttrs;
if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) {
std::string name = i.name;
@@ -234,9 +232,7 @@ void completeFlakeRefWithFragment(
prefix. */
try {
auto hash = prefix.find('#');
if (hash == std::string::npos) {
completeFlakeRef(evalState->store, prefix);
} else {
if (hash != std::string::npos) {
auto fragment = prefix.substr(hash + 1);
auto flakeRefS = std::string(prefix.substr(0, hash));
// FIXME: do tilde expansion.
@@ -252,8 +248,6 @@ void completeFlakeRefWithFragment(
flake. */
attrPathPrefixes.push_back("");
completionType = ctAttrs;
for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment);
@@ -291,13 +285,12 @@ void completeFlakeRefWithFragment(
} catch (Error & e) {
warn(e.msg());
}
completeFlakeRef(evalState->store, prefix);
}
void completeFlakeRef(ref<Store> store, std::string_view prefix)
{
if (!settings.isExperimentalFeatureEnabled(Xp::Flakes))
return;
if (prefix == "")
completions->add(".");
@@ -345,18 +338,6 @@ Installable::getCursor(EvalState & state)
return cursors[0];
}
static StorePath getDeriver(
ref<Store> store,
const Installable & i,
const StorePath & drvPath)
{
auto derivers = store->queryValidDerivers(drvPath);
if (derivers.empty())
throw Error("'%s' does not have a known deriver", i.what());
// FIXME: use all derivers?
return *derivers.begin();
}
struct InstallableStorePath : Installable
{
ref<Store> store;
@@ -365,7 +346,7 @@ struct InstallableStorePath : Installable
InstallableStorePath(ref<Store> store, StorePath && storePath)
: store(store), storePath(std::move(storePath)) { }
std::string what() const override { return store->printStorePath(storePath); }
std::string what() override { return store->printStorePath(storePath); }
DerivedPaths toDerivedPaths() override
{
@@ -386,15 +367,6 @@ struct InstallableStorePath : Installable
}
}
StorePathSet toDrvPaths(ref<Store> store) override
{
if (storePath.isDerivation()) {
return {storePath};
} else {
return {getDeriver(store, *this, storePath)};
}
}
std::optional<StorePath> getStorePath() override
{
return storePath;
@@ -423,14 +395,6 @@ DerivedPaths InstallableValue::toDerivedPaths()
return res;
}
StorePathSet InstallableValue::toDrvPaths(ref<Store> store)
{
StorePathSet res;
for (auto & drv : toDerivations())
res.insert(drv.drvPath);
return res;
}
struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
@@ -441,7 +405,7 @@ struct InstallableAttrPath : InstallableValue
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
{ }
std::string what() const override { return attrPath; }
std::string what() override { return attrPath; }
std::pair<Value *, Pos> toValue(EvalState & state) override
{
@@ -865,7 +829,11 @@ StorePathSet toDerivations(
[&](const DerivedPath::Opaque & bo) {
if (!useDeriver)
throw Error("argument '%s' did not evaluate to a derivation", i->what());
drvPaths.insert(getDeriver(store, *i, bo.path));
auto derivers = store->queryValidDerivers(bo.path);
if (derivers.empty())
throw Error("'%s' does not have a known deriver", i->what());
// FIXME: use all derivers?
drvPaths.insert(*derivers.begin());
},
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);

View File

@@ -33,15 +33,10 @@ struct Installable
{
virtual ~Installable() { }
virtual std::string what() const = 0;
virtual std::string what() = 0;
virtual DerivedPaths toDerivedPaths() = 0;
virtual StorePathSet toDrvPaths(ref<Store> store)
{
throw Error("'%s' cannot be converted to a derivation path", what());
}
DerivedPath toDerivedPath();
UnresolvedApp toApp(EvalState & state);
@@ -86,8 +81,6 @@ struct InstallableValue : Installable
virtual std::vector<DerivationInfo> toDerivations() = 0;
DerivedPaths toDerivedPaths() override;
StorePathSet toDrvPaths(ref<Store> store) override;
};
struct InstallableFlake : InstallableValue
@@ -106,7 +99,7 @@ struct InstallableFlake : InstallableValue
Strings && prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();

View File

@@ -7,19 +7,26 @@
namespace nix {
/* Allocate a new array of attributes for an attribute set with a specific
capacity. The space is implicitly reserved after the Bindings
structure. */
Bindings * EvalState::allocBindings(size_t capacity)
{
if (capacity == 0)
return &emptyBindings;
if (capacity > std::numeric_limits<Bindings::size_t>::max())
throw Error("attribute set of size %d is too big", capacity);
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
}
void EvalState::mkAttrs(Value & v, size_t capacity)
{
if (capacity == 0) {
v = vEmptySet;
return;
}
v.mkAttrs(allocBindings(capacity));
nrAttrsets++;
nrAttrsInAttrsets += capacity;
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
}
@@ -34,36 +41,15 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
}
Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
Value * EvalState::allocAttr(Value & vAttrs, const std::string & name)
{
return allocAttr(vAttrs, symbols.create(name));
}
Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
{
auto value = state.allocValue();
bindings->push_back(Attr(name, value, pos));
return *value;
}
Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos)
{
return alloc(state.symbols.create(name), pos);
}
void Bindings::sort()
{
if (size_) std::sort(begin(), end());
}
Value & Value::mkAttrs(BindingsBuilder & bindings)
{
mkAttrs(bindings.finish());
return *this;
std::sort(begin(), end());
}

View File

@@ -113,52 +113,5 @@ public:
friend class EvalState;
};
/* A wrapper around Bindings that ensures that its always in sorted
order at the end. The only way to consume a BindingsBuilder is to
call finish(), which sorts the bindings. */
class BindingsBuilder
{
Bindings * bindings;
public:
// needed by std::back_inserter
using value_type = Attr;
EvalState & state;
BindingsBuilder(EvalState & state, Bindings * bindings)
: bindings(bindings), state(state)
{ }
void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
{
insert(Attr(name, value, pos));
}
void insert(const Attr & attr)
{
push_back(attr);
}
void push_back(const Attr & attr)
{
bindings->push_back(attr);
}
Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));
Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));
Bindings * finish()
{
bindings->sort();
return bindings;
}
Bindings * alreadySorted()
{
return bindings;
}
};
}

View File

@@ -73,16 +73,17 @@ MixEvalArgs::MixEvalArgs()
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
{
auto res = state.buildBindings(autoArgs.size());
Bindings * res = state.allocBindings(autoArgs.size());
for (auto & i : autoArgs) {
auto v = state.allocValue();
Value * v = state.allocValue();
if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
mkString(*v, string(i.second, 1));
res->push_back(Attr(state.symbols.create(i.first), v));
}
return res.finish();
res->sort();
return res;
}
Path lookupFileArg(EvalState & state, string s)

View File

@@ -36,19 +36,6 @@
namespace nix {
static char * allocString(size_t size)
{
char * t;
#if HAVE_BOEHMGC
t = (char *) GC_MALLOC_ATOMIC(size);
#else
t = malloc(size);
#endif
if (!t) throw std::bad_alloc();
return t;
}
static char * dupString(const char * s)
{
char * t;
@@ -132,14 +119,15 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
case tList2:
case tListN:
str << "[ ";
for (auto v2 : v.listItems()) {
printValue(str, active, *v2);
for (unsigned int n = 0; n < v.listSize(); ++n) {
printValue(str, active, *v.listElems()[n]);
str << " ";
}
str << "]";
break;
case tThunk:
case tApp:
case tPartialApp:
str << "<CODE>";
break;
case tLambda:
@@ -158,7 +146,7 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
str << v.fpoint;
break;
default:
abort();
throw Error("invalid value");
}
active.erase(&v);
@@ -425,21 +413,10 @@ EvalState::EvalState(
, sDescription(symbols.create("description"))
, sSelf(symbols.create("self"))
, sEpsilon(symbols.create(""))
, sStartSet(symbols.create("startSet"))
, sOperator(symbols.create("operator"))
, sKey(symbols.create("key"))
, sPath(symbols.create("path"))
, sPrefix(symbols.create("prefix"))
, repair(NoRepair)
, emptyBindings(0)
, store(store)
, buildStore(buildStore ? buildStore : store)
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
#else
, valueAllocCache(std::make_shared<void *>(nullptr))
#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
{
@@ -478,6 +455,8 @@ EvalState::EvalState(
}
}
vEmptySet.mkAttrs(allocBindings(0));
createBaseEnv();
}
@@ -541,12 +520,8 @@ Path EvalState::checkSourcePath(const Path & path_)
}
}
if (!found) {
auto modeInformation = evalSettings.pureEval
? "in pure eval mode (use '--impure' to override)"
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", abspath, modeInformation);
}
if (!found)
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", abspath);
/* Resolve symlinks. */
debug(format("checking access to '%s'") % abspath);
@@ -559,7 +534,7 @@ Path EvalState::checkSourcePath(const Path & path_)
}
}
throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path);
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path);
}
@@ -635,7 +610,7 @@ Value * EvalState::addPrimOp(const string & name,
auto vPrimOp = allocValue();
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
Value v;
v.mkApp(vPrimOp, vPrimOp);
mkApp(v, *vPrimOp, *vPrimOp);
return addConstant(name, v);
}
@@ -657,7 +632,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
auto vPrimOp = allocValue();
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
Value v;
v.mkApp(vPrimOp, vPrimOp);
mkApp(v, *vPrimOp, *vPrimOp);
return addConstant(primOp.name, v);
}
@@ -788,14 +763,15 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
}
void Value::mkString(std::string_view s)
void mkString(Value & v, const char * s)
{
mkString(dupStringWithLen(s.data(), s.size()));
v.mkString(dupString(s));
}
static void copyContextToValue(Value & v, const PathSet & context)
Value & mkString(Value & v, std::string_view s, const PathSet & context)
{
v.mkString(dupStringWithLen(s.data(), s.size()));
if (!context.empty()) {
size_t n = 0;
v.string.context = (const char * *)
@@ -804,24 +780,13 @@ static void copyContextToValue(Value & v, const PathSet & context)
v.string.context[n++] = dupString(i.c_str());
v.string.context[n] = 0;
}
}
void Value::mkString(std::string_view s, const PathSet & context)
{
mkString(s);
copyContextToValue(*this, context);
}
void Value::mkStringMove(const char * s, const PathSet & context)
{
mkString(s);
copyContextToValue(*this, context);
return v;
}
void Value::mkPath(std::string_view s)
void mkPath(Value & v, const char * s)
{
mkPath(dupStringWithLen(s.data(), s.size()));
v.mkPath(dupString(s));
}
@@ -853,23 +818,8 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
Value * EvalState::allocValue()
{
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
GC_malloc_many returns a linked list of objects of the given size, where the first word
of each object is also the pointer to the next object in the list. This also means that we
have to explicitly clear the first word of every object we take. */
if (!*valueAllocCache) {
*valueAllocCache = GC_malloc_many(sizeof(Value));
if (!*valueAllocCache) throw std::bad_alloc();
}
/* GC_NEXT is a convenience macro for accessing the first word of an object.
Take the first list item, advance the list to the next item, and clear the next pointer. */
void * p = *valueAllocCache;
GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
GC_NEXT(p) = nullptr;
nrValues++;
auto v = (Value *) p;
auto v = (Value *) allocBytes(sizeof(Value));
return v;
}
@@ -914,13 +864,13 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
void EvalState::mkPos(Value & v, ptr<Pos> pos)
{
if (pos->file.set()) {
auto attrs = buildBindings(3);
attrs.alloc(sFile).mkString(pos->file);
attrs.alloc(sLine).mkInt(pos->line);
attrs.alloc(sColumn).mkInt(pos->column);
v.mkAttrs(attrs);
mkAttrs(v, 3);
mkString(*allocAttr(v, sFile), pos->file);
mkInt(*allocAttr(v, sLine), pos->line);
mkInt(*allocAttr(v, sColumn), pos->column);
v.attrs->sort();
} else
v.mkNull();
mkNull(v);
}
@@ -1099,8 +1049,8 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
auto dynamicEnv = &env;
state.mkAttrs(v, attrs.size() + dynamicAttrs.size());
Env *dynamicEnv = &env;
if (recursive) {
/* Create a new environment that contains the attributes in
@@ -1202,8 +1152,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v)
{
state.mkList(v, elems.size());
for (auto [n, v2] : enumerate(v.listItems()))
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
for (size_t n = 0; n < elems.size(); ++n)
v.listElems()[n] = elems[n]->maybeThunk(state, env);
}
@@ -1291,14 +1241,14 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
v.mkBool(false);
mkBool(v, false);
return;
} else {
vAttrs = j->value;
}
}
v.mkBool(true);
mkBool(v, true);
}
@@ -1326,37 +1276,28 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
}
};
Attr * functor;
auto callLambda = [&](Env * env, ExprLambda & lambda, Value * * args)
{
Env & env2(allocEnv(lambda.envSize));
env2.up = env;
while (nrArgs > 0) {
Displacement displ = 0;
if (vCur.isLambda()) {
for (auto & arg : lambda.args) {
auto vArg = *args++;
ExprLambda & lambda(*vCur.lambda.fun);
if (arg.arg != sEpsilon)
env2.values[displ++] = vArg;
auto size =
(lambda.arg.empty() ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size));
env2.up = vCur.lambda.env;
Displacement displ = 0;
if (!lambda.hasFormals())
env2.values[displ++] = args[0];
else {
forceAttrs(*args[0], pos);
if (!lambda.arg.empty())
env2.values[displ++] = args[0];
if (arg.formals) {
forceAttrs(*vArg, pos);
/* For each formal argument, get the actual argument. If
there is no matching actual argument but the formal
argument has a default, use the default. */
size_t attrsUsed = 0;
for (auto & i : lambda.formals->formals) {
auto j = args[0]->attrs->get(i.name);
for (auto & i : arg.formals->formals) {
auto j = vArg->attrs->get(i.name);
if (!j) {
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
lambda, i.name);
@@ -1369,35 +1310,96 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Check that each actual argument is listed as a formal
argument (unless the attribute match specifies a `...'). */
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) {
if (!arg.formals->ellipsis && attrsUsed != vArg->attrs->size()) {
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
if (!lambda.formals->argNames.count(i.name))
for (auto & i : *vArg->attrs)
if (arg.formals->argNames.find(i.name) == arg.formals->argNames.end())
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
}
}
nrFunctionCalls++;
if (countCalls) incrFunctionCall(&lambda);
assert(displ == lambda.envSize);
/* Evaluate the body. */
try {
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
(lambda.name.set()
? "'" + (string) lambda.name + "'"
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
throw;
nrFunctionCalls++;
if (countCalls) incrFunctionCall(&lambda);
/* Evaluate the body. */
try {
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
(lambda.name.set()
? "'" + (string) lambda.name + "'"
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
throw;
}
};
nrArgs--;
args += 1;
while (nrArgs > 0) {
if (vCur.isLambda()) {
ExprLambda & lambda(*vCur.lambda.fun);
if (nrArgs < lambda.args.size()) {
vRes = vCur;
for (size_t i = 0; i < nrArgs; ++i) {
auto fun2 = allocValue();
*fun2 = vRes;
vRes.mkPartialApp(fun2, args[i]);
}
return;
} else {
callLambda(vCur.lambda.env, lambda, args);
nrArgs -= lambda.args.size();
args += lambda.args.size();
}
}
else if (vCur.isPartialApp()) {
/* Figure out the number of arguments still needed. */
size_t argsDone = 0;
Value * lambda = &vCur;
while (lambda->isPartialApp()) {
argsDone++;
lambda = lambda->app.left;
}
assert(lambda->isLambda());
auto arity = lambda->lambda.fun->args.size();
auto argsLeft = arity - argsDone;
if (nrArgs < argsLeft) {
/* We still don't have enough arguments, so extend the tPartialApp chain. */
vRes = vCur;
for (size_t i = 0; i < nrArgs; ++i) {
auto fun2 = allocValue();
*fun2 = vRes;
vRes.mkPartialApp(fun2, args[i]);
}
return;
} else {
/* We have all the arguments, so call the function
with the previous and new arguments. */
Value * vArgs[arity];
auto n = argsDone;
for (Value * arg = &vCur; arg->isPartialApp(); arg = arg->app.left)
vArgs[--n] = arg->app.right;
for (size_t i = 0; i < argsLeft; ++i)
vArgs[argsDone + i] = args[i];
nrArgs -= argsLeft;
args += argsLeft;
callLambda(lambda->lambda.env, *lambda->lambda.fun, vArgs);
}
}
else if (vCur.isPrimOp()) {
@@ -1456,16 +1458,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
}
}
else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) {
/* 'vCur' may be allocated on the stack of the calling
function, but for functors we may keep a reference, so
heap-allocate a copy and use that instead. */
Value * args2[] = {allocValue(), args[0]};
*args2[0] = vCur;
/* !!! Should we use the attr pos here? */
callFunction(*functor->value, 2, args2, vCur, pos);
nrArgs--;
args++;
else if (vCur.type() == nAttrs) {
if (auto functor = vCur.attrs->get(sFunctor)) {
/* 'vCur" may be allocated on the stack of the calling
function, but for functors we may keep a reference,
so heap-allocate a copy and use that instead. */
Value * args2[] = {allocValue()};
*args2[0] = vCur;
/* !!! Should we use the attr pos here? */
callFunction(*functor->value, 1, args2, vCur, pos);
}
}
else
@@ -1511,38 +1513,48 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
}
}
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
if (!fun.isLambda()) {
res = fun;
return;
}
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda.fun->formals->formals.size()), args.size()));
Value * actualArgs[fun.lambda.fun->args.size()];
if (fun.lambda.fun->formals->ellipsis) {
// If the formals have an ellipsis (eg the function accepts extra args) pass
// all available automatic arguments (which includes arguments specified on
// the command line via --arg/--argstr)
for (auto & v : args)
attrs.insert(v);
} else {
// Otherwise, only pass the arguments that the function accepts
for (auto & i : fun.lambda.fun->formals->formals) {
Bindings::iterator j = args.find(i.name);
if (j != args.end()) {
attrs.insert(*j);
} else if (!i.def) {
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
for (const auto & [i, arg] : enumerate(fun.lambda.fun->args)) {
if (!arg.formals) {
res = fun;
return;
}
actualArgs[i] = allocValue();
mkAttrs(*actualArgs[i], std::max(arg.formals->formals.size(), static_cast<size_t>(args.size())));
if (arg.formals->ellipsis) {
/* If the formals have an ellipsis (i.e. the function
accepts extra args), pass all available automatic
arguments. */
for (auto & v : args)
actualArgs[i]->attrs->push_back(v);
} else {
/* Otherwise, only pass the arguments that the function
accepts. */
for (auto & j : arg.formals->formals) {
if (auto attr = args.get(j.name))
actualArgs[i]->attrs->push_back(*attr);
else if (!j.def)
throwMissingArgumentError(j.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/#ss-functions.)", i.name);
https://nixos.org/manual/nix/stable/#ss-functions.)", j.name);
}
}
actualArgs[i]->attrs->sort();
}
callFunction(fun, allocValue()->mkAttrs(attrs), res, noPos);
callFunction(fun, fun.lambda.fun->args.size(), actualArgs, res, noPos);
}
@@ -1577,7 +1589,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
{
v.mkBool(!state.evalBool(env, e));
mkBool(v, !state.evalBool(env, e));
}
@@ -1585,7 +1597,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(state.eqValues(v1, v2));
mkBool(v, state.eqValues(v1, v2));
}
@@ -1593,25 +1605,25 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(!state.eqValues(v1, v2));
mkBool(v, !state.eqValues(v1, v2));
}
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
{
v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
mkBool(v, state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
}
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
{
v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
mkBool(v, state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
}
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
{
v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
mkBool(v, !state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
}
@@ -1626,7 +1638,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
if (v1.attrs->size() == 0) { v = v2; return; }
if (v2.attrs->size() == 0) { v = v1; return; }
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size());
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
/* Merge the sets, preferring values from the second set. Make
sure to keep the resulting vector in sorted order. */
@@ -1635,19 +1647,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
while (i != v1.attrs->end() && j != v2.attrs->end()) {
if (i->name == j->name) {
attrs.insert(*j);
v.attrs->push_back(*j);
++i; ++j;
}
else if (i->name < j->name)
attrs.insert(*i++);
v.attrs->push_back(*i++);
else
attrs.insert(*j++);
v.attrs->push_back(*j++);
}
while (i != v1.attrs->end()) attrs.insert(*i++);
while (j != v2.attrs->end()) attrs.insert(*j++);
v.mkAttrs(attrs.alreadySorted());
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
state.nrOpUpdateValuesCopied += v.attrs->size();
}
@@ -1694,35 +1704,14 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
std::vector<std::string> s;
size_t sSize = 0;
std::ostringstream s;
NixInt n = 0;
NixFloat nf = 0;
bool first = !forceString;
ValueType firstType = nString;
const auto str = [&] {
std::string result;
result.reserve(sSize);
for (const auto & part : s) result += part;
return result;
};
/* c_str() is not str().c_str() because we want to create a string
Value. allocating a GC'd string directly and moving it into a
Value lets us avoid an allocation and copy. */
const auto c_str = [&] {
char * result = allocString(sSize + 1);
char * tmp = result;
for (const auto & part : s) {
memcpy(tmp, part.c_str(), part.size());
tmp += part.size();
}
*tmp = 0;
return result;
};
for (auto & [i_pos, i] : *es) {
for (auto & i : *es) {
Value vTmp;
i->eval(state, env, vTmp);
@@ -1743,37 +1732,34 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
} else {
if (s.empty()) s.reserve(es->size());
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
} else
/* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type
path */
s.emplace_back(
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
sSize += s.back().size();
}
s << state.coerceToString(pos, vTmp, context, false, firstType == nString, !first);
first = false;
}
if (firstType == nInt)
v.mkInt(n);
mkInt(v, n);
else if (firstType == nFloat)
v.mkFloat(nf);
mkFloat(v, nf);
else if (firstType == nPath) {
if (!context.empty())
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
v.mkPath(canonPath(str()));
auto path = canonPath(s.str());
mkPath(v, path.c_str());
} else
v.mkStringMove(c_str(), context);
mkString(v, s.str(), context);
}
@@ -1805,8 +1791,8 @@ void EvalState::forceValueDeep(Value & v)
}
else if (v.isList()) {
for (auto v2 : v.listItems())
recurse(*v2);
for (size_t n = 0; n < v.listSize(); ++n)
recurse(*v.listElems()[n]);
}
};
@@ -1990,12 +1976,12 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.isList()) {
string result;
for (auto [n, v2] : enumerate(v.listItems())) {
result += coerceToString(pos, *v2,
for (size_t n = 0; n < v.listSize(); ++n) {
result += coerceToString(pos, *v.listElems()[n],
context, coerceMore, copyToStore);
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
&& (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
result += " ";
}
return result;

View File

@@ -44,6 +44,8 @@ struct Env
};
Value & mkString(Value & v, std::string_view s, const PathSet & context = PathSet());
void copyContext(const Value & v, PathSet & context);
@@ -80,8 +82,7 @@ public:
sContentAddressed,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
sPrefix;
sDescription, sSelf, sEpsilon;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -92,7 +93,7 @@ public:
mode. */
std::optional<PathSet> allowedPaths;
Bindings emptyBindings;
Value vEmptySet;
/* Store used to materialise .drv files. */
const ref<Store> store;
@@ -132,9 +133,6 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr<RegexCache> regexCache;
/* Allocation cache for GC'd Value objects. */
std::shared_ptr<void *> valueAllocCache;
public:
EvalState(
@@ -180,8 +178,8 @@ public:
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */
Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(std::string s, const Path & basePath);
Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(std::string_view s, const Path & basePath);
Expr * parseStdin();
@@ -309,7 +307,7 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
Expr * parse(const char * text, FileOrigin origin, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:
@@ -338,16 +336,12 @@ public:
Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, const Symbol & name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Value * allocAttr(Value & vAttrs, const std::string & name);
Bindings * allocBindings(size_t capacity);
BindingsBuilder buildBindings(size_t capacity)
{
return BindingsBuilder(*this, allocBindings(capacity));
}
void mkList(Value & v, size_t length);
void mkAttrs(Value & v, size_t capacity);
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, ptr<Pos> pos);
@@ -356,10 +350,7 @@ public:
/* Print statistics. */
void printStats();
/* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const PathSet & context);
void realiseContext(const PathSet & context);
private:
@@ -400,9 +391,6 @@ private:
friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend struct Value;
};

View File

@@ -1,5 +1,4 @@
#include "flake.hh"
#include "globals.hh"
#include <nlohmann/json.hpp>
@@ -38,11 +37,11 @@ void ConfigFile::apply()
// FIXME: Move into libutil/config.cc.
std::string valueS;
if (auto* s = std::get_if<std::string>(&value))
if (auto s = std::get_if<std::string>(&value))
valueS = *s;
else if (auto* n = std::get_if<int64_t>(&value))
valueS = fmt("%d", *n);
else if (auto* b = std::get_if<Explicit<bool>>(&value))
else if (auto n = std::get_if<int64_t>(&value))
valueS = fmt("%d", n);
else if (auto b = std::get_if<Explicit<bool>>(&value))
valueS = b->t ? "true" : "false";
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
valueS = concatStringsSep(" ", *ss); // FIXME: evil
@@ -53,19 +52,21 @@ void ConfigFile::apply()
auto trustedList = readTrustedList();
bool trusted = false;
if (nix::settings.acceptFlakeConfig){
trusted = true;
} else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
trusted = *saved;
warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS);
} else {
// FIXME: filter ANSI escapes, newlines, \r, etc.
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') {
trusted = true;
}
if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') {
trustedList[name][valueS] = trusted;
writeTrustedList(trustedList);
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) != 'y') {
if (std::tolower(logger->ask("do you want to permanently mark this value as untrusted (y/N)?").value_or('n')) == 'y') {
trustedList[name][valueS] = false;
writeTrustedList(trustedList);
}
} else {
if (std::tolower(logger->ask("do you want to permanently mark this value as trusted (y/N)?").value_or('n')) == 'y') {
trustedList[name][valueS] = trusted = true;
writeTrustedList(trustedList);
}
}
}

View File

@@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
if (!attrs.empty())
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
if (url)
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
input.ref = parseFlakeRef(*url, baseDir, true);
}
if (!input.follows && !input.ref)
@@ -194,8 +194,8 @@ static Flake getFlake(
state, originalRef, allowLookup, flakeCache);
// Guard against symlink attacks.
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir);
auto flakeFile = canonPath(flakeDir + "/flake.nix");
if (!isInDir(flakeFile, sourceInfo.actualPath))
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
lockedRef, state.store->printStorePath(sourceInfo.storePath));
@@ -230,8 +230,13 @@ static Flake getFlake(
if (auto outputs = vInfo.attrs->get(sOutputs)) {
expectType(state, nFunction, *outputs->value, *outputs->pos);
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
if (outputs->value->lambda.fun->args.size() != 1)
throw Error("the 'outputs' attribute of flake '%s' is not a unary function", lockedRef);
auto & arg = outputs->value->lambda.fun->args[0];
if (arg.formals) {
for (auto & formal : arg.formals->formals) {
if (formal.name != state.sSelf)
flake.inputs.emplace(formal.name, FlakeInput {
.ref = parseFlakeRef(formal.name)
@@ -251,17 +256,14 @@ static Flake getFlake(
forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
}
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
else if (setting.value->type() == nBool)
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
else if (setting.value->type() == nList) {
std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) {
for (unsigned int n = 0; n < setting.value->listSize(); ++n) {
auto elem = setting.value->listElems()[n];
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value));
@@ -310,7 +312,7 @@ LockedFlake lockFlake(
if (lockFlags.applyNixConfig) {
flake.config.apply();
state.store->setOptions();
// FIXME: send new config to the daemon.
}
try {
@@ -348,8 +350,7 @@ LockedFlake lockFlake(
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
const LockParent & parent,
const Path & parentPath,
bool trustLock)>
const Path & parentPath)>
computeLocks;
computeLocks = [&](
@@ -358,8 +359,7 @@ LockedFlake lockFlake(
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
const LockParent & parent,
const Path & parentPath,
bool trustLock)
const Path & parentPath)
{
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
@@ -451,18 +451,22 @@ LockedFlake lockFlake(
update it. */
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
auto mustRefetch =
auto hasChildUpdate =
lb != lockFlags.inputUpdates.end()
&& lb->size() > inputPath.size()
&& std::equal(inputPath.begin(), inputPath.end(), lb->begin());
FlakeInputs fakeInputs;
if (!mustRefetch) {
if (hasChildUpdate) {
auto inputFlake = getFlake(
state, oldLock->lockedRef, false, flakeCache);
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, parent, parentPath);
} else {
/* No need to fetch this flake, we can be
lazy. However there may be new overrides on the
inputs of this flake, so we need to check
those. */
FlakeInputs fakeInputs;
for (auto & i : oldLock->inputs) {
if (auto lockedNode = std::get_if<0>(&i.second)) {
fakeInputs.emplace(i.first, FlakeInput {
@@ -470,39 +474,15 @@ LockedFlake lockFlake(
.isFlake = (*lockedNode)->isFlake,
});
} else if (auto follows = std::get_if<1>(&i.second)) {
if (! trustLock) {
// It is possible that the flake has changed,
// so we must confirm all the follows that are in the lockfile are also in the flake.
auto overridePath(inputPath);
overridePath.push_back(i.first);
auto o = overrides.find(overridePath);
// If the override disappeared, we have to refetch the flake,
// since some of the inputs may not be present in the lockfile.
if (o == overrides.end()) {
mustRefetch = true;
// There's no point populating the rest of the fake inputs,
// since we'll refetch the flake anyways.
break;
}
}
fakeInputs.emplace(i.first, FlakeInput {
.follows = *follows,
});
}
}
computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath);
}
LockParent newParent {
.path = inputPath,
.absolute = true
};
computeLocks(
mustRefetch
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
: fakeInputs,
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
} else {
/* We need to create a new lock file entry. So fetch
this input. */
@@ -558,7 +538,7 @@ LockedFlake lockFlake(
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
newParent, localPath, false);
newParent, localPath);
}
else {
@@ -582,11 +562,11 @@ LockedFlake lockFlake(
};
// Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir);
computeLocks(
flake.inputs, newLockFile.root, {},
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath);
for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first))
@@ -633,24 +613,12 @@ LockedFlake lockFlake(
newLockFile.write(path);
std::optional<std::string> commitMessage = std::nullopt;
if (lockFlags.commitLockFile) {
std::string cm;
cm = settings.commitLockFileSummary.get();
if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
}
cm += "\n\nFlake lock file updates:\n\n";
cm += filterANSIEscapes(diff, true);
commitMessage = cm;
}
topRef.input.markChangedFile(
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
commitMessage);
lockFlags.commitLockFile
? std::optional<std::string>(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
: std::nullopt);
/* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could
@@ -698,7 +666,7 @@ void callFlake(EvalState & state,
auto vTmp1 = state.allocValue();
auto vTmp2 = state.allocValue();
vLocks->mkString(lockedFlake.lockFile.to_string());
mkString(*vLocks, lockedFlake.lockFile.to_string());
emitTreeAttrs(
state,
@@ -708,7 +676,7 @@ void callFlake(EvalState & state,
false,
lockedFlake.flake.forceDirty);
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
mkString(*vRootSubdir, lockedFlake.flake.lockedRef.subdir);
if (!state.vCallFlake) {
state.vCallFlake = allocRootValue(state.allocValue());

View File

@@ -48,12 +48,9 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
}
FlakeRef parseFlakeRef(
const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
{
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing);
if (fragment != "")
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
return flakeRef;
@@ -70,10 +67,7 @@ std::optional<FlakeRef> maybeParseFlakeRef(
}
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
{
using namespace fetchers;
@@ -118,71 +112,46 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
path = absPath(path, baseDir);
path = absPath(path, baseDir, true);
if (isFlake) {
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
if (!allowMissing && !pathExists(path + "/flake.nix")){
notice("path '%s' does not contain a 'flake.nix', searching up",path);
if (!allowMissing && !pathExists(path + "/flake.nix"))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
// Save device to detect filesystem boundary
dev_t device = lstat(path).st_dev;
bool found = false;
while (path != "/") {
if (pathExists(path + "/flake.nix")) {
found = true;
break;
} else if (pathExists(path + "/.git"))
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
else {
if (lstat(path).st_dev != device)
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
}
path = dirOf(path);
}
if (!found)
throw BadURL("could not find a flake.nix file");
}
auto flakeRoot = path;
std::string subdir;
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
while (flakeRoot != "/") {
if (pathExists(flakeRoot + "/.git")) {
auto base = std::string("git+file://") + flakeRoot;
if (!allowMissing && !pathExists(path + "/flake.nix"))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
auto parsedURL = ParsedURL{
.url = base, // FIXME
.base = base,
.scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = decodeQuery(match[2]),
};
auto flakeRoot = path;
std::string subdir;
while (flakeRoot != "/") {
if (pathExists(flakeRoot + "/.git")) {
auto base = std::string("git+file://") + flakeRoot;
auto parsedURL = ParsedURL{
.url = base, // FIXME
.base = base,
.scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = decodeQuery(match[2]),
};
if (subdir != "") {
if (parsedURL.query.count("dir"))
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
parsedURL.query.insert_or_assign("dir", subdir);
}
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
fragment);
if (subdir != "") {
if (parsedURL.query.count("dir"))
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
parsedURL.query.insert_or_assign("dir", subdir);
}
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
fragment);
}
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
}
} else {

View File

@@ -62,19 +62,13 @@ struct FlakeRef
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
FlakeRef parseFlakeRef(
const std::string & url,
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true);
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
std::optional<FlakeRef> maybeParseFlake(
const std::string & url, const std::optional<Path> & baseDir = {});
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url,
const std::optional<Path> & baseDir = {},
bool allowMissing = false,
bool isFlake = true);
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir = {});

View File

@@ -102,9 +102,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
state->forceList(*i->value, *i->pos);
/* For each output... */
for (auto elem : i->value->listItems()) {
for (unsigned int j = 0; j < i->value->listSize(); ++j) {
/* Evaluate the corresponding set. */
string name = state->forceStringNoCtx(*elem, *i->pos);
string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value);
@@ -128,9 +128,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* ^ this shows during `nix-env -i` right under the bad derivation */
if (!outTI->isList()) throw errMsg;
Outputs result;
for (auto elem : outTI->listItems()) {
if (elem->type() != nString) throw errMsg;
auto out = outputs.find(elem->string.s);
for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) {
if ((*i)->type() != nString) throw errMsg;
auto out = outputs.find((*i)->string.s);
if (out == outputs.end()) throw errMsg;
result.insert(*out);
}
@@ -174,8 +174,8 @@ bool DrvInfo::checkMeta(Value & v)
{
state->forceValue(v);
if (v.type() == nList) {
for (auto elem : v.listItems())
if (!checkMeta(*elem)) return false;
for (unsigned int n = 0; n < v.listSize(); ++n)
if (!checkMeta(*v.listElems()[n])) return false;
return true;
}
else if (v.type() == nAttrs) {
@@ -254,14 +254,15 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
void DrvInfo::setMeta(const string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
Bindings * old = meta;
meta = state->allocBindings(1 + (old ? old->size() : 0));
Symbol sym = state->symbols.create(name);
if (meta)
for (auto i : *meta)
if (old)
for (auto i : *old)
if (i.name != sym)
attrs.insert(i);
if (v) attrs.insert(sym, v);
meta = attrs.finish();
meta->push_back(i);
if (v) meta->push_back(Attr(sym, v));
meta->sort();
}
@@ -363,10 +364,10 @@ static void getDerivations(EvalState & state, Value & vIn,
}
else if (v.type() == nList) {
for (auto [n, elem] : enumerate(v.listItems())) {
string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
for (unsigned int n = 0; n < v.listSize(); ++n) {
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}

View File

@@ -37,10 +37,10 @@ class JSONSax : nlohmann::json_sax<json> {
ValueMap attrs;
std::unique_ptr<JSONState> resolve(EvalState & state) override
{
auto attrs2 = state.buildBindings(attrs.size());
Value & v = parent->value(state);
state.mkAttrs(v, attrs.size());
for (auto & i : attrs)
attrs2.insert(i.first, i.second);
parent->value(state).mkAttrs(attrs2.alreadySorted());
v.attrs->push_back(Attr(i.first, i.second));
return std::move(parent);
}
void add() override { v = nullptr; }
@@ -76,51 +76,45 @@ class JSONSax : nlohmann::json_sax<json> {
EvalState & state;
std::unique_ptr<JSONState> rs;
template<typename T, typename... Args> inline bool handle_value(T f, Args... args)
{
f(rs->value(state), args...);
rs->add();
return true;
}
public:
JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};
bool null()
{
rs->value(state).mkNull();
rs->add();
return true;
return handle_value(mkNull);
}
bool boolean(bool val)
{
rs->value(state).mkBool(val);
rs->add();
return true;
return handle_value(mkBool, val);
}
bool number_integer(number_integer_t val)
{
rs->value(state).mkInt(val);
rs->add();
return true;
return handle_value(mkInt, val);
}
bool number_unsigned(number_unsigned_t val)
{
rs->value(state).mkInt(val);
rs->add();
return true;
return handle_value(mkInt, val);
}
bool number_float(number_float_t val, const string_t & s)
{
rs->value(state).mkFloat(val);
rs->add();
return true;
return handle_value(mkFloat, val);
}
bool string(string_t & val)
{
rs->value(state).mkString(val);
rs->add();
return true;
return handle_value<void(Value&, const char*)>(mkString, val.c_str());
}
#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
bool binary(binary_t&)
{

View File

@@ -64,32 +64,29 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
}
// we make use of the fact that the parser receives a private copy of the input
// string and can munge around in it.
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
// FIXME: optimize
static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
{
char * result = s;
char * t = s;
string t;
t.reserve(length);
char c;
// the input string is terminated with *two* NULs, so we can safely take
// *one* character after the one being checked against.
while ((c = *s++)) {
if (c == '\\') {
assert(*s);
c = *s++;
if (c == 'n') *t = '\n';
else if (c == 'r') *t = '\r';
else if (c == 't') *t = '\t';
else *t = c;
if (c == 'n') t += '\n';
else if (c == 'r') t += '\r';
else if (c == 't') t += '\t';
else t += c;
}
else if (c == '\r') {
/* Normalise CR and CR/LF into LF. */
*t = '\n';
t += '\n';
if (*s == '\n') s++; /* cr/lf */
}
else *t = c;
t++;
else t += c;
}
return new ExprString(symbols.create({result, size_t(t - result)}));
return new ExprString(symbols.create(t));
}
@@ -142,7 +139,7 @@ or { return OR_KW; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
{ID} { yylval->id = strdup(yytext); return ID; }
{INT} { errno = 0;
try {
yylval->n = boost::lexical_cast<int64_t>(yytext);
@@ -224,14 +221,14 @@ or { return OR_KW; }
<PATH_START>{PATH_SEG} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
yylval->path = {yytext, (size_t) yyleng};
yylval->path = strdup(yytext);
return PATH;
}
<PATH_START>{HPATH_START} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
yylval->path = {yytext, (size_t) yyleng};
yylval->path = strdup(yytext);
return HPATH;
}
@@ -240,7 +237,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->path = {yytext, (size_t) yyleng};
yylval->path = strdup(yytext);
return PATH;
}
{HPATH} {
@@ -248,7 +245,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->path = {yytext, (size_t) yyleng};
yylval->path = strdup(yytext);
return HPATH;
}
@@ -283,8 +280,8 @@ or { return OR_KW; }
throw ParseError("path has a trailing slash");
}
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */

View File

@@ -124,23 +124,26 @@ void ExprList::show(std::ostream & str) const
void ExprLambda::show(std::ostream & str) const
{
str << "(";
if (hasFormals()) {
str << "{ ";
bool first = true;
for (auto & i : formals->formals) {
if (first) first = false; else str << ", ";
str << i.name;
if (i.def) str << " ? " << *i.def;
for (auto & arg : args) {
if (arg.formals) {
str << "{ ";
bool first = true;
for (auto & i : arg.formals->formals) {
if (first) first = false; else str << ", ";
str << i.name;
if (i.def) str << " ? " << *i.def;
}
if (arg.formals->ellipsis) {
if (!first) str << ", ";
str << "...";
}
str << " }";
if (!arg.arg.empty()) str << " @ ";
}
if (formals->ellipsis) {
if (!first) str << ", ";
str << "...";
}
str << " }";
if (!arg.empty()) str << " @ ";
if (!arg.arg.empty()) str << arg.arg;
str << ": ";
}
if (!arg.empty()) str << arg;
str << ": " << *body << ")";
str << *body << ")";
}
void ExprCall::show(std::ostream & str) const
@@ -191,7 +194,7 @@ void ExprConcatStrings::show(std::ostream & str) const
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
str << *i.second;
str << *i;
}
str << ")";
}
@@ -279,8 +282,7 @@ void ExprVar::bindVars(const StaticEnv & env)
if (curEnv->isWith) {
if (withLevel == -1) withLevel = level;
} else {
auto i = curEnv->find(name);
if (i != curEnv->vars.end()) {
if (auto i = curEnv->get(name)) {
fromWith = false;
this->level = level;
displ = i->second;
@@ -354,25 +356,48 @@ void ExprList::bindVars(const StaticEnv & env)
void ExprLambda::bindVars(const StaticEnv & env)
{
StaticEnv newEnv(
false, &env,
(hasFormals() ? formals->formals.size() : 0) +
(arg.empty() ? 0 : 1));
/* The parser adds arguments in reverse order. Let's fix that
now. */
std::reverse(args.begin(), args.end());
envSize = 0;
for (auto & arg :args) {
if (!arg.arg.empty()) envSize++;
if (arg.formals) envSize += arg.formals->formals.size();
}
StaticEnv newEnv(false, &env, envSize);
Displacement displ = 0;
if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++);
for (auto & arg : args) {
if (!arg.arg.empty()) {
if (auto i = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(arg.arg)))
i->second = displ++;
else
newEnv.vars.emplace_back(arg.arg, displ++);
}
if (hasFormals()) {
for (auto & i : formals->formals)
newEnv.vars.emplace_back(i.name, displ++);
if (arg.formals) {
for (auto & i : arg.formals->formals) {
if (auto j = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(i.name)))
j->second = displ++;
else
newEnv.vars.emplace_back(i.name, displ++);
}
newEnv.sort();
newEnv.sort();
for (auto & i : formals->formals)
if (i.def) i.def->bindVars(newEnv);
for (auto & i : arg.formals->formals)
if (i.def) i.def->bindVars(newEnv);
}
}
assert(displ == envSize);
newEnv.sort();
body->bindVars(newEnv);
}
@@ -439,7 +464,7 @@ void ExprOpNot::bindVars(const StaticEnv & env)
void ExprConcatStrings::bindVars(const StaticEnv & env)
{
for (auto & i : *es)
i.second->bindVars(env);
i->bindVars(env);
}
void ExprPos::bindVars(const StaticEnv & env)
@@ -473,7 +498,7 @@ string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
for (auto & i : store)
for (auto & i : symbols)
n += i.size();
return n;
}

View File

@@ -94,7 +94,7 @@ struct ExprInt : Expr
{
NixInt n;
Value v;
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@@ -103,7 +103,7 @@ struct ExprFloat : Expr
{
NixFloat nf;
Value v;
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@@ -112,7 +112,7 @@ struct ExprString : Expr
{
Symbol s;
Value v;
ExprString(const Symbol & s) : s(s) { v.mkString(s); };
ExprString(const Symbol & s) : s(s) { mkString(v, s); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@@ -233,21 +233,24 @@ struct ExprLambda : Expr
{
Pos pos;
Symbol name;
Symbol arg;
Formals * formals;
Expr * body;
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body)
struct Arg
{
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos
});
Symbol arg;
Formals * formals;
};
std::vector<Arg> args;
Expr * body;
Displacement envSize = 0; // initialized by bindVars()
ExprLambda(const Pos & pos, Expr * body)
: pos(pos), body(body)
{ };
void setName(Symbol & name);
string showNamePos() const;
inline bool hasFormals() const { return formals != nullptr; }
COMMON_METHODS
};
@@ -332,8 +335,8 @@ struct ExprConcatStrings : Expr
{
Pos pos;
bool forceString;
vector<std::pair<Pos, Expr *> > * es;
ExprConcatStrings(const Pos & pos, bool forceString, vector<std::pair<Pos, Expr *> > * es)
vector<Expr *> * es;
ExprConcatStrings(const Pos & pos, bool forceString, vector<Expr *> * es)
: pos(pos), forceString(forceString), es(es) { };
COMMON_METHODS
};
@@ -364,27 +367,16 @@ struct StaticEnv
void sort()
{
std::stable_sort(vars.begin(), vars.end(),
std::sort(vars.begin(), vars.end(),
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
}
void deduplicate()
{
auto it = vars.begin(), jt = it, end = vars.end();
while (jt != end) {
*it = *jt++;
while (jt != end && it->first == jt->first) *it = *jt++;
it++;
}
vars.erase(it, end);
}
Vars::const_iterator find(const Symbol & name) const
const Vars::value_type * get(const Symbol & name) const
{
Vars::value_type key(name, 0);
auto i = std::lower_bound(vars.begin(), vars.end(), key);
if (i != vars.end() && i->first == name) return i;
return vars.end();
if (i != vars.end() && i->first == name) return &*i;
return {};
}
};

View File

@@ -33,9 +33,11 @@ namespace nix {
Symbol file;
FileOrigin origin;
std::optional<ErrorInfo> error;
Symbol sLetBody;
ParseData(EvalState & state)
: state(state)
, symbols(state.symbols)
, sLetBody(symbols.create("<let-body>"))
{ };
};
@@ -152,7 +154,25 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
}
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
static Expr * addArg(const Pos & pos, Expr * e, ExprLambda::Arg && arg)
{
if (!arg.arg.empty() && arg.formals && arg.formals->argNames.count(arg.arg))
throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg.arg),
.errPos = pos
});
auto e2 = dynamic_cast<ExprLambda *>(e); // FIXME: slow?
if (!e2)
e2 = new ExprLambda(pos, e);
else
e2->pos = pos;
e2->args.emplace_back(std::move(arg));
return e2;
}
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Expr *> & es)
{
if (es.empty()) return new ExprString(symbols.create(""));
@@ -162,7 +182,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
bool atStartOfLine = true; /* = seen only whitespace in the current line */
size_t minIndent = 1000000;
size_t curIndent = 0;
for (auto & [i_pos, i] : es) {
for (auto & i : es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
@@ -192,12 +212,12 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
}
/* Strip spaces from each line. */
vector<std::pair<Pos, Expr *> > * es2 = new vector<std::pair<Pos, Expr *> >;
vector<Expr *> * es2 = new vector<Expr *>;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
if (!e) {
atStartOfLine = false;
curDropped = 0;
@@ -234,11 +254,11 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
s2 = string(s2, 0, p + 1);
}
es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
es2->push_back(new ExprString(symbols.create(s2)));
}
/* If this is a single string, then don't do a concatenation. */
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2);
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2);
}
@@ -273,18 +293,11 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
// using C a struct allows us to avoid having to define the special
// members that using string_view here would implicitly delete.
struct StringToken {
const char * p;
size_t l;
operator std::string_view() const { return {p, l}; }
};
StringToken id; // !!! -> Symbol
StringToken path;
StringToken uri;
const char * id; // !!! -> Symbol
char * path;
char * uri;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
std::vector<nix::Expr *> * string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -329,13 +342,13 @@ expr: expr_function;
expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
{ $$ = addArg(CUR_POS, $3, {data->symbols.create($1), nullptr}); }
| '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
{ $$ = addArg(CUR_POS, $5, {data->state.sEpsilon, $2}); }
| '{' formals '}' '@' ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($5), $2}); }
| ID '@' '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($1), $4}); }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
@@ -371,7 +384,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<Expr *>({$1, $3})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@@ -404,8 +417,7 @@ expr_select
expr_simple
: ID {
std::string_view s = "__curPos";
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
if (strcmp($1, "__curPos") == 0)
$$ = new ExprPos(CUR_POS);
else
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
@@ -418,11 +430,11 @@ expr_simple
}
| path_start PATH_END { $$ = $1; }
| path_start string_parts_interpolated PATH_END {
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
$2->insert($2->begin(), $1);
$$ = new ExprConcatStrings(CUR_POS, false, $2);
}
| SPATH {
string path($1.p + 1, $1.l - 2);
string path($1 + 1, strlen($1) - 2);
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
@@ -452,38 +464,38 @@ expr_simple
string_parts
: STR
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
| { $$ = new ExprString(data->symbols.create("")); }
| { $$ = new ExprString(data->state.sEpsilon); }
;
string_parts_interpolated
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new vector<std::pair<Pos, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), $1);
$$->emplace_back(makeCurPos(@2, data), $3);
$$ = new vector<Expr *>;
$$->push_back($1);
$$->push_back($3);
}
;
path_start
: PATH {
Path path(absPath({$1.p, $1.l}, data->basePath));
Path path(absPath($1, data->basePath));
/* add back in the trailing '/' to the first segment */
if ($1.p[$1.l-1] == '/' && $1.l > 1)
if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
path += "/";
$$ = new ExprPath(path);
}
| HPATH {
Path path(getHome() + string($1.p + 1, $1.l - 1));
Path path(getHome() + string($1 + 1));
$$ = new ExprPath(path);
}
;
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| { $$ = new vector<std::pair<Pos, Expr *> >; }
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
;
binds
@@ -551,7 +563,7 @@ attrpath
attr
: ID { $$ = $1; }
| OR_KW { $$ = {"or", 2}; }
| OR_KW { $$ = "or"; }
;
string_attr
@@ -597,7 +609,7 @@ formal
namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
Expr * EvalState::parse(const char * text, FileOrigin origin,
const Path & path, const Path & basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
@@ -617,7 +629,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
data.basePath = basePath;
yylex_init(&scanner);
yy_scan_buffer(text, length, scanner);
yy_scan_string(text, scanner);
int res = yyparse(scanner, &data);
yylex_destroy(scanner);
@@ -663,33 +675,26 @@ Expr * EvalState::parseExprFromFile(const Path & path)
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
{
auto buffer = readFile(path);
// readFile should have left some extra space for terminators
buffer.append("\0\0", 2);
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
}
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
{
s.append("\0\0", 2);
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
return parse(s.data(), foString, "", basePath, staticEnv);
}
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath)
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
{
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
return parseExprFromString(s, basePath, staticBaseEnv);
}
Expr * EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
auto buffer = drainFD(0);
// drainFD should have left some extra space for terminators
buffer.append("\0\0", 2);
return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,8 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
v.mkString(state.coerceToString(pos, *args[0], context));
string s = state.coerceToString(pos, *args[0], context);
mkString(v, s, PathSet());
}
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
@@ -17,7 +18,7 @@ static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args,
{
PathSet context;
state.forceString(*args[0], context, pos);
v.mkBool(!context.empty());
mkBool(v, !context.empty());
}
static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
@@ -38,7 +39,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
v.mkString(s, context2);
mkString(v, s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
@@ -102,26 +103,28 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
}
}
auto attrs = state.buildBindings(contextInfos.size());
state.mkAttrs(v, contextInfos.size());
auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs");
for (const auto & info : contextInfos) {
auto infoAttrs = state.buildBindings(3);
auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first));
state.mkAttrs(infoVal, 3);
if (info.second.path)
infoAttrs.alloc(sPath).mkBool(true);
mkBool(*state.allocAttr(infoVal, sPath), true);
if (info.second.allOutputs)
infoAttrs.alloc(sAllOutputs).mkBool(true);
mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
if (!info.second.outputs.empty()) {
auto & outputsVal = infoAttrs.alloc(state.sOutputs);
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
state.mkList(outputsVal, info.second.outputs.size());
for (const auto & [i, output] : enumerate(info.second.outputs))
(outputsVal.listElems()[i] = state.allocValue())->mkString(output);
size_t i = 0;
for (const auto & output : info.second.outputs) {
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
}
}
attrs.alloc(info.first).mkAttrs(infoAttrs);
infoVal.attrs->sort();
}
v.mkAttrs(attrs);
v.attrs->sort();
}
static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
@@ -178,14 +181,14 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
.errPos = *i.pos
});
}
for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos);
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
context.insert("!" + name + "!" + string(i.name));
}
}
}
v.mkString(orig, context);
mkString(v, orig, context);
}
static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext);

View File

@@ -70,19 +70,19 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
// FIXME: use name
auto [tree, input2] = input.fetch(state.store);
auto attrs2 = state.buildBindings(8);
state.mkAttrs(v, 8);
auto storePath = state.store->printStorePath(tree.storePath);
attrs2.alloc(state.sOutPath).mkString(storePath, {storePath});
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
if (input2.getRef())
attrs2.alloc("branch").mkString(*input2.getRef());
mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2.getRef());
// Backward compatibility: set 'rev' to
// 0000000000000000000000000000000000000000 for a dirty tree.
auto rev2 = input2.getRev().value_or(Hash(htSHA1));
attrs2.alloc("rev").mkString(rev2.gitRev());
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12));
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12));
if (auto revCount = input2.getRevCount())
attrs2.alloc("revCount").mkInt(*revCount);
v.mkAttrs(attrs2);
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
v.attrs->sort();
state.allowPath(tree.storePath);
}

View File

@@ -21,48 +21,49 @@ void emitTreeAttrs(
{
assert(input.isImmutable());
auto attrs = state.buildBindings(8);
state.mkAttrs(v, 8);
auto storePath = state.store->printStorePath(tree.storePath);
attrs.alloc(state.sOutPath).mkString(storePath, {storePath});
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
// FIXME: support arbitrary input attributes.
auto narHash = input.getNarHash();
assert(narHash);
attrs.alloc("narHash").mkString(narHash->to_string(SRI, true));
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
narHash->to_string(SRI, true));
if (input.getType() == "git")
attrs.alloc("submodules").mkBool(
mkBool(*state.allocAttr(v, state.symbols.create("submodules")),
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
if (!forceDirty) {
if (auto rev = input.getRev()) {
attrs.alloc("rev").mkString(rev->gitRev());
attrs.alloc("shortRev").mkString(rev->gitShortRev());
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev());
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev());
} else if (emptyRevFallback) {
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
auto emptyHash = Hash(htSHA1);
attrs.alloc("rev").mkString(emptyHash.gitRev());
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev());
mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev());
}
if (auto revCount = input.getRevCount())
attrs.alloc("revCount").mkInt(*revCount);
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
else if (emptyRevFallback)
attrs.alloc("revCount").mkInt(0);
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0);
}
if (auto lastModified = input.getLastModified()) {
attrs.alloc("lastModified").mkInt(*lastModified);
attrs.alloc("lastModifiedDate").mkString(
mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified);
mkString(*state.allocAttr(v, state.symbols.create("lastModifiedDate")),
fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
}
v.mkAttrs(attrs);
v.attrs->sort();
}
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
@@ -73,10 +74,7 @@ std::string fixURI(std::string uri, EvalState & state, const std::string & defau
std::string fixURIForGit(std::string uri, EvalState & state)
{
/* Detects scp-style uris (e.g. git@github.com:NixOS/nix) and fixes
* them by removing the `:` and assuming a scheme of `ssh://`
* */
static std::regex scp_uri("([^/]*)@(.*):(.*)");
static std::regex scp_uri("([^/].*)@(.*):(.*)");
if (uri[0] != '/' && std::regex_match(uri, scp_uri))
return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh");
else
@@ -247,7 +245,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.allowPath(storePath);
auto path = state.store->printStorePath(storePath);
v.mkString(path, PathSet({path}));
mkString(v, path, PathSet({path}));
}
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)

View File

@@ -1,76 +1,86 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "../../toml11/toml.hpp"
#include "../../cpptoml/cpptoml.h"
namespace nix {
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
using namespace cpptoml;
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(toml);
std::function<void(Value &, toml::value)> visit;
std::function<void(Value &, std::shared_ptr<base>)> visit;
visit = [&](Value & v, toml::value t) {
visit = [&](Value & v, std::shared_ptr<base> t) {
switch(t.type())
{
case toml::value_t::table:
{
auto table = toml::get<toml::table>(t);
if (auto t2 = t->as_table()) {
size_t size = 0;
for (auto & i : table) { (void) i; size++; }
size_t size = 0;
for (auto & i : *t2) { (void) i; size++; }
auto attrs = state.buildBindings(size);
state.mkAttrs(v, size);
for(auto & elem : table)
visit(attrs.alloc(elem.first), elem.second);
for (auto & i : *t2) {
auto & v2 = *state.allocAttr(v, state.symbols.create(i.first));
v.mkAttrs(attrs);
if (auto i2 = i.second->as_table_array()) {
size_t size2 = i2->get().size();
state.mkList(v2, size2);
for (size_t j = 0; j < size2; ++j)
visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]);
}
break;;
case toml::value_t::array:
{
auto array = toml::get<std::vector<toml::value>>(t);
size_t size = array.size();
state.mkList(v, size);
for (size_t i = 0; i < size; ++i)
visit(*(v.listElems()[i] = state.allocValue()), array[i]);
}
break;;
case toml::value_t::boolean:
v.mkBool(toml::get<bool>(t));
break;;
case toml::value_t::integer:
v.mkInt(toml::get<int64_t>(t));
break;;
case toml::value_t::floating:
v.mkFloat(toml::get<NixFloat>(t));
break;;
case toml::value_t::string:
v.mkString(toml::get<std::string>(t));
break;;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time:
// We fail since Nix doesn't have date and time types
throw std::runtime_error("Dates and times are not supported");
break;;
case toml::value_t::empty:
v.mkNull();
break;;
else
visit(v2, i.second);
}
v.attrs->sort();
}
else if (auto t2 = t->as_array()) {
size_t size = t2->get().size();
state.mkList(v, size);
for (size_t i = 0; i < size; ++i)
visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]);
}
// Handle cases like 'a = [[{ a = true }]]', which IMHO should be
// parsed as a array containing an array containing a table,
// but instead are parsed as an array containing a table array
// containing a table.
else if (auto t2 = t->as_table_array()) {
size_t size = t2->get().size();
state.mkList(v, size);
for (size_t j = 0; j < size; ++j)
visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]);
}
else if (t->is_value()) {
if (auto val = t->as<int64_t>())
mkInt(v, val->get());
else if (auto val = t->as<NixFloat>())
mkFloat(v, val->get());
else if (auto val = t->as<bool>())
mkBool(v, val->get());
else if (auto val = t->as<std::string>())
mkString(v, val->get());
else
throw EvalError("unsupported value type in TOML");
}
else abort();
};
try {
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
} catch (std::exception & e) { // TODO: toml::syntax_error
visit(v, parser(tomlStream).parse());
} catch (std::runtime_error & e) {
throw EvalError({
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
.errPos = pos

View File

@@ -1,8 +1,7 @@
#pragma once
#include <list>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include "types.hh"
@@ -71,21 +70,15 @@ public:
class SymbolTable
{
private:
std::unordered_map<std::string_view, Symbol> symbols;
std::list<string> store;
typedef std::unordered_set<string> Symbols;
Symbols symbols;
public:
Symbol create(std::string_view s)
{
// Most symbols are looked up more than once, so we trade off insertion performance
// for lookup performance.
// TODO: could probably be done more efficiently with transparent Hash and Equals
// on the original implementation using unordered_set
auto it = symbols.find(s);
if (it != symbols.end()) return it->second;
const string & rawSym = store.emplace_back(s);
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
// FIXME: avoid allocation if 's' already exists in the symbol table.
std::pair<Symbols::iterator, bool> res = symbols.emplace(std::string(s));
return Symbol(&*res.first);
}
size_t size() const
@@ -98,7 +91,7 @@ public:
template<typename T>
void dump(T callback)
{
for (auto & s : store)
for (auto & s : symbols)
callback(s);
}
};

View File

@@ -10,11 +10,11 @@
namespace nix {
void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
Value & v, JSONPlaceholder & out, PathSet & context)
{
checkInterrupt();
if (strict) state.forceValue(v, pos);
if (strict) state.forceValue(v);
switch (v.type()) {
@@ -40,7 +40,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
case nAttrs: {
auto maybeString = state.tryAttrsToString(pos, v, context, false, false);
auto maybeString = state.tryAttrsToString(noPos, v, context, false, false);
if (maybeString) {
out.write(*maybeString);
break;
@@ -54,18 +54,18 @@ void printValueAsJSON(EvalState & state, bool strict,
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j));
printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
printValueAsJSON(state, strict, *a.value, placeholder, context);
}
} else
printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
printValueAsJSON(state, strict, *i->value, out, context);
break;
}
case nList: {
auto list(out.list());
for (auto elem : v.listItems()) {
for (unsigned int n = 0; n < v.listSize(); ++n) {
auto placeholder(list.placeholder());
printValueAsJSON(state, strict, *elem, pos, placeholder, context);
printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context);
}
break;
}
@@ -79,20 +79,18 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
case nThunk:
throw TypeError("cannot convert %1% to JSON", showType(v));
case nFunction:
auto e = TypeError({
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
.errPos = v.determinePos(pos)
});
throw e.addTrace(pos, hintfmt("message for the trace"));
throw TypeError("cannot convert %1% to JSON", showType(v));
}
}
void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, std::ostream & str, PathSet & context)
Value & v, std::ostream & str, PathSet & context)
{
JSONPlaceholder out(str);
printValueAsJSON(state, strict, v, pos, out, context);
printValueAsJSON(state, strict, v, out, context);
}
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,

View File

@@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
Value & v, JSONPlaceholder & out, PathSet & context);
void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, std::ostream & str, PathSet & context);
Value & v, std::ostream & str, PathSet & context);
}

View File

@@ -18,8 +18,7 @@ static XMLAttrs singletonAttrs(const string & name, const string & value)
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos);
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
@@ -47,18 +46,17 @@ static void showAttrs(EvalState & state, bool strict, bool location,
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location,
*a.value, doc, context, drvsSeen, *a.pos);
*a.value, doc, context, drvsSeen);
}
}
static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos)
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
checkInterrupt();
if (strict) state.forceValue(v, pos);
if (strict) state.forceValue(v);
switch (v.type()) {
@@ -93,14 +91,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
Path drvPath;
a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value, *a->pos);
if (strict) state.forceValue(*a->value);
if (a->value->type() == nString)
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
}
a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value, *a->pos);
if (strict) state.forceValue(*a->value);
if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->string.s;
}
@@ -122,36 +120,40 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
case nList: {
XMLOpenElement _(doc, "list");
for (auto v2 : v.listItems())
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
break;
}
case nFunction: {
if (!v.isLambda()) {
// FIXME: Serialize primops and primopapps
// FIXME: Serialize primops and partial apps
doc.writeEmptyElement("unevaluated");
break;
}
XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->hasFormals()) {
auto & arg = v.lambda.fun->args[0];
if (arg.formals) {
XMLAttrs attrs;
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
if (arg.arg != state.sEpsilon) attrs["name"] = arg.arg;
if (arg.formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.lambda.fun->formals->formals)
for (auto & i : arg.formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
doc.writeEmptyElement("varpat", singletonAttrs("name", arg.arg));
break;
}
case nExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break;
case nFloat:
@@ -165,20 +167,19 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos) const
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
{
doc.writeEmptyElement("unevaluated");
}
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const Pos & pos)
Value & v, std::ostream & out, PathSet & context)
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
PathSet drvsSeen;
printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos);
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
}

View File

@@ -9,6 +9,6 @@
namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const Pos & pos);
Value & v, std::ostream & out, PathSet & context);
}

View File

@@ -1,7 +1,5 @@
#pragma once
#include <cassert>
#include "symbol-table.hh"
#if HAVE_BOEHMGC
@@ -10,8 +8,6 @@
namespace nix {
class BindingsBuilder;
typedef enum {
tInt = 1,
@@ -25,6 +21,7 @@ typedef enum {
tListN,
tThunk,
tApp,
tPartialApp,
tLambda,
tBlackhole,
tPrimOp,
@@ -98,8 +95,7 @@ class ExternalValueBase
/* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos) const;
XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const;
virtual ~ExternalValueBase()
{
@@ -130,6 +126,7 @@ public:
// type() == nFunction
inline bool isLambda() const { return internalType == tLambda; };
inline bool isPartialApp() const { return internalType == tPartialApp; };
inline bool isPrimOp() const { return internalType == tPrimOp; };
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; };
@@ -201,7 +198,7 @@ public:
case tNull: return nNull;
case tAttrs: return nAttrs;
case tList1: case tList2: case tListN: return nList;
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
case tLambda: case tPartialApp: case tPrimOp: case tPrimOpApp: return nFunction;
case tExternal: return nExternal;
case tFloat: return nFloat;
case tThunk: case tApp: case tBlackhole: return nThunk;
@@ -237,17 +234,6 @@ public:
string.context = context;
}
void mkString(std::string_view s);
void mkString(std::string_view s, const PathSet & context);
void mkStringMove(const char * s, const PathSet & context);
inline void mkString(const Symbol & s)
{
mkString(((const std::string &) s).c_str());
}
inline void mkPath(const char * s)
{
clearValue();
@@ -255,8 +241,6 @@ public:
path = s;
}
void mkPath(std::string_view s);
inline void mkNull()
{
clearValue();
@@ -270,8 +254,6 @@ public:
attrs = a;
}
Value & mkAttrs(BindingsBuilder & bindings);
inline void mkList(size_t size)
{
clearValue();
@@ -327,6 +309,13 @@ public:
app.right = r;
}
inline void mkPartialApp(Value * l, Value * r)
{
internalType = tPartialApp;
app.left = l;
app.right = r;
}
inline void mkExternal(ExternalValueBase * e)
{
clearValue();
@@ -369,45 +358,54 @@ public:
bool isTrivial() const;
std::vector<std::pair<Path, std::string>> getContext();
auto listItems()
{
struct ListIterable
{
typedef Value * const * iterator;
iterator _begin, _end;
iterator begin() const { return _begin; }
iterator end() const { return _end; }
};
assert(isList());
auto begin = listElems();
return ListIterable { begin, begin + listSize() };
}
auto listItems() const
{
struct ConstListIterable
{
typedef const Value * const * iterator;
iterator _begin, _end;
iterator begin() const { return _begin; }
iterator end() const { return _end; }
};
assert(isList());
auto begin = listElems();
return ConstListIterable { begin, begin + listSize() };
}
};
// TODO: Remove these static functions, replace call sites with v.mk* instead
static inline void mkInt(Value & v, NixInt n)
{
v.mkInt(n);
}
static inline void mkFloat(Value & v, NixFloat n)
{
v.mkFloat(n);
}
static inline void mkBool(Value & v, bool b)
{
v.mkBool(b);
}
static inline void mkNull(Value & v)
{
v.mkNull();
}
static inline void mkApp(Value & v, Value & left, Value & right)
{
v.mkApp(&left, &right);
}
static inline void mkString(Value & v, const Symbol & s)
{
v.mkString(((const string &) s).c_str());
}
void mkString(Value & v, const char * s);
void mkPath(Value & v, const char * s);
#if HAVE_BOEHMGC
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
#else
typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap;
typedef std::map<Symbol, ValueVector> ValueVectorMap;
#endif

View File

@@ -51,7 +51,7 @@ struct GitInputScheme : InputScheme
for (auto &[name, value] : url.query) {
if (name == "rev" || name == "ref")
attrs.emplace(name, value);
else if (name == "shallow" || name == "submodules")
else if (name == "shallow")
attrs.emplace(name, Explicit<bool> { value == "1" });
else
url2.query.emplace(name, value);
@@ -324,13 +324,17 @@ struct GitInputScheme : InputScheme
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
repoDir = cacheDir;
Path cacheDirLock = cacheDir + ".lock";
createDirs(dirOf(cacheDir));
PathLocks cacheDirLock({cacheDir + ".lock"});
AutoCloseFD lock = openLockFile(cacheDirLock, true);
lockFile(lock.get(), ltWrite, true);
if (!pathExists(cacheDir)) {
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", "--bare", repoDir });
}
deleteLockFile(cacheDirLock, lock.get());
Path localRefFile =
input.getRef()->compare(0, 5, "refs/") == 0
? cacheDir + "/" + *input.getRef()
@@ -395,8 +399,6 @@ struct GitInputScheme : InputScheme
if (!input.getRev())
input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(readFile(localRefFile)), htSHA1).gitRev());
// cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder
}
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";

View File

@@ -300,7 +300,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
if ("PAT" == token.substr(0, fldsplit))
return std::make_pair("Private-token", token.substr(fldsplit+1));
warn("Unrecognized GitLab token type %s", token.substr(0, fldsplit));
return std::make_pair(token.substr(0,fldsplit), token.substr(fldsplit+1));
return std::nullopt;
}
Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override

View File

@@ -97,7 +97,7 @@ struct PathInputScheme : InputScheme
// for security, ensure that if the parent is a store path, it's inside it
if (store->isInStore(parent)) {
auto storePath = store->printStorePath(store->toStorePath(parent).first);
if (!isDirOrInDir(absPath, storePath))
if (!isInDir(absPath, storePath))
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
}
} else

View File

@@ -67,18 +67,18 @@ DownloadFileResult downloadFile(
storePath = std::move(cached->storePath);
} else {
StringSink sink;
dumpString(res.data, sink);
auto hash = hashString(htSHA256, res.data);
dumpString(*res.data, sink);
auto hash = hashString(htSHA256, *res.data);
ValidPathInfo info {
store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name),
hashString(htSHA256, sink.s),
hashString(htSHA256, *sink.s),
};
info.narSize = sink.s.size();
info.narSize = sink.s->size();
info.ca = FixedOutputHash {
.method = FileIngestionMethod::Flat,
.hash = hash,
};
auto source = StringSource(sink.s);
auto source = StringSource { *sink.s };
store->addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path);
}
@@ -176,7 +176,6 @@ struct TarballInputScheme : InputScheme
if (!hasSuffix(url.path, ".zip")
&& !hasSuffix(url.path, ".tar")
&& !hasSuffix(url.path, ".tgz")
&& !hasSuffix(url.path, ".tar.gz")
&& !hasSuffix(url.path, ".tar.xz")
&& !hasSuffix(url.path, ".tar.bz2")

View File

@@ -11,7 +11,7 @@
namespace nix {
static std::string_view getS(const std::vector<Logger::Field> & fields, size_t n)
static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
{
assert(n < fields.size());
assert(fields[n].type == Logger::Field::tString);
@@ -103,19 +103,17 @@ public:
~ProgressBar()
{
stop();
updateThread.join();
}
void stop() override
{
{
auto state(state_.lock());
if (!state->active) return;
state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one();
quitCV.notify_one();
}
updateThread.join();
auto state(state_.lock());
if (!state->active) return;
state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one();
quitCV.notify_one();
}
bool isVerbose() override {

View File

@@ -15,14 +15,9 @@
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#ifdef __linux__
#include <features.h>
#endif
#ifdef __GLIBC__
#include <gnu/lib-names.h>
#include <nss.h>
#include <dlfcn.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <openssl/crypto.h>
@@ -126,30 +121,21 @@ static void preloadNSS() {
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
load its lookup libraries in the parent before any child gets a chance to. */
std::call_once(dns_resolve_flag, []() {
#ifdef __GLIBC__
/* On linux, glibc will run every lookup through the nss layer.
* That means every lookup goes, by default, through nscd, which acts as a local
* cache.
* Because we run builds in a sandbox, we also remove access to nscd otherwise
* lookups would leak into the sandbox.
*
* But now we have a new problem, we need to make sure the nss_dns backend that
* does the dns lookups when nscd is not available is loaded or available.
*
* We can't make it available without leaking nix's environment, so instead we'll
* load the backend, and configure nss so it does not try to run dns lookups
* through nscd.
*
* This is technically only used for builtins:fetch* functions so we only care
* about dns.
*
* All other platforms are unaffected.
*/
if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
warn("unable to load nss_dns backend");
// FIXME: get hosts entry from nsswitch.conf.
__nss_configure_lookup("hosts", "files dns");
#endif
struct addrinfo *res = NULL;
/* nss will only force the "local" (not through nscd) dns resolution if its on the LOCALDOMAIN.
We need the resolution to be done locally, as nscd socket will not be accessible in the
sandbox. */
char * previous_env = getenv("LOCALDOMAIN");
setenv("LOCALDOMAIN", "invalid", 1);
if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) == 0) {
if (res) freeaddrinfo(res);
}
if (previous_env) {
setenv("LOCALDOMAIN", previous_env, 1);
} else {
unsetenv("LOCALDOMAIN");
}
});
}
@@ -427,7 +413,7 @@ RunPager::RunPager()
});
pid.setKillSignal(SIGINT);
stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
}
@@ -438,7 +424,7 @@ RunPager::~RunPager()
try {
if (pid != -1) {
std::cout.flush();
dup2(stdout, STDOUT_FILENO);
close(STDOUT_FILENO);
pid.wait();
}
} catch (...) {

View File

@@ -88,7 +88,6 @@ public:
private:
Pid pid;
int stdout;
};
extern volatile ::sig_atomic_t blockInt;

View File

@@ -31,7 +31,7 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
StringSink sink;
sink << narVersionMagic1;
narMagic = sink.s;
narMagic = *sink.s;
}
void BinaryCacheStore::init()
@@ -68,7 +68,7 @@ void BinaryCacheStore::upsertFile(const std::string & path,
}
void BinaryCacheStore::getFile(const std::string & path,
Callback<std::optional<std::string>> callback) noexcept
Callback<std::shared_ptr<std::string>> callback) noexcept
{
try {
callback(getFile(path));
@@ -77,9 +77,9 @@ void BinaryCacheStore::getFile(const std::string & path,
void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
{
std::promise<std::optional<std::string>> promise;
std::promise<std::shared_ptr<std::string>> promise;
getFile(path,
{[&](std::future<std::optional<std::string>> result) {
{[&](std::future<std::shared_ptr<std::string>> result) {
try {
promise.set_value(result.get());
} catch (...) {
@@ -89,15 +89,15 @@ void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
sink(*promise.get_future().get());
}
std::optional<std::string> BinaryCacheStore::getFile(const std::string & path)
std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
{
StringSink sink;
try {
getFile(path, sink);
} catch (NoSuchBinaryCacheFile &) {
return std::nullopt;
return nullptr;
}
return std::move(sink.s);
return sink.s;
}
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
@@ -308,17 +308,16 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
}
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
{
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)
unsupported("addToStoreFromDump");
return addToStoreCommon(dump, repair, CheckSigs, [&](HashResult nar) {
ValidPathInfo info {
makeFixedOutputPath(method, nar.first, name, references),
makeFixedOutputPath(method, nar.first, name),
nar.first,
};
info.narSize = nar.second;
info.references = references;
return info;
})->path;
}
@@ -367,11 +366,11 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
getFile(narInfoFile,
{[=](std::future<std::optional<std::string>> fut) {
{[=](std::future<std::shared_ptr<std::string>> fut) {
try {
auto data = fut.get();
if (!data) return (*callbackPtr)({});
if (!data) return (*callbackPtr)(nullptr);
stats.narInfoRead++;
@@ -386,7 +385,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
}
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair, const StorePathSet & references)
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
{
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
non-recursive+sha256 so we can just use the default
@@ -405,11 +404,10 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
});
return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) {
ValidPathInfo info {
makeFixedOutputPath(method, h, name, references),
makeFixedOutputPath(method, h, name),
nar.first,
};
info.narSize = nar.second;
info.references = references;
info.ca = FixedOutputHash {
.method = method,
.hash = h,
@@ -429,7 +427,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
StringSink sink;
dumpString(s, sink);
StringSource source(sink.s);
auto source = StringSource { *sink.s };
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
ValidPathInfo info { path, nar.first };
info.narSize = nar.second;
@@ -439,29 +437,40 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
})->path;
}
void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
{
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
Callback<std::optional<std::string>> newCallback = {
[=](std::future<std::optional<std::string>> fut) {
try {
auto data = fut.get();
if (!data) return (*callbackPtr)({});
auto realisation = Realisation::fromJSON(
nlohmann::json::parse(*data), outputInfoFilePath);
return (*callbackPtr)(std::make_shared<const Realisation>(realisation));
} catch (...) {
callbackPtr->rethrow();
}
if (diskCache) {
auto [cacheOutcome, maybeCachedRealisation] =
diskCache->lookupRealisation(getUri(), id);
switch (cacheOutcome) {
case NarInfoDiskCache::oValid:
debug("Returning a cached realisation for %s", id.to_string());
return *maybeCachedRealisation;
case NarInfoDiskCache::oInvalid:
debug("Returning a cached missing realisation for %s", id.to_string());
return {};
case NarInfoDiskCache::oUnknown:
break;
}
};
}
getFile(outputInfoFilePath, std::move(newCallback));
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
auto rawOutputInfo = getFile(outputInfoFilePath);
if (rawOutputInfo) {
auto realisation = Realisation::fromJSON(
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath);
if (diskCache)
diskCache->upsertRealisation(
getUri(), realisation);
return {realisation};
} else {
if (diskCache)
diskCache->upsertAbsentRealisation(getUri(), id);
return std::nullopt;
}
}
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
@@ -490,7 +499,7 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
writeNarInfo(narInfo);
}
std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
{
auto drvPath = path;
@@ -498,10 +507,10 @@ std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
try {
auto info = queryPathInfo(path);
// FIXME: add a "Log" field to .narinfo
if (!info->deriver) return std::nullopt;
if (!info->deriver) return nullptr;
drvPath = *info->deriver;
} catch (InvalidPath &) {
return std::nullopt;
return nullptr;
}
}
@@ -512,14 +521,4 @@ std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
return getFile(logPath);
}
void BinaryCacheStore::addBuildLog(const StorePath & drvPath, std::string_view log)
{
assert(drvPath.isDerivation());
upsertFile(
"log/" + std::string(drvPath.to_string()),
(std::string) log, // FIXME: don't copy
"text/plain; charset=utf-8");
}
}

View File

@@ -51,7 +51,6 @@ public:
const std::string & mimeType) = 0;
void upsertFile(const std::string & path,
// FIXME: use std::string_view
std::string && data,
const std::string & mimeType);
@@ -63,11 +62,10 @@ public:
/* Fetch the specified file and call the specified callback with
the result. A subclass may implement this asynchronously. */
virtual void getFile(
const std::string & path,
Callback<std::optional<std::string>> callback) noexcept;
virtual void getFile(const std::string & path,
Callback<std::shared_ptr<std::string>> callback) noexcept;
std::optional<std::string> getFile(const std::string & path);
std::shared_ptr<std::string> getFile(const std::string & path);
public:
@@ -99,19 +97,18 @@ public:
RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references ) override;
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo,
PathFilter & filter, RepairFlag repair, const StorePathSet & references) override;
PathFilter & filter, RepairFlag repair) override;
StorePath addTextToStore(const string & name, const string & s,
const StorePathSet & references, RepairFlag repair) override;
void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
std::optional<const Realisation> queryRealisation(const DrvOutput &) override;
void narFromPath(const StorePath & path, Sink & sink) override;
@@ -119,9 +116,7 @@ public:
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
std::optional<std::string> getBuildLog(const StorePath & path) override;
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
};

View File

@@ -17,7 +17,6 @@
#include <regex>
#include <queue>
#include <fstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -194,7 +193,7 @@ void DerivationGoal::loadDerivation()
assert(worker.evalStore.isValidPath(drvPath));
/* Get the derivation. */
drv = std::make_unique<Derivation>(worker.evalStore.readDerivation(drvPath));
drv = std::make_unique<Derivation>(worker.evalStore.derivationFromPath(drvPath));
haveDerivation();
}
@@ -278,7 +277,7 @@ void DerivationGoal::outputsSubstitutionTried()
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
done(BuildResult::TransientFailure,
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
fmt("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
worker.store.printStorePath(drvPath)));
return;
}
@@ -465,6 +464,7 @@ void DerivationGoal::inputsRealised()
Derivation drvResolved { *std::move(attempt) };
auto pathResolved = writeDerivation(worker.store, drvResolved);
resolvedDrv = drvResolved;
auto msg = fmt("Resolved derivation: '%s' -> '%s'",
worker.store.printStorePath(drvPath),
@@ -475,9 +475,9 @@ void DerivationGoal::inputsRealised()
worker.store.printStorePath(pathResolved),
});
resolvedDrvGoal = worker.makeDerivationGoal(
auto resolvedGoal = worker.makeDerivationGoal(
pathResolved, wantedOutputs, buildMode);
addWaitee(resolvedDrvGoal);
addWaitee(resolvedGoal);
state = &DerivationGoal::resolvedFinished;
return;
@@ -655,7 +655,7 @@ void DerivationGoal::tryLocalBuild() {
throw Error(
"unable to build with a primary store that isn't a local store; "
"either pass a different '--store' or enable remote builds."
"\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html");
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
}
@@ -938,17 +938,16 @@ void DerivationGoal::buildDone()
}
void DerivationGoal::resolvedFinished() {
assert(resolvedDrvGoal);
auto resolvedDrv = *resolvedDrvGoal->drv;
assert(resolvedDrv);
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
StorePathSet outputPaths;
// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
realWantedOutputs = resolvedDrv.outputNames();
realWantedOutputs = resolvedDrv->outputNames();
for (auto & wantedOutput : realWantedOutputs) {
assert(initialOutputs.count(wantedOutput) != 0);
@@ -980,17 +979,9 @@ void DerivationGoal::resolvedFinished() {
outputPaths
);
auto status = [&]() {
auto resolvedResult = resolvedDrvGoal->getResult();
switch (resolvedResult.status) {
case BuildResult::AlreadyValid:
return BuildResult::ResolvesToAlreadyValid;
default:
return resolvedResult.status;
}
}();
done(status);
// This is potentially a bit fishy in terms of error reporting. Not sure
// how to do it in a cleaner way
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
}
HookReply DerivationGoal::tryBuildHook()
@@ -1338,13 +1329,6 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
}
worker.updateProgress();
auto traceBuiltOutputsFile = getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or("");
if (traceBuiltOutputsFile != "") {
std::fstream fs;
fs.open(traceBuiltOutputsFile, std::fstream::out);
fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
}
}

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