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
98 changed files with 1117 additions and 1555 deletions

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

@@ -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,9 +67,9 @@ 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"

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

@@ -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)

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

@@ -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 wouldnt 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

@@ -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

@@ -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

@@ -1,59 +0,0 @@
# Using Nix within Docker
To run the latest stable release of Nix with Docker run the following command:
```console
$ docker -ti run 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' 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
$ docker run -ti nix:2.5pre20211105
```

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, its much more
<http://nixos.org/nixpkgs/download.html>. However, its 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
@@ -47,45 +47,41 @@ $ nix-channel --update
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 youve 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 Nixs 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 Firefoxs 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

@@ -395,7 +395,6 @@ dramforever,
Dustin DeWeese,
edef,
Eelco Dolstra,
Ellie Hermaszewska,
Emilio Karakey,
Emily,
Eric Culp,
@@ -406,7 +405,7 @@ Federico Pellegrin,
Finn Behrens,
Florian Franzen,
Félix Baylac-Jacqué,
Gabriella Gonzalez,
Gabriel Gonzalez,
Geoff Reedy,
Georges Dubus,
Graham Christensen,
@@ -429,6 +428,7 @@ Jaroslavas Pocepko,
Jarrett Keifer,
Jeremy Schlatter,
Joachim Breitner,
Joe Hermaszewski,
Joe Pea,
John Ericson,
Jonathan Ringer,

View File

@@ -3,5 +3,3 @@
* 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.

View File

@@ -1,251 +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
];
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;
};
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
mkdir $out
cp -a ${rootEnv}/* $out/
cat > $out/manifest.nix <<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
'';
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
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/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
'';
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"
];
};
}

View File

@@ -91,7 +91,7 @@
libarchive
boost
lowdown-nix
gtest
gmock
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
@@ -405,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;
@@ -524,9 +509,7 @@
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
} // (if system == "x86_64-linux" then {
dockerImage = self.hydraJobs.dockerImage.${system};
} else {}));
});
packages = forAllSystems (system: {
inherit (nixpkgsFor.${system}) nix;

View File

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

View File

@@ -742,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"

View File

@@ -387,28 +387,19 @@ EOF
fi
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
@@ -818,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

@@ -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
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

View File

@@ -119,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:
@@ -519,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);
@@ -537,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);
}
@@ -1155,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);
}
@@ -1279,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);
@@ -1322,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.find(i.name) == lambda.formals->argNames.end())
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()) {
@@ -1409,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
@@ -1464,42 +1513,48 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
}
}
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
if (!fun.isLambda()) {
res = fun;
return;
}
Value * actualArgs = allocValue();
mkAttrs(*actualArgs, 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) {
actualArgs->attrs->push_back(v);
for (const auto & [i, arg] : enumerate(fun.lambda.fun->args)) {
if (!arg.formals) {
res = fun;
return;
}
} 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()) {
actualArgs->attrs->push_back(*j);
} else if (!i.def) {
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
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();
}
actualArgs->attrs->sort();
callFunction(fun, *actualArgs, res, noPos);
callFunction(fun, fun.lambda.fun->args.size(), actualArgs, res, noPos);
}
@@ -1736,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]);
}
};
@@ -1921,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

@@ -1,5 +1,4 @@
#include "flake.hh"
#include "globals.hh"
#include <nlohmann/json.hpp>
@@ -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

@@ -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)
@@ -257,7 +262,8 @@ static Flake getFlake(
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));
@@ -306,7 +312,7 @@ LockedFlake lockFlake(
if (lockFlags.applyNixConfig) {
flake.config.apply();
state.store->setOptions();
// FIXME: send new config to the daemon.
}
try {
@@ -445,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 {
@@ -464,27 +474,14 @@ LockedFlake lockFlake(
.isFlake = (*lockedNode)->isFlake,
});
} else if (auto follows = std::get_if<1>(&i.second)) {
auto o = input.overrides.find(i.first);
// If the override disappeared, we have to refetch the flake,
// since some of the inputs may not be present in the lockfile.
if (o == input.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(
mustRefetch
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
: fakeInputs,
childNode, inputPath, oldLock, parent, parentPath);
computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath);
}
} else {
/* We need to create a new lock file entry. So fetch

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) {
@@ -364,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

@@ -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
@@ -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);
}

View File

@@ -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
};
@@ -368,12 +371,12 @@ struct StaticEnv
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
}
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,6 +154,24 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
}
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(""));
@@ -322,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
@@ -444,7 +464,7 @@ 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

View File

@@ -70,7 +70,7 @@ void EvalState::realiseContext(const PathSet & context)
if (outputPaths.count(outputName) == 0)
throw Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName);
allowPath(outputPaths.at(outputName));
allowedPaths->insert(store->printStorePath(outputPaths.at(outputName)));
}
}
}
@@ -335,8 +335,9 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false);
Strings commandArgs;
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
}
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
@@ -516,11 +517,7 @@ static RegisterPrimOp primop_isPath({
struct CompareValues
{
EvalState & state;
CompareValues(EvalState & state) : state(state) { };
bool operator () (Value * v1, Value * v2) const
bool operator () (const Value * v1, const Value * v2) const
{
if (v1->type() == nFloat && v2->type() == nInt)
return v1->fpoint < v2->integer;
@@ -537,17 +534,6 @@ struct CompareValues
return strcmp(v1->string.s, v2->string.s) < 0;
case nPath:
return strcmp(v1->path, v2->path) < 0;
case nList:
// Lexicographic comparison
for (size_t i = 0;; i++) {
if (i == v2->listSize()) {
return false;
} else if (i == v1->listSize()) {
return true;
} else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i])) {
return (*this)(v1->listElems()[i], v2->listElems()[i]);
}
}
default:
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
}
@@ -615,8 +601,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*startSet->value, pos);
ValueList workSet;
for (auto elem : startSet->value->listItems())
workSet.push_back(elem);
for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
workSet.push_back(startSet->value->listElems()[n]);
/* Get the operator. */
Bindings::iterator op = getAttr(
@@ -635,8 +621,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
ValueList res;
// `doneKeys' doesn't need to be a GC root, because its values are
// reachable from res.
auto cmp = CompareValues(state);
set<Value *, decltype(cmp)> doneKeys(cmp);
set<Value *, CompareValues> doneKeys;
while (!workSet.empty()) {
Value * e = *(workSet.begin());
workSet.pop_front();
@@ -661,9 +646,9 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(call, pos);
/* Add the values returned by the operator to the work set. */
for (auto elem : call.listItems()) {
state.forceValue(*elem, pos);
workSet.push_back(elem);
for (unsigned int n = 0; n < call.listSize(); ++n) {
state.forceValue(*call.listElems()[n], pos);
workSet.push_back(call.listElems()[n]);
}
}
@@ -1003,17 +988,16 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
if (i->name == state.sContentAddressed) {
settings.requireExperimentalFeature(Xp::CaDerivations);
contentAddressed = state.forceBool(*i->value, pos);
if (contentAddressed)
settings.requireExperimentalFeature(Xp::CaDerivations);
}
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) {
string s = state.coerceToString(posDrvName, *elem, context, true);
for (unsigned int n = 0; n < i->value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i->value->listElems()[n], context, true);
drv.args.push_back(s);
}
}
@@ -1027,7 +1011,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (i->name == state.sStructuredAttrs) continue;
auto placeholder(jsonObject->placeholder(key));
printValueAsJSON(state, true, *i->value, pos, placeholder, context);
printValueAsJSON(state, true, *i->value, placeholder, context);
if (i->name == state.sBuilder)
drv.builder = state.forceString(*i->value, context, posDrvName);
@@ -1043,8 +1027,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Require outputs to be a list of strings. */
state.forceList(*i->value, posDrvName);
Strings ss;
for (auto elem : i->value->listItems())
ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName));
for (unsigned int n = 0; n < i->value->listSize(); ++n)
ss.emplace_back(state.forceStringNoCtx(*i->value->listElems()[n], posDrvName));
handleOutputs(ss);
}
@@ -1459,19 +1443,20 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
SearchPath searchPath;
for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos);
for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
Value & v2(*args[0]->listElems()[n]);
state.forceAttrs(v2, pos);
string prefix;
Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix"));
if (i != v2->attrs->end())
Bindings::iterator i = v2.attrs->find(state.symbols.create("prefix"));
if (i != v2.attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos);
i = getAttr(
state,
"findFile",
"path",
v2->attrs,
v2.attrs,
pos
);
@@ -1597,7 +1582,7 @@ static void prim_toXML(EvalState & state, const Pos & pos, Value * * args, Value
{
std::ostringstream out;
PathSet context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
printValueAsXML(state, true, false, *args[0], out, context);
mkString(v, out.str(), context);
}
@@ -1705,7 +1690,7 @@ static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Valu
{
std::ostringstream out;
PathSet context;
printValueAsJSON(state, true, *args[0], pos, out, context);
printValueAsJSON(state, true, *args[0], out, context);
mkString(v, out.str(), context);
}
@@ -1877,12 +1862,12 @@ static void addPath(
// be rewritten to the actual output).
state.realiseContext(context);
StorePathSet refs;
if (state.store->isInStore(path)) {
auto [storePath, subPath] = state.store->toStorePath(path);
// FIXME: we should scanForReferences on the path before adding it
refs = state.store->queryPathInfo(storePath)->references;
auto info = state.store->queryPathInfo(storePath);
if (!info->references.empty())
throw EvalError("store path '%s' is not allowed to have references",
state.store->printStorePath(storePath));
path = state.store->toRealPath(storePath) + subPath;
}
@@ -1920,7 +1905,7 @@ static void addPath(
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
dstPath = state.store->printStorePath(settings.readOnlyMode
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs));
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
} else
@@ -2237,9 +2222,9 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
/* Get the attribute names to be removed. */
std::set<Symbol> names;
for (auto elem : args[1]->listItems()) {
state.forceStringNoCtx(*elem, pos);
names.insert(state.symbols.create(elem->string.s));
for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
state.forceStringNoCtx(*args[1]->listElems()[i], pos);
names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
}
/* Copy all attributes not in that set. Note that we don't need
@@ -2247,7 +2232,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
vector. */
state.mkAttrs(v, args[0]->attrs->size());
for (auto & i : *args[0]->attrs) {
if (!names.count(i.name))
if (names.find(i.name) == names.end())
v.attrs->push_back(i);
}
}
@@ -2281,14 +2266,15 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
std::set<Symbol> seen;
for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos);
for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
Value & v2(*args[0]->listElems()[i]);
state.forceAttrs(v2, pos);
Bindings::iterator j = getAttr(
state,
"listToAttrs",
state.sName,
v2->attrs,
v2.attrs,
pos
);
@@ -2300,7 +2286,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
state,
"listToAttrs",
state.sValue,
v2->attrs,
v2.attrs,
pos
);
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
@@ -2367,10 +2353,11 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
Value * res[args[1]->listSize()];
unsigned int found = 0;
for (auto v2 : args[1]->listItems()) {
state.forceAttrs(*v2, pos);
Bindings::iterator i = v2->attrs->find(attrName);
if (i != v2->attrs->end())
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value & v2(*args[1]->listElems()[n]);
state.forceAttrs(v2, pos);
Bindings::iterator i = v2.attrs->find(attrName);
if (i != v2.attrs->end())
res[found++] = i->value;
}
@@ -2399,23 +2386,38 @@ static RegisterPrimOp primop_catAttrs({
static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
state.mkAttrs(v, 0);
return;
}
if (!args[0]->isLambda())
if (!args[0]->isLambda() && !args[0]->isPartialApp())
throw TypeError({
.msg = hintfmt("'functionArgs' requires a function"),
.errPos = pos
});
if (!args[0]->lambda.fun->hasFormals()) {
size_t argsDone = 0;
auto lambda = args[0];
while (lambda->isPartialApp()) {
argsDone++;
lambda = lambda->app.left;
}
assert(lambda->isLambda());
assert(argsDone < lambda->lambda.fun->args.size());
// FIXME: handle partially applied functions
auto formals = lambda->lambda.fun->args[argsDone].formals;
if (!formals) {
state.mkAttrs(v, 0);
return;
}
state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
for (auto & i : args[0]->lambda.fun->formals->formals) {
state.mkAttrs(v, formals->formals.size());
for (auto & i : formals->formals) {
// !!! should optimise booleans (allocate only once)
Value * value = state.allocValue();
v.attrs->push_back(Attr(i.name, value, ptr(&i.pos)));
@@ -2645,8 +2647,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
{
bool res = false;
state.forceList(*args[1], pos);
for (auto elem : args[1]->listItems())
if (state.eqValues(*args[0], *elem)) {
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
res = true;
break;
}
@@ -2705,8 +2707,8 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args,
if (args[2]->listSize()) {
Value * vCur = args[1];
for (auto [n, elem] : enumerate(args[2]->listItems())) {
Value * vs []{vCur, elem};
for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
Value * vs []{vCur, args[2]->listElems()[n]};
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
state.callFunction(*args[0], 2, vs, *vCur, pos);
}
@@ -2722,9 +2724,9 @@ static RegisterPrimOp primop_foldlStrict({
.args = {"op", "nul", "list"},
.doc = R"(
Reduce a list by applying a binary operator, from left to right,
e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
e.g. `foldl op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
...`. The operator is applied strictly, i.e., its arguments are
evaluated first. For example, `foldl' (x: y: x + y) 0 [1 2 3]`
evaluated first. For example, `foldl (x: y: x + y) 0 [1 2 3]`
evaluates to 6.
)",
.fun = prim_foldlStrict,
@@ -2736,8 +2738,8 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
state.forceList(*args[1], pos);
Value vTmp;
for (auto elem : args[1]->listItems()) {
state.callFunction(*args[0], *elem, vTmp, pos);
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
bool res = state.forceBool(vTmp, pos);
if (res == any) {
mkBool(v, any);
@@ -2833,7 +2835,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
/* Optimization: if the comparator is lessThan, bypass
callFunction. */
if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan)
return CompareValues(state)(a, b);
return CompareValues()(a, b);
Value * vs[] = {a, b};
Value vBool;
@@ -3115,7 +3117,7 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va
{
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
CompareValues comp{state};
CompareValues comp;
mkBool(v, comp(args[0], args[1]));
}
@@ -3466,9 +3468,9 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
res.reserve((args[1]->listSize() + 32) * sep.size());
bool first = true;
for (auto elem : args[1]->listItems()) {
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
if (first) first = false; else res += sep;
res += state.coerceToString(pos, *elem, context);
res += state.coerceToString(pos, *args[1]->listElems()[n], context);
}
mkString(v, res, context);
@@ -3497,14 +3499,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
vector<string> from;
from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems())
from.push_back(state.forceString(*elem, pos));
for (unsigned int n = 0; n < args[0]->listSize(); ++n)
from.push_back(state.forceString(*args[0]->listElems()[n], pos));
vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
PathSet ctx;
auto s = state.forceString(*elem, ctx, pos);
auto s = state.forceString(*args[1]->listElems()[n], ctx, pos);
to.push_back(std::make_pair(std::move(s), std::move(ctx)));
}
@@ -3705,7 +3707,7 @@ void EvalState::createBaseEnv()
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
mkInt(v, 6);
mkInt(v, 5);
addConstant("__langVersion", v);
// Miscellaneous

View File

@@ -118,8 +118,9 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
state.mkList(outputsVal, info.second.outputs.size());
size_t i = 0;
for (const auto & output : info.second.outputs)
for (const auto & output : info.second.outputs) {
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
}
}
infoVal.attrs->sort();
}
@@ -180,8 +181,8 @@ 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));
}
}

View File

@@ -74,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

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
@@ -23,6 +21,7 @@ typedef enum {
tListN,
tThunk,
tApp,
tPartialApp,
tLambda,
tBlackhole,
tPrimOp,
@@ -96,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()
{
@@ -128,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; };
@@ -199,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;
@@ -310,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();
@@ -352,34 +358,6 @@ 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() };
}
};

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

@@ -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) == NULL) {
printMsg(Verbosity::lvlWarn, fmt("Unable to load nss_dns backend"));
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");
}
__nss_configure_lookup ("hosts", "dns");
#endif
});
}

View File

@@ -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;
}
@@ -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,
@@ -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::shared_ptr<std::string>> newCallback = {
[=](std::future<std::shared_ptr<std::string>> fut) {
try {
auto data = fut.get();
if (!data) return (*callbackPtr)(nullptr);
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) {

View File

@@ -97,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;

View File

@@ -1,8 +1,6 @@
#include "drv-output-substitution-goal.hh"
#include "finally.hh"
#include "worker.hh"
#include "substitution-goal.hh"
#include "callback.hh"
namespace nix {
@@ -52,42 +50,14 @@ void DrvOutputSubstitutionGoal::tryNext()
return;
}
sub = subs.front();
auto sub = subs.front();
subs.pop_front();
// FIXME: Make async
// outputInfo = sub->queryRealisation(id);
outPipe.create();
promise = decltype(promise)();
sub->queryRealisation(
id, { [&](std::future<std::shared_ptr<const Realisation>> res) {
try {
Finally updateStats([this]() { outPipe.writeSide.close(); });
promise.set_value(res.get());
} catch (...) {
promise.set_exception(std::current_exception());
}
} });
worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false);
state = &DrvOutputSubstitutionGoal::realisationFetched;
}
void DrvOutputSubstitutionGoal::realisationFetched()
{
worker.childTerminated(this);
try {
outputInfo = promise.get_future().get();
} catch (std::exception & e) {
printError(e.what());
substituterFailed = true;
}
outputInfo = sub->queryRealisation(id);
if (!outputInfo) {
return tryNext();
tryNext();
return;
}
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
@@ -149,10 +119,4 @@ void DrvOutputSubstitutionGoal::work()
(this->*state)();
}
void DrvOutputSubstitutionGoal::handleEOF(int fd)
{
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
}
}

View File

@@ -3,8 +3,6 @@
#include "store-api.hh"
#include "goal.hh"
#include "realisation.hh"
#include <thread>
#include <future>
namespace nix {
@@ -22,18 +20,11 @@ private:
// The realisation corresponding to the given output id.
// Will be filled once we can get it.
std::shared_ptr<const Realisation> outputInfo;
std::optional<Realisation> outputInfo;
/* The remaining substituters. */
std::list<ref<Store>> subs;
/* The current substituter. */
std::shared_ptr<Store> sub;
Pipe outPipe;
std::thread thr;
std::promise<std::shared_ptr<const Realisation>> promise;
/* Whether a substituter failed. */
bool substituterFailed = false;
@@ -45,7 +36,6 @@ public:
void init();
void tryNext();
void realisationFetched();
void outPathValid();
void finished();
@@ -54,7 +44,7 @@ public:
string key() override;
void work() override;
void handleEOF(int fd) override;
};
}

View File

@@ -342,7 +342,7 @@ int childEntry(void * arg)
return 1;
}
#if __linux__
static void linkOrCopy(const Path & from, const Path & to)
{
if (link(from.c_str(), to.c_str()) == -1) {
@@ -358,7 +358,6 @@ static void linkOrCopy(const Path & from, const Path & to)
copyPath(from, to);
}
}
#endif
void LocalDerivationGoal::startBuilder()
@@ -918,9 +917,7 @@ void LocalDerivationGoal::startBuilder()
} else
#endif
{
#if __linux__
fallback:
#endif
pid = startProcess([&]() {
runChild();
});
@@ -1182,8 +1179,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet()) override
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override
{ throw Error("addToStore"); }
void addToStore(const ValidPathInfo & info, Source & narSource,
@@ -1202,10 +1198,9 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
}
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet()) override
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override
{
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references);
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair);
goal.addDependency(path);
return path;
}
@@ -1229,14 +1224,13 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
// corresponds to an allowed derivation
{ throw Error("registerDrvOutput"); }
void queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
// XXX: This should probably be allowed if the realisation corresponds to
// an allowed derivation
{
if (!goal.isAllowed(id))
callback(nullptr);
next->queryRealisation(id, std::move(callback));
throw InvalidPath("cannot query an unknown output id '%s' in recursive Nix", id.to_string());
return next->queryRealisation(id);
}
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
@@ -1997,7 +1991,7 @@ void LocalDerivationGoal::runChild()
else if (drv->builder == "builtin:unpack-channel")
builtinUnpackChannel(drv2);
else
throw Error("unsupported builtin builder '%1%'", string(drv->builder, 8));
throw Error("unsupported builtin function '%1%'", string(drv->builder, 8));
_exit(0);
} catch (std::exception & e) {
writeFull(STDERR_FILENO, e.what() + std::string("\n"));

View File

@@ -120,10 +120,8 @@ ContentAddress parseContentAddress(std::string_view rawCa) {
ContentAddressMethod parseContentAddressMethod(std::string_view caMethod)
{
std::string asPrefix = std::string{caMethod} + ":";
// parseContentAddressMethodPrefix takes its argument by reference
std::string_view asPrefixView = asPrefix;
return parseContentAddressMethodPrefix(asPrefixView);
std::string_view asPrefix {std::string{caMethod} + ":"};
return parseContentAddressMethodPrefix(asPrefix);
}
std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt)

View File

@@ -403,7 +403,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
return store->queryPathInfo(path);
},
[&](FixedOutputHashMethod & fohm) {
auto path = store->addToStoreFromDump(source, name, fohm.fileIngestionMethod, fohm.hashType, repair, refs);
if (!refs.empty())
throw UnimplementedError("cannot yet have refs with flat or nar-hashed data");
auto path = store->addToStoreFromDump(source, name, fohm.fileIngestionMethod, fohm.hashType, repair);
return store->queryPathInfo(path);
},
}, contentAddressMethod);

View File

@@ -50,9 +50,8 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
void narFromPath(const StorePath & path, Sink & sink) override
{ unsupported("narFromPath"); }
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
{ callback(nullptr); }
std::optional<const Realisation> queryRealisation(const DrvOutput&) override
{ unsupported("queryRealisation"); }
};
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;

View File

@@ -544,14 +544,6 @@ struct curlFileTransfer : public FileTransfer
stopWorkerThread();
});
#ifdef __linux__
/* Cause this thread to not share any FS attributes with the main thread,
because this causes setns() in restoreMountNamespace() to fail.
Ideally, this would happen in the std::thread() constructor. */
if (unshare(CLONE_FS) != 0)
throw SysError("unsharing filesystem state in download thread");
#endif
std::map<CURL *, std::shared_ptr<TransferItem>> items;
bool quit = false;

View File

@@ -324,7 +324,6 @@ static string quoteRegexChars(const string & raw)
return std::regex_replace(raw, specialRegex, R"(\$&)");
}
#if __linux__
static void readFileRoots(const char * path, UncheckedRoots & roots)
{
try {
@@ -334,7 +333,6 @@ static void readFileRoots(const char * path, UncheckedRoots & roots)
throw;
}
}
#endif
void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
{
@@ -416,7 +414,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
}
#endif
#if __linux__
#if defined(__linux__)
readFileRoots("/proc/sys/kernel/modprobe", unchecked);
readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);

View File

@@ -122,7 +122,7 @@ StringSet Settings::getDefaultSystemFeatures()
/* For backwards compatibility, accept some "features" that are
used in Nixpkgs to route builds to certain machines but don't
actually require anything special on the machines. */
StringSet features{"nixos-test", "benchmark", "big-parallel"};
StringSet features{"nixos-test", "benchmark", "big-parallel", "recursive-nix"};
#if __linux__
if (access("/dev/kvm", R_OK | W_OK) == 0)

View File

@@ -951,9 +951,6 @@ public:
Setting<bool> useRegistries{this, true, "use-registries",
"Whether to use flake registries to resolve flake references."};
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
"Whether to accept nix configuration from a flake without prompting."};
};

View File

@@ -227,7 +227,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
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
{ unsupported("addToStore"); }
StorePath addTextToStore(const string & name, const string & s,
@@ -367,8 +367,7 @@ public:
return conn->remoteVersion;
}
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
std::optional<const Realisation> queryRealisation(const DrvOutput&) override
// TODO: Implement
{ unsupported("queryRealisation"); }
};

View File

@@ -504,6 +504,9 @@ void LocalStore::makeStoreWritable()
throw SysError("getting info about the Nix store mount point");
if (stat.f_flag & ST_RDONLY) {
if (unshare(CLONE_NEWNS) == -1)
throw SysError("setting up a private mount namespace");
if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
throw SysError("remounting %1% writable", realStoreDir);
}
@@ -1308,7 +1311,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
{
/* For computing the store path. */
auto hashSink = std::make_unique<HashSink>(hashAlgo);
@@ -1364,7 +1367,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
auto [hash, size] = hashSink->finish();
auto dstPath = makeFixedOutputPath(method, hash, name, references);
auto dstPath = makeFixedOutputPath(method, hash, name);
addTempRoot(dstPath);
@@ -1411,7 +1414,6 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
ValidPathInfo info { dstPath, narHash.first };
info.narSize = narHash.second;
info.references = references;
info.ca = FixedOutputHash { .method = method, .hash = hash };
registerValidPath(info);
}
@@ -1836,24 +1838,13 @@ std::optional<const Realisation> LocalStore::queryRealisation_(
return { res };
}
void LocalStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
std::optional<const Realisation>
LocalStore::queryRealisation(const DrvOutput & id)
{
try {
auto maybeRealisation
= retrySQLite<std::optional<const Realisation>>([&]() {
auto state(_state.lock());
return queryRealisation_(*state, id);
});
if (maybeRealisation)
callback(
std::make_shared<const Realisation>(maybeRealisation.value()));
else
callback(nullptr);
} catch (...) {
callback.rethrow();
}
return retrySQLite<std::optional<const Realisation>>([&]() {
auto state(_state.lock());
return queryRealisation_(*state, id);
});
}
FixedOutputHash LocalStore::hashCAPath(

View File

@@ -145,7 +145,7 @@ 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 addTextToStore(const string & name, const string & s,
const StorePathSet & references, RepairFlag repair) override;
@@ -207,8 +207,7 @@ public:
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
void queryRealisationUncached(const DrvOutput&,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
private:

View File

@@ -54,12 +54,12 @@ void RefScanSink::operator () (std::string_view data)
fragment, so search in the concatenation of the tail of the
previous fragment and the start of the current fragment. */
auto s = tail;
auto tailLen = std::min(data.size(), refLength);
s.append(data.data(), tailLen);
s.append(data.data(), refLength);
search(s, hashes, seen);
search(data, hashes, seen);
auto tailLen = std::min(data.size(), refLength);
auto rest = refLength - tailLen;
if (rest < tail.size())
tail = tail.substr(tail.size() - rest);

View File

@@ -290,10 +290,6 @@ ConnectionHandle RemoteStore::getConnection()
return ConnectionHandle(connections->get());
}
void RemoteStore::setOptions()
{
setOptions(*(getConnection().handle));
}
bool RemoteStore::isValidPathUncached(const StorePath & path)
{
@@ -582,8 +578,9 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore(
StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references)
FileIngestionMethod method, HashType hashType, RepairFlag repair)
{
StorePathSet references;
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
}
@@ -680,33 +677,23 @@ void RemoteStore::registerDrvOutput(const Realisation & info)
conn.processStderr();
}
void RemoteStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
std::optional<const Realisation> RemoteStore::queryRealisation(const DrvOutput & id)
{
auto conn(getConnection());
conn->to << wopQueryRealisation;
conn->to << id.to_string();
conn.processStderr();
auto real = [&]() -> std::shared_ptr<const Realisation> {
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
auto outPaths = worker_proto::read(
*this, conn->from, Phantom<std::set<StorePath>> {});
if (outPaths.empty())
return nullptr;
return std::make_shared<const Realisation>(Realisation { .id = id, .outPath = *outPaths.begin() });
} else {
auto realisations = worker_proto::read(
*this, conn->from, Phantom<std::set<Realisation>> {});
if (realisations.empty())
return nullptr;
return std::make_shared<const Realisation>(*realisations.begin());
}
}();
try {
callback(std::shared_ptr<const Realisation>(real));
} catch (...) { return callback.rethrow(); }
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
auto outPaths = worker_proto::read(*this, conn->from, Phantom<std::set<StorePath>>{});
if (outPaths.empty())
return std::nullopt;
return {Realisation{.id = id, .outPath = *outPaths.begin()}};
} else {
auto realisations = worker_proto::read(*this, conn->from, Phantom<std::set<Realisation>>{});
if (realisations.empty())
return std::nullopt;
return *realisations.begin();
}
}
static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, const std::vector<DerivedPath> & reqs)

View File

@@ -73,7 +73,7 @@ public:
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;
void addToStore(const ValidPathInfo & info, Source & nar,
RepairFlag repair, CheckSigsFlag checkSigs) override;
@@ -88,8 +88,7 @@ public:
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 buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
@@ -148,8 +147,6 @@ protected:
virtual void setOptions(Connection & conn);
void setOptions() override;
ConnectionHandle getConnection();
friend struct ConnectionHandle;

View File

@@ -100,5 +100,4 @@
; Allow Rosetta 2 to run x86_64 binaries on aarch64-darwin.
(allow file-read*
(subpath "/Library/Apple/usr/libexec/oah")
(subpath "/System/Library/Apple/usr/libexec/oah"))
(subpath "/Library/Apple/usr/libexec/oah"))

View File

@@ -237,7 +237,7 @@ StorePath Store::computeStorePathForText(const string & name, const string & s,
StorePath Store::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)
{
Path srcPath(absPath(_srcPath));
auto source = sinkToSource([&](Sink & sink) {
@@ -246,7 +246,7 @@ StorePath Store::addToStore(const string & name, const Path & _srcPath,
else
readFile(srcPath, sink);
});
return addToStoreFromDump(*source, name, method, hashAlgo, repair, references);
return addToStoreFromDump(*source, name, method, hashAlgo, repair);
}
@@ -355,13 +355,8 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
StringSet StoreConfig::getDefaultSystemFeatures()
{
auto res = settings.systemFeatures.get();
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
res.insert("ca-derivations");
if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix))
res.insert("recursive-nix");
return res;
}
@@ -547,74 +542,6 @@ void Store::queryPathInfo(const StorePath & storePath,
}});
}
void Store::queryRealisation(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
{
try {
if (diskCache) {
auto [cacheOutcome, maybeCachedRealisation]
= diskCache->lookupRealisation(getUri(), id);
switch (cacheOutcome) {
case NarInfoDiskCache::oValid:
debug("Returning a cached realisation for %s", id.to_string());
callback(maybeCachedRealisation);
return;
case NarInfoDiskCache::oInvalid:
debug(
"Returning a cached missing realisation for %s",
id.to_string());
callback(nullptr);
return;
case NarInfoDiskCache::oUnknown:
break;
}
}
} catch (...) {
return callback.rethrow();
}
auto callbackPtr
= std::make_shared<decltype(callback)>(std::move(callback));
queryRealisationUncached(
id,
{ [this, id, callbackPtr](
std::future<std::shared_ptr<const Realisation>> fut) {
try {
auto info = fut.get();
if (diskCache) {
if (info)
diskCache->upsertRealisation(getUri(), *info);
else
diskCache->upsertAbsentRealisation(getUri(), id);
}
(*callbackPtr)(std::shared_ptr<const Realisation>(info));
} catch (...) {
callbackPtr->rethrow();
}
} });
}
std::shared_ptr<const Realisation> Store::queryRealisation(const DrvOutput & id)
{
using RealPtr = std::shared_ptr<const Realisation>;
std::promise<RealPtr> promise;
queryRealisation(id,
{[&](std::future<RealPtr> result) {
try {
promise.set_value(result.get());
} catch (...) {
promise.set_exception(std::current_exception());
}
}});
return promise.get_future().get();
}
void Store::substitutePaths(const StorePathSet & paths)
{

View File

@@ -369,14 +369,6 @@ public:
void queryPathInfo(const StorePath & path,
Callback<ref<const ValidPathInfo>> callback) noexcept;
/* Query the information about a realisation. */
std::shared_ptr<const Realisation> queryRealisation(const DrvOutput &);
/* Asynchronous version of queryRealisation(). */
void queryRealisation(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept;
/* Check whether the given valid path info is sufficiently attested, by
either being signed by a trusted public key or content-addressed, in
order to be included in the given store.
@@ -401,11 +393,11 @@ protected:
virtual void queryPathInfoUncached(const StorePath & path,
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0;
virtual void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept = 0;
public:
virtual std::optional<const Realisation> queryRealisation(const DrvOutput &) = 0;
/* Queries the set of incoming FS references for a store path.
The result is not cleared. */
virtual void queryReferrers(const StorePath & path, StorePathSet & referrers)
@@ -460,7 +452,7 @@ public:
libutil/archive.hh). */
virtual StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet());
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair);
/* Copy the contents of a path to the store and register the
validity the resulting path, using a constant amount of
@@ -476,8 +468,7 @@ public:
`dump` may be drained */
// FIXME: remove?
virtual StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet())
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair)
{ unsupported("addToStoreFromDump"); }
/* Like addToStore, but the contents written to the output path is
@@ -733,11 +724,6 @@ public:
virtual void createUser(const std::string & userName, uid_t userId)
{ }
/*
* Synchronises the options of the client with those of the daemon
* (a no-op when theres no daemon)
*/
virtual void setOptions() { }
protected:
Stats stats;

View File

@@ -562,7 +562,7 @@ Path getConfigDir()
std::vector<Path> getConfigDirs()
{
Path configHome = getConfigDir();
string configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg");
string configDirs = getEnv("XDG_CONFIG_DIRS").value_or("");
std::vector<Path> result = tokenizeString<std::vector<string>>(configDirs, ":");
result.insert(result.begin(), configHome);
return result;
@@ -1205,7 +1205,7 @@ void closeOnExec(int fd)
//////////////////////////////////////////////////////////////////////
std::atomic<bool> _isInterrupted = false;
bool _isInterrupted = false;
static thread_local bool interruptThrown = false;
thread_local std::function<bool()> interruptCheck;
@@ -1632,39 +1632,9 @@ void setStackSize(size_t stackSize)
#endif
}
static AutoCloseFD fdSavedMountNamespace;
void saveMountNamespace()
{
#if __linux__
static std::once_flag done;
std::call_once(done, []() {
AutoCloseFD fd = open("/proc/self/ns/mnt", O_RDONLY);
if (!fd)
throw SysError("saving parent mount namespace");
fdSavedMountNamespace = std::move(fd);
});
#endif
}
void restoreMountNamespace()
{
#if __linux__
try {
if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1)
throw SysError("restoring parent mount namespace");
} catch (Error & e) {
debug(e.msg());
}
#endif
}
void restoreProcessContext(bool restoreMounts)
void restoreProcessContext()
{
restoreSignals();
if (restoreMounts) {
restoreMountNamespace();
}
restoreAffinity();
@@ -1804,7 +1774,7 @@ void commonChildInit(Pipe & logPipe)
logger = makeSimpleLogger();
const static string pathNullDevice = "/dev/null";
restoreProcessContext(false);
restoreProcessContext();
/* Put the child in a separate session (and thus a separate
process group) so that it has no controlling terminal (meaning

View File

@@ -300,15 +300,7 @@ void setStackSize(size_t stackSize);
/* Restore the original inherited Unix process context (such as signal
masks, stack size, CPU affinity). */
void restoreProcessContext(bool restoreMounts = true);
/* Save the current mount namespace. Ignored if called more than
once. */
void saveMountNamespace();
/* Restore the mount namespace saved by saveMountNamespace(). Ignored
if saveMountNamespace() was never called. */
void restoreMountNamespace();
void restoreProcessContext();
class ExecError : public Error
@@ -337,7 +329,7 @@ void closeOnExec(int fd);
/* User interruption. */
extern std::atomic<bool> _isInterrupted;
extern bool _isInterrupted;
extern thread_local std::function<bool()> interruptCheck;

View File

@@ -105,8 +105,7 @@ static void main_nix_build(int argc, char * * argv)
// List of environment variables kept for --pure
std::set<string> keepVars{
"HOME", "XDG_RUNTIME_DIR", "USER", "LOGNAME", "DISPLAY",
"WAYLAND_DISPLAY", "WAYLAND_SOCKET", "PATH", "TERM", "IN_NIX_SHELL",
"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL",
"NIX_SHELL_PRESERVE_PROMPT", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL",
"http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy"
};

View File

@@ -879,7 +879,7 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
placeholder.write(nullptr);
} else {
PathSet context;
printValueAsJSON(*globals.state, true, *v, noPos, placeholder, context);
printValueAsJSON(*globals.state, true, *v, placeholder, context);
}
}
}
@@ -1149,10 +1149,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
} else if (v->type() == nList) {
attrs2["type"] = "strings";
XMLOpenElement m(xml, "meta", attrs2);
for (auto elem : v->listItems()) {
if (elem->type() != nString) continue;
for (unsigned int j = 0; j < v->listSize(); ++j) {
if (v->listElems()[j]->type() != nString) continue;
XMLAttrs attrs3;
attrs3["value"] = elem->string.s;
attrs3["value"] = v->listElems()[j]->string.s;
xml.writeEmptyElement("string", attrs3);
}
} else if (v->type() == nAttrs) {

View File

@@ -50,9 +50,9 @@ void processExpr(EvalState & state, const Strings & attrPaths,
else
state.autoCallFunction(autoArgs, v, vRes);
if (output == okXML)
printValueAsXML(state, strict, location, vRes, std::cout, context, noPos);
printValueAsXML(state, strict, location, vRes, std::cout, context);
else if (output == okJSON)
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
printValueAsJSON(state, strict, vRes, std::cout, context);
else {
if (strict) state.forceValueDeep(vRes);
std::cout << vRes << std::endl;

View File

@@ -112,7 +112,7 @@ struct CmdEval : MixJSON, InstallableCommand
else if (json) {
JSONPlaceholder jsonOut(std::cout);
printValueAsJSON(*state, true, *v, pos, jsonOut, context);
printValueAsJSON(*state, true, *v, jsonOut, context);
}
else {

View File

@@ -31,38 +31,38 @@ at the first error.
The following flake output attributes must be derivations:
* `checks.`*system*`.`*name*
* `defaultPackage.`*system*
* `devShell.`*system*
* `devShells.`*system*`.`*name*
* `nixosConfigurations.`*name*`.config.system.build.toplevel`
* `defaultPackage.`*system*`
* `devShell.`*system*`
* `devShells.`*system*`.`*name*`
* `nixosConfigurations.`*name*`.config.system.build.toplevel
* `packages.`*system*`.`*name*
The following flake output attributes must be [app
definitions](./nix3-run.md):
* `apps.`*system*`.`*name*
* `defaultApp.`*system*
* `defaultApp.`*system*`
The following flake output attributes must be [template
definitions](./nix3-flake-init.md):
* `defaultTemplate`
* `templates.`*name*
* `templates`.`*name*
The following flake output attributes must be *Nixpkgs overlays*:
* `overlay`
* `overlays.`*name*
* `overlays`.`*name*
The following flake output attributes must be *NixOS modules*:
* `nixosModule`
* `nixosModules.`*name*
* `nixosModules`.`*name*
The following flake output attributes must be
[bundlers](./nix3-bundle.md):
* `bundlers.`*name*
* `bundlers`.`*name*
* `defaultBundler`
In addition, the `hydraJobs` output is evaluated in the same way as

View File

@@ -355,14 +355,12 @@ struct CmdFlakeCheck : FlakeCommand
try {
state->forceValue(v, pos);
if (!v.isLambda()
|| v.lambda.fun->hasFormals()
|| !argHasName(v.lambda.fun->arg, "final"))
throw Error("overlay does not take an argument named 'final'");
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
if (!body
|| body->hasFormals()
|| !argHasName(body->arg, "prev"))
throw Error("overlay does not take an argument named 'prev'");
|| v.lambda.fun->args.size() != 2
|| v.lambda.fun->args[0].formals
|| !argHasName(v.lambda.fun->args[0].arg, "final")
|| v.lambda.fun->args[1].formals
|| !argHasName(v.lambda.fun->args[1].arg, "prev"))
throw Error("overlay is not a binary function with arguments 'final' and 'prev'");
// FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay.
} catch (Error & e) {
@@ -375,7 +373,9 @@ struct CmdFlakeCheck : FlakeCommand
try {
state->forceValue(v, pos);
if (v.isLambda()) {
if (!v.lambda.fun->hasFormals() || !v.lambda.fun->formals->ellipsis)
if (v.lambda.fun->args.size() != 1
|| !v.lambda.fun->args[0].formals
|| !v.lambda.fun->args[0].formals->ellipsis)
throw Error("module must match an open attribute set ('{ config, ... }')");
} else if (v.type() == nAttrs) {
for (auto & attr : *v.attrs)
@@ -473,12 +473,12 @@ struct CmdFlakeCheck : FlakeCommand
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v, pos);
if (!v.isLambda())
throw Error("bundler must be a function");
if (!v.lambda.fun->formals ||
!v.lambda.fun->formals->argNames.count(state->symbols.create("program")) ||
!v.lambda.fun->formals->argNames.count(state->symbols.create("system")))
throw Error("bundler must take formal arguments 'program' and 'system'");
if (!v.isLambda()
|| v.lambda.fun->args.size() != 1
|| !v.lambda.fun->args[0].formals
|| !v.lambda.fun->args[0].formals->argNames.count(state->symbols.create("program"))
|| !v.lambda.fun->args[0].formals->argNames.count(state->symbols.create("system")))
throw Error("bundler must be a function that takes take arguments 'program' and 'system'");
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
reportError(e);
@@ -638,6 +638,142 @@ struct CmdFlakeCheck : FlakeCommand
}
};
struct CmdFlakeInitCommon : virtual Args, EvalCommand
{
std::string templateUrl = "templates";
Path destDir;
const Strings attrsPathPrefixes{"templates."};
const LockFlags lockFlags{ .writeLockFile = false };
CmdFlakeInitCommon()
{
addFlag({
.longName = "template",
.shortName = 't',
.description = "The template to use.",
.labels = {"template"},
.handler = {&templateUrl},
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRefWithFragment(
getEvalState(),
lockFlags,
attrsPathPrefixes,
{"defaultTemplate"},
prefix);
}}
});
}
void run(nix::ref<nix::Store> store) override
{
auto flakeDir = absPath(destDir);
auto evalState = getEvalState();
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
auto installable = InstallableFlake(nullptr,
evalState, std::move(templateFlakeRef),
Strings{templateName == "" ? "defaultTemplate" : templateName},
Strings(attrsPathPrefixes), lockFlags);
auto [cursor, attrPath] = installable.getCursor(*evalState);
auto templateDir = cursor->getAttr("path")->getString();
assert(store->isInStore(templateDir));
std::vector<Path> files;
std::function<void(const Path & from, const Path & to)> copyDir;
copyDir = [&](const Path & from, const Path & to)
{
createDirs(to);
for (auto & entry : readDirectory(from)) {
auto from2 = from + "/" + entry.name;
auto to2 = to + "/" + entry.name;
auto st = lstat(from2);
if (S_ISDIR(st.st_mode))
copyDir(from2, to2);
else if (S_ISREG(st.st_mode)) {
auto contents = readFile(from2);
if (pathExists(to2)) {
auto contents2 = readFile(to2);
if (contents != contents2)
throw Error("refusing to overwrite existing file '%s'", to2);
} else
writeFile(to2, contents);
}
else if (S_ISLNK(st.st_mode)) {
auto target = readLink(from2);
if (pathExists(to2)) {
if (readLink(to2) != target)
throw Error("refusing to overwrite existing symlink '%s'", to2);
} else
createSymlink(target, to2);
}
else
throw Error("file '%s' has unsupported type", from2);
files.push_back(to2);
}
};
copyDir(templateDir, flakeDir);
if (pathExists(flakeDir + "/.git")) {
Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" };
for (auto & s : files) args.push_back(s);
runProgram("git", true, args);
}
}
};
struct CmdFlakeInit : CmdFlakeInitCommon
{
std::string description() override
{
return "create a flake in the current directory from a template";
}
std::string doc() override
{
return
#include "flake-init.md"
;
}
CmdFlakeInit()
{
destDir = ".";
}
};
struct CmdFlakeNew : CmdFlakeInitCommon
{
std::string description() override
{
return "create a flake in the specified directory from a template";
}
std::string doc() override
{
return
#include "flake-new.md"
;
}
CmdFlakeNew()
{
expectArgs({
.label = "dest-dir",
.handler = {&destDir},
.completer = completePath
});
}
};
struct CmdFlakeClone : FlakeCommand
{
Path destDir;
@@ -916,8 +1052,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 1 && attrPath[0] == "overlay")
|| (attrPath.size() == 2 && attrPath[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
(attrPath.size() == 1 && attrPath[0] == "nixosModule")
|| (attrPath.size() == 2 && attrPath[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
attrPath.size() == 2 && attrPath[0] == "nixosModules" ? std::make_pair("nixos-module", "NixOS module") :
std::make_pair("unknown", "unknown");
if (json) {
j.emplace("type", type);
@@ -989,6 +1124,8 @@ struct CmdFlake : NixMultiCommand
{"metadata", []() { return make_ref<CmdFlakeMetadata>(); }},
{"info", []() { return make_ref<CmdFlakeInfo>(); }},
{"check", []() { return make_ref<CmdFlakeCheck>(); }},
{"init", []() { return make_ref<CmdFlakeInit>(); }},
{"new", []() { return make_ref<CmdFlakeNew>(); }},
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
{"show", []() { return make_ref<CmdFlakeShow>(); }},

View File

@@ -106,28 +106,26 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
});
}
std::map<std::vector<std::string>, std::vector<std::string>> aliases = {
{{"add-to-store"}, {"store", "add-path"}},
{{"cat-nar"}, {"nar", "cat"}},
{{"cat-store"}, {"store", "cat"}},
{{"copy-sigs"}, {"store", "copy-sigs"}},
{{"dev-shell"}, {"develop"}},
{{"diff-closures"}, {"store", "diff-closures"}},
{{"dump-path"}, {"store", "dump-path"}},
{{"hash-file"}, {"hash", "file"}},
{{"hash-path"}, {"hash", "path"}},
{{"ls-nar"}, {"nar", "ls"}},
{{"ls-store"}, {"store", "ls"}},
{{"make-content-addressable"}, {"store", "make-content-addressable"}},
{{"optimise-store"}, {"store", "optimise"}},
{{"ping-store"}, {"store", "ping"}},
{{"sign-paths"}, {"store", "sign"}},
{{"to-base16"}, {"hash", "to-base16"}},
{{"to-base32"}, {"hash", "to-base32"}},
{{"to-base64"}, {"hash", "to-base64"}},
{{"verify"}, {"store", "verify"}},
{{"flake", "init"}, {"init"}},
{{"flake", "new"}, {"new"}},
std::map<std::string, std::vector<std::string>> aliases = {
{"add-to-store", {"store", "add-path"}},
{"cat-nar", {"nar", "cat"}},
{"cat-store", {"store", "cat"}},
{"copy-sigs", {"store", "copy-sigs"}},
{"dev-shell", {"develop"}},
{"diff-closures", {"store", "diff-closures"}},
{"dump-path", {"store", "dump-path"}},
{"hash-file", {"hash", "file"}},
{"hash-path", {"hash", "path"}},
{"ls-nar", {"nar", "ls"}},
{"ls-store", {"store", "ls"}},
{"make-content-addressable", {"store", "make-content-addressable"}},
{"optimise-store", {"store", "optimise"}},
{"ping-store", {"store", "ping"}},
{"sign-paths", {"store", "sign"}},
{"to-base16", {"hash", "to-base16"}},
{"to-base32", {"hash", "to-base32"}},
{"to-base64", {"hash", "to-base64"}},
{"verify", {"store", "verify"}},
};
bool aliasUsed = false;
@@ -136,33 +134,14 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{
if (aliasUsed || command || pos == args.end()) return pos;
auto arg = *pos;
// Loop through the aliases to see whether the current cli corresponds
// to one of them.
for (auto & [from, to] : aliases) {
auto i = pos;
bool isCurrentAlias = true;
// Is the current alias a prefix of the args?
for (auto & fromItem : from) {
if (i == args.end() || *i != fromItem) {
// The current alias doesnt match the args
isCurrentAlias = false;
break;
}
i++;
}
// If we went through to the end of the previous loop, then we match
// the currently considered alias.
// So rewrite the alias in the current args.
if (isCurrentAlias) {
warn("'%s' is a deprecated alias for '%s'",
concatStringsSep(" ", from), concatStringsSep(" ", to));
pos = args.erase(pos, i);
pos = args.insert(pos, to.begin(), to.end());
aliasUsed = true;
return pos;
}
}
auto i = aliases.find(arg);
if (i == aliases.end()) return pos;
warn("'%s' is a deprecated alias for '%s'",
arg, concatStringsSep(" ", i->second));
pos = args.erase(pos);
for (auto j = i->second.rbegin(); j != i->second.rend(); ++j)
pos = args.insert(pos, *j);
aliasUsed = true;
return pos;
}
@@ -276,16 +255,6 @@ void mainWrapped(int argc, char * * argv)
initNix();
initGC();
#if __linux__
if (getuid() == 0) {
try {
saveMountNamespace();
if (unshare(CLONE_NEWNS) == -1)
throw SysError("setting up a private mount namespace");
} catch (Error & e) { }
}
#endif
programPath = argv[0];
auto programName = std::string(baseNameOf(programPath));

View File

@@ -1,150 +0,0 @@
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "fs-accessor.hh"
#include "eval-cache.hh"
using namespace nix;
using namespace nix::flake;
struct CmdFlakeInitCommon : virtual Args, EvalCommand
{
std::string templateUrl = "templates";
Path destDir;
const Strings attrsPathPrefixes{"templates."};
const LockFlags lockFlags{ .writeLockFile = false };
CmdFlakeInitCommon()
{
addFlag({
.longName = "template",
.shortName = 't',
.description = "The template to use.",
.labels = {"template"},
.handler = {&templateUrl},
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRefWithFragment(
getEvalState(),
lockFlags,
attrsPathPrefixes,
{"defaultTemplate"},
prefix);
}}
});
}
void run(nix::ref<nix::Store> store) override
{
auto flakeDir = absPath(destDir);
auto evalState = getEvalState();
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
auto installable = InstallableFlake(nullptr,
evalState, std::move(templateFlakeRef),
Strings{templateName == "" ? "defaultTemplate" : templateName},
Strings(attrsPathPrefixes), lockFlags);
auto [cursor, attrPath] = installable.getCursor(*evalState);
auto templateDir = cursor->getAttr("path")->getString();
assert(store->isInStore(templateDir));
std::vector<Path> files;
std::function<void(const Path & from, const Path & to)> copyDir;
copyDir = [&](const Path & from, const Path & to)
{
createDirs(to);
for (auto & entry : readDirectory(from)) {
auto from2 = from + "/" + entry.name;
auto to2 = to + "/" + entry.name;
auto st = lstat(from2);
if (S_ISDIR(st.st_mode))
copyDir(from2, to2);
else if (S_ISREG(st.st_mode)) {
auto contents = readFile(from2);
if (pathExists(to2)) {
auto contents2 = readFile(to2);
if (contents != contents2)
throw Error("refusing to overwrite existing file '%s'", to2);
} else
writeFile(to2, contents);
}
else if (S_ISLNK(st.st_mode)) {
auto target = readLink(from2);
if (pathExists(to2)) {
if (readLink(to2) != target)
throw Error("refusing to overwrite existing symlink '%s'", to2);
} else
createSymlink(target, to2);
}
else
throw Error("file '%s' has unsupported type", from2);
files.push_back(to2);
}
};
copyDir(templateDir, flakeDir);
if (pathExists(flakeDir + "/.git")) {
Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" };
for (auto & s : files) args.push_back(s);
runProgram("git", true, args);
}
}
};
struct CmdFlakeInit : CmdFlakeInitCommon
{
std::string description() override
{
return "create a flake in the current directory from a template";
}
std::string doc() override
{
return
#include "init.md"
;
}
CmdFlakeInit()
{
destDir = ".";
}
};
struct CmdFlakeNew : CmdFlakeInitCommon
{
std::string description() override
{
return "create a flake in the specified directory from a template";
}
std::string doc() override
{
return
#include "new.md"
;
}
CmdFlakeNew()
{
expectArgs({
.label = "dest-dir",
.handler = {&destDir},
.completer = completePath
});
}
};
static auto r0 = registerCommand<CmdFlakeInit>("init");
static auto r1 = registerCommand<CmdFlakeNew>("new");

View File

@@ -226,7 +226,6 @@ struct CmdRegistry : virtual NixMultiCommand
void run() override
{
settings.requireExperimentalFeature(Xp::Flakes);
if (!command)
throw UsageError("'nix registry' requires a sub-command.");
command->second->prepare();

View File

@@ -471,10 +471,7 @@ bool NixRepl::processLine(string line)
auto args = editorFor(pos);
auto editor = args.front();
args.pop_front();
// runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
runProgram(editor, true, args);
// Reload right after exiting the editor
state->resetFileCache();
@@ -507,8 +504,8 @@ bool NixRepl::processLine(string line)
state->store->buildPaths({DerivedPath::Built{drvPath}});
auto drv = state->store->readDerivation(drvPath);
logger->cout("\nThis derivation produced the following outputs:");
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath))
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
for (auto & i : drv.outputsAndOptPaths(*state->store))
logger->cout(" %s -> %s", i.first, state->store->printStorePath(*i.second.second));
} else if (command == ":i") {
runNix("nix-env", {"-i", drvPathRaw});
} else {
@@ -771,12 +768,12 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "[ ";
if (maxDepth > 0)
for (auto elem : v.listItems()) {
if (seen.count(elem))
for (unsigned int n = 0; n < v.listSize(); ++n) {
if (seen.find(v.listElems()[n]) != seen.end())
str << "«repeated»";
else
try {
printValue(str, *elem, maxDepth - 1, seen);
printValue(str, *v.listElems()[n], maxDepth - 1, seen);
} catch (AssertionError & e) {
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
}

View File

@@ -218,7 +218,8 @@ struct CmdKey : NixMultiCommand
void run() override
{
if (!command)
throw UsageError("'nix key' requires a sub-command.");
throw UsageError("'nix flake' requires a sub-command.");
settings.requireExperimentalFeature(Xp::Flakes);
command->second->prepare();
command->second->run();
}

View File

@@ -1,5 +0,0 @@
source common.sh
export NIX_TESTS_CA_BY_DEFAULT=1
cd .. && source repl.sh

View File

@@ -36,9 +36,8 @@ export PATH=@bindir@:$PATH
if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then
export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH
fi
DAEMON_PATH="$PATH"
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH"
export NIX_DAEMON_COMMAND="$NIX_DAEMON_PACKAGE/bin/nix-daemon"
fi
coreutils=@coreutils@
@@ -90,7 +89,7 @@ startDaemon() {
# Start the daemon, wait for the socket to appear. !!!
# nix-daemon should have an option to fork into the background.
rm -f $NIX_DAEMON_SOCKET_PATH
PATH=$DAEMON_PATH nix daemon &
${NIX_DAEMON_COMMAND:-nix daemon} &
for ((i = 0; i < 30; i++)); do
if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi
sleep 1

View File

@@ -1,34 +0,0 @@
source common.sh
clearStore
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
cd $TEST_HOME
rm -f post-hook-ran
cat <<EOF > echoing-post-hook.sh
#!/bin/sh
echo "ThePostHookRan" > $PWD/post-hook-ran
EOF
chmod +x echoing-post-hook.sh
cat <<EOF > flake.nix
{
nixConfig.post-build-hook = "$PWD/echoing-post-hook.sh";
outputs = a: {
defaultPackage.$system = import ./simple.nix;
};
}
EOF
# Without --accept-flake-config, the post hook should not run.
nix build < /dev/null
(! [[ -f post-hook-ran ]])
clearStore
nix build --accept-flake-config
test -f post-hook-ran || fail "The post hook should have ran"

View File

@@ -707,10 +707,10 @@ cat > $flakeFollowsA/flake.nix <<EOF
B = {
url = "path:./flakeB";
inputs.foobar.follows = "D";
inputs.nonFlake.follows = "D";
};
D.url = "path:./flakeD";
foobar.url = "path:./flakeE";
};
outputs = { ... }: {};
}
@@ -720,8 +720,7 @@ cat > $flakeFollowsB/flake.nix <<EOF
{
description = "Flake B";
inputs = {
foobar.url = "path:$flakeFollowsA/flakeE";
nonFlake.url = "path:$nonFlakeDir";
foobar.url = "path:./../flakeE";
C = {
url = "path:./flakeC";
inputs.foobar.follows = "foobar";
@@ -735,7 +734,7 @@ cat > $flakeFollowsC/flake.nix <<EOF
{
description = "Flake C";
inputs = {
foobar.url = "path:$flakeFollowsA/flakeE";
foobar.url = "path:./../../flakeE";
};
outputs = { ... }: {};
}
@@ -766,27 +765,6 @@ nix flake lock $flakeFollowsA
[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '["D"]' ]]
[[ $(jq -c .nodes.C.inputs.foobar $flakeFollowsA/flake.lock) = '["B","foobar"]' ]]
# Ensure removing follows from flake.nix removes them from the lockfile
cat > $flakeFollowsA/flake.nix <<EOF
{
description = "Flake A";
inputs = {
B = {
url = "path:./flakeB";
inputs.nonFlake.follows = "D";
};
D.url = "path:./flakeD";
};
outputs = { ... }: {};
}
EOF
nix flake lock $flakeFollowsA
[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]]
jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$"
# Ensure a relative path is not allowed to go outside the store path
cat > $flakeFollowsA/flake.nix <<EOF
{

View File

@@ -1 +1 @@
[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] [ [ ] [ ] [ 1 ] [ 1 4 ] [ 1 5 ] [ 1 6 ] [ 2 ] [ 2 3 ] [ 3 ] [ 3 ] ] ]
[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] ]

View File

@@ -4,17 +4,5 @@ with builtins;
(sort (x: y: y < x) [ 483 249 526 147 42 77 ])
(sort lessThan [ "foo" "bar" "xyzzy" "fnord" ])
(sort (x: y: x.key < y.key)
[ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ])
(sort lessThan [
[ 1 6 ]
[ ]
[ 2 3 ]
[ 3 ]
[ 1 5 ]
[ 2 ]
[ 1 ]
[ ]
[ 1 4 ]
[ 3 ]
])
[ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ])
]

View File

@@ -46,10 +46,9 @@ nix_tests = \
recursive.sh \
describe-stores.sh \
flakes.sh \
flake-local-settings.sh \
build.sh \
compute-levels.sh \
repl.sh ca/repl.sh \
repl.sh \
ca/build.sh \
ca/build-with-garbage-path.sh \
ca/duplicate-realisation-in-closure.sh \

View File

@@ -6,10 +6,7 @@ nix eval --expr 'assert 1 + 2 == 3; true'
[[ $(nix eval --impure --expr 'builtins.readFile ./pure-eval.sh') =~ clearStore ]]
missingImpureErrorMsg=$(! nix eval --expr 'builtins.readFile ./pure-eval.sh' 2>&1)
echo "$missingImpureErrorMsg" | grep -q -- --impure || \
fail "The error message should mention the “--impure” flag to unblock users"
(! nix eval --expr 'builtins.readFile ./pure-eval.sh')
(! nix eval --expr builtins.currentTime)
(! nix eval --expr builtins.currentSystem)

View File

@@ -7,9 +7,7 @@ simple = import ./simple.nix
testRepl () {
local nixArgs=("$@")
local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")"
echo "$replOutput"
local outPath=$(echo "$replOutput" |&
local outPath=$(nix repl "${nixArgs[@]}" <<< "$replCmds" |&
grep -o -E "$NIX_STORE_DIR/\w*-simple")
nix path-info "${nixArgs[@]}" "$outPath"
}