mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 22:16:16 +02:00
Merge remote-tracking branch 'nixos/master'
This commit is contained in:
commit
cae2c834f2
103 changed files with 2135 additions and 741 deletions
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -10,24 +10,6 @@
|
||||||
|
|
||||||
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
||||||
|
|
||||||
# Checklist for maintainers
|
|
||||||
|
|
||||||
<!-- Contributors: please leave this as is -->
|
|
||||||
|
|
||||||
Maintainers: tick if completed or explain if not relevant
|
|
||||||
|
|
||||||
- [ ] agreed on idea
|
|
||||||
- [ ] agreed on implementation strategy
|
|
||||||
- [ ] tests, as appropriate
|
|
||||||
- functional tests - `tests/**.sh`
|
|
||||||
- unit tests - `src/*/tests`
|
|
||||||
- integration tests - `tests/nixos/*`
|
|
||||||
- [ ] documentation in the manual
|
|
||||||
- [ ] documentation in the internal API docs
|
|
||||||
- [ ] code and comments are self-explanatory
|
|
||||||
- [ ] commit message explains why the change was made
|
|
||||||
- [ ] new feature or incompatible change: updated release notes
|
|
||||||
|
|
||||||
# Priorities
|
# Priorities
|
||||||
|
|
||||||
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).
|
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||||
|
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
# required to find all branches
|
# required to find all branches
|
||||||
|
|
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
|
@ -17,10 +17,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v23
|
||||||
with:
|
with:
|
||||||
# The sandbox would otherwise be disabled by default on Darwin
|
# The sandbox would otherwise be disabled by default on Darwin
|
||||||
extra_nix_config: "sandbox = true"
|
extra_nix_config: "sandbox = true"
|
||||||
|
@ -58,11 +58,11 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v23
|
||||||
with:
|
with:
|
||||||
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
||||||
- uses: cachix/cachix-action@v12
|
- uses: cachix/cachix-action@v12
|
||||||
|
@ -82,9 +82,9 @@ jobs:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v23
|
||||||
with:
|
with:
|
||||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||||
|
@ -108,10 +108,10 @@ jobs:
|
||||||
needs.check_secrets.outputs.docker == 'true'
|
needs.check_secrets.outputs.docker == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v23
|
||||||
with:
|
with:
|
||||||
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
|
@ -127,7 +127,7 @@ jobs:
|
||||||
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
|
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
|
||||||
- run: docker tag nix:$NIX_VERSION nixos/nix:master
|
- run: docker tag nix:$NIX_VERSION nixos/nix:master
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
2
.github/workflows/hydra_status.yml
vendored
2
.github/workflows/hydra_status.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
||||||
if: github.repository_owner == 'NixOS'
|
if: github.repository_owner == 'NixOS'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: bash scripts/check-hydra-status.sh
|
- run: bash scripts/check-hydra-status.sh
|
||||||
|
|
2
.version
2
.version
|
@ -1 +1 @@
|
||||||
2.18.0
|
2.19.0
|
||||||
|
|
|
@ -30,8 +30,8 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
|
||||||
|
|
||||||
2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue.
|
2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue.
|
||||||
|
|
||||||
Issues labeled ["good first issue"](https://github.com/NixOS/nix/labels/good-first-issue) should be relatively easy to fix and are likely to get merged quickly.
|
Issues labeled [good first issue](https://github.com/NixOS/nix/labels/good-first-issue) should be relatively easy to fix and are likely to get merged quickly.
|
||||||
Pull requests addressing issues labeled ["idea approved"](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review.
|
Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review.
|
||||||
|
|
||||||
3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests.
|
3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests.
|
||||||
|
|
||||||
|
@ -40,14 +40,27 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
|
||||||
4. Make your changes!
|
4. Make your changes!
|
||||||
|
|
||||||
5. [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) for your changes.
|
5. [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) for your changes.
|
||||||
* [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes.
|
|
||||||
* Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request).
|
|
||||||
* Link related issues in your pull request to inform interested parties and future contributors about your change.
|
* Link related issues in your pull request to inform interested parties and future contributors about your change.
|
||||||
|
* Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request).
|
||||||
If your pull request closes one or multiple issues, note that in the description using `Closes: #<number>`, as it will then happen automatically when your change is merged.
|
If your pull request closes one or multiple issues, note that in the description using `Closes: #<number>`, as it will then happen automatically when your change is merged.
|
||||||
|
* [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes.
|
||||||
|
|
||||||
6. Do not expect your pull request to be reviewed immediately.
|
6. Do not expect your pull request to be reviewed immediately.
|
||||||
Nix maintainers follow a [structured process for reviews and design decisions](https://github.com/NixOS/nix/tree/master/maintainers#project-board-protocol), which may or may not prioritise your work.
|
Nix maintainers follow a [structured process for reviews and design decisions](https://github.com/NixOS/nix/tree/master/maintainers#project-board-protocol), which may or may not prioritise your work.
|
||||||
|
|
||||||
|
Following this checklist will make the process smoother for everyone:
|
||||||
|
|
||||||
|
- [ ] Fixes an [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) issue
|
||||||
|
- [ ] Tests, as appropriate:
|
||||||
|
- Functional tests – [`tests/**.sh`](./tests)
|
||||||
|
- Unit tests – [`src/*/tests`](./src/)
|
||||||
|
- Integration tests – [`tests/nixos/*`](./tests/nixos)
|
||||||
|
- [ ] User documentation in the [manual](..doc/manual/src)
|
||||||
|
- [ ] API documentation in header files
|
||||||
|
- [ ] Code and comments are self-explanatory
|
||||||
|
- [ ] Commit message explains **why** the change was made
|
||||||
|
- [ ] New feature or incompatible change: updated [release notes](./doc/manual/src/release-notes/rl-next.md)
|
||||||
|
|
||||||
7. If you need additional feedback or help to getting pull request into shape, ask other contributors using [@mentions](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams).
|
7. If you need additional feedback or help to getting pull request into shape, ask other contributors using [@mentions](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams).
|
||||||
|
|
||||||
## Making changes to the Nix manual
|
## Making changes to the Nix manual
|
||||||
|
|
|
@ -70,6 +70,7 @@ let
|
||||||
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# TODO: move this confusing special case out of here when implementing #8496
|
||||||
maybeDocumentation = optionalString
|
maybeDocumentation = optionalString
|
||||||
(details ? doc)
|
(details ? doc)
|
||||||
(replaceStrings ["@stores@"] [storeDocs] details.doc);
|
(replaceStrings ["@stores@"] [storeDocs] details.doc);
|
||||||
|
@ -78,6 +79,10 @@ let
|
||||||
# Options
|
# Options
|
||||||
|
|
||||||
${showOptions details.flags toplevel.flags}
|
${showOptions details.flags toplevel.flags}
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
showOptions = options: commonOptions:
|
showOptions = options: commonOptions:
|
||||||
|
@ -98,7 +103,7 @@ let
|
||||||
(option ? labels)
|
(option ? labels)
|
||||||
(concatStringsSep " " (map (s: "*${s}*") option.labels));
|
(concatStringsSep " " (map (s: "*${s}*") option.labels));
|
||||||
in trim ''
|
in trim ''
|
||||||
- `--${name}` ${shortName} ${labels}
|
- <span id="opt-${name}">[`--${name}`](#opt-${name})</span> ${shortName} ${labels}
|
||||||
|
|
||||||
${option.description}
|
${option.description}
|
||||||
'';
|
'';
|
||||||
|
@ -147,7 +152,7 @@ let
|
||||||
To use this store, you need to make sure the corresponding experimental feature,
|
To use this store, you need to make sure the corresponding experimental feature,
|
||||||
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
|
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
|
||||||
is enabled.
|
is enabled.
|
||||||
For example, include the following in [`nix.conf`](#):
|
For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md):
|
||||||
|
|
||||||
```
|
```
|
||||||
extra-experimental-features = ${experimentalFeature}
|
extra-experimental-features = ${experimentalFeature}
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
- [File System Object](architecture/file-system-object.md)
|
- [File System Object](architecture/file-system-object.md)
|
||||||
- [Protocols](protocols/protocols.md)
|
- [Protocols](protocols/protocols.md)
|
||||||
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
||||||
|
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
|
||||||
- [Glossary](glossary.md)
|
- [Glossary](glossary.md)
|
||||||
- [Contributing](contributing/contributing.md)
|
- [Contributing](contributing/contributing.md)
|
||||||
- [Hacking](contributing/hacking.md)
|
- [Hacking](contributing/hacking.md)
|
||||||
|
@ -109,6 +110,7 @@
|
||||||
- [C++ style guide](contributing/cxx.md)
|
- [C++ style guide](contributing/cxx.md)
|
||||||
- [Release Notes](release-notes/release-notes.md)
|
- [Release Notes](release-notes/release-notes.md)
|
||||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||||
|
- [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md)
|
||||||
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
|
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
|
||||||
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
|
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
|
||||||
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
|
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
|
||||||
|
|
|
@ -19,18 +19,21 @@ current generation of the active profile, to which a set of store paths
|
||||||
described by *args* is added. The arguments *args* map to store paths in
|
described by *args* is added. The arguments *args* map to store paths in
|
||||||
a number of possible ways:
|
a number of possible ways:
|
||||||
|
|
||||||
- By default, *args* is a set of derivation names denoting derivations
|
|
||||||
|
- By default, *args* is a set of [derivation] names denoting derivations
|
||||||
in the active Nix expression. These are realised, and the resulting
|
in the active Nix expression. These are realised, and the resulting
|
||||||
output paths are installed. Currently installed derivations with a
|
output paths are installed. Currently installed derivations with a
|
||||||
name equal to the name of a derivation being added are removed
|
name equal to the name of a derivation being added are removed
|
||||||
unless the option `--preserve-installed` is specified.
|
unless the option `--preserve-installed` is specified.
|
||||||
|
|
||||||
|
[derivation]: @docroot@/language/derivations.md
|
||||||
|
|
||||||
If there are multiple derivations matching a name in *args* that
|
If there are multiple derivations matching a name in *args* that
|
||||||
have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the
|
have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the
|
||||||
derivation with the highest *priority* is used. A derivation can
|
derivation with the highest *priority* is used. A derivation can
|
||||||
define a priority by declaring the `meta.priority` attribute. This
|
define a priority by declaring the `meta.priority` attribute. This
|
||||||
attribute should be a number, with a higher value denoting a lower
|
attribute should be a number, with a higher value denoting a lower
|
||||||
priority. The default priority is `0`.
|
priority. The default priority is `5`.
|
||||||
|
|
||||||
If there are multiple matching derivations with the same priority,
|
If there are multiple matching derivations with the same priority,
|
||||||
then the derivation with the highest version will be installed.
|
then the derivation with the highest version will be installed.
|
||||||
|
@ -66,8 +69,59 @@ a number of possible ways:
|
||||||
- If *args* are store paths that are not store derivations, then these
|
- If *args* are store paths that are not store derivations, then these
|
||||||
are [realised](@docroot@/command-ref/nix-store/realise.md) and installed.
|
are [realised](@docroot@/command-ref/nix-store/realise.md) and installed.
|
||||||
|
|
||||||
- By default all outputs are installed for each derivation. That can
|
- By default all outputs are installed for each derivation.
|
||||||
be reduced by setting `meta.outputsToInstall`.
|
This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names.
|
||||||
|
|
||||||
|
<!-- TODO: add anchor link to `outputs` when #7320 is merged -->
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
The file `example.nix` defines a [derivation] with two outputs `foo` and `bar`, each containing a file.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# example.nix
|
||||||
|
let
|
||||||
|
pkgs = import <nixpkgs> {};
|
||||||
|
command = ''
|
||||||
|
${pkgs.coreutils}/bin/mkdir -p $foo $bar
|
||||||
|
echo foo > $foo/foo-file
|
||||||
|
echo bar > $bar/bar-file
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
derivation {
|
||||||
|
name = "example";
|
||||||
|
builder = "${pkgs.bash}/bin/bash";
|
||||||
|
args = [ "-c" command ];
|
||||||
|
outputs = [ "foo" "bar" ];
|
||||||
|
system = builtins.currentSystem;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Installing from this Nix expression will make files from both outputs appear in the current profile.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-env --install --file example.nix
|
||||||
|
installing 'example'
|
||||||
|
$ ls ~/.nix-profile
|
||||||
|
foo-file
|
||||||
|
bar-file
|
||||||
|
manifest.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# example-outputs.nix
|
||||||
|
import ./example.nix // { meta.outputsToInstall = [ "bar" ]; }
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-env --install --file example-outputs.nix
|
||||||
|
installing 'example'
|
||||||
|
$ ls ~/.nix-profile
|
||||||
|
bar-file
|
||||||
|
manifest.nix
|
||||||
|
```
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
|
|
||||||
|
|
|
@ -31,15 +31,18 @@ store already contains a file with the same hash and base name.
|
||||||
Otherwise, the file is downloaded, and an error is signaled if the
|
Otherwise, the file is downloaded, and an error is signaled if the
|
||||||
actual hash of the file does not match the specified hash.
|
actual hash of the file does not match the specified hash.
|
||||||
|
|
||||||
This command prints the hash on standard output. Additionally, if the
|
This command prints the hash on standard output.
|
||||||
option `--print-path` is used, the path of the downloaded file in the
|
The hash is printed using base-32 unless `--type md5` is specified,
|
||||||
Nix store is also printed.
|
in which case it's printed using base-16.
|
||||||
|
Additionally, if the option `--print-path` is used,
|
||||||
|
the path of the downloaded file in the Nix store is also printed.
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
|
|
||||||
- `--type` *hashAlgo*\
|
- `--type` *hashAlgo*\
|
||||||
Use the specified cryptographic hash algorithm, which can be one of
|
Use the specified cryptographic hash algorithm,
|
||||||
`md5`, `sha1`, `sha256`, and `sha512`.
|
which can be one of `md5`, `sha1`, `sha256`, and `sha512`.
|
||||||
|
The default is `sha256`.
|
||||||
|
|
||||||
- `--print-path`\
|
- `--print-path`\
|
||||||
Print the store path of the downloaded file on standard output.
|
Print the store path of the downloaded file on standard output.
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
# Synopsis
|
# Synopsis
|
||||||
|
|
||||||
`nix-store` {`--query` | `-q`}
|
`nix-store` {`--query` | `-q`}
|
||||||
{`--outputs` | `--requisites` | `-R` | `--references` |
|
{`--outputs` | `--requisites` | `-R` | `--references` | `--referrers` |
|
||||||
`--referrers` | `--referrers-closure` | `--deriver` | `-d` |
|
`--referrers-closure` | `--deriver` | `-d` | `--valid-derivers` |
|
||||||
`--graph` | `--tree` | `--binding` *name* | `-b` *name* | `--hash` |
|
`--graph` | `--tree` | `--binding` *name* | `-b` *name* | `--hash` |
|
||||||
`--size` | `--roots`}
|
`--size` | `--roots`}
|
||||||
[`--use-output`] [`-u`] [`--force-realise`] [`-f`]
|
[`--use-output`] [`-u`] [`--force-realise`] [`-f`]
|
||||||
|
@ -82,13 +82,21 @@ symlink.
|
||||||
in the Nix store that are dependent on *paths*.
|
in the Nix store that are dependent on *paths*.
|
||||||
|
|
||||||
- `--deriver`; `-d`\
|
- `--deriver`; `-d`\
|
||||||
Prints the [deriver] of the store paths *paths*. If
|
Prints the [deriver] that was used to build the store paths *paths*. If
|
||||||
the path has no deriver (e.g., if it is a source file), or if the
|
the path has no deriver (e.g., if it is a source file), or if the
|
||||||
deriver is not known (e.g., in the case of a binary-only
|
deriver is not known (e.g., in the case of a binary-only
|
||||||
deployment), the string `unknown-deriver` is printed.
|
deployment), the string `unknown-deriver` is printed.
|
||||||
|
The returned deriver is not guaranteed to exist in the local store, for
|
||||||
|
example when *paths* were substituted from a binary cache.
|
||||||
|
Use `--valid-derivers` instead to obtain valid paths only.
|
||||||
|
|
||||||
[deriver]: ../../glossary.md#gloss-deriver
|
[deriver]: ../../glossary.md#gloss-deriver
|
||||||
|
|
||||||
|
- `--valid-derivers`\
|
||||||
|
Prints a set of derivation files (`.drv`) which are supposed produce
|
||||||
|
said paths when realized. Might print nothing, for example for source paths
|
||||||
|
or paths subsituted from a binary cache.
|
||||||
|
|
||||||
- `--graph`\
|
- `--graph`\
|
||||||
Prints the references graph of the store paths *paths* in the format
|
Prints the references graph of the store paths *paths* in the format
|
||||||
of the `dot` tool of AT\&T's [Graphviz
|
of the `dot` tool of AT\&T's [Graphviz
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Name
|
# Name
|
||||||
|
|
||||||
`nix-store --realise` - realise specified store paths
|
`nix-store --realise` - build or fetch store objects
|
||||||
|
|
||||||
# Synopsis
|
# Synopsis
|
||||||
|
|
||||||
|
@ -8,33 +8,35 @@
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
The operation `--realise` essentially “builds” the specified store
|
|
||||||
paths. Realisation is a somewhat overloaded term:
|
|
||||||
|
|
||||||
- If the store path is a *derivation*, realisation ensures that the
|
Each of *paths* is processed as follows:
|
||||||
output paths of the derivation are [valid] (i.e.,
|
|
||||||
the output path and its closure exist in the file system). This
|
|
||||||
can be done in several ways. First, it is possible that the
|
|
||||||
outputs are already valid, in which case we are done
|
|
||||||
immediately. Otherwise, there may be [substitutes]
|
|
||||||
that produce the outputs (e.g., by downloading them). Finally, the
|
|
||||||
outputs can be produced by running the build task described
|
|
||||||
by the derivation.
|
|
||||||
|
|
||||||
- If the store path is not a derivation, realisation ensures that the
|
- If the path leads to a [store derivation]:
|
||||||
specified path is valid (i.e., it and its closure exist in the file
|
1. If it is not [valid], substitute the store derivation file itself.
|
||||||
system). If the path is already valid, we are done immediately.
|
2. Realise its [output paths]:
|
||||||
Otherwise, the path and any missing paths in its closure may be
|
- Try to fetch from [substituters] the [store objects] associated with the output paths in the store derivation's [closure].
|
||||||
produced through substitutes. If there are no (successful)
|
- With [content-addressed derivations] (experimental): Determine the output paths to realise by querying content-addressed realisation entries in the [Nix database].
|
||||||
substitutes, realisation fails.
|
- For any store paths that cannot be substituted, produce the required store objects. This involves first realising all outputs of the derivation's dependencies and then running the derivation's [`builder`](@docroot@/language/derivations.md#attr-builder) executable. <!-- TODO: Link to build process page #8888 -->
|
||||||
|
- Otherwise, and if the path is not already valid: Try to fetch the associated [store objects] in the path's [closure] from [substituters].
|
||||||
|
|
||||||
|
If no substitutes are available and no store derivation is given, realisation fails.
|
||||||
|
|
||||||
|
[store paths]: @docroot@/glossary.md#gloss-store-path
|
||||||
[valid]: @docroot@/glossary.md#gloss-validity
|
[valid]: @docroot@/glossary.md#gloss-validity
|
||||||
[substitutes]: @docroot@/glossary.md#gloss-substitute
|
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
|
||||||
|
[output paths]: @docroot@/glossary.md#gloss-output-path
|
||||||
|
[store objects]: @docroot@/glossary.md#gloss-store-object
|
||||||
|
[closure]: @docroot@/glossary.md#gloss-closure
|
||||||
|
[substituters]: @docroot@/command-ref/conf-file.md#conf-substituters
|
||||||
|
[content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations
|
||||||
|
[Nix database]: @docroot@/glossary.md#gloss-nix-database
|
||||||
|
|
||||||
The output path of each derivation is printed on standard output. (For
|
The resulting paths are printed on standard output.
|
||||||
non-derivations argument, the argument itself is printed.)
|
For non-derivation arguments, the argument itself is printed.
|
||||||
|
|
||||||
The following flags are available:
|
{{#include ../status-build-failure.md}}
|
||||||
|
|
||||||
|
# Options
|
||||||
|
|
||||||
- `--dry-run`\
|
- `--dry-run`\
|
||||||
Print on standard error a description of what packages would be
|
Print on standard error a description of what packages would be
|
||||||
|
@ -54,8 +56,6 @@ The following flags are available:
|
||||||
previous build, the new output path is left in
|
previous build, the new output path is left in
|
||||||
`/nix/store/name.check.`
|
`/nix/store/name.check.`
|
||||||
|
|
||||||
{{#include ../status-build-failure.md}}
|
|
||||||
|
|
||||||
{{#include ./opt-common.md}}
|
{{#include ./opt-common.md}}
|
||||||
|
|
||||||
{{#include ../opt-common.md}}
|
{{#include ../opt-common.md}}
|
||||||
|
@ -67,8 +67,6 @@ The following flags are available:
|
||||||
This operation is typically used to build [store derivation]s produced by
|
This operation is typically used to build [store derivation]s produced by
|
||||||
[`nix-instantiate`](@docroot@/command-ref/nix-instantiate.md):
|
[`nix-instantiate`](@docroot@/command-ref/nix-instantiate.md):
|
||||||
|
|
||||||
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
|
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-store --realise $(nix-instantiate ./test.nix)
|
$ nix-store --realise $(nix-instantiate ./test.nix)
|
||||||
/nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1
|
/nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
\--help
|
|
||||||
|
|
||||||
\--version
|
|
||||||
|
|
||||||
\--verbose
|
|
||||||
|
|
||||||
\-v
|
|
||||||
|
|
||||||
\--quiet
|
|
||||||
|
|
||||||
\--log-format
|
|
||||||
|
|
||||||
format
|
|
||||||
|
|
||||||
\--no-build-output
|
|
||||||
|
|
||||||
\-Q
|
|
||||||
|
|
||||||
\--max-jobs
|
|
||||||
|
|
||||||
\-j
|
|
||||||
|
|
||||||
number
|
|
||||||
|
|
||||||
\--cores
|
|
||||||
|
|
||||||
number
|
|
||||||
|
|
||||||
\--max-silent-time
|
|
||||||
|
|
||||||
number
|
|
||||||
|
|
||||||
\--timeout
|
|
||||||
|
|
||||||
number
|
|
||||||
|
|
||||||
\--keep-going
|
|
||||||
|
|
||||||
\-k
|
|
||||||
|
|
||||||
\--keep-failed
|
|
||||||
|
|
||||||
\-K
|
|
||||||
|
|
||||||
\--fallback
|
|
||||||
|
|
||||||
\--readonly-mode
|
|
||||||
|
|
||||||
\-I
|
|
||||||
|
|
||||||
path
|
|
||||||
|
|
||||||
\--option
|
|
||||||
|
|
||||||
name
|
|
||||||
|
|
||||||
value
|
|
|
@ -2,56 +2,66 @@
|
||||||
|
|
||||||
Most Nix commands accept the following command-line options:
|
Most Nix commands accept the following command-line options:
|
||||||
|
|
||||||
- <span id="opt-help">[`--help`](#opt-help)</span>\
|
- <span id="opt-help">[`--help`](#opt-help)</span>
|
||||||
|
|
||||||
Prints out a summary of the command syntax and exits.
|
Prints out a summary of the command syntax and exits.
|
||||||
|
|
||||||
- <span id="opt-version">[`--version`](#opt-version)</span>\
|
- <span id="opt-version">[`--version`](#opt-version)</span>
|
||||||
|
|
||||||
Prints out the Nix version number on standard output and exits.
|
Prints out the Nix version number on standard output and exits.
|
||||||
|
|
||||||
- <span id="opt-verbose">[`--verbose`](#opt-verbose)</span> / `-v`\
|
- <span id="opt-verbose">[`--verbose`](#opt-verbose)</span> / `-v`
|
||||||
Increases the level of verbosity of diagnostic messages printed on
|
|
||||||
standard error. For each Nix operation, the information printed on
|
|
||||||
standard output is well-defined; any diagnostic information is
|
|
||||||
printed on standard error, never on standard output.
|
|
||||||
|
|
||||||
This option may be specified repeatedly. Currently, the following
|
Increases the level of verbosity of diagnostic messages printed on standard error.
|
||||||
verbosity levels exist:
|
For each Nix operation, the information printed on standard output is well-defined;
|
||||||
|
any diagnostic information is printed on standard error, never on standard output.
|
||||||
|
|
||||||
- 0\
|
This option may be specified repeatedly.
|
||||||
“Errors only”: only print messages explaining why the Nix
|
Currently, the following verbosity levels exist:
|
||||||
invocation failed.
|
|
||||||
|
|
||||||
- 1\
|
- `0` “Errors only”
|
||||||
“Informational”: print *useful* messages about what Nix is
|
|
||||||
doing. This is the default.
|
|
||||||
|
|
||||||
- 2\
|
Only print messages explaining why the Nix invocation failed.
|
||||||
“Talkative”: print more informational messages.
|
|
||||||
|
|
||||||
- 3\
|
- `1` “Informational”
|
||||||
“Chatty”: print even more informational messages.
|
|
||||||
|
|
||||||
- 4\
|
Print *useful* messages about what Nix is doing.
|
||||||
“Debug”: print debug information.
|
This is the default.
|
||||||
|
|
||||||
- 5\
|
- `2` “Talkative”
|
||||||
“Vomit”: print vast amounts of debug information.
|
|
||||||
|
|
||||||
- <span id="opt-quiet">[`--quiet`](#opt-quiet)</span>\
|
Print more informational messages.
|
||||||
Decreases the level of verbosity of diagnostic messages printed on
|
|
||||||
standard error. This is the inverse option to `-v` / `--verbose`.
|
|
||||||
|
|
||||||
This option may be specified repeatedly. See the previous verbosity
|
- `3` “Chatty”
|
||||||
levels list.
|
|
||||||
|
|
||||||
- <span id="opt-log-format">[`--log-format`](#opt-log-format)</span> *format*\
|
Print even more informational messages.
|
||||||
This option can be used to change the output of the log format, with
|
|
||||||
*format* being one of:
|
- `4` “Debug”
|
||||||
|
|
||||||
|
Print debug information.
|
||||||
|
|
||||||
|
- `5` “Vomit”
|
||||||
|
|
||||||
|
Print vast amounts of debug information.
|
||||||
|
|
||||||
|
- <span id="opt-quiet">[`--quiet`](#opt-quiet)</span>
|
||||||
|
|
||||||
|
Decreases the level of verbosity of diagnostic messages printed on standard error.
|
||||||
|
This is the inverse option to `-v` / `--verbose`.
|
||||||
|
|
||||||
|
This option may be specified repeatedly.
|
||||||
|
See the previous verbosity levels list.
|
||||||
|
|
||||||
|
- <span id="opt-log-format">[`--log-format`](#opt-log-format)</span> *format*
|
||||||
|
|
||||||
|
This option can be used to change the output of the log format, with *format* being one of:
|
||||||
|
|
||||||
|
- `raw`
|
||||||
|
|
||||||
- raw\
|
|
||||||
This is the raw format, as outputted by nix-build.
|
This is the raw format, as outputted by nix-build.
|
||||||
|
|
||||||
- internal-json\
|
- `internal-json`
|
||||||
|
|
||||||
Outputs the logs in a structured manner.
|
Outputs the logs in a structured manner.
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
|
@ -60,100 +70,85 @@ Most Nix commands accept the following command-line options:
|
||||||
> the error-messages (namely of the `msg`-field) can change
|
> the error-messages (namely of the `msg`-field) can change
|
||||||
> between releases.
|
> between releases.
|
||||||
|
|
||||||
- bar\
|
- `bar`
|
||||||
|
|
||||||
Only display a progress bar during the builds.
|
Only display a progress bar during the builds.
|
||||||
|
|
||||||
- bar-with-logs\
|
- `bar-with-logs`
|
||||||
|
|
||||||
Display the raw logs, with the progress bar at the bottom.
|
Display the raw logs, with the progress bar at the bottom.
|
||||||
|
|
||||||
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`\
|
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
|
||||||
By default, output written by builders to standard output and
|
|
||||||
standard error is echoed to the Nix command's standard error. This
|
|
||||||
option suppresses this behaviour. Note that the builder's standard
|
|
||||||
output and error are always written to a log file in
|
|
||||||
`prefix/nix/var/log/nix`.
|
|
||||||
|
|
||||||
- <span id="opt-max-jobs">[`--max-jobs`](#opt-max-jobs)</span> / `-j` *number*\
|
By default, output written by builders to standard output and standard error is echoed to the Nix command's standard error.
|
||||||
Sets the maximum number of build jobs that Nix will perform in
|
This option suppresses this behaviour.
|
||||||
parallel to the specified number. Specify `auto` to use the number
|
Note that the builder's standard output and error are always written to a log file in `prefix/nix/var/log/nix`.
|
||||||
of CPUs in the system. The default is specified by the `max-jobs`
|
|
||||||
configuration setting, which itself defaults to `1`. A higher
|
|
||||||
value is useful on SMP systems or to exploit I/O latency.
|
|
||||||
|
|
||||||
Setting it to `0` disallows building on the local machine, which is
|
- <span id="opt-max-jobs">[`--max-jobs`](#opt-max-jobs)</span> / `-j` *number*
|
||||||
useful when you want builds to happen only on remote builders.
|
|
||||||
|
|
||||||
- <span id="opt-cores">[`--cores`](#opt-cores)</span>\
|
Sets the maximum number of build jobs that Nix will perform in parallel to the specified number.
|
||||||
Sets the value of the `NIX_BUILD_CORES` environment variable in
|
Specify `auto` to use the number of CPUs in the system.
|
||||||
the invocation of builders. Builders can use this variable at
|
The default is specified by the `max-jobs` configuration setting, which itself defaults to `1`.
|
||||||
their discretion to control the maximum amount of parallelism. For
|
A higher value is useful on SMP systems or to exploit I/O latency.
|
||||||
instance, in Nixpkgs, if the derivation attribute
|
|
||||||
`enableParallelBuilding` is set to `true`, the builder passes the
|
|
||||||
`-jN` flag to GNU Make. It defaults to the value of the `cores`
|
|
||||||
configuration setting, if set, or `1` otherwise. The value `0`
|
|
||||||
means that the builder should use all available CPU cores in the
|
|
||||||
system.
|
|
||||||
|
|
||||||
- <span id="opt-max-silent-time">[`--max-silent-time`](#opt-max-silent-time)</span>\
|
Setting it to `0` disallows building on the local machine, which is useful when you want builds to happen only on remote builders.
|
||||||
Sets the maximum number of seconds that a builder can go without
|
|
||||||
producing any data on standard output or standard error. The
|
|
||||||
default is specified by the `max-silent-time` configuration
|
|
||||||
setting. `0` means no time-out.
|
|
||||||
|
|
||||||
- <span id="opt-timeout">[`--timeout`](#opt-timeout)</span>\
|
- <span id="opt-cores">[`--cores`](#opt-cores)</span>
|
||||||
Sets the maximum number of seconds that a builder can run. The
|
|
||||||
default is specified by the `timeout` configuration setting. `0`
|
|
||||||
means no timeout.
|
|
||||||
|
|
||||||
- <span id="opt-keep-going">[`--keep-going`](#opt-keep-going)</span> / `-k`\
|
Sets the value of the `NIX_BUILD_CORES` environment variable in the invocation of builders.
|
||||||
Keep going in case of failed builds, to the greatest extent
|
Builders can use this variable at their discretion to control the maximum amount of parallelism.
|
||||||
possible. That is, if building an input of some derivation fails,
|
For instance, in Nixpkgs, if the derivation attribute `enableParallelBuilding` is set to `true`, the builder passes the `-jN` flag to GNU Make.
|
||||||
Nix will still build the other inputs, but not the derivation
|
It defaults to the value of the `cores` configuration setting, if set, or `1` otherwise.
|
||||||
itself. Without this option, Nix stops if any build fails (except
|
The value `0` means that the builder should use all available CPU cores in the system.
|
||||||
for builds of substitutes), possibly killing builds in progress (in
|
|
||||||
case of parallel or distributed builds).
|
|
||||||
|
|
||||||
- <span id="opt-keep-failed">[`--keep-failed`](#opt-keep-failed)</span> / `-K`\
|
- <span id="opt-max-silent-time">[`--max-silent-time`](#opt-max-silent-time)</span>
|
||||||
Specifies that in case of a build failure, the temporary directory
|
|
||||||
(usually in `/tmp`) in which the build takes place should not be
|
|
||||||
deleted. The path of the build directory is printed as an
|
|
||||||
informational message.
|
|
||||||
|
|
||||||
- <span id="opt-fallback">[`--fallback`](#opt-fallback)</span>\
|
Sets the maximum number of seconds that a builder can go without producing any data on standard output or standard error.
|
||||||
Whenever Nix attempts to build a derivation for which substitutes
|
The default is specified by the `max-silent-time` configuration setting.
|
||||||
are known for each output path, but realising the output paths
|
`0` means no time-out.
|
||||||
through the substitutes fails, fall back on building the derivation.
|
|
||||||
|
|
||||||
The most common scenario in which this is useful is when we have
|
- <span id="opt-timeout">[`--timeout`](#opt-timeout)</span>
|
||||||
registered substitutes in order to perform binary distribution from,
|
|
||||||
say, a network repository. If the repository is down, the
|
|
||||||
realisation of the derivation will fail. When this option is
|
|
||||||
specified, Nix will build the derivation instead. Thus, installation
|
|
||||||
from binaries falls back on installation from source. This option is
|
|
||||||
not the default since it is generally not desirable for a transient
|
|
||||||
failure in obtaining the substitutes to lead to a full build from
|
|
||||||
source (with the related consumption of resources).
|
|
||||||
|
|
||||||
- <span id="opt-readonly-mode">[`--readonly-mode`](#opt-readonly-mode)</span>\
|
Sets the maximum number of seconds that a builder can run.
|
||||||
When this option is used, no attempt is made to open the Nix
|
The default is specified by the `timeout` configuration setting.
|
||||||
database. Most Nix operations do need database access, so those
|
`0` means no timeout.
|
||||||
operations will fail.
|
|
||||||
|
|
||||||
- <span id="opt-arg">[`--arg`](#opt-arg)</span> *name* *value*\
|
- <span id="opt-keep-going">[`--keep-going`](#opt-keep-going)</span> / `-k`
|
||||||
This option is accepted by `nix-env`, `nix-instantiate`,
|
|
||||||
`nix-shell` and `nix-build`. When evaluating Nix expressions, the
|
|
||||||
expression evaluator will automatically try to call functions that
|
|
||||||
it encounters. It can automatically call functions for which every
|
|
||||||
argument has a [default
|
|
||||||
value](@docroot@/language/constructs.md#functions) (e.g.,
|
|
||||||
`{ argName ? defaultValue }: ...`). With `--arg`, you can also
|
|
||||||
call functions that have arguments without a default value (or
|
|
||||||
override a default value). That is, if the evaluator encounters a
|
|
||||||
function with an argument named *name*, it will call it with value
|
|
||||||
*value*.
|
|
||||||
|
|
||||||
For instance, the top-level `default.nix` in Nixpkgs is actually a
|
Keep going in case of failed builds, to the greatest extent possible.
|
||||||
function:
|
That is, if building an input of some derivation fails, Nix will still build the other inputs, but not the derivation itself.
|
||||||
|
Without this option, Nix stops if any build fails (except for builds of substitutes), possibly killing builds in progress (in case of parallel or distributed builds).
|
||||||
|
|
||||||
|
- <span id="opt-keep-failed">[`--keep-failed`](#opt-keep-failed)</span> / `-K`
|
||||||
|
|
||||||
|
Specifies that in case of a build failure, the temporary directory (usually in `/tmp`) in which the build takes place should not be deleted.
|
||||||
|
The path of the build directory is printed as an informational message.
|
||||||
|
|
||||||
|
- <span id="opt-fallback">[`--fallback`](#opt-fallback)</span>
|
||||||
|
|
||||||
|
Whenever Nix attempts to build a derivation for which substitutes are known for each output path, but realising the output paths through the substitutes fails, fall back on building the derivation.
|
||||||
|
|
||||||
|
The most common scenario in which this is useful is when we have registered substitutes in order to perform binary distribution from, say, a network repository.
|
||||||
|
If the repository is down, the realisation of the derivation will fail.
|
||||||
|
When this option is specified, Nix will build the derivation instead.
|
||||||
|
Thus, installation from binaries falls back on installation from source.
|
||||||
|
This option is not the default since it is generally not desirable for a transient failure in obtaining the substitutes to lead to a full build from source (with the related consumption of resources).
|
||||||
|
|
||||||
|
- <span id="opt-readonly-mode">[`--readonly-mode`](#opt-readonly-mode)</span>
|
||||||
|
|
||||||
|
When this option is used, no attempt is made to open the Nix database.
|
||||||
|
Most Nix operations do need database access, so those operations will fail.
|
||||||
|
|
||||||
|
- <span id="opt-arg">[`--arg`](#opt-arg)</span> *name* *value*
|
||||||
|
|
||||||
|
This option is accepted by `nix-env`, `nix-instantiate`, `nix-shell` and `nix-build`.
|
||||||
|
When evaluating Nix expressions, the expression evaluator will automatically try to call functions that it encounters.
|
||||||
|
It can automatically call functions for which every argument has a [default value](@docroot@/language/constructs.md#functions) (e.g., `{ argName ? defaultValue }: ...`).
|
||||||
|
|
||||||
|
With `--arg`, you can also call functions that have arguments without a default value (or override a default value).
|
||||||
|
That is, if the evaluator encounters a function with an argument named *name*, it will call it with value *value*.
|
||||||
|
|
||||||
|
For instance, the top-level `default.nix` in Nixpkgs is actually a function:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{ # The system (e.g., `i686-linux') for which to build the packages.
|
{ # The system (e.g., `i686-linux') for which to build the packages.
|
||||||
|
@ -162,57 +157,53 @@ Most Nix commands accept the following command-line options:
|
||||||
}: ...
|
}: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
So if you call this Nix expression (e.g., when you do `nix-env --install --attr
|
So if you call this Nix expression (e.g., when you do `nix-env --install --attr pkgname`), the function will be called automatically using the value [`builtins.currentSystem`](@docroot@/language/builtins.md) for the `system` argument.
|
||||||
pkgname`), the function will be called automatically using the
|
You can override this using `--arg`, e.g., `nix-env --install --attr pkgname --arg system \"i686-freebsd\"`.
|
||||||
value [`builtins.currentSystem`](@docroot@/language/builtins.md) for
|
(Note that since the argument is a Nix string literal, you have to escape the quotes.)
|
||||||
the `system` argument. You can override this using `--arg`, e.g.,
|
|
||||||
`nix-env --install --attr pkgname --arg system \"i686-freebsd\"`. (Note that
|
|
||||||
since the argument is a Nix string literal, you have to escape the
|
|
||||||
quotes.)
|
|
||||||
|
|
||||||
- <span id="opt-argstr">[`--argstr`](#opt-argstr)</span> *name* *value*\
|
- <span id="opt-argstr">[`--argstr`](#opt-argstr)</span> *name* *value*
|
||||||
This option is like `--arg`, only the value is not a Nix
|
|
||||||
expression but a string. So instead of `--arg system
|
|
||||||
\"i686-linux\"` (the outer quotes are to keep the shell happy) you
|
|
||||||
can say `--argstr system i686-linux`.
|
|
||||||
|
|
||||||
- <span id="opt-attr">[`--attr`](#opt-attr)</span> / `-A` *attrPath*\
|
This option is like `--arg`, only the value is not a Nix expression but a string.
|
||||||
Select an attribute from the top-level Nix expression being
|
So instead of `--arg system \"i686-linux\"` (the outer quotes are to keep the shell happy) you can say `--argstr system i686-linux`.
|
||||||
evaluated. (`nix-env`, `nix-instantiate`, `nix-build` and
|
|
||||||
`nix-shell` only.) The *attribute path* *attrPath* is a sequence
|
- <span id="opt-attr">[`--attr`](#opt-attr)</span> / `-A` *attrPath*
|
||||||
of attribute names separated by dots. For instance, given a
|
|
||||||
top-level Nix expression *e*, the attribute path `xorg.xorgserver`
|
Select an attribute from the top-level Nix expression being evaluated.
|
||||||
would cause the expression `e.xorg.xorgserver` to be used. See
|
(`nix-env`, `nix-instantiate`, `nix-build` and `nix-shell` only.)
|
||||||
[`nix-env --install`](@docroot@/command-ref/nix-env/install.md) for some
|
The *attribute path* *attrPath* is a sequence of attribute names separated by dots.
|
||||||
concrete examples.
|
For instance, given a top-level Nix expression *e*, the attribute path `xorg.xorgserver` would cause the expression `e.xorg.xorgserver` to be used.
|
||||||
|
See [`nix-env --install`](@docroot@/command-ref/nix-env/install.md) for some concrete examples.
|
||||||
|
|
||||||
In addition to attribute names, you can also specify array indices.
|
In addition to attribute names, you can also specify array indices.
|
||||||
For instance, the attribute path `foo.3.bar` selects the `bar`
|
For instance, the attribute path `foo.3.bar` selects the `bar`
|
||||||
attribute of the fourth element of the array in the `foo` attribute
|
attribute of the fourth element of the array in the `foo` attribute
|
||||||
of the top-level expression.
|
of the top-level expression.
|
||||||
|
|
||||||
- <span id="opt-expr">[`--expr`](#opt-expr)</span> / `-E`\
|
- <span id="opt-expr">[`--expr`](#opt-expr)</span> / `-E`
|
||||||
Interpret the command line arguments as a list of Nix expressions to
|
|
||||||
be parsed and evaluated, rather than as a list of file names of Nix
|
|
||||||
expressions. (`nix-instantiate`, `nix-build` and `nix-shell` only.)
|
|
||||||
|
|
||||||
For `nix-shell`, this option is commonly used to give you a shell in
|
Interpret the command line arguments as a list of Nix expressions to be parsed and evaluated, rather than as a list of file names of Nix expressions.
|
||||||
which you can build the packages returned by the expression. If you
|
(`nix-instantiate`, `nix-build` and `nix-shell` only.)
|
||||||
want to get a shell which contain the *built* packages ready for
|
|
||||||
use, give your expression to the `nix-shell --packages ` convenience flag
|
For `nix-shell`, this option is commonly used to give you a shell in which you can build the packages returned by the expression.
|
||||||
instead.
|
If you want to get a shell which contain the *built* packages ready for use, give your expression to the `nix-shell --packages ` convenience flag instead.
|
||||||
|
|
||||||
|
- <span id="opt-I">[`-I`](#opt-I)</span> *path*
|
||||||
|
|
||||||
- <span id="opt-I">[`-I`](#opt-I)</span> *path*\
|
|
||||||
Add an entry to the [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path).
|
Add an entry to the [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path).
|
||||||
This option may be given multiple times.
|
This option may be given multiple times.
|
||||||
Paths added through `-I` take precedence over [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH).
|
Paths added through `-I` take precedence over [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH).
|
||||||
|
|
||||||
- <span id="opt-option">[`--option`](#opt-option)</span> *name* *value*\
|
- <span id="opt-option">[`--option`](#opt-option)</span> *name* *value*
|
||||||
Set the Nix configuration option *name* to *value*. This overrides
|
|
||||||
settings in the Nix configuration file (see nix.conf5).
|
|
||||||
|
|
||||||
- <span id="opt-repair">[`--repair`](#opt-repair)</span>\
|
Set the Nix configuration option *name* to *value*.
|
||||||
Fix corrupted or missing store paths by redownloading or rebuilding
|
This overrides settings in the Nix configuration file (see nix.conf5).
|
||||||
them. Note that this is slow because it requires computing a
|
|
||||||
cryptographic hash of the contents of every path in the closure of
|
- <span id="opt-repair">[`--repair`](#opt-repair)</span>
|
||||||
the build. Also note the warning under `nix-store --repair-path`.
|
|
||||||
|
Fix corrupted or missing store paths by redownloading or rebuilding them.
|
||||||
|
Note that this is slow because it requires computing a cryptographic hash of the contents of every path in the closure of the build.
|
||||||
|
Also note the warning under `nix-store --repair-path`.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
\--prebuilt-only
|
|
||||||
|
|
||||||
\-b
|
|
||||||
|
|
||||||
\--attr
|
|
||||||
|
|
||||||
\-A
|
|
||||||
|
|
||||||
\--from-expression
|
|
||||||
|
|
||||||
\-E
|
|
||||||
|
|
||||||
\--from-profile
|
|
||||||
|
|
||||||
path
|
|
|
@ -271,17 +271,3 @@ or inside a `nix develop` shell by running:
|
||||||
# make internal-api-html
|
# make internal-api-html
|
||||||
# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
|
# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
## Coverage analysis
|
|
||||||
|
|
||||||
A coverage analysis report is [available
|
|
||||||
online](https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage). You
|
|
||||||
can build it yourself:
|
|
||||||
|
|
||||||
```
|
|
||||||
# nix build .#hydraJobs.coverage
|
|
||||||
# xdg-open ./result/coverage/index.html
|
|
||||||
```
|
|
||||||
|
|
||||||
Metrics about the change in line/function coverage over time are also
|
|
||||||
[available](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts).
|
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
# Running tests
|
# Running tests
|
||||||
|
|
||||||
|
## Coverage analysis
|
||||||
|
|
||||||
|
A [coverage analysis report] is available online
|
||||||
|
You can build it yourself:
|
||||||
|
|
||||||
|
[coverage analysis report]: https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage
|
||||||
|
|
||||||
|
```
|
||||||
|
# nix build .#hydraJobs.coverage
|
||||||
|
# xdg-open ./result/coverage/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
[Extensive records of build metrics](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts), such as test coverage over time, are also available online.
|
||||||
|
|
||||||
## Unit-tests
|
## Unit-tests
|
||||||
|
|
||||||
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
||||||
|
@ -7,7 +21,8 @@ under `src/{library_name}/tests` using the
|
||||||
[googletest](https://google.github.io/googletest/) and
|
[googletest](https://google.github.io/googletest/) and
|
||||||
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
|
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
|
||||||
|
|
||||||
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
|
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`.
|
||||||
|
Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable.
|
||||||
|
|
||||||
## Functional tests
|
## Functional tests
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
Ensure a [store path] is [valid][validity].
|
Ensure a [store path] is [valid][validity].
|
||||||
|
|
||||||
This means either running the `builder` executable as specified in the corresponding [derivation] or fetching a pre-built [store object] from a [substituter].
|
This means either running the [`builder`](@docroot@/language/derivations.md#attr-builder) executable as specified in the corresponding [derivation], or fetching a pre-built [store object] from a [substituter], or delegating to a [remote builder](@docroot@/advanced-topics/distributed-builds.html) and retrieving the outputs. <!-- TODO: link [running] to build process page, #8888 -->
|
||||||
|
|
||||||
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md).
|
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md).
|
||||||
|
|
||||||
|
@ -197,9 +197,15 @@
|
||||||
|
|
||||||
[closure]: #gloss-closure
|
[closure]: #gloss-closure
|
||||||
|
|
||||||
|
- [output]{#gloss-output}
|
||||||
|
|
||||||
|
A [store object] produced by a [derivation].
|
||||||
|
|
||||||
|
[output]: #gloss-output
|
||||||
|
|
||||||
- [output path]{#gloss-output-path}
|
- [output path]{#gloss-output-path}
|
||||||
|
|
||||||
A [store path] produced by a [derivation].
|
The [store path] to the [output] of a [derivation].
|
||||||
|
|
||||||
[output path]: #gloss-output-path
|
[output path]: #gloss-output-path
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ the attributes of which specify the inputs of the build.
|
||||||
string. This is used as a symbolic name for the package by
|
string. This is used as a symbolic name for the package by
|
||||||
`nix-env`, and it is appended to the output paths of the derivation.
|
`nix-env`, and it is appended to the output paths of the derivation.
|
||||||
|
|
||||||
- There must be an attribute named `builder` that identifies the
|
- There must be an attribute named [`builder`]{#attr-builder} that identifies the
|
||||||
program that is executed to perform the build. It can be either a
|
program that is executed to perform the build. It can be either a
|
||||||
derivation or a source (a local file reference, e.g.,
|
derivation or a source (a local file reference, e.g.,
|
||||||
`./builder.sh`).
|
`./builder.sh`).
|
||||||
|
|
|
@ -83,7 +83,8 @@ This is an incomplete overview of language features, by example.
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
A multi-line string. Strips common prefixed whitespace. Evaluates to `"multi\n line\n string"`.
|
<!-- FIXME: using two no-break spaces, because apparently mdBook swallows the second regular space! -->
|
||||||
|
A multi-line string. Strips common prefixed whitespace. Evaluates to `"multi\n line\n string"`.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
19
doc/manual/src/protocols/derivation-aterm.md
Normal file
19
doc/manual/src/protocols/derivation-aterm.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Derivation "ATerm" file format
|
||||||
|
|
||||||
|
For historical reasons, [derivations](@docroot@/glossary.md#gloss-store-derivation) are stored on-disk in [ATerm](https://homepages.cwi.nl/~daybuild/daily-books/technology/aterm-guide/aterm-guide.html) format.
|
||||||
|
|
||||||
|
Derivations are serialised in one of the following formats:
|
||||||
|
|
||||||
|
- ```
|
||||||
|
Derive(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
For all stable derivations.
|
||||||
|
|
||||||
|
- ```
|
||||||
|
DrvWithVersion(<version-string>, ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
The only `version-string`s that are in use today are for [experimental features](@docroot@/contributing/experimental-features.md):
|
||||||
|
|
||||||
|
- `"xp-dyn-drv"` for the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature.
|
|
@ -20,8 +20,8 @@ Link: <flakeref>; rel="immutable"
|
||||||
|
|
||||||
(Note the required `<` and `>` characters around *flakeref*.)
|
(Note the required `<` and `>` characters around *flakeref*.)
|
||||||
|
|
||||||
*flakeref* must be a tarball flakeref. It can contain flake attributes
|
*flakeref* must be a tarball flakeref. It can contain the tarball flake attributes
|
||||||
such as `narHash`, `rev` and `revCount`. If `narHash` is included, its
|
`narHash`, `rev`, `revCount` and `lastModified`. If `narHash` is included, its
|
||||||
value must be the NAR hash of the unpacked tarball (as computed via
|
value must be the NAR hash of the unpacked tarball (as computed via
|
||||||
`nix hash path`). Nix checks the contents of the returned tarball
|
`nix hash path`). Nix checks the contents of the returned tarball
|
||||||
against the `narHash` attribute. The `rev` and `revCount` attributes
|
against the `narHash` attribute. The `rev` and `revCount` attributes
|
||||||
|
|
28
doc/manual/src/release-notes/rl-2.18.md
Normal file
28
doc/manual/src/release-notes/rl-2.18.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Release 2.18 (2023-09-20)
|
||||||
|
|
||||||
|
- Two new builtin functions,
|
||||||
|
[`builtins.parseFlakeRef`](@docroot@/language/builtins.md#builtins-parseFlakeRef)
|
||||||
|
and
|
||||||
|
[`builtins.flakeRefToString`](@docroot@/language/builtins.md#builtins-flakeRefToString),
|
||||||
|
have been added.
|
||||||
|
These functions are useful for converting between flake references encoded as attribute sets and URLs.
|
||||||
|
|
||||||
|
- [`builtins.toJSON`](@docroot@/language/builtins.md#builtins-parseFlakeRef) now prints [--show-trace](@docroot@/command-ref/conf-file.html#conf-show-trace) items for the path in which it finds an evaluation error.
|
||||||
|
|
||||||
|
- Error messages regarding malformed input to [`nix derivation add`](@docroot@/command-ref/new-cli/nix3-derivation-add.md) are now clearer and more detailed.
|
||||||
|
|
||||||
|
- The `discard-references` feature has been stabilized.
|
||||||
|
This means that the
|
||||||
|
[unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references)
|
||||||
|
attribute is no longer guarded by an experimental flag and can be used
|
||||||
|
freely.
|
||||||
|
|
||||||
|
- The JSON output for derived paths which are store paths is now a string, not an object with a single `path` field.
|
||||||
|
This only affects `nix-build --json` when "building" non-derivation things like fetched sources, which is a no-op.
|
||||||
|
|
||||||
|
- A new builtin [`outputOf`](@docroot@/language/builtins.md#builtins-outputOf) has been added.
|
||||||
|
It is part of the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature.
|
||||||
|
|
||||||
|
- Flake follow paths at depths greater than 2 are now handled correctly, preventing "follows a non-existent input" errors.
|
||||||
|
|
||||||
|
- [`nix-store --query`](@docroot@/command-ref/nix-store/query.md) gained a new type of query: `--valid-derivers`. It returns all `.drv` files in the local store that *can be* used to build the output passed in argument. This is in contrast to `--deriver`, which returns the single `.drv` file that *was actually* used to build the output passed in argument. In case the output was substituted from a binary cache, this `.drv` file may only exist on said binary cache and not locally.
|
|
@ -1,24 +1 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
- Two new builtin functions,
|
|
||||||
[`builtins.parseFlakeRef`](@docroot@/language/builtins.md#builtins-parseFlakeRef)
|
|
||||||
and
|
|
||||||
[`builtins.flakeRefToString`](@docroot@/language/builtins.md#builtins-flakeRefToString),
|
|
||||||
have been added.
|
|
||||||
These functions are useful for converting between flake references encoded as attribute sets and URLs.
|
|
||||||
|
|
||||||
- [`builtins.toJSON`](@docroot@/language/builtins.md#builtins-parseFlakeRef) now prints [--show-trace](@docroot@/command-ref/conf-file.html#conf-show-trace) items for the path in which it finds an evaluation error.
|
|
||||||
|
|
||||||
- Error messages regarding malformed input to [`derivation add`](@docroot@/command-ref/new-cli/nix3-derivation-add.md) are now clearer and more detailed.
|
|
||||||
|
|
||||||
- The `discard-references` feature has been stabilized.
|
|
||||||
This means that the
|
|
||||||
[unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references)
|
|
||||||
attribute is no longer guarded by an experimental flag and can be used
|
|
||||||
freely.
|
|
||||||
|
|
||||||
- The JSON output for derived paths with are store paths is now a string, not an object with a single `path` field.
|
|
||||||
This only affects `nix-build --json` when "building" non-derivation things like fetched sources, which is a no-op.
|
|
||||||
|
|
||||||
- Introduce a new [`outputOf`](@docroot@/language/builtins.md#builtins-outputOf) builtin.
|
|
||||||
It is part of the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature.
|
|
||||||
|
|
12
flake.lock
12
flake.lock
|
@ -34,16 +34,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1670461440,
|
"lastModified": 1695124524,
|
||||||
"narHash": "sha256-jy1LB8HOMKGJEGXgzFRLDU1CBGL0/LlkolgnqIsF0D8=",
|
"narHash": "sha256-trXDytVCqf3KryQQQrHOZKUabu1/lB8/ndOAuZKQrOE=",
|
||||||
"owner": "NixOS",
|
"owner": "edolstra",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "04a75b2eecc0acf6239acf9dd04485ff8d14f425",
|
"rev": "a3d30b525535e3158221abc1a957ce798ab159fe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "edolstra",
|
||||||
"ref": "nixos-22.11-small",
|
"ref": "fix-aws-sdk-cpp",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
description = "The purely functional package manager - but super!";
|
description = "The purely functional package manager - but super!";
|
||||||
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11-small";
|
#inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
|
||||||
|
inputs.nixpkgs.url = "github:edolstra/nixpkgs/fix-aws-sdk-cpp";
|
||||||
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||||
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||||
|
@ -587,6 +588,8 @@
|
||||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||||
|
|
||||||
hardeningDisable = ["fortify"];
|
hardeningDisable = ["fortify"];
|
||||||
|
|
||||||
|
NIX_CFLAGS_COMPILE = "-DCOVERAGE=1";
|
||||||
};
|
};
|
||||||
|
|
||||||
# API docs for Nix's unstable internal C++ interfaces.
|
# API docs for Nix's unstable internal C++ interfaces.
|
||||||
|
@ -656,7 +659,9 @@
|
||||||
tests.nixpkgsLibTests =
|
tests.nixpkgsLibTests =
|
||||||
forAllSystems (system:
|
forAllSystems (system:
|
||||||
import (nixpkgs + "/lib/tests/release.nix")
|
import (nixpkgs + "/lib/tests/release.nix")
|
||||||
{ pkgs = nixpkgsFor.${system}.native; }
|
{ pkgs = nixpkgsFor.${system}.native;
|
||||||
|
nixVersions = [ self.packages.${system}.nix ];
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
||||||
|
|
|
@ -324,7 +324,7 @@ SV * derivationFromPath(char * drvPath)
|
||||||
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
||||||
|
|
||||||
AV * inputDrvs = newAV();
|
AV * inputDrvs = newAV();
|
||||||
for (auto & i : drv.inputDrvs)
|
for (auto & i : drv.inputDrvs.map)
|
||||||
av_push(inputDrvs, newSVpv(store()->printStorePath(i.first).c_str(), 0)); // !!! ignores i->second
|
av_push(inputDrvs, newSVpv(store()->printStorePath(i.first).c_str(), 0)); // !!! ignores i->second
|
||||||
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
||||||
|
|
||||||
|
|
|
@ -314,7 +314,7 @@ connected:
|
||||||
//
|
//
|
||||||
// 2. Changing the `inputSrcs` set changes the associated
|
// 2. Changing the `inputSrcs` set changes the associated
|
||||||
// output ids, which break CA derivations
|
// output ids, which break CA derivations
|
||||||
if (!drv.inputDrvs.empty())
|
if (!drv.inputDrvs.map.empty())
|
||||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||||
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
|
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
|
||||||
auto & result = *optResult;
|
auto & result = *optResult;
|
||||||
|
|
|
@ -58,6 +58,28 @@ StorePathSet BuiltPath::outPaths() const
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SingleDerivedPath::Built SingleBuiltPath::Built::discardOutputPath() const
|
||||||
|
{
|
||||||
|
return SingleDerivedPath::Built {
|
||||||
|
.drvPath = make_ref<SingleDerivedPath>(drvPath->discardOutputPath()),
|
||||||
|
.output = output.first,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleDerivedPath SingleBuiltPath::discardOutputPath() const
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[](const SingleBuiltPath::Opaque & p) -> SingleDerivedPath {
|
||||||
|
return p;
|
||||||
|
},
|
||||||
|
[](const SingleBuiltPath::Built & b) -> SingleDerivedPath {
|
||||||
|
return b.discardOutputPath();
|
||||||
|
},
|
||||||
|
}, raw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json BuiltPath::Built::toJSON(const Store & store) const
|
nlohmann::json BuiltPath::Built::toJSON(const Store & store) const
|
||||||
{
|
{
|
||||||
nlohmann::json res;
|
nlohmann::json res;
|
||||||
|
|
|
@ -9,6 +9,8 @@ struct SingleBuiltPathBuilt {
|
||||||
ref<SingleBuiltPath> drvPath;
|
ref<SingleBuiltPath> drvPath;
|
||||||
std::pair<std::string, StorePath> output;
|
std::pair<std::string, StorePath> output;
|
||||||
|
|
||||||
|
SingleDerivedPathBuilt discardOutputPath() const;
|
||||||
|
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
static SingleBuiltPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
static SingleBuiltPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
||||||
nlohmann::json toJSON(const Store & store) const;
|
nlohmann::json toJSON(const Store & store) const;
|
||||||
|
@ -34,6 +36,8 @@ struct SingleBuiltPath : _SingleBuiltPathRaw {
|
||||||
|
|
||||||
StorePath outPath() const;
|
StorePath outPath() const;
|
||||||
|
|
||||||
|
SingleDerivedPath discardOutputPath() const;
|
||||||
|
|
||||||
static SingleBuiltPath parse(const Store & store, std::string_view);
|
static SingleBuiltPath parse(const Store & store, std::string_view);
|
||||||
nlohmann::json toJSON(const Store & store) const;
|
nlohmann::json toJSON(const Store & store) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -490,33 +490,38 @@ bool NixRepl::processLine(std::string line)
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< " <expr> Evaluate and print expression\n"
|
<< " <expr> Evaluate and print expression\n"
|
||||||
<< " <x> = <expr> Bind expression to variable\n"
|
<< " <x> = <expr> Bind expression to variable\n"
|
||||||
<< " :a <expr> Add attributes from resulting set to scope\n"
|
<< " :a, :add <expr> Add attributes from resulting set to scope\n"
|
||||||
<< " :b <expr> Build a derivation\n"
|
<< " :b <expr> Build a derivation\n"
|
||||||
<< " :bl <expr> Build a derivation, creating GC roots in the working directory\n"
|
<< " :bl <expr> Build a derivation, creating GC roots in the\n"
|
||||||
<< " :e <expr> Open package or function in $EDITOR\n"
|
<< " working directory\n"
|
||||||
<< " :i <expr> Build derivation, then install result into current profile\n"
|
<< " :e, :edit <expr> Open package or function in $EDITOR\n"
|
||||||
<< " :l <path> Load Nix expression and add it to scope\n"
|
<< " :i <expr> Build derivation, then install result into\n"
|
||||||
<< " :lf <ref> Load Nix flake and add it to scope\n"
|
<< " current profile\n"
|
||||||
<< " :p <expr> Evaluate and print expression recursively\n"
|
<< " :l, :load <path> Load Nix expression and add it to scope\n"
|
||||||
<< " :q Exit nix-repl\n"
|
<< " :lf, :load-flake <ref> Load Nix flake and add it to scope\n"
|
||||||
<< " :r Reload all files\n"
|
<< " :p, :print <expr> Evaluate and print expression recursively\n"
|
||||||
<< " :sh <expr> Build dependencies of derivation, then start nix-shell\n"
|
<< " :q, :quit Exit nix-repl\n"
|
||||||
|
<< " :r, :reload Reload all files\n"
|
||||||
|
<< " :sh <expr> Build dependencies of derivation, then start\n"
|
||||||
|
<< " nix-shell\n"
|
||||||
<< " :t <expr> Describe result of evaluation\n"
|
<< " :t <expr> Describe result of evaluation\n"
|
||||||
<< " :u <expr> Build derivation, then start nix-shell\n"
|
<< " :u <expr> Build derivation, then start nix-shell\n"
|
||||||
<< " :doc <expr> Show documentation of a builtin function\n"
|
<< " :doc <expr> Show documentation of a builtin function\n"
|
||||||
<< " :log <expr> Show logs for a derivation\n"
|
<< " :log <expr> Show logs for a derivation\n"
|
||||||
<< " :te [bool] Enable, disable or toggle showing traces for errors\n"
|
<< " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n"
|
||||||
|
<< " errors\n"
|
||||||
|
<< " :?, :help Brings up this help menu\n"
|
||||||
;
|
;
|
||||||
if (state->debugRepl) {
|
if (state->debugRepl) {
|
||||||
std::cout
|
std::cout
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< " Debug mode commands\n"
|
<< " Debug mode commands\n"
|
||||||
<< " :env Show env stack\n"
|
<< " :env Show env stack\n"
|
||||||
<< " :bt Show trace stack\n"
|
<< " :bt, :backtrace Show trace stack\n"
|
||||||
<< " :st Show current trace\n"
|
<< " :st Show current trace\n"
|
||||||
<< " :st <idx> Change to another trace in the stack\n"
|
<< " :st <idx> Change to another trace in the stack\n"
|
||||||
<< " :c Go until end of program, exception, or builtins.break\n"
|
<< " :c, :continue Go until end of program, exception, or builtins.break\n"
|
||||||
<< " :s Go one step\n"
|
<< " :s, :step Go one step\n"
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct EvalSettings : Config
|
||||||
R"(
|
R"(
|
||||||
List of directories to be searched for `<...>` file references
|
List of directories to be searched for `<...>` file references
|
||||||
|
|
||||||
In particular, outside of [pure evaluation mode](#conf-pure-evaluation), this determines the value of
|
In particular, outside of [pure evaluation mode](#conf-pure-eval), this determines the value of
|
||||||
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath).
|
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath).
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
|
|
@ -520,11 +520,6 @@ LockedFlake lockFlake(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto localPath(parentPath);
|
|
||||||
// If this input is a path, recurse it down.
|
|
||||||
// This allows us to resolve path inputs relative to the current flake.
|
|
||||||
if ((*input.ref).input.getType() == "path")
|
|
||||||
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
mustRefetch
|
mustRefetch
|
||||||
? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
|
? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
|
||||||
|
|
|
@ -345,7 +345,7 @@ void LockFile::check()
|
||||||
|
|
||||||
for (auto & [inputPath, input] : inputs) {
|
for (auto & [inputPath, input] : inputs) {
|
||||||
if (auto follows = std::get_if<1>(&input)) {
|
if (auto follows = std::get_if<1>(&input)) {
|
||||||
if (!follows->empty() && !get(inputs, *follows))
|
if (!follows->empty() && !findInput(*follows))
|
||||||
throw Error("input '%s' follows a non-existent input '%s'",
|
throw Error("input '%s' follows a non-existent input '%s'",
|
||||||
printInputPath(inputPath),
|
printInputPath(inputPath),
|
||||||
printInputPath(*follows));
|
printInputPath(*follows));
|
||||||
|
|
|
@ -1252,15 +1252,13 @@ drvName, Bindings * attrs, Value & v)
|
||||||
state.store->computeFSClosure(d.drvPath, refs);
|
state.store->computeFSClosure(d.drvPath, refs);
|
||||||
for (auto & j : refs) {
|
for (auto & j : refs) {
|
||||||
drv.inputSrcs.insert(j);
|
drv.inputSrcs.insert(j);
|
||||||
if (j.isDerivation())
|
if (j.isDerivation()) {
|
||||||
drv.inputDrvs[j] = state.store->readDerivation(j).outputNames();
|
drv.inputDrvs.map[j].value = state.store->readDerivation(j).outputNames();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](const NixStringContextElem::Built & b) {
|
[&](const NixStringContextElem::Built & b) {
|
||||||
if (auto * p = std::get_if<DerivedPath::Opaque>(&*b.drvPath))
|
drv.inputDrvs.ensureSlot(*b.drvPath).value.insert(b.output);
|
||||||
drv.inputDrvs[p->path].insert(b.output);
|
|
||||||
else
|
|
||||||
throw UnimplementedError("Dependencies on the outputs of dynamic derivations are not yet supported");
|
|
||||||
},
|
},
|
||||||
[&](const NixStringContextElem::Opaque & o) {
|
[&](const NixStringContextElem::Opaque & o) {
|
||||||
drv.inputSrcs.insert(o.path);
|
drv.inputSrcs.insert(o.path);
|
||||||
|
@ -1520,15 +1518,25 @@ static RegisterPrimOp primop_storePath({
|
||||||
|
|
||||||
static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
auto & arg = *args[0];
|
||||||
|
|
||||||
/* We don’t check the path right now, because we don’t want to
|
/* We don’t check the path right now, because we don’t want to
|
||||||
throw if the path isn’t allowed, but just return false (and we
|
throw if the path isn’t allowed, but just return false (and we
|
||||||
can’t just catch the exception here because we still want to
|
can’t just catch the exception here because we still want to
|
||||||
throw if something in the evaluation of `*args[0]` tries to
|
throw if something in the evaluation of `arg` tries to
|
||||||
access an unauthorized path). */
|
access an unauthorized path). */
|
||||||
auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
|
auto path = realisePath(state, pos, arg, { .checkForPureEval = false });
|
||||||
|
|
||||||
|
/* SourcePath doesn't know about trailing slash. */
|
||||||
|
auto mustBeDir = arg.type() == nString && arg.str().ends_with("/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
v.mkBool(state.checkSourcePath(path).pathExists());
|
auto checked = state.checkSourcePath(path);
|
||||||
|
auto exists = checked.pathExists();
|
||||||
|
if (exists && mustBeDir) {
|
||||||
|
exists = checked.lstat().type == InputAccessor::tDirectory;
|
||||||
|
}
|
||||||
|
v.mkBool(exists);
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
/* Don't give away info from errors while canonicalising
|
/* Don't give away info from errors while canonicalising
|
||||||
‘path’ in restricted mode. */
|
‘path’ in restricted mode. */
|
||||||
|
@ -1843,7 +1851,7 @@ static void prim_outputOf(EvalState & state, const PosIdx pos, Value * * args, V
|
||||||
{
|
{
|
||||||
SingleDerivedPath drvPath = state.coerceToSingleDerivedPath(pos, *args[0], "while evaluating the first argument to builtins.outputOf");
|
SingleDerivedPath drvPath = state.coerceToSingleDerivedPath(pos, *args[0], "while evaluating the first argument to builtins.outputOf");
|
||||||
|
|
||||||
std::string_view outputName = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument to builtins.outputOf");
|
OutputNameView outputName = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument to builtins.outputOf");
|
||||||
|
|
||||||
state.mkSingleDerivedPathString(
|
state.mkSingleDerivedPathString(
|
||||||
SingleDerivedPath::Built {
|
SingleDerivedPath::Built {
|
||||||
|
|
|
@ -18,6 +18,8 @@ TEST_F(DerivedPathExpressionTest, force_init)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef COVERAGE
|
||||||
|
|
||||||
RC_GTEST_FIXTURE_PROP(
|
RC_GTEST_FIXTURE_PROP(
|
||||||
DerivedPathExpressionTest,
|
DerivedPathExpressionTest,
|
||||||
prop_opaque_path_round_trip,
|
prop_opaque_path_round_trip,
|
||||||
|
@ -61,4 +63,6 @@ RC_GTEST_FIXTURE_PROP(
|
||||||
RC_ASSERT(SingleDerivedPath { b } == d);
|
RC_ASSERT(SingleDerivedPath { b } == d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} /* namespace nix */
|
} /* namespace nix */
|
||||||
|
|
|
@ -147,6 +147,8 @@ Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
#ifndef COVERAGE
|
||||||
|
|
||||||
RC_GTEST_PROP(
|
RC_GTEST_PROP(
|
||||||
NixStringContextElemTest,
|
NixStringContextElemTest,
|
||||||
prop_round_rip,
|
prop_round_rip,
|
||||||
|
@ -155,4 +157,6 @@ RC_GTEST_PROP(
|
||||||
RC_ASSERT(o == NixStringContextElem::parse(o.to_string()));
|
RC_ASSERT(o == NixStringContextElem::parse(o.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,7 @@ struct CurlInputScheme : InputScheme
|
||||||
if (type != inputType()) return {};
|
if (type != inputType()) return {};
|
||||||
|
|
||||||
// FIXME: some of these only apply to TarballInputScheme.
|
// FIXME: some of these only apply to TarballInputScheme.
|
||||||
std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack", "rev", "revCount"};
|
std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack", "rev", "revCount", "lastModified"};
|
||||||
for (auto & [name, value] : attrs)
|
for (auto & [name, value] : attrs)
|
||||||
if (!allowedNames.count(name))
|
if (!allowedNames.count(name))
|
||||||
throw Error("unsupported %s input attribute '%s'", *type, name);
|
throw Error("unsupported %s input attribute '%s'", *type, name);
|
||||||
|
@ -310,6 +310,9 @@ struct TarballInputScheme : CurlInputScheme
|
||||||
input = immutableInput;
|
input = immutableInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.lastModified && !input.attrs.contains("lastModified"))
|
||||||
|
input.attrs.insert_or_assign("lastModified", uint64_t(result.lastModified));
|
||||||
|
|
||||||
return {result.tree.storePath, std::move(input)};
|
return {result.tree.storePath, std::move(input)};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
157
src/libstore/build/create-derivation-and-realise-goal.cc
Normal file
157
src/libstore/build/create-derivation-and-realise-goal.cc
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#include "create-derivation-and-realise-goal.hh"
|
||||||
|
#include "worker.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
CreateDerivationAndRealiseGoal::CreateDerivationAndRealiseGoal(ref<SingleDerivedPath> drvReq,
|
||||||
|
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
|
: Goal(worker, DerivedPath::Built { .drvPath = drvReq, .outputs = wantedOutputs })
|
||||||
|
, drvReq(drvReq)
|
||||||
|
, wantedOutputs(wantedOutputs)
|
||||||
|
, buildMode(buildMode)
|
||||||
|
{
|
||||||
|
state = &CreateDerivationAndRealiseGoal::getDerivation;
|
||||||
|
name = fmt(
|
||||||
|
"outer obtaining drv from '%s' and then building outputs %s",
|
||||||
|
drvReq->to_string(worker.store),
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All) -> std::string {
|
||||||
|
return "* (all of them)";
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names os) {
|
||||||
|
return concatStringsSep(", ", quoteStrings(os));
|
||||||
|
},
|
||||||
|
}, wantedOutputs.raw));
|
||||||
|
trace("created outer");
|
||||||
|
|
||||||
|
worker.updateProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CreateDerivationAndRealiseGoal::~CreateDerivationAndRealiseGoal()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static StorePath pathPartOfReq(const SingleDerivedPath & req)
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const SingleDerivedPath::Opaque & bo) {
|
||||||
|
return bo.path;
|
||||||
|
},
|
||||||
|
[&](const SingleDerivedPath::Built & bfd) {
|
||||||
|
return pathPartOfReq(*bfd.drvPath);
|
||||||
|
},
|
||||||
|
}, req.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string CreateDerivationAndRealiseGoal::key()
|
||||||
|
{
|
||||||
|
/* Ensure that derivations get built in order of their name,
|
||||||
|
i.e. a derivation named "aardvark" always comes before "baboon". And
|
||||||
|
substitution goals and inner derivation goals always happen before
|
||||||
|
derivation goals (due to "b$"). */
|
||||||
|
return "c$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + drvReq->to_string(worker.store);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::timedOut(Error && ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::work()
|
||||||
|
{
|
||||||
|
(this->*state)();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::addWantedOutputs(const OutputsSpec & outputs)
|
||||||
|
{
|
||||||
|
/* If we already want all outputs, there is nothing to do. */
|
||||||
|
auto newWanted = wantedOutputs.union_(outputs);
|
||||||
|
bool needRestart = !newWanted.isSubsetOf(wantedOutputs);
|
||||||
|
wantedOutputs = newWanted;
|
||||||
|
|
||||||
|
if (!needRestart) return;
|
||||||
|
|
||||||
|
if (!optDrvPath)
|
||||||
|
// haven't started steps where the outputs matter yet
|
||||||
|
return;
|
||||||
|
worker.makeDerivationGoal(*optDrvPath, outputs, buildMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::getDerivation()
|
||||||
|
{
|
||||||
|
trace("outer init");
|
||||||
|
|
||||||
|
/* The first thing to do is to make sure that the derivation
|
||||||
|
exists. If it doesn't, it may be created through a
|
||||||
|
substitute. */
|
||||||
|
if (auto optDrvPath = [this]() -> std::optional<StorePath> {
|
||||||
|
if (buildMode != bmNormal) return std::nullopt;
|
||||||
|
|
||||||
|
auto drvPath = StorePath::dummy;
|
||||||
|
try {
|
||||||
|
drvPath = resolveDerivedPath(worker.store, *drvReq);
|
||||||
|
} catch (MissingRealisation &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath)
|
||||||
|
? std::optional { drvPath }
|
||||||
|
: std::nullopt;
|
||||||
|
}()) {
|
||||||
|
trace(fmt("already have drv '%s' for '%s', can go straight to building",
|
||||||
|
worker.store.printStorePath(*optDrvPath),
|
||||||
|
drvReq->to_string(worker.store)));
|
||||||
|
|
||||||
|
loadAndBuildDerivation();
|
||||||
|
} else {
|
||||||
|
trace("need to obtain drv we want to build");
|
||||||
|
|
||||||
|
addWaitee(worker.makeGoal(DerivedPath::fromSingle(*drvReq)));
|
||||||
|
|
||||||
|
state = &CreateDerivationAndRealiseGoal::loadAndBuildDerivation;
|
||||||
|
if (waitees.empty()) work();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::loadAndBuildDerivation()
|
||||||
|
{
|
||||||
|
trace("outer load and build derivation");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePath drvPath = resolveDerivedPath(worker.store, *drvReq);
|
||||||
|
/* Build this step! */
|
||||||
|
concreteDrvGoal = worker.makeDerivationGoal(drvPath, wantedOutputs, buildMode);
|
||||||
|
addWaitee(upcast_goal(concreteDrvGoal));
|
||||||
|
state = &CreateDerivationAndRealiseGoal::buildDone;
|
||||||
|
optDrvPath = std::move(drvPath);
|
||||||
|
if (waitees.empty()) work();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CreateDerivationAndRealiseGoal::buildDone()
|
||||||
|
{
|
||||||
|
trace("outer build done");
|
||||||
|
|
||||||
|
buildResult = upcast_goal(concreteDrvGoal)->getBuildResult(DerivedPath::Built {
|
||||||
|
.drvPath = drvReq,
|
||||||
|
.outputs = wantedOutputs,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (buildResult.success())
|
||||||
|
amDone(ecSuccess);
|
||||||
|
else
|
||||||
|
amDone(ecFailed, Error("building '%s' failed", drvReq->to_string(worker.store)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
96
src/libstore/build/create-derivation-and-realise-goal.hh
Normal file
96
src/libstore/build/create-derivation-and-realise-goal.hh
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "parsed-derivations.hh"
|
||||||
|
#include "lock.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
#include "goal.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct DerivationGoal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This goal type is essentially the serial composition (like function
|
||||||
|
* composition) of a goal for getting a derivation, and then a
|
||||||
|
* `DerivationGoal` using the newly-obtained derivation.
|
||||||
|
*
|
||||||
|
* In the (currently experimental) general inductive case of derivations
|
||||||
|
* that are themselves build outputs, that first goal will be *another*
|
||||||
|
* `CreateDerivationAndRealiseGoal`. In the (much more common) base-case
|
||||||
|
* where the derivation has no provence and is just referred to by
|
||||||
|
* (content-addressed) store path, that first goal is a
|
||||||
|
* `SubstitutionGoal`.
|
||||||
|
*
|
||||||
|
* If we already have the derivation (e.g. if the evalutator has created
|
||||||
|
* the derivation locally and then instructured the store to build it),
|
||||||
|
* we can skip the first goal entirely as a small optimization.
|
||||||
|
*/
|
||||||
|
struct CreateDerivationAndRealiseGoal : public Goal
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* How to obtain a store path of the derivation to build.
|
||||||
|
*/
|
||||||
|
ref<SingleDerivedPath> drvReq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path of the derivation, once obtained.
|
||||||
|
**/
|
||||||
|
std::optional<StorePath> optDrvPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The goal for the corresponding concrete derivation.
|
||||||
|
**/
|
||||||
|
std::shared_ptr<DerivationGoal> concreteDrvGoal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specific outputs that we need to build.
|
||||||
|
*/
|
||||||
|
OutputsSpec wantedOutputs;
|
||||||
|
|
||||||
|
typedef void (CreateDerivationAndRealiseGoal::*GoalState)();
|
||||||
|
GoalState state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The final output paths of the build.
|
||||||
|
*
|
||||||
|
* - For input-addressed derivations, always the precomputed paths
|
||||||
|
*
|
||||||
|
* - For content-addressed derivations, calcuated from whatever the
|
||||||
|
* hash ends up being. (Note that fixed outputs derivations that
|
||||||
|
* produce the "wrong" output still install that data under its
|
||||||
|
* true content-address.)
|
||||||
|
*/
|
||||||
|
OutputPathMap finalOutputs;
|
||||||
|
|
||||||
|
BuildMode buildMode;
|
||||||
|
|
||||||
|
CreateDerivationAndRealiseGoal(ref<SingleDerivedPath> drvReq,
|
||||||
|
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||||
|
BuildMode buildMode = bmNormal);
|
||||||
|
virtual ~CreateDerivationAndRealiseGoal();
|
||||||
|
|
||||||
|
void timedOut(Error && ex) override;
|
||||||
|
|
||||||
|
std::string key() override;
|
||||||
|
|
||||||
|
void work() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add wanted outputs to an already existing derivation goal.
|
||||||
|
*/
|
||||||
|
void addWantedOutputs(const OutputsSpec & outputs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The states.
|
||||||
|
*/
|
||||||
|
void getDerivation();
|
||||||
|
void loadAndBuildDerivation();
|
||||||
|
void buildDone();
|
||||||
|
|
||||||
|
JobCategory jobCategory() const override {
|
||||||
|
return JobCategory::Administration;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
state = &DerivationGoal::getDerivation;
|
state = &DerivationGoal::loadDerivation;
|
||||||
name = fmt(
|
name = fmt(
|
||||||
"building of '%s' from .drv file",
|
"building of '%s' from .drv file",
|
||||||
DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store));
|
DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store));
|
||||||
|
@ -164,24 +164,6 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::getDerivation()
|
|
||||||
{
|
|
||||||
trace("init");
|
|
||||||
|
|
||||||
/* The first thing to do is to make sure that the derivation
|
|
||||||
exists. If it doesn't, it may be created through a
|
|
||||||
substitute. */
|
|
||||||
if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
|
|
||||||
loadDerivation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath)));
|
|
||||||
|
|
||||||
state = &DerivationGoal::loadDerivation;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::loadDerivation()
|
void DerivationGoal::loadDerivation()
|
||||||
{
|
{
|
||||||
trace("loading derivation");
|
trace("loading derivation");
|
||||||
|
@ -368,19 +350,36 @@ void DerivationGoal::gaveUpOnSubstitution()
|
||||||
|
|
||||||
/* The inputs must be built before we can build this goal. */
|
/* The inputs must be built before we can build this goal. */
|
||||||
inputDrvOutputs.clear();
|
inputDrvOutputs.clear();
|
||||||
if (useDerivation)
|
if (useDerivation) {
|
||||||
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
|
std::function<void(ref<SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)> addWaiteeDerivedPath;
|
||||||
|
|
||||||
|
addWaiteeDerivedPath = [&](ref<SingleDerivedPath> inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
if (!inputNode.value.empty())
|
||||||
|
addWaitee(worker.makeGoal(
|
||||||
|
DerivedPath::Built {
|
||||||
|
.drvPath = inputDrv,
|
||||||
|
.outputs = inputNode.value,
|
||||||
|
},
|
||||||
|
buildMode == bmRepair ? bmRepair : bmNormal));
|
||||||
|
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
addWaiteeDerivedPath(
|
||||||
|
make_ref<SingleDerivedPath>(SingleDerivedPath::Built { inputDrv, outputName }),
|
||||||
|
childNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & [inputDrvPath, inputNode] : dynamic_cast<Derivation *>(drv.get())->inputDrvs.map) {
|
||||||
/* Ensure that pure, non-fixed-output derivations don't
|
/* Ensure that pure, non-fixed-output derivations don't
|
||||||
depend on impure derivations. */
|
depend on impure derivations. */
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
|
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
|
||||||
auto inputDrv = worker.evalStore.readDerivation(i.first);
|
auto inputDrv = worker.evalStore.readDerivation(inputDrvPath);
|
||||||
if (!inputDrv.type().isPure())
|
if (!inputDrv.type().isPure())
|
||||||
throw Error("pure derivation '%s' depends on impure derivation '%s'",
|
throw Error("pure derivation '%s' depends on impure derivation '%s'",
|
||||||
worker.store.printStorePath(drvPath),
|
worker.store.printStorePath(drvPath),
|
||||||
worker.store.printStorePath(i.first));
|
worker.store.printStorePath(inputDrvPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
|
addWaiteeDerivedPath(makeConstantStorePathRef(inputDrvPath), inputNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the input sources from the eval store to the build
|
/* Copy the input sources from the eval store to the build
|
||||||
|
@ -452,7 +451,12 @@ void DerivationGoal::repairClosure()
|
||||||
if (drvPath2 == outputsToDrv.end())
|
if (drvPath2 == outputsToDrv.end())
|
||||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
||||||
else
|
else
|
||||||
addWaitee(worker.makeDerivationGoal(drvPath2->second, OutputsSpec::All(), bmRepair));
|
addWaitee(worker.makeGoal(
|
||||||
|
DerivedPath::Built {
|
||||||
|
.drvPath = makeConstantStorePathRef(drvPath2->second),
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
|
bmRepair));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitees.empty()) {
|
if (waitees.empty()) {
|
||||||
|
@ -509,7 +513,7 @@ void DerivationGoal::inputsRealised()
|
||||||
return ia.deferred;
|
return ia.deferred;
|
||||||
},
|
},
|
||||||
[&](const DerivationType::ContentAddressed & ca) {
|
[&](const DerivationType::ContentAddressed & ca) {
|
||||||
return !fullDrv.inputDrvs.empty() && (
|
return !fullDrv.inputDrvs.map.empty() && (
|
||||||
ca.fixed
|
ca.fixed
|
||||||
/* Can optionally resolve if fixed, which is good
|
/* Can optionally resolve if fixed, which is good
|
||||||
for avoiding unnecessary rebuilds. */
|
for avoiding unnecessary rebuilds. */
|
||||||
|
@ -523,7 +527,7 @@ void DerivationGoal::inputsRealised()
|
||||||
}
|
}
|
||||||
}, drvType.raw);
|
}, drvType.raw);
|
||||||
|
|
||||||
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
|
if (resolveDrv && !fullDrv.inputDrvs.map.empty()) {
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
|
|
||||||
/* We are be able to resolve this derivation based on the
|
/* We are be able to resolve this derivation based on the
|
||||||
|
@ -560,11 +564,13 @@ void DerivationGoal::inputsRealised()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & [depDrvPath, wantedDepOutputs] : fullDrv.inputDrvs) {
|
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths;
|
||||||
|
|
||||||
|
accumInputPaths = [&](const StorePath & depDrvPath, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
/* Add the relevant output closures of the input derivation
|
/* Add the relevant output closures of the input derivation
|
||||||
`i' as input paths. Only add the closures of output paths
|
`i' as input paths. Only add the closures of output paths
|
||||||
that are specified as inputs. */
|
that are specified as inputs. */
|
||||||
for (auto & j : wantedDepOutputs) {
|
auto getOutput = [&](const std::string & outputName) {
|
||||||
/* TODO (impure derivations-induced tech debt):
|
/* TODO (impure derivations-induced tech debt):
|
||||||
Tracking input derivation outputs statefully through the
|
Tracking input derivation outputs statefully through the
|
||||||
goals is error prone and has led to bugs.
|
goals is error prone and has led to bugs.
|
||||||
|
@ -576,21 +582,30 @@ void DerivationGoal::inputsRealised()
|
||||||
a representation in the store, which is a usability problem
|
a representation in the store, which is a usability problem
|
||||||
in itself. When implementing this logic entirely with lookups
|
in itself. When implementing this logic entirely with lookups
|
||||||
make sure that they're cached. */
|
make sure that they're cached. */
|
||||||
if (auto outPath = get(inputDrvOutputs, { depDrvPath, j })) {
|
if (auto outPath = get(inputDrvOutputs, { depDrvPath, outputName })) {
|
||||||
worker.store.computeFSClosure(*outPath, inputPaths);
|
return *outPath;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto outMap = worker.evalStore.queryDerivationOutputMap(depDrvPath);
|
auto outMap = worker.evalStore.queryDerivationOutputMap(depDrvPath);
|
||||||
auto outMapPath = outMap.find(j);
|
auto outMapPath = outMap.find(outputName);
|
||||||
if (outMapPath == outMap.end()) {
|
if (outMapPath == outMap.end()) {
|
||||||
throw Error(
|
throw Error(
|
||||||
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
|
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
|
||||||
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
|
worker.store.printStorePath(drvPath), outputName, worker.store.printStorePath(depDrvPath));
|
||||||
}
|
|
||||||
worker.store.computeFSClosure(outMapPath->second, inputPaths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return outMapPath->second;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto & outputName : inputNode.value)
|
||||||
|
worker.store.computeFSClosure(getOutput(outputName), inputPaths);
|
||||||
|
|
||||||
|
for (auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
accumInputPaths(getOutput(outputName), childNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto & [depDrvPath, depNode] : fullDrv.inputDrvs.map)
|
||||||
|
accumInputPaths(depDrvPath, depNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second, the input sources. */
|
/* Second, the input sources. */
|
||||||
|
@ -1483,22 +1498,24 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
|
||||||
if (!useDerivation) return;
|
if (!useDerivation) return;
|
||||||
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
|
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
|
||||||
|
|
||||||
auto * dg = dynamic_cast<DerivationGoal *>(&*waitee);
|
std::optional info = tryGetConcreteDrvGoal(waitee);
|
||||||
if (!dg) return;
|
if (!info) return;
|
||||||
|
const auto & [dg, drvReq] = *info;
|
||||||
|
|
||||||
auto outputs = fullDrv.inputDrvs.find(dg->drvPath);
|
auto * nodeP = fullDrv.inputDrvs.findSlot(drvReq.get());
|
||||||
if (outputs == fullDrv.inputDrvs.end()) return;
|
if (!nodeP) return;
|
||||||
|
auto & outputs = nodeP->value;
|
||||||
|
|
||||||
for (auto & outputName : outputs->second) {
|
for (auto & outputName : outputs) {
|
||||||
auto buildResult = dg->getBuildResult(DerivedPath::Built {
|
auto buildResult = dg.get().getBuildResult(DerivedPath::Built {
|
||||||
.drvPath = makeConstantStorePathRef(dg->drvPath),
|
.drvPath = makeConstantStorePathRef(dg.get().drvPath),
|
||||||
.outputs = OutputsSpec::Names { outputName },
|
.outputs = OutputsSpec::Names { outputName },
|
||||||
});
|
});
|
||||||
if (buildResult.success()) {
|
if (buildResult.success()) {
|
||||||
auto i = buildResult.builtOutputs.find(outputName);
|
auto i = buildResult.builtOutputs.find(outputName);
|
||||||
if (i != buildResult.builtOutputs.end())
|
if (i != buildResult.builtOutputs.end())
|
||||||
inputDrvOutputs.insert_or_assign(
|
inputDrvOutputs.insert_or_assign(
|
||||||
{ dg->drvPath, outputName },
|
{ dg.get().drvPath, outputName },
|
||||||
i->second.outPath);
|
i->second.outPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,13 @@ struct InitialOutput {
|
||||||
std::optional<InitialOutputStatus> known;
|
std::optional<InitialOutputStatus> known;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A goal for building some or all of the outputs of a derivation.
|
||||||
|
*
|
||||||
|
* The derivation must already be present, either in the store in a drv
|
||||||
|
* or in memory. If the derivation itself needs to be gotten first, a
|
||||||
|
* `CreateDerivationAndRealiseGoal` goal must be used instead.
|
||||||
|
*/
|
||||||
struct DerivationGoal : public Goal
|
struct DerivationGoal : public Goal
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -66,8 +73,7 @@ struct DerivationGoal : public Goal
|
||||||
std::shared_ptr<DerivationGoal> resolvedDrvGoal;
|
std::shared_ptr<DerivationGoal> resolvedDrvGoal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The specific outputs that we need to build. Empty means all of
|
* The specific outputs that we need to build.
|
||||||
* them.
|
|
||||||
*/
|
*/
|
||||||
OutputsSpec wantedOutputs;
|
OutputsSpec wantedOutputs;
|
||||||
|
|
||||||
|
@ -229,7 +235,6 @@ struct DerivationGoal : public Goal
|
||||||
/**
|
/**
|
||||||
* The states.
|
* The states.
|
||||||
*/
|
*/
|
||||||
void getDerivation();
|
|
||||||
void loadDerivation();
|
void loadDerivation();
|
||||||
void haveDerivation();
|
void haveDerivation();
|
||||||
void outputsSubstitutionTried();
|
void outputsSubstitutionTried();
|
||||||
|
@ -334,7 +339,9 @@ struct DerivationGoal : public Goal
|
||||||
|
|
||||||
StorePathSet exportReferences(const StorePathSet & storePaths);
|
StorePathSet exportReferences(const StorePathSet & storePaths);
|
||||||
|
|
||||||
JobCategory jobCategory() override { return JobCategory::Build; };
|
JobCategory jobCategory() const override {
|
||||||
|
return JobCategory::Build;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
MakeError(NotDeterministic, BuildError);
|
MakeError(NotDeterministic, BuildError);
|
||||||
|
|
|
@ -73,7 +73,9 @@ public:
|
||||||
void work() override;
|
void work() override;
|
||||||
void handleEOF(int fd) override;
|
void handleEOF(int fd) override;
|
||||||
|
|
||||||
JobCategory jobCategory() override { return JobCategory::Substitution; };
|
JobCategory jobCategory() const override {
|
||||||
|
return JobCategory::Substitution;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "worker.hh"
|
#include "worker.hh"
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
|
#include "create-derivation-and-realise-goal.hh"
|
||||||
#include "derivation-goal.hh"
|
#include "derivation-goal.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
|
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
|
|
||||||
StorePathSet failed;
|
StringSet failed;
|
||||||
std::optional<Error> ex;
|
std::optional<Error> ex;
|
||||||
for (auto & i : goals) {
|
for (auto & i : goals) {
|
||||||
if (i->ex) {
|
if (i->ex) {
|
||||||
|
@ -25,8 +26,10 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
ex = std::move(i->ex);
|
ex = std::move(i->ex);
|
||||||
}
|
}
|
||||||
if (i->exitCode != Goal::ecSuccess) {
|
if (i->exitCode != Goal::ecSuccess) {
|
||||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get())) failed.insert(i2->drvPath);
|
if (auto i2 = dynamic_cast<CreateDerivationAndRealiseGoal *>(i.get()))
|
||||||
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get())) failed.insert(i2->storePath);
|
failed.insert(i2->drvReq->to_string(*this));
|
||||||
|
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
||||||
|
failed.insert(printStorePath(i2->storePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
throw std::move(*ex);
|
throw std::move(*ex);
|
||||||
} else if (!failed.empty()) {
|
} else if (!failed.empty()) {
|
||||||
if (ex) logError(ex->info());
|
if (ex) logError(ex->info());
|
||||||
throw Error(worker.failingExitStatus(), "build of %s failed", showPaths(failed));
|
throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,8 +127,11 @@ void Store::repairPath(const StorePath & path)
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
if (info->deriver && isValidPath(*info->deriver)) {
|
if (info->deriver && isValidPath(*info->deriver)) {
|
||||||
goals.clear();
|
goals.clear();
|
||||||
|
goals.insert(worker.makeGoal(DerivedPath::Built {
|
||||||
|
.drvPath = makeConstantStorePathRef(*info->deriver),
|
||||||
// FIXME: Should just build the specific output we need.
|
// FIXME: Should just build the specific output we need.
|
||||||
goals.insert(worker.makeDerivationGoal(*info->deriver, OutputsSpec::All { }, bmRepair));
|
.outputs = OutputsSpec::All { },
|
||||||
|
}, bmRepair));
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
} else
|
} else
|
||||||
throw Error(worker.failingExitStatus(), "cannot repair path '%s'", printStorePath(path));
|
throw Error(worker.failingExitStatus(), "cannot repair path '%s'", printStorePath(path));
|
||||||
|
|
|
@ -11,7 +11,7 @@ bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BuildResult Goal::getBuildResult(const DerivedPath & req) {
|
BuildResult Goal::getBuildResult(const DerivedPath & req) const {
|
||||||
BuildResult res { buildResult };
|
BuildResult res { buildResult };
|
||||||
|
|
||||||
if (auto pbp = std::get_if<DerivedPath::Built>(&req)) {
|
if (auto pbp = std::get_if<DerivedPath::Built>(&req)) {
|
||||||
|
|
|
@ -41,8 +41,24 @@ typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
||||||
* of each category in parallel.
|
* of each category in parallel.
|
||||||
*/
|
*/
|
||||||
enum struct JobCategory {
|
enum struct JobCategory {
|
||||||
|
/**
|
||||||
|
* A build of a derivation; it will use CPU and disk resources.
|
||||||
|
*/
|
||||||
Build,
|
Build,
|
||||||
|
/**
|
||||||
|
* A substitution an arbitrary store object; it will use network resources.
|
||||||
|
*/
|
||||||
Substitution,
|
Substitution,
|
||||||
|
/**
|
||||||
|
* A goal that does no "real" work by itself, and just exists to depend on
|
||||||
|
* other goals which *do* do real work. These goals therefore are not
|
||||||
|
* limited.
|
||||||
|
*
|
||||||
|
* These goals cannot infinitely create themselves, so there is no risk of
|
||||||
|
* a "fork bomb" type situation (which would be a problem even though the
|
||||||
|
* goal do no real work) either.
|
||||||
|
*/
|
||||||
|
Administration,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Goal : public std::enable_shared_from_this<Goal>
|
struct Goal : public std::enable_shared_from_this<Goal>
|
||||||
|
@ -110,7 +126,7 @@ public:
|
||||||
* sake of both privacy and determinism, and this "safe accessor"
|
* sake of both privacy and determinism, and this "safe accessor"
|
||||||
* ensures we don't.
|
* ensures we don't.
|
||||||
*/
|
*/
|
||||||
BuildResult getBuildResult(const DerivedPath &);
|
BuildResult getBuildResult(const DerivedPath &) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception containing an error message, if any.
|
* Exception containing an error message, if any.
|
||||||
|
@ -144,7 +160,7 @@ public:
|
||||||
|
|
||||||
void trace(std::string_view s);
|
void trace(std::string_view s);
|
||||||
|
|
||||||
std::string getName()
|
std::string getName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +182,7 @@ public:
|
||||||
* @brief Hint for the scheduler, which concurrency limit applies.
|
* @brief Hint for the scheduler, which concurrency limit applies.
|
||||||
* @see JobCategory
|
* @see JobCategory
|
||||||
*/
|
*/
|
||||||
virtual JobCategory jobCategory() = 0;
|
virtual JobCategory jobCategory() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
||||||
|
|
|
@ -2955,7 +2955,7 @@ bool LocalDerivationGoal::isReadDesc(int fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalDerivationGoal::makeFallbackPath(std::string_view outputName)
|
StorePath LocalDerivationGoal::makeFallbackPath(OutputNameView outputName)
|
||||||
{
|
{
|
||||||
return worker.store.makeStorePath(
|
return worker.store.makeStorePath(
|
||||||
"rewrite:" + std::string(drvPath.to_string()) + ":name:" + std::string(outputName),
|
"rewrite:" + std::string(drvPath.to_string()) + ":name:" + std::string(outputName),
|
||||||
|
|
|
@ -297,7 +297,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
* @todo Add option to randomize, so we can audit whether our
|
* @todo Add option to randomize, so we can audit whether our
|
||||||
* rewrites caught everything
|
* rewrites caught everything
|
||||||
*/
|
*/
|
||||||
StorePath makeFallbackPath(std::string_view outputName);
|
StorePath makeFallbackPath(OutputNameView outputName);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,9 @@ public:
|
||||||
/* Called by destructor, can't be overridden */
|
/* Called by destructor, can't be overridden */
|
||||||
void cleanup() override final;
|
void cleanup() override final;
|
||||||
|
|
||||||
JobCategory jobCategory() override { return JobCategory::Substitution; };
|
JobCategory jobCategory() const override {
|
||||||
|
return JobCategory::Substitution;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "worker.hh"
|
#include "worker.hh"
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
#include "drv-output-substitution-goal.hh"
|
#include "drv-output-substitution-goal.hh"
|
||||||
|
#include "create-derivation-and-realise-goal.hh"
|
||||||
#include "local-derivation-goal.hh"
|
#include "local-derivation-goal.hh"
|
||||||
#include "hook-instance.hh"
|
#include "hook-instance.hh"
|
||||||
|
|
||||||
|
@ -41,6 +42,24 @@ Worker::~Worker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<CreateDerivationAndRealiseGoal> Worker::makeCreateDerivationAndRealiseGoal(
|
||||||
|
ref<SingleDerivedPath> drvReq,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
BuildMode buildMode)
|
||||||
|
{
|
||||||
|
std::weak_ptr<CreateDerivationAndRealiseGoal> & goal_weak = outerDerivationGoals.ensureSlot(*drvReq).value;
|
||||||
|
std::shared_ptr<CreateDerivationAndRealiseGoal> goal = goal_weak.lock();
|
||||||
|
if (!goal) {
|
||||||
|
goal = std::make_shared<CreateDerivationAndRealiseGoal>(drvReq, wantedOutputs, *this, buildMode);
|
||||||
|
goal_weak = goal;
|
||||||
|
wakeUp(goal);
|
||||||
|
} else {
|
||||||
|
goal->addWantedOutputs(wantedOutputs);
|
||||||
|
}
|
||||||
|
return goal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
const OutputsSpec & wantedOutputs,
|
const OutputsSpec & wantedOutputs,
|
||||||
|
@ -111,10 +130,7 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
||||||
if (auto bop = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath))
|
return makeCreateDerivationAndRealiseGoal(bfd.drvPath, bfd.outputs, buildMode);
|
||||||
return makeDerivationGoal(bop->path, bfd.outputs, buildMode);
|
|
||||||
else
|
|
||||||
throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented.");
|
|
||||||
},
|
},
|
||||||
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
||||||
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
||||||
|
@ -123,24 +139,46 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename K, typename V, typename F>
|
||||||
|
static void cullMap(std::map<K, V> & goalMap, F f)
|
||||||
|
{
|
||||||
|
for (auto i = goalMap.begin(); i != goalMap.end();)
|
||||||
|
if (!f(i->second))
|
||||||
|
i = goalMap.erase(i);
|
||||||
|
else ++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename K, typename G>
|
template<typename K, typename G>
|
||||||
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
|
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
|
||||||
{
|
{
|
||||||
/* !!! inefficient */
|
/* !!! inefficient */
|
||||||
for (auto i = goalMap.begin();
|
cullMap(goalMap, [&](const std::weak_ptr<G> & gp) -> bool {
|
||||||
i != goalMap.end(); )
|
return gp.lock() != goal;
|
||||||
if (i->second.lock() == goal) {
|
});
|
||||||
auto j = i; ++j;
|
|
||||||
goalMap.erase(i);
|
|
||||||
i = j;
|
|
||||||
}
|
}
|
||||||
else ++i;
|
|
||||||
|
template<typename K>
|
||||||
|
static void removeGoal(std::shared_ptr<CreateDerivationAndRealiseGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>>::ChildNode> & goalMap);
|
||||||
|
|
||||||
|
template<typename K>
|
||||||
|
static void removeGoal(std::shared_ptr<CreateDerivationAndRealiseGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>>::ChildNode> & goalMap)
|
||||||
|
{
|
||||||
|
/* !!! inefficient */
|
||||||
|
cullMap(goalMap, [&](DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>>::ChildNode & node) -> bool {
|
||||||
|
if (node.value.lock() == goal)
|
||||||
|
node.value.reset();
|
||||||
|
removeGoal(goal, node.childMap);
|
||||||
|
return !node.value.expired() || !node.childMap.empty();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Worker::removeGoal(GoalPtr goal)
|
void Worker::removeGoal(GoalPtr goal)
|
||||||
{
|
{
|
||||||
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
if (auto drvGoal = std::dynamic_pointer_cast<CreateDerivationAndRealiseGoal>(goal))
|
||||||
|
nix::removeGoal(drvGoal, outerDerivationGoals.map);
|
||||||
|
else if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
||||||
nix::removeGoal(drvGoal, derivationGoals);
|
nix::removeGoal(drvGoal, derivationGoals);
|
||||||
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
||||||
nix::removeGoal(subGoal, substitutionGoals);
|
nix::removeGoal(subGoal, substitutionGoals);
|
||||||
|
@ -198,8 +236,19 @@ void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
|
||||||
child.respectTimeouts = respectTimeouts;
|
child.respectTimeouts = respectTimeouts;
|
||||||
children.emplace_back(child);
|
children.emplace_back(child);
|
||||||
if (inBuildSlot) {
|
if (inBuildSlot) {
|
||||||
if (goal->jobCategory() == JobCategory::Substitution) nrSubstitutions++;
|
switch (goal->jobCategory()) {
|
||||||
else nrLocalBuilds++;
|
case JobCategory::Substitution:
|
||||||
|
nrSubstitutions++;
|
||||||
|
break;
|
||||||
|
case JobCategory::Build:
|
||||||
|
nrLocalBuilds++;
|
||||||
|
break;
|
||||||
|
case JobCategory::Administration:
|
||||||
|
/* Intentionally not limited, see docs */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,12 +260,20 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers)
|
||||||
if (i == children.end()) return;
|
if (i == children.end()) return;
|
||||||
|
|
||||||
if (i->inBuildSlot) {
|
if (i->inBuildSlot) {
|
||||||
if (goal->jobCategory() == JobCategory::Substitution) {
|
switch (goal->jobCategory()) {
|
||||||
|
case JobCategory::Substitution:
|
||||||
assert(nrSubstitutions > 0);
|
assert(nrSubstitutions > 0);
|
||||||
nrSubstitutions--;
|
nrSubstitutions--;
|
||||||
} else {
|
break;
|
||||||
|
case JobCategory::Build:
|
||||||
assert(nrLocalBuilds > 0);
|
assert(nrLocalBuilds > 0);
|
||||||
nrLocalBuilds--;
|
nrLocalBuilds--;
|
||||||
|
break;
|
||||||
|
case JobCategory::Administration:
|
||||||
|
/* Intentionally not limited, see docs */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,9 +324,9 @@ void Worker::run(const Goals & _topGoals)
|
||||||
|
|
||||||
for (auto & i : _topGoals) {
|
for (auto & i : _topGoals) {
|
||||||
topGoals.insert(i);
|
topGoals.insert(i);
|
||||||
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
if (auto goal = dynamic_cast<CreateDerivationAndRealiseGoal *>(i.get())) {
|
||||||
topPaths.push_back(DerivedPath::Built {
|
topPaths.push_back(DerivedPath::Built {
|
||||||
.drvPath = makeConstantStorePathRef(goal->drvPath),
|
.drvPath = goal->drvReq,
|
||||||
.outputs = goal->wantedOutputs,
|
.outputs = goal->wantedOutputs,
|
||||||
});
|
});
|
||||||
} else if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
} else if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
||||||
|
@ -522,11 +579,29 @@ void Worker::markContentsGood(const StorePath & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal) {
|
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal)
|
||||||
return subGoal;
|
{
|
||||||
}
|
|
||||||
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal) {
|
|
||||||
return subGoal;
|
return subGoal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal)
|
||||||
|
{
|
||||||
|
return subGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal)
|
||||||
|
{
|
||||||
|
return subGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<std::reference_wrapper<const DerivationGoal>, std::reference_wrapper<const SingleDerivedPath>>> tryGetConcreteDrvGoal(GoalPtr waitee)
|
||||||
|
{
|
||||||
|
auto * odg = dynamic_cast<CreateDerivationAndRealiseGoal *>(&*waitee);
|
||||||
|
if (!odg) return std::nullopt;
|
||||||
|
return {{
|
||||||
|
std::cref(*odg->concreteDrvGoal),
|
||||||
|
std::cref(*odg->drvReq),
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "lock.hh"
|
#include "lock.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "derived-path-map.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/* Forward definition. */
|
/* Forward definition. */
|
||||||
|
struct CreateDerivationAndRealiseGoal;
|
||||||
struct DerivationGoal;
|
struct DerivationGoal;
|
||||||
struct PathSubstitutionGoal;
|
struct PathSubstitutionGoal;
|
||||||
class DrvOutputSubstitutionGoal;
|
class DrvOutputSubstitutionGoal;
|
||||||
|
@ -31,9 +33,24 @@ class DrvOutputSubstitutionGoal;
|
||||||
*/
|
*/
|
||||||
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal);
|
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal);
|
||||||
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal);
|
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal);
|
||||||
|
GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal);
|
||||||
|
|
||||||
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current implementation of impure derivations has
|
||||||
|
* `DerivationGoal`s accumulate realisations from their waitees.
|
||||||
|
* Unfortunately, `DerivationGoal`s don't directly depend on other
|
||||||
|
* goals, but instead depend on `CreateDerivationAndRealiseGoal`s.
|
||||||
|
*
|
||||||
|
* We try not to share any of the details of any goal type with any
|
||||||
|
* other, for sake of modularity and quicker rebuilds. This means we
|
||||||
|
* cannot "just" downcast and fish out the field. So as an escape hatch,
|
||||||
|
* we have made the function, written in `worker.cc` where all the goal
|
||||||
|
* types are visible, and use it instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::optional<std::pair<std::reference_wrapper<const DerivationGoal>, std::reference_wrapper<const SingleDerivedPath>>> tryGetConcreteDrvGoal(GoalPtr waitee);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping used to remember for each child process to what goal it
|
* A mapping used to remember for each child process to what goal it
|
||||||
|
@ -102,6 +119,9 @@ private:
|
||||||
* Maps used to prevent multiple instantiations of a goal for the
|
* Maps used to prevent multiple instantiations of a goal for the
|
||||||
* same derivation / path.
|
* same derivation / path.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>> outerDerivationGoals;
|
||||||
|
|
||||||
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
||||||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||||
|
@ -189,6 +209,9 @@ public:
|
||||||
* @ref DerivationGoal "derivation goal"
|
* @ref DerivationGoal "derivation goal"
|
||||||
*/
|
*/
|
||||||
private:
|
private:
|
||||||
|
std::shared_ptr<CreateDerivationAndRealiseGoal> makeCreateDerivationAndRealiseGoal(
|
||||||
|
ref<SingleDerivedPath> drvPath,
|
||||||
|
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
|
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const
|
std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, OutputNameView outputName) const
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> {
|
[](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> {
|
||||||
|
@ -36,7 +36,7 @@ std::optional<StorePath> DerivationOutput::path(const Store & store, std::string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const
|
StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, OutputNameView outputName) const
|
||||||
{
|
{
|
||||||
return store.makeFixedOutputPathFromCA(
|
return store.makeFixedOutputPathFromCA(
|
||||||
outputPathName(drvName, outputName),
|
outputPathName(drvName, outputName),
|
||||||
|
@ -136,7 +136,7 @@ StorePath writeDerivation(Store & store,
|
||||||
const Derivation & drv, RepairFlag repair, bool readOnly)
|
const Derivation & drv, RepairFlag repair, bool readOnly)
|
||||||
{
|
{
|
||||||
auto references = drv.inputSrcs;
|
auto references = drv.inputSrcs;
|
||||||
for (auto & i : drv.inputDrvs)
|
for (auto & i : drv.inputDrvs.map)
|
||||||
references.insert(i.first);
|
references.insert(i.first);
|
||||||
/* Note that the outputs of a derivation are *not* references
|
/* Note that the outputs of a derivation are *not* references
|
||||||
(that can be missing (of course) and should not necessarily be
|
(that can be missing (of course) and should not necessarily be
|
||||||
|
@ -154,8 +154,9 @@ static void expect(std::istream & str, std::string_view s)
|
||||||
{
|
{
|
||||||
char s2[s.size()];
|
char s2[s.size()];
|
||||||
str.read(s2, s.size());
|
str.read(s2, s.size());
|
||||||
if (std::string(s2, s.size()) != s)
|
std::string_view s2View { s2, s.size() };
|
||||||
throw FormatError("expected string '%1%'", s);
|
if (s2View != s)
|
||||||
|
throw FormatError("expected string '%s', got '%s'", s, s2View);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -207,23 +208,27 @@ static bool endOfList(std::istream & str)
|
||||||
static StringSet parseStrings(std::istream & str, bool arePaths)
|
static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||||
{
|
{
|
||||||
StringSet res;
|
StringSet res;
|
||||||
|
expect(str, "[");
|
||||||
while (!endOfList(str))
|
while (!endOfList(str))
|
||||||
res.insert(arePaths ? parsePath(str) : parseString(str));
|
res.insert(arePaths ? parsePath(str) : parseString(str));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static DerivationOutput parseDerivationOutput(const Store & store,
|
static DerivationOutput parseDerivationOutput(
|
||||||
std::string_view pathS, std::string_view hashAlgo, std::string_view hashS)
|
const Store & store,
|
||||||
|
std::string_view pathS, std::string_view hashAlgo, std::string_view hashS,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
if (hashAlgo != "") {
|
if (hashAlgo != "") {
|
||||||
ContentAddressMethod method = ContentAddressMethod::parsePrefix(hashAlgo);
|
ContentAddressMethod method = ContentAddressMethod::parsePrefix(hashAlgo);
|
||||||
if (method == TextIngestionMethod {})
|
if (method == TextIngestionMethod {})
|
||||||
experimentalFeatureSettings.require(Xp::DynamicDerivations);
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
const auto hashType = parseHashType(hashAlgo);
|
const auto hashType = parseHashType(hashAlgo);
|
||||||
if (hashS == "impure") {
|
if (hashS == "impure") {
|
||||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
xpSettings.require(Xp::ImpureDerivations);
|
||||||
assert(pathS == "");
|
if (pathS != "")
|
||||||
|
throw FormatError("impure derivation output should not specify output path");
|
||||||
return DerivationOutput::Impure {
|
return DerivationOutput::Impure {
|
||||||
.method = std::move(method),
|
.method = std::move(method),
|
||||||
.hashType = std::move(hashType),
|
.hashType = std::move(hashType),
|
||||||
|
@ -238,8 +243,9 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
xpSettings.require(Xp::CaDerivations);
|
||||||
assert(pathS == "");
|
if (pathS != "")
|
||||||
|
throw FormatError("content-addressed derivation output should not specify output path");
|
||||||
return DerivationOutput::CAFloating {
|
return DerivationOutput::CAFloating {
|
||||||
.method = std::move(method),
|
.method = std::move(method),
|
||||||
.hashType = std::move(hashType),
|
.hashType = std::move(hashType),
|
||||||
|
@ -256,29 +262,116 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str)
|
static DerivationOutput parseDerivationOutput(
|
||||||
|
const Store & store, std::istringstream & str,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings)
|
||||||
{
|
{
|
||||||
expect(str, ","); const auto pathS = parseString(str);
|
expect(str, ","); const auto pathS = parseString(str);
|
||||||
expect(str, ","); const auto hashAlgo = parseString(str);
|
expect(str, ","); const auto hashAlgo = parseString(str);
|
||||||
expect(str, ","); const auto hash = parseString(str);
|
expect(str, ","); const auto hash = parseString(str);
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
|
|
||||||
return parseDerivationOutput(store, pathS, hashAlgo, hash);
|
return parseDerivationOutput(store, pathS, hashAlgo, hash, xpSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All ATerm Derivation format versions currently known.
|
||||||
|
*
|
||||||
|
* Unknown versions are rejected at the parsing stage.
|
||||||
|
*/
|
||||||
|
enum struct DerivationATermVersion {
|
||||||
|
/**
|
||||||
|
* Older unversioned form
|
||||||
|
*/
|
||||||
|
Traditional,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Newer versioned form; only this version so far.
|
||||||
|
*/
|
||||||
|
DynamicDerivations,
|
||||||
|
};
|
||||||
|
|
||||||
|
static DerivedPathMap<StringSet>::ChildNode parseDerivedPathMapNode(
|
||||||
|
const Store & store,
|
||||||
|
std::istringstream & str,
|
||||||
|
DerivationATermVersion version)
|
||||||
|
{
|
||||||
|
DerivedPathMap<StringSet>::ChildNode node;
|
||||||
|
|
||||||
|
auto parseNonDynamic = [&]() {
|
||||||
|
node.value = parseStrings(str, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Older derivation should never use new form, but newer
|
||||||
|
// derivaiton can use old form.
|
||||||
|
switch (version) {
|
||||||
|
case DerivationATermVersion::Traditional:
|
||||||
|
parseNonDynamic();
|
||||||
|
break;
|
||||||
|
case DerivationATermVersion::DynamicDerivations:
|
||||||
|
switch (str.peek()) {
|
||||||
|
case '[':
|
||||||
|
parseNonDynamic();
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
expect(str, "(");
|
||||||
|
node.value = parseStrings(str, false);
|
||||||
|
expect(str, ",[");
|
||||||
|
while (!endOfList(str)) {
|
||||||
|
expect(str, "(");
|
||||||
|
auto outputName = parseString(str);
|
||||||
|
expect(str, ",");
|
||||||
|
node.childMap.insert_or_assign(outputName, parseDerivedPathMapNode(store, str, version));
|
||||||
|
expect(str, ")");
|
||||||
|
}
|
||||||
|
expect(str, ")");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw FormatError("invalid inputDrvs entry in derivation");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// invalid format, not a parse error but internal error
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name)
|
Derivation parseDerivation(
|
||||||
|
const Store & store, std::string && s, std::string_view name,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
Derivation drv;
|
Derivation drv;
|
||||||
drv.name = name;
|
drv.name = name;
|
||||||
|
|
||||||
std::istringstream str(std::move(s));
|
std::istringstream str(std::move(s));
|
||||||
expect(str, "Derive([");
|
expect(str, "D");
|
||||||
|
DerivationATermVersion version;
|
||||||
|
switch (str.peek()) {
|
||||||
|
case 'e':
|
||||||
|
expect(str, "erive(");
|
||||||
|
version = DerivationATermVersion::Traditional;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
expect(str, "rvWithVersion(");
|
||||||
|
auto versionS = parseString(str);
|
||||||
|
if (versionS == "xp-dyn-drv") {
|
||||||
|
// Only verison we have so far
|
||||||
|
version = DerivationATermVersion::DynamicDerivations;
|
||||||
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
|
} else {
|
||||||
|
throw FormatError("Unknown derivation ATerm format version '%s'", versionS);
|
||||||
|
}
|
||||||
|
expect(str, ",");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse the list of outputs. */
|
/* Parse the list of outputs. */
|
||||||
|
expect(str, "[");
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "("); std::string id = parseString(str);
|
expect(str, "("); std::string id = parseString(str);
|
||||||
auto output = parseDerivationOutput(store, str);
|
auto output = parseDerivationOutput(store, str, xpSettings);
|
||||||
drv.outputs.emplace(std::move(id), std::move(output));
|
drv.outputs.emplace(std::move(id), std::move(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,12 +380,12 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "(");
|
expect(str, "(");
|
||||||
Path drvPath = parsePath(str);
|
Path drvPath = parsePath(str);
|
||||||
expect(str, ",[");
|
expect(str, ",");
|
||||||
drv.inputDrvs.insert_or_assign(store.parseStorePath(drvPath), parseStrings(str, false));
|
drv.inputDrvs.map.insert_or_assign(store.parseStorePath(drvPath), parseDerivedPathMapNode(store, str, version));
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(str, ",["); drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true));
|
expect(str, ","); drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true));
|
||||||
expect(str, ","); drv.platform = parseString(str);
|
expect(str, ","); drv.platform = parseString(str);
|
||||||
expect(str, ","); drv.builder = parseString(str);
|
expect(str, ","); drv.builder = parseString(str);
|
||||||
|
|
||||||
|
@ -376,14 +469,67 @@ static void printUnquotedStrings(std::string & res, ForwardIterator i, ForwardIt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unparseDerivedPathMapNode(const Store & store, std::string & s, const DerivedPathMap<StringSet>::ChildNode & node)
|
||||||
|
{
|
||||||
|
s += ',';
|
||||||
|
if (node.childMap.empty()) {
|
||||||
|
printUnquotedStrings(s, node.value.begin(), node.value.end());
|
||||||
|
} else {
|
||||||
|
s += "(";
|
||||||
|
printUnquotedStrings(s, node.value.begin(), node.value.end());
|
||||||
|
s += ",[";
|
||||||
|
bool first = true;
|
||||||
|
for (auto & [outputName, childNode] : node.childMap) {
|
||||||
|
if (first) first = false; else s += ',';
|
||||||
|
s += '('; printUnquotedString(s, outputName);
|
||||||
|
unparseDerivedPathMapNode(store, s, childNode);
|
||||||
|
s += ')';
|
||||||
|
}
|
||||||
|
s += "])";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the derivation have a dependency on the output of a dynamic
|
||||||
|
* derivation?
|
||||||
|
*
|
||||||
|
* In other words, does it on the output of derivation that is itself an
|
||||||
|
* ouput of a derivation? This corresponds to a dependency that is an
|
||||||
|
* inductive derived path with more than one layer of
|
||||||
|
* `DerivedPath::Built`.
|
||||||
|
*/
|
||||||
|
static bool hasDynamicDrvDep(const Derivation & drv)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
std::find_if(
|
||||||
|
drv.inputDrvs.map.begin(),
|
||||||
|
drv.inputDrvs.map.end(),
|
||||||
|
[](auto & kv) { return !kv.second.childMap.empty(); })
|
||||||
|
!= drv.inputDrvs.map.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string Derivation::unparse(const Store & store, bool maskOutputs,
|
std::string Derivation::unparse(const Store & store, bool maskOutputs,
|
||||||
std::map<std::string, StringSet> * actualInputs) const
|
DerivedPathMap<StringSet>::ChildNode::Map * actualInputs) const
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
s.reserve(65536);
|
s.reserve(65536);
|
||||||
s += "Derive([";
|
|
||||||
|
/* Use older unversioned form if possible, for wider compat. Use
|
||||||
|
newer form only if we need it, which we do for
|
||||||
|
`Xp::DynamicDerivations`. */
|
||||||
|
if (hasDynamicDrvDep(*this)) {
|
||||||
|
s += "DrvWithVersion(";
|
||||||
|
// Only version we have so far
|
||||||
|
printUnquotedString(s, "xp-dyn-drv");
|
||||||
|
s += ",";
|
||||||
|
} else {
|
||||||
|
s += "Derive(";
|
||||||
|
}
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
s += "[";
|
||||||
for (auto & i : outputs) {
|
for (auto & i : outputs) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printUnquotedString(s, i.first);
|
s += '('; printUnquotedString(s, i.first);
|
||||||
|
@ -421,17 +567,17 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
|
||||||
s += "],[";
|
s += "],[";
|
||||||
first = true;
|
first = true;
|
||||||
if (actualInputs) {
|
if (actualInputs) {
|
||||||
for (auto & i : *actualInputs) {
|
for (auto & [drvHashModulo, childMap] : *actualInputs) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printUnquotedString(s, i.first);
|
s += '('; printUnquotedString(s, drvHashModulo);
|
||||||
s += ','; printUnquotedStrings(s, i.second.begin(), i.second.end());
|
unparseDerivedPathMapNode(store, s, childMap);
|
||||||
s += ')';
|
s += ')';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto & i : inputDrvs) {
|
for (auto & [drvPath, childMap] : inputDrvs.map) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printUnquotedString(s, store.printStorePath(i.first));
|
s += '('; printUnquotedString(s, store.printStorePath(drvPath));
|
||||||
s += ','; printUnquotedStrings(s, i.second.begin(), i.second.end());
|
unparseDerivedPathMapNode(store, s, childMap);
|
||||||
s += ')';
|
s += ')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,7 +612,7 @@ bool isDerivation(std::string_view fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string outputPathName(std::string_view drvName, std::string_view outputName) {
|
std::string outputPathName(std::string_view drvName, OutputNameView outputName) {
|
||||||
std::string res { drvName };
|
std::string res { drvName };
|
||||||
if (outputName != "out") {
|
if (outputName != "out") {
|
||||||
res += "-";
|
res += "-";
|
||||||
|
@ -665,18 +811,16 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
|
||||||
}
|
}
|
||||||
}, drv.type().raw);
|
}, drv.type().raw);
|
||||||
|
|
||||||
std::map<std::string, StringSet> inputs2;
|
DerivedPathMap<StringSet>::ChildNode::Map inputs2;
|
||||||
for (auto & [drvPath, inputOutputs0] : drv.inputDrvs) {
|
for (auto & [drvPath, node] : drv.inputDrvs.map) {
|
||||||
// Avoid lambda capture restriction with standard / Clang
|
|
||||||
auto & inputOutputs = inputOutputs0;
|
|
||||||
const auto & res = pathDerivationModulo(store, drvPath);
|
const auto & res = pathDerivationModulo(store, drvPath);
|
||||||
if (res.kind == DrvHash::Kind::Deferred)
|
if (res.kind == DrvHash::Kind::Deferred)
|
||||||
kind = DrvHash::Kind::Deferred;
|
kind = DrvHash::Kind::Deferred;
|
||||||
for (auto & outputName : inputOutputs) {
|
for (auto & outputName : node.value) {
|
||||||
const auto h = get(res.hashes, outputName);
|
const auto h = get(res.hashes, outputName);
|
||||||
if (!h)
|
if (!h)
|
||||||
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
|
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
|
||||||
inputs2[h->to_string(Base16, false)].insert(outputName);
|
inputs2[h->to_string(Base16, false)].value.insert(outputName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,7 +850,7 @@ static DerivationOutput readDerivationOutput(Source & in, const Store & store)
|
||||||
const auto hashAlgo = readString(in);
|
const auto hashAlgo = readString(in);
|
||||||
const auto hash = readString(in);
|
const auto hash = readString(in);
|
||||||
|
|
||||||
return parseDerivationOutput(store, pathS, hashAlgo, hash);
|
return parseDerivationOutput(store, pathS, hashAlgo, hash, experimentalFeatureSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSet BasicDerivation::outputNames() const
|
StringSet BasicDerivation::outputNames() const
|
||||||
|
@ -810,7 +954,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string hashPlaceholder(const std::string_view outputName)
|
std::string hashPlaceholder(const OutputNameView outputName)
|
||||||
{
|
{
|
||||||
// FIXME: memoize?
|
// FIXME: memoize?
|
||||||
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
|
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
|
||||||
|
@ -821,6 +965,8 @@ std::string hashPlaceholder(const std::string_view outputName)
|
||||||
|
|
||||||
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
|
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
|
||||||
{
|
{
|
||||||
|
debug("Rewriting the derivation");
|
||||||
|
|
||||||
for (auto & rewrite : rewrites) {
|
for (auto & rewrite : rewrites) {
|
||||||
debug("rewriting %s as %s", rewrite.first, rewrite.second);
|
debug("rewriting %s as %s", rewrite.first, rewrite.second);
|
||||||
}
|
}
|
||||||
|
@ -859,14 +1005,70 @@ std::optional<BasicDerivation> Derivation::tryResolve(Store & store) const
|
||||||
{
|
{
|
||||||
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
|
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
|
||||||
|
|
||||||
for (auto & input : inputDrvs)
|
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accum;
|
||||||
for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(input.first))
|
accum = [&](auto & inputDrv, auto & node) {
|
||||||
if (outputPath)
|
for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(inputDrv)) {
|
||||||
inputDrvOutputs.insert_or_assign({input.first, outputName}, *outputPath);
|
if (outputPath) {
|
||||||
|
inputDrvOutputs.insert_or_assign({inputDrv, outputName}, *outputPath);
|
||||||
|
if (auto p = get(node.childMap, outputName))
|
||||||
|
accum(*outputPath, *p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto & [inputDrv, node] : inputDrvs.map)
|
||||||
|
accum(inputDrv, node);
|
||||||
|
|
||||||
return tryResolve(store, inputDrvOutputs);
|
return tryResolve(store, inputDrvOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool tryResolveInput(
|
||||||
|
Store & store, StorePathSet & inputSrcs, StringMap & inputRewrites,
|
||||||
|
const DownstreamPlaceholder * placeholderOpt,
|
||||||
|
const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode,
|
||||||
|
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs)
|
||||||
|
{
|
||||||
|
auto getOutput = [&](const std::string & outputName) {
|
||||||
|
auto * actualPathOpt = get(inputDrvOutputs, { inputDrv, outputName });
|
||||||
|
if (!actualPathOpt)
|
||||||
|
warn("output %s of input %s missing, aborting the resolving",
|
||||||
|
outputName,
|
||||||
|
store.printStorePath(inputDrv)
|
||||||
|
);
|
||||||
|
return actualPathOpt;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getPlaceholder = [&](const std::string & outputName) {
|
||||||
|
return placeholderOpt
|
||||||
|
? DownstreamPlaceholder::unknownDerivation(*placeholderOpt, outputName)
|
||||||
|
: DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto & outputName : inputNode.value) {
|
||||||
|
auto actualPathOpt = getOutput(outputName);
|
||||||
|
if (!actualPathOpt) return false;
|
||||||
|
auto actualPath = *actualPathOpt;
|
||||||
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
|
inputRewrites.emplace(
|
||||||
|
getPlaceholder(outputName).render(),
|
||||||
|
store.printStorePath(actualPath));
|
||||||
|
}
|
||||||
|
inputSrcs.insert(std::move(actualPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto & [outputName, childNode] : inputNode.childMap) {
|
||||||
|
auto actualPathOpt = getOutput(outputName);
|
||||||
|
if (!actualPathOpt) return false;
|
||||||
|
auto actualPath = *actualPathOpt;
|
||||||
|
auto nextPlaceholder = getPlaceholder(outputName);
|
||||||
|
if (!tryResolveInput(store, inputSrcs, inputRewrites,
|
||||||
|
&nextPlaceholder, actualPath, childNode,
|
||||||
|
inputDrvOutputs))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<BasicDerivation> Derivation::tryResolve(
|
std::optional<BasicDerivation> Derivation::tryResolve(
|
||||||
Store & store,
|
Store & store,
|
||||||
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const
|
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const
|
||||||
|
@ -876,23 +1078,10 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
||||||
// Input paths that we'll want to rewrite in the derivation
|
// Input paths that we'll want to rewrite in the derivation
|
||||||
StringMap inputRewrites;
|
StringMap inputRewrites;
|
||||||
|
|
||||||
for (auto & [inputDrv, inputOutputs] : inputDrvs) {
|
for (auto & [inputDrv, inputNode] : inputDrvs.map)
|
||||||
for (auto & outputName : inputOutputs) {
|
if (!tryResolveInput(store, resolved.inputSrcs, inputRewrites,
|
||||||
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
|
nullptr, inputDrv, inputNode, inputDrvOutputs))
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
return std::nullopt;
|
||||||
inputRewrites.emplace(
|
|
||||||
DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName).render(),
|
|
||||||
store.printStorePath(*actualPath));
|
|
||||||
}
|
|
||||||
resolved.inputSrcs.insert(*actualPath);
|
|
||||||
} else {
|
|
||||||
warn("output '%s' of input '%s' missing, aborting the resolving",
|
|
||||||
outputName,
|
|
||||||
store.printStorePath(inputDrv));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rewriteDerivation(store, resolved, inputRewrites);
|
rewriteDerivation(store, resolved, inputRewrites);
|
||||||
|
|
||||||
|
@ -963,7 +1152,7 @@ void Derivation::checkInvariants(Store & store, const StorePath & drvPath) const
|
||||||
const Hash impureOutputHash = hashString(htSHA256, "impure");
|
const Hash impureOutputHash = hashString(htSHA256, "impure");
|
||||||
|
|
||||||
nlohmann::json DerivationOutput::toJSON(
|
nlohmann::json DerivationOutput::toJSON(
|
||||||
const Store & store, std::string_view drvName, std::string_view outputName) const
|
const Store & store, std::string_view drvName, OutputNameView outputName) const
|
||||||
{
|
{
|
||||||
nlohmann::json res = nlohmann::json::object();
|
nlohmann::json res = nlohmann::json::object();
|
||||||
std::visit(overloaded {
|
std::visit(overloaded {
|
||||||
|
@ -990,7 +1179,7 @@ nlohmann::json DerivationOutput::toJSON(
|
||||||
|
|
||||||
|
|
||||||
DerivationOutput DerivationOutput::fromJSON(
|
DerivationOutput DerivationOutput::fromJSON(
|
||||||
const Store & store, std::string_view drvName, std::string_view outputName,
|
const Store & store, std::string_view drvName, OutputNameView outputName,
|
||||||
const nlohmann::json & _json,
|
const nlohmann::json & _json,
|
||||||
const ExperimentalFeatureSettings & xpSettings)
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
|
@ -1080,11 +1269,26 @@ nlohmann::json Derivation::toJSON(const Store & store) const
|
||||||
inputsList.emplace_back(store.printStorePath(input));
|
inputsList.emplace_back(store.printStorePath(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::function<nlohmann::json(const DerivedPathMap<StringSet>::ChildNode &)> doInput;
|
||||||
|
doInput = [&](const auto & inputNode) {
|
||||||
|
auto value = nlohmann::json::object();
|
||||||
|
value["outputs"] = inputNode.value;
|
||||||
|
{
|
||||||
|
auto next = nlohmann::json::object();
|
||||||
|
for (auto & [outputId, childNode] : inputNode.childMap)
|
||||||
|
next[outputId] = doInput(childNode);
|
||||||
|
value["dynamicOutputs"] = std::move(next);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
{
|
{
|
||||||
auto& inputDrvsObj = res["inputDrvs"];
|
auto& inputDrvsObj = res["inputDrvs"];
|
||||||
inputDrvsObj = nlohmann::json::object();
|
inputDrvsObj = nlohmann::json::object();
|
||||||
for (auto & input : inputDrvs)
|
for (auto & [inputDrv, inputNode] : inputDrvs.map) {
|
||||||
inputDrvsObj[store.printStorePath(input.first)] = input.second;
|
inputDrvsObj[store.printStorePath(inputDrv)] = doInput(inputNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res["system"] = platform;
|
res["system"] = platform;
|
||||||
|
@ -1098,7 +1302,8 @@ nlohmann::json Derivation::toJSON(const Store & store) const
|
||||||
|
|
||||||
Derivation Derivation::fromJSON(
|
Derivation Derivation::fromJSON(
|
||||||
const Store & store,
|
const Store & store,
|
||||||
const nlohmann::json & json)
|
const nlohmann::json & json,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
using nlohmann::detail::value_t;
|
using nlohmann::detail::value_t;
|
||||||
|
|
||||||
|
@ -1130,12 +1335,21 @@ Derivation Derivation::fromJSON(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto & inputDrvsObj = ensureType(valueAt(json, "inputDrvs"), value_t::object);
|
std::function<DerivedPathMap<StringSet>::ChildNode(const nlohmann::json &)> doInput;
|
||||||
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items()) {
|
doInput = [&](const auto & json) {
|
||||||
ensureType(inputOutputs, value_t::array);
|
DerivedPathMap<StringSet>::ChildNode node;
|
||||||
res.inputDrvs[store.parseStorePath(inputDrvPath)] =
|
node.value = static_cast<const StringSet &>(
|
||||||
static_cast<const StringSet &>(inputOutputs);
|
ensureType(valueAt(json, "outputs"), value_t::array));
|
||||||
|
for (auto & [outputId, childNode] : ensureType(valueAt(json, "dynamicOutputs"), value_t::object).items()) {
|
||||||
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
|
node.childMap[outputId] = doInput(childNode);
|
||||||
}
|
}
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
auto & inputDrvsObj = ensureType(valueAt(json, "inputDrvs"), value_t::object);
|
||||||
|
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items())
|
||||||
|
res.inputDrvs.map[store.parseStorePath(inputDrvPath)] =
|
||||||
|
doInput(inputOutputs);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while reading key 'inputDrvs'");
|
e.addTrace({}, "while reading key 'inputDrvs'");
|
||||||
throw;
|
throw;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "content-address.hh"
|
#include "content-address.hh"
|
||||||
#include "repair-flag.hh"
|
#include "repair-flag.hh"
|
||||||
#include "derived-path.hh"
|
#include "derived-path-map.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "comparator.hh"
|
#include "comparator.hh"
|
||||||
#include "variant-wrapper.hh"
|
#include "variant-wrapper.hh"
|
||||||
|
@ -55,7 +55,7 @@ struct DerivationOutput
|
||||||
* @param drvName The name of the derivation this is an output of, without the `.drv`.
|
* @param drvName The name of the derivation this is an output of, without the `.drv`.
|
||||||
* @param outputName The name of this output.
|
* @param outputName The name of this output.
|
||||||
*/
|
*/
|
||||||
StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;
|
StorePath path(const Store & store, std::string_view drvName, OutputNameView outputName) const;
|
||||||
|
|
||||||
GENERATE_CMP(CAFixed, me->ca);
|
GENERATE_CMP(CAFixed, me->ca);
|
||||||
};
|
};
|
||||||
|
@ -132,19 +132,19 @@ struct DerivationOutput
|
||||||
* the safer interface provided by
|
* the safer interface provided by
|
||||||
* BasicDerivation::outputsAndOptPaths
|
* BasicDerivation::outputsAndOptPaths
|
||||||
*/
|
*/
|
||||||
std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;
|
std::optional<StorePath> path(const Store & store, std::string_view drvName, OutputNameView outputName) const;
|
||||||
|
|
||||||
nlohmann::json toJSON(
|
nlohmann::json toJSON(
|
||||||
const Store & store,
|
const Store & store,
|
||||||
std::string_view drvName,
|
std::string_view drvName,
|
||||||
std::string_view outputName) const;
|
OutputNameView outputName) const;
|
||||||
/**
|
/**
|
||||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||||
*/
|
*/
|
||||||
static DerivationOutput fromJSON(
|
static DerivationOutput fromJSON(
|
||||||
const Store & store,
|
const Store & store,
|
||||||
std::string_view drvName,
|
std::string_view drvName,
|
||||||
std::string_view outputName,
|
OutputNameView outputName,
|
||||||
const nlohmann::json & json,
|
const nlohmann::json & json,
|
||||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
};
|
};
|
||||||
|
@ -323,13 +323,13 @@ struct Derivation : BasicDerivation
|
||||||
/**
|
/**
|
||||||
* inputs that are sub-derivations
|
* inputs that are sub-derivations
|
||||||
*/
|
*/
|
||||||
DerivationInputs inputDrvs;
|
DerivedPathMap<std::set<OutputName>> inputDrvs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print a derivation.
|
* Print a derivation.
|
||||||
*/
|
*/
|
||||||
std::string unparse(const Store & store, bool maskOutputs,
|
std::string unparse(const Store & store, bool maskOutputs,
|
||||||
std::map<std::string, StringSet> * actualInputs = nullptr) const;
|
DerivedPathMap<StringSet>::ChildNode::Map * actualInputs = nullptr) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the underlying basic derivation but with these changes:
|
* Return the underlying basic derivation but with these changes:
|
||||||
|
@ -368,7 +368,8 @@ struct Derivation : BasicDerivation
|
||||||
nlohmann::json toJSON(const Store & store) const;
|
nlohmann::json toJSON(const Store & store) const;
|
||||||
static Derivation fromJSON(
|
static Derivation fromJSON(
|
||||||
const Store & store,
|
const Store & store,
|
||||||
const nlohmann::json & json);
|
const nlohmann::json & json,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
|
|
||||||
GENERATE_CMP(Derivation,
|
GENERATE_CMP(Derivation,
|
||||||
static_cast<const BasicDerivation &>(*me),
|
static_cast<const BasicDerivation &>(*me),
|
||||||
|
@ -389,7 +390,11 @@ StorePath writeDerivation(Store & store,
|
||||||
/**
|
/**
|
||||||
* Read a derivation from a file.
|
* Read a derivation from a file.
|
||||||
*/
|
*/
|
||||||
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
|
Derivation parseDerivation(
|
||||||
|
const Store & store,
|
||||||
|
std::string && s,
|
||||||
|
std::string_view name,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \todo Remove.
|
* \todo Remove.
|
||||||
|
@ -405,7 +410,7 @@ bool isDerivation(std::string_view fileName);
|
||||||
* This is usually <drv-name>-<output-name>, but is just <drv-name> when
|
* This is usually <drv-name>-<output-name>, but is just <drv-name> when
|
||||||
* the output name is "out".
|
* the output name is "out".
|
||||||
*/
|
*/
|
||||||
std::string outputPathName(std::string_view drvName, std::string_view outputName);
|
std::string outputPathName(std::string_view drvName, OutputNameView outputName);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -499,7 +504,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
* own outputs without needing to use the hash of a derivation in
|
* own outputs without needing to use the hash of a derivation in
|
||||||
* itself, making the hash near-impossible to calculate.
|
* itself, making the hash near-impossible to calculate.
|
||||||
*/
|
*/
|
||||||
std::string hashPlaceholder(const std::string_view outputName);
|
std::string hashPlaceholder(const OutputNameView outputName);
|
||||||
|
|
||||||
extern const Hash impureOutputHash;
|
extern const Hash impureOutputHash;
|
||||||
|
|
||||||
|
|
72
src/libstore/derived-path-map.cc
Normal file
72
src/libstore/derived-path-map.cc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include "derived-path-map.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
typename DerivedPathMap<V>::ChildNode & DerivedPathMap<V>::ensureSlot(const SingleDerivedPath & k)
|
||||||
|
{
|
||||||
|
std::function<ChildNode &(const SingleDerivedPath & )> initIter;
|
||||||
|
initIter = [&](const auto & k) -> auto & {
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const SingleDerivedPath::Opaque & bo) -> auto & {
|
||||||
|
// will not overwrite if already there
|
||||||
|
return map[bo.path];
|
||||||
|
},
|
||||||
|
[&](const SingleDerivedPath::Built & bfd) -> auto & {
|
||||||
|
auto & n = initIter(*bfd.drvPath);
|
||||||
|
return n.childMap[bfd.output];
|
||||||
|
},
|
||||||
|
}, k.raw());
|
||||||
|
};
|
||||||
|
return initIter(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
typename DerivedPathMap<V>::ChildNode * DerivedPathMap<V>::findSlot(const SingleDerivedPath & k)
|
||||||
|
{
|
||||||
|
std::function<ChildNode *(const SingleDerivedPath & )> initIter;
|
||||||
|
initIter = [&](const auto & k) {
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const SingleDerivedPath::Opaque & bo) {
|
||||||
|
auto it = map.find(bo.path);
|
||||||
|
return it != map.end()
|
||||||
|
? &it->second
|
||||||
|
: nullptr;
|
||||||
|
},
|
||||||
|
[&](const SingleDerivedPath::Built & bfd) {
|
||||||
|
auto * n = initIter(*bfd.drvPath);
|
||||||
|
if (!n) return (ChildNode *)nullptr;
|
||||||
|
|
||||||
|
auto it = n->childMap.find(bfd.output);
|
||||||
|
return it != n->childMap.end()
|
||||||
|
? &it->second
|
||||||
|
: nullptr;
|
||||||
|
},
|
||||||
|
}, k.raw());
|
||||||
|
};
|
||||||
|
return initIter(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiations
|
||||||
|
|
||||||
|
#include "create-derivation-and-realise-goal.hh"
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
template struct DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>>;
|
||||||
|
|
||||||
|
GENERATE_CMP_EXT(
|
||||||
|
template<>,
|
||||||
|
DerivedPathMap<std::set<std::string>>::ChildNode,
|
||||||
|
me->value,
|
||||||
|
me->childMap);
|
||||||
|
|
||||||
|
GENERATE_CMP_EXT(
|
||||||
|
template<>,
|
||||||
|
DerivedPathMap<std::set<std::string>>,
|
||||||
|
me->map);
|
||||||
|
|
||||||
|
template struct DerivedPathMap<std::set<std::string>>;
|
||||||
|
|
||||||
|
};
|
98
src/libstore/derived-path-map.hh
Normal file
98
src/libstore/derived-path-map.hh
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "derived-path.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple Trie, of sorts. Conceptually a map of `SingleDerivedPath` to
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* Concretely, an n-ary tree, as described below. A
|
||||||
|
* `SingleDerivedPath::Opaque` maps to the value of an immediate child
|
||||||
|
* of the root node. A `SingleDerivedPath::Built` maps to a deeper child
|
||||||
|
* node: the `SingleDerivedPath::Built::drvPath` is first mapped to a a
|
||||||
|
* child node (inductively), and then the
|
||||||
|
* `SingleDerivedPath::Built::output` is used to look up that child's
|
||||||
|
* child via its map. In this manner, every `SingleDerivedPath` is
|
||||||
|
* mapped to a child node.
|
||||||
|
*
|
||||||
|
* @param V A type to instantiate for each output. It should probably
|
||||||
|
* should be an "optional" type so not every interior node has to have a
|
||||||
|
* value. For example, the scheduler uses
|
||||||
|
* `DerivedPathMap<std::weak_ptr<CreateDerivationAndRealiseGoal>>` to
|
||||||
|
* remember which goals correspond to which outputs. `* const Something`
|
||||||
|
* or `std::optional<Something>` would also be good choices for
|
||||||
|
* "optional" types.
|
||||||
|
*/
|
||||||
|
template<typename V>
|
||||||
|
struct DerivedPathMap {
|
||||||
|
/**
|
||||||
|
* A child node (non-root node).
|
||||||
|
*/
|
||||||
|
struct ChildNode {
|
||||||
|
/**
|
||||||
|
* Value of this child node.
|
||||||
|
*
|
||||||
|
* @see DerivedPathMap for what `V` should be.
|
||||||
|
*/
|
||||||
|
V value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map type for the root node.
|
||||||
|
*/
|
||||||
|
using Map = std::map<OutputName, ChildNode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of the root node.
|
||||||
|
*/
|
||||||
|
Map childMap;
|
||||||
|
|
||||||
|
DECLARE_CMP(ChildNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map type for the root node.
|
||||||
|
*/
|
||||||
|
using Map = std::map<StorePath, ChildNode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of root node.
|
||||||
|
*/
|
||||||
|
Map map;
|
||||||
|
|
||||||
|
DECLARE_CMP(DerivedPathMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the node for `k`, creating it if needed.
|
||||||
|
*
|
||||||
|
* The node is referred to as a "slot" on the assumption that `V` is
|
||||||
|
* some sort of optional type, so the given key can be set or unset
|
||||||
|
* by changing this node.
|
||||||
|
*/
|
||||||
|
ChildNode & ensureSlot(const SingleDerivedPath & k);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `ensureSlot` but does not create the slot if it doesn't exist.
|
||||||
|
*
|
||||||
|
* Read the entire description of `ensureSlot` to understand an
|
||||||
|
* important caveat here that "have slot" does *not* imply "key is
|
||||||
|
* set in map". To ensure a key is set one would need to get the
|
||||||
|
* child node (with `findSlot` or `ensureSlot`) *and* check the
|
||||||
|
* `ChildNode::value`.
|
||||||
|
*/
|
||||||
|
ChildNode * findSlot(const SingleDerivedPath & k);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DECLARE_CMP_EXT(
|
||||||
|
template<>,
|
||||||
|
DerivedPathMap<std::set<std::string>>::,
|
||||||
|
DerivedPathMap<std::set<std::string>>);
|
||||||
|
DECLARE_CMP_EXT(
|
||||||
|
template<>,
|
||||||
|
DerivedPathMap<std::set<std::string>>::ChildNode::,
|
||||||
|
DerivedPathMap<std::set<std::string>>::ChildNode);
|
||||||
|
|
||||||
|
}
|
|
@ -167,7 +167,7 @@ void drvRequireExperiment(
|
||||||
|
|
||||||
SingleDerivedPath::Built SingleDerivedPath::Built::parse(
|
SingleDerivedPath::Built SingleDerivedPath::Built::parse(
|
||||||
const Store & store, ref<SingleDerivedPath> drv,
|
const Store & store, ref<SingleDerivedPath> drv,
|
||||||
std::string_view output,
|
OutputNameView output,
|
||||||
const ExperimentalFeatureSettings & xpSettings)
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
drvRequireExperiment(*drv, xpSettings);
|
drvRequireExperiment(*drv, xpSettings);
|
||||||
|
@ -179,7 +179,7 @@ SingleDerivedPath::Built SingleDerivedPath::Built::parse(
|
||||||
|
|
||||||
DerivedPath::Built DerivedPath::Built::parse(
|
DerivedPath::Built DerivedPath::Built::parse(
|
||||||
const Store & store, ref<SingleDerivedPath> drv,
|
const Store & store, ref<SingleDerivedPath> drv,
|
||||||
std::string_view outputsS,
|
OutputNameView outputsS,
|
||||||
const ExperimentalFeatureSettings & xpSettings)
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
drvRequireExperiment(*drv, xpSettings);
|
drvRequireExperiment(*drv, xpSettings);
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct SingleDerivedPath;
|
||||||
*/
|
*/
|
||||||
struct SingleDerivedPathBuilt {
|
struct SingleDerivedPathBuilt {
|
||||||
ref<SingleDerivedPath> drvPath;
|
ref<SingleDerivedPath> drvPath;
|
||||||
std::string output;
|
OutputName output;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the store path this is ultimately derived from (by realising
|
* Get the store path this is ultimately derived from (by realising
|
||||||
|
@ -71,7 +71,7 @@ struct SingleDerivedPathBuilt {
|
||||||
*/
|
*/
|
||||||
static SingleDerivedPathBuilt parse(
|
static SingleDerivedPathBuilt parse(
|
||||||
const Store & store, ref<SingleDerivedPath> drvPath,
|
const Store & store, ref<SingleDerivedPath> drvPath,
|
||||||
std::string_view outputs,
|
OutputNameView outputs,
|
||||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
nlohmann::json toJSON(Store & store) const;
|
nlohmann::json toJSON(Store & store) const;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ std::string DownstreamPlaceholder::render() const
|
||||||
|
|
||||||
DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
|
DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
std::string_view outputName,
|
OutputNameView outputName,
|
||||||
const ExperimentalFeatureSettings & xpSettings)
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
xpSettings.require(Xp::CaDerivations);
|
xpSettings.require(Xp::CaDerivations);
|
||||||
|
@ -25,7 +25,7 @@ DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
|
||||||
|
|
||||||
DownstreamPlaceholder DownstreamPlaceholder::unknownDerivation(
|
DownstreamPlaceholder DownstreamPlaceholder::unknownDerivation(
|
||||||
const DownstreamPlaceholder & placeholder,
|
const DownstreamPlaceholder & placeholder,
|
||||||
std::string_view outputName,
|
OutputNameView outputName,
|
||||||
const ExperimentalFeatureSettings & xpSettings)
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
xpSettings.require(Xp::DynamicDerivations);
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static DownstreamPlaceholder unknownCaOutput(
|
static DownstreamPlaceholder unknownCaOutput(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
std::string_view outputName,
|
OutputNameView outputName,
|
||||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,7 +72,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static DownstreamPlaceholder unknownDerivation(
|
static DownstreamPlaceholder unknownDerivation(
|
||||||
const DownstreamPlaceholder & drvPlaceholder,
|
const DownstreamPlaceholder & drvPlaceholder,
|
||||||
std::string_view outputName,
|
OutputNameView outputName,
|
||||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -554,7 +554,7 @@ public:
|
||||||
R"(
|
R"(
|
||||||
This option determines the maximum size of the `tmpfs` filesystem
|
This option determines the maximum size of the `tmpfs` filesystem
|
||||||
mounted on `/dev/shm` in Linux sandboxes. For the format, see the
|
mounted on `/dev/shm` in Linux sandboxes. For the format, see the
|
||||||
description of the `size` option of `tmpfs` in mount8. The default
|
description of the `size` option of `tmpfs` in mount(8). The default
|
||||||
is `50%`.
|
is `50%`.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
|
|
@ -125,14 +125,26 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
||||||
|
|
||||||
std::function<void(DerivedPath)> doPath;
|
std::function<void(DerivedPath)> doPath;
|
||||||
|
|
||||||
|
std::function<void(ref<SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)> enqueueDerivedPaths;
|
||||||
|
|
||||||
|
enqueueDerivedPaths = [&](ref<SingleDerivedPath> inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
if (!inputNode.value.empty())
|
||||||
|
pool.enqueue(std::bind(doPath, DerivedPath::Built { inputDrv, inputNode.value }));
|
||||||
|
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
enqueueDerivedPaths(
|
||||||
|
make_ref<SingleDerivedPath>(SingleDerivedPath::Built { inputDrv, outputName }),
|
||||||
|
childNode);
|
||||||
|
};
|
||||||
|
|
||||||
auto mustBuildDrv = [&](const StorePath & drvPath, const Derivation & drv) {
|
auto mustBuildDrv = [&](const StorePath & drvPath, const Derivation & drv) {
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->willBuild.insert(drvPath);
|
state->willBuild.insert(drvPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : drv.inputDrvs)
|
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map) {
|
||||||
pool.enqueue(std::bind(doPath, DerivedPath::Built { makeConstantStorePathRef(i.first), i.second }));
|
enqueueDerivedPaths(makeConstantStorePathRef(inputDrv), inputNode);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkOutput = [&](
|
auto checkOutput = [&](
|
||||||
|
@ -322,10 +334,13 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
{
|
{
|
||||||
std::set<Realisation> inputRealisations;
|
std::set<Realisation> inputRealisations;
|
||||||
|
|
||||||
for (const auto & [inputDrv, outputNames] : drv.inputDrvs) {
|
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumRealisations;
|
||||||
const auto outputHashes =
|
|
||||||
|
accumRealisations = [&](const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
if (!inputNode.value.empty()) {
|
||||||
|
auto outputHashes =
|
||||||
staticOutputHashes(store, store.readDerivation(inputDrv));
|
staticOutputHashes(store, store.readDerivation(inputDrv));
|
||||||
for (const auto & outputName : outputNames) {
|
for (const auto & outputName : inputNode.value) {
|
||||||
auto outputHash = get(outputHashes, outputName);
|
auto outputHash = get(outputHashes, outputName);
|
||||||
if (!outputHash)
|
if (!outputHash)
|
||||||
throw Error(
|
throw Error(
|
||||||
|
@ -335,11 +350,25 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
DrvOutput{*outputHash, outputName});
|
DrvOutput{*outputHash, outputName});
|
||||||
if (!thisRealisation)
|
if (!thisRealisation)
|
||||||
throw Error(
|
throw Error(
|
||||||
"output '%s' of derivation '%s' isn't built", outputName,
|
"output '%s' of derivation '%s' isn’t built", outputName,
|
||||||
store.printStorePath(inputDrv));
|
store.printStorePath(inputDrv));
|
||||||
inputRealisations.insert(*thisRealisation);
|
inputRealisations.insert(*thisRealisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!inputNode.value.empty()) {
|
||||||
|
auto d = makeConstantStorePathRef(inputDrv);
|
||||||
|
for (const auto & [outputName, childNode] : inputNode.childMap) {
|
||||||
|
SingleDerivedPath next = SingleDerivedPath::Built { d, outputName };
|
||||||
|
accumRealisations(
|
||||||
|
// TODO deep resolutions for dynamic derivations, issue #8947, would go here.
|
||||||
|
resolveDerivedPath(store, next),
|
||||||
|
childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map)
|
||||||
|
accumRealisations(inputDrv, inputNode);
|
||||||
|
|
||||||
auto info = store.queryPathInfo(outputPath);
|
auto info = store.queryPathInfo(outputPath);
|
||||||
|
|
||||||
|
@ -399,8 +428,7 @@ StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store
|
||||||
store.printStorePath(drvPath), bfd.output);
|
store.printStorePath(drvPath), bfd.output);
|
||||||
auto & optPath = outputPaths.at(bfd.output);
|
auto & optPath = outputPaths.at(bfd.output);
|
||||||
if (!optPath)
|
if (!optPath)
|
||||||
throw Error("'%s' does not yet map to a known concrete store path",
|
throw MissingRealisation(bfd.drvPath->to_string(store), bfd.output);
|
||||||
bfd.to_string(store));
|
|
||||||
return *optPath;
|
return *optPath;
|
||||||
},
|
},
|
||||||
}, req.raw());
|
}, req.raw());
|
||||||
|
|
|
@ -13,24 +13,36 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An (owned) output name. Just a type alias used to make code more
|
||||||
|
* readible.
|
||||||
|
*/
|
||||||
|
typedef std::string OutputName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A borrowed output name. Just a type alias used to make code more
|
||||||
|
* readible.
|
||||||
|
*/
|
||||||
|
typedef std::string_view OutputNameView;
|
||||||
|
|
||||||
struct OutputsSpec {
|
struct OutputsSpec {
|
||||||
/**
|
/**
|
||||||
* A non-empty set of outputs, specified by name
|
* A non-empty set of outputs, specified by name
|
||||||
*/
|
*/
|
||||||
struct Names : std::set<std::string> {
|
struct Names : std::set<OutputName> {
|
||||||
using std::set<std::string>::set;
|
using std::set<OutputName>::set;
|
||||||
|
|
||||||
/* These need to be "inherited manually" */
|
/* These need to be "inherited manually" */
|
||||||
|
|
||||||
Names(const std::set<std::string> & s)
|
Names(const std::set<OutputName> & s)
|
||||||
: std::set<std::string>(s)
|
: std::set<OutputName>(s)
|
||||||
{ assert(!empty()); }
|
{ assert(!empty()); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Needs to be "inherited manually"
|
* Needs to be "inherited manually"
|
||||||
*/
|
*/
|
||||||
Names(std::set<std::string> && s)
|
Names(std::set<OutputName> && s)
|
||||||
: std::set<std::string>(s)
|
: std::set<OutputName>(s)
|
||||||
{ assert(!empty()); }
|
{ assert(!empty()); }
|
||||||
|
|
||||||
/* This set should always be non-empty, so we delete this
|
/* This set should always be non-empty, so we delete this
|
||||||
|
@ -57,7 +69,7 @@ struct OutputsSpec {
|
||||||
*/
|
*/
|
||||||
OutputsSpec() = delete;
|
OutputsSpec() = delete;
|
||||||
|
|
||||||
bool contains(const std::string & output) const;
|
bool contains(const OutputName & output) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new OutputsSpec which is the union of this and that.
|
* Create a new OutputsSpec which is the union of this and that.
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct DrvOutput {
|
||||||
/**
|
/**
|
||||||
* The name of the output.
|
* The name of the output.
|
||||||
*/
|
*/
|
||||||
std::string outputName;
|
OutputName outputName;
|
||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ struct Realisation {
|
||||||
* Since these are the outputs of a single derivation, we know the
|
* Since these are the outputs of a single derivation, we know the
|
||||||
* output names are unique so we can use them as the map key.
|
* output names are unique so we can use them as the map key.
|
||||||
*/
|
*/
|
||||||
typedef std::map<std::string, Realisation> SingleDrvOutputs;
|
typedef std::map<OutputName, Realisation> SingleDrvOutputs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection type for multiple derivations' outputs' `Realisation`s.
|
* Collection type for multiple derivations' outputs' `Realisation`s.
|
||||||
|
@ -146,7 +146,7 @@ public:
|
||||||
MissingRealisation(DrvOutput & outputId)
|
MissingRealisation(DrvOutput & outputId)
|
||||||
: MissingRealisation(outputId.outputName, outputId.strHash())
|
: MissingRealisation(outputId.outputName, outputId.strHash())
|
||||||
{}
|
{}
|
||||||
MissingRealisation(std::string_view drv, std::string outputName)
|
MissingRealisation(std::string_view drv, OutputName outputName)
|
||||||
: Error( "cannot operate on output '%s' of the "
|
: Error( "cannot operate on output '%s' of the "
|
||||||
"unbuilt derivation '%s'",
|
"unbuilt derivation '%s'",
|
||||||
outputName,
|
outputName,
|
||||||
|
|
|
@ -172,7 +172,24 @@ void RemoteStore::ConnectionHandle::processStderr(Sink * sink, Source * source,
|
||||||
auto ex = handle->processStderr(sink, source, flush);
|
auto ex = handle->processStderr(sink, source, flush);
|
||||||
if (ex) {
|
if (ex) {
|
||||||
daemonException = true;
|
daemonException = true;
|
||||||
|
try {
|
||||||
std::rethrow_exception(ex);
|
std::rethrow_exception(ex);
|
||||||
|
} catch (const Error & e) {
|
||||||
|
// Nix versions before #4628 did not have an adequate behavior for reporting that the derivation format was upgraded.
|
||||||
|
// To avoid having to add compatibility logic in many places, we expect to catch almost all occurrences of the
|
||||||
|
// old incomprehensible error here, so that we can explain to users what's going on when their daemon is
|
||||||
|
// older than #4628 (2023).
|
||||||
|
if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) &&
|
||||||
|
GET_PROTOCOL_MINOR(handle->daemonVersion) <= 35)
|
||||||
|
{
|
||||||
|
auto m = e.msg();
|
||||||
|
if (m.find("parsing derivation") != std::string::npos &&
|
||||||
|
m.find("expected string") != std::string::npos &&
|
||||||
|
m.find("Derive([") != std::string::npos)
|
||||||
|
throw Error("%s, this might be because the daemon is too old to understand dependencies on dynamic derivations. Check to see if the raw dervation is in the form '%s'", std::move(m), "DrvWithVersion(..)");
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,26 @@ class ImpureDerivationTest : public DerivationTest
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_F(DerivationTest, BadATerm_version) {
|
||||||
|
ASSERT_THROW(
|
||||||
|
parseDerivation(
|
||||||
|
*store,
|
||||||
|
R"(DrvWithVersion("invalid-version",[],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",["cat","dog"])],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
|
||||||
|
"whatever",
|
||||||
|
mockXpSettings),
|
||||||
|
FormatError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DynDerivationTest, BadATerm_oldVersionDynDeps) {
|
||||||
|
ASSERT_THROW(
|
||||||
|
parseDerivation(
|
||||||
|
*store,
|
||||||
|
R"(Derive([],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",(["cat","dog"],[("cat",["kitten"]),("goose",["gosling"])]))],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
|
||||||
|
"dyn-dep-derivation",
|
||||||
|
mockXpSettings),
|
||||||
|
FormatError);
|
||||||
|
}
|
||||||
|
|
||||||
#define TEST_JSON(FIXTURE, NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
|
#define TEST_JSON(FIXTURE, NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
|
||||||
TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _to_json) { \
|
TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _to_json) { \
|
||||||
using nlohmann::literals::operator "" _json; \
|
using nlohmann::literals::operator "" _json; \
|
||||||
|
@ -143,60 +163,63 @@ TEST_JSON(ImpureDerivationTest, impure,
|
||||||
|
|
||||||
#undef TEST_JSON
|
#undef TEST_JSON
|
||||||
|
|
||||||
#define TEST_JSON(NAME, STR, VAL, DRV_NAME) \
|
#define TEST_JSON(FIXTURE, NAME, STR, VAL) \
|
||||||
TEST_F(DerivationTest, Derivation_ ## NAME ## _to_json) { \
|
TEST_F(FIXTURE, Derivation_ ## NAME ## _to_json) { \
|
||||||
using nlohmann::literals::operator "" _json; \
|
using nlohmann::literals::operator "" _json; \
|
||||||
ASSERT_EQ( \
|
ASSERT_EQ( \
|
||||||
STR ## _json, \
|
STR ## _json, \
|
||||||
(Derivation { VAL }).toJSON(*store)); \
|
(VAL).toJSON(*store)); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
TEST_F(DerivationTest, Derivation_ ## NAME ## _from_json) { \
|
TEST_F(FIXTURE, Derivation_ ## NAME ## _from_json) { \
|
||||||
using nlohmann::literals::operator "" _json; \
|
using nlohmann::literals::operator "" _json; \
|
||||||
ASSERT_EQ( \
|
ASSERT_EQ( \
|
||||||
Derivation { VAL }, \
|
(VAL), \
|
||||||
Derivation::fromJSON( \
|
Derivation::fromJSON( \
|
||||||
*store, \
|
*store, \
|
||||||
STR ## _json)); \
|
STR ## _json, \
|
||||||
|
mockXpSettings)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_JSON(simple,
|
#define TEST_ATERM(FIXTURE, NAME, STR, VAL, DRV_NAME) \
|
||||||
R"({
|
TEST_F(FIXTURE, Derivation_ ## NAME ## _to_aterm) { \
|
||||||
"name": "my-derivation",
|
ASSERT_EQ( \
|
||||||
"inputSrcs": [
|
STR, \
|
||||||
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
|
(VAL).unparse(*store, false)); \
|
||||||
],
|
} \
|
||||||
"inputDrvs": {
|
\
|
||||||
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": [
|
TEST_F(FIXTURE, Derivation_ ## NAME ## _from_aterm) { \
|
||||||
"cat",
|
auto parsed = parseDerivation( \
|
||||||
"dog"
|
*store, \
|
||||||
]
|
STR, \
|
||||||
},
|
DRV_NAME, \
|
||||||
"system": "wasm-sel4",
|
mockXpSettings); \
|
||||||
"builder": "foo",
|
ASSERT_EQ( \
|
||||||
"args": [
|
(VAL).toJSON(*store), \
|
||||||
"bar",
|
parsed.toJSON(*store)); \
|
||||||
"baz"
|
ASSERT_EQ( \
|
||||||
],
|
(VAL), \
|
||||||
"env": {
|
parsed); \
|
||||||
"BIG_BAD": "WOLF"
|
}
|
||||||
},
|
|
||||||
"outputs": {}
|
Derivation makeSimpleDrv(const Store & store) {
|
||||||
})",
|
|
||||||
({
|
|
||||||
Derivation drv;
|
Derivation drv;
|
||||||
drv.name = "my-derivation";
|
drv.name = "simple-derivation";
|
||||||
drv.inputSrcs = {
|
drv.inputSrcs = {
|
||||||
store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
||||||
};
|
};
|
||||||
drv.inputDrvs = {
|
drv.inputDrvs = {
|
||||||
|
.map = {
|
||||||
{
|
{
|
||||||
store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
|
||||||
{
|
{
|
||||||
|
.value = {
|
||||||
"cat",
|
"cat",
|
||||||
"dog",
|
"dog",
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
drv.platform = "wasm-sel4";
|
drv.platform = "wasm-sel4";
|
||||||
drv.builder = "foo";
|
drv.builder = "foo";
|
||||||
|
@ -210,10 +233,137 @@ TEST_JSON(simple,
|
||||||
"WOLF",
|
"WOLF",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
drv;
|
return drv;
|
||||||
}),
|
}
|
||||||
"drv-name")
|
|
||||||
|
TEST_JSON(DerivationTest, simple,
|
||||||
|
R"({
|
||||||
|
"name": "simple-derivation",
|
||||||
|
"inputSrcs": [
|
||||||
|
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
|
||||||
|
],
|
||||||
|
"inputDrvs": {
|
||||||
|
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
|
||||||
|
"dynamicOutputs": {},
|
||||||
|
"outputs": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": "wasm-sel4",
|
||||||
|
"builder": "foo",
|
||||||
|
"args": [
|
||||||
|
"bar",
|
||||||
|
"baz"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"BIG_BAD": "WOLF"
|
||||||
|
},
|
||||||
|
"outputs": {}
|
||||||
|
})",
|
||||||
|
makeSimpleDrv(*store))
|
||||||
|
|
||||||
|
TEST_ATERM(DerivationTest, simple,
|
||||||
|
R"(Derive([],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",["cat","dog"])],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
|
||||||
|
makeSimpleDrv(*store),
|
||||||
|
"simple-derivation")
|
||||||
|
|
||||||
|
Derivation makeDynDepDerivation(const Store & store) {
|
||||||
|
Derivation drv;
|
||||||
|
drv.name = "dyn-dep-derivation";
|
||||||
|
drv.inputSrcs = {
|
||||||
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
||||||
|
};
|
||||||
|
drv.inputDrvs = {
|
||||||
|
.map = {
|
||||||
|
{
|
||||||
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
|
||||||
|
DerivedPathMap<StringSet>::ChildNode {
|
||||||
|
.value = {
|
||||||
|
"cat",
|
||||||
|
"dog",
|
||||||
|
},
|
||||||
|
.childMap = {
|
||||||
|
{
|
||||||
|
"cat",
|
||||||
|
DerivedPathMap<StringSet>::ChildNode {
|
||||||
|
.value = {
|
||||||
|
"kitten",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"goose",
|
||||||
|
DerivedPathMap<StringSet>::ChildNode {
|
||||||
|
.value = {
|
||||||
|
"gosling",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
drv.platform = "wasm-sel4";
|
||||||
|
drv.builder = "foo";
|
||||||
|
drv.args = {
|
||||||
|
"bar",
|
||||||
|
"baz",
|
||||||
|
};
|
||||||
|
drv.env = {
|
||||||
|
{
|
||||||
|
"BIG_BAD",
|
||||||
|
"WOLF",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return drv;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_JSON(DynDerivationTest, dynDerivationDeps,
|
||||||
|
R"({
|
||||||
|
"name": "dyn-dep-derivation",
|
||||||
|
"inputSrcs": [
|
||||||
|
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
|
||||||
|
],
|
||||||
|
"inputDrvs": {
|
||||||
|
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
|
||||||
|
"dynamicOutputs": {
|
||||||
|
"cat": {
|
||||||
|
"dynamicOutputs": {},
|
||||||
|
"outputs": ["kitten"]
|
||||||
|
},
|
||||||
|
"goose": {
|
||||||
|
"dynamicOutputs": {},
|
||||||
|
"outputs": ["gosling"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": "wasm-sel4",
|
||||||
|
"builder": "foo",
|
||||||
|
"args": [
|
||||||
|
"bar",
|
||||||
|
"baz"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"BIG_BAD": "WOLF"
|
||||||
|
},
|
||||||
|
"outputs": {}
|
||||||
|
})",
|
||||||
|
makeDynDepDerivation(*store))
|
||||||
|
|
||||||
|
TEST_ATERM(DynDerivationTest, dynDerivationDeps,
|
||||||
|
R"(DrvWithVersion("xp-dyn-drv",[],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",(["cat","dog"],[("cat",["kitten"]),("goose",["gosling"])]))],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
|
||||||
|
makeDynDepDerivation(*store),
|
||||||
|
"dyn-dep-derivation")
|
||||||
|
|
||||||
#undef TEST_JSON
|
#undef TEST_JSON
|
||||||
|
#undef TEST_ATERM
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,8 @@ TEST_F(DerivedPathTest, built_built_xp) {
|
||||||
MissingExperimentalFeature);
|
MissingExperimentalFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef COVERAGE
|
||||||
|
|
||||||
RC_GTEST_FIXTURE_PROP(
|
RC_GTEST_FIXTURE_PROP(
|
||||||
DerivedPathTest,
|
DerivedPathTest,
|
||||||
prop_legacy_round_rip,
|
prop_legacy_round_rip,
|
||||||
|
@ -146,4 +148,6 @@ RC_GTEST_FIXTURE_PROP(
|
||||||
RC_ASSERT(o == DerivedPath::parse(*store, o.to_string(*store)));
|
RC_ASSERT(o == DerivedPath::parse(*store, o.to_string(*store)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,8 @@ Gen<OutputsSpec> Arbitrary<OutputsSpec>::arbitrary()
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
#ifndef COVERAGE
|
||||||
|
|
||||||
RC_GTEST_PROP(
|
RC_GTEST_PROP(
|
||||||
OutputsSpec,
|
OutputsSpec,
|
||||||
prop_round_rip,
|
prop_round_rip,
|
||||||
|
@ -232,4 +234,6 @@ RC_GTEST_PROP(
|
||||||
RC_ASSERT(o == OutputsSpec::parse(o.to_string()));
|
RC_ASSERT(o == OutputsSpec::parse(o.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,8 @@ Gen<StorePath> Arbitrary<StorePath>::arbitrary()
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
#ifndef COVERAGE
|
||||||
|
|
||||||
RC_GTEST_FIXTURE_PROP(
|
RC_GTEST_FIXTURE_PROP(
|
||||||
StorePathTest,
|
StorePathTest,
|
||||||
prop_regex_accept,
|
prop_regex_accept,
|
||||||
|
@ -150,4 +152,6 @@ RC_GTEST_FIXTURE_PROP(
|
||||||
RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
|
RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,7 @@ nlohmann::json Args::toJSON()
|
||||||
|
|
||||||
for (auto & [name, flag] : longFlags) {
|
for (auto & [name, flag] : longFlags) {
|
||||||
auto j = nlohmann::json::object();
|
auto j = nlohmann::json::object();
|
||||||
|
if (hiddenCategories.count(flag->category)) continue;
|
||||||
if (flag->aliases.count(name)) continue;
|
if (flag->aliases.count(name)) continue;
|
||||||
if (flag->shortName)
|
if (flag->shortName)
|
||||||
j["shortName"] = std::string(1, flag->shortName);
|
j["shortName"] = std::string(1, flag->shortName);
|
||||||
|
|
|
@ -1,21 +1,48 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#define DECLARE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE) \
|
||||||
|
PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const;
|
||||||
|
#define DECLARE_EQUAL(prefix, qualification, my_type) \
|
||||||
|
DECLARE_ONE_CMP(prefix, qualification, ==, my_type)
|
||||||
|
#define DECLARE_LEQ(prefix, qualification, my_type) \
|
||||||
|
DECLARE_ONE_CMP(prefix, qualification, <, my_type)
|
||||||
|
#define DECLARE_NEQ(prefix, qualification, my_type) \
|
||||||
|
DECLARE_ONE_CMP(prefix, qualification, !=, my_type)
|
||||||
|
|
||||||
|
#define GENERATE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE, ...) \
|
||||||
|
PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const { \
|
||||||
|
__VA_OPT__(const MY_TYPE * me = this;) \
|
||||||
|
auto fields1 = std::make_tuple( __VA_ARGS__ ); \
|
||||||
|
__VA_OPT__(me = &other;) \
|
||||||
|
auto fields2 = std::make_tuple( __VA_ARGS__ ); \
|
||||||
|
return fields1 COMPARATOR fields2; \
|
||||||
|
}
|
||||||
|
#define GENERATE_EQUAL(prefix, qualification, my_type, args...) \
|
||||||
|
GENERATE_ONE_CMP(prefix, qualification, ==, my_type, args)
|
||||||
|
#define GENERATE_LEQ(prefix, qualification, my_type, args...) \
|
||||||
|
GENERATE_ONE_CMP(prefix, qualification, <, my_type, args)
|
||||||
|
#define GENERATE_NEQ(prefix, qualification, my_type, args...) \
|
||||||
|
GENERATE_ONE_CMP(prefix, qualification, !=, my_type, args)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declare comparison methods without defining them.
|
* Declare comparison methods without defining them.
|
||||||
*/
|
*/
|
||||||
#define DECLARE_ONE_CMP(COMPARATOR, MY_TYPE) \
|
|
||||||
bool operator COMPARATOR(const MY_TYPE & other) const;
|
|
||||||
#define DECLARE_EQUAL(my_type) \
|
|
||||||
DECLARE_ONE_CMP(==, my_type)
|
|
||||||
#define DECLARE_LEQ(my_type) \
|
|
||||||
DECLARE_ONE_CMP(<, my_type)
|
|
||||||
#define DECLARE_NEQ(my_type) \
|
|
||||||
DECLARE_ONE_CMP(!=, my_type)
|
|
||||||
#define DECLARE_CMP(my_type) \
|
#define DECLARE_CMP(my_type) \
|
||||||
DECLARE_EQUAL(my_type) \
|
DECLARE_EQUAL(,,my_type) \
|
||||||
DECLARE_LEQ(my_type) \
|
DECLARE_LEQ(,,my_type) \
|
||||||
DECLARE_NEQ(my_type)
|
DECLARE_NEQ(,,my_type)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param prefix This is for something before each declaration like
|
||||||
|
* `template<classname Foo>`.
|
||||||
|
*
|
||||||
|
* @param my_type the type are defining operators for.
|
||||||
|
*/
|
||||||
|
#define DECLARE_CMP_EXT(prefix, qualification, my_type) \
|
||||||
|
DECLARE_EQUAL(prefix, qualification, my_type) \
|
||||||
|
DECLARE_LEQ(prefix, qualification, my_type) \
|
||||||
|
DECLARE_NEQ(prefix, qualification, my_type)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Awful hacky generation of the comparison operators by doing a lexicographic
|
* Awful hacky generation of the comparison operators by doing a lexicographic
|
||||||
|
@ -33,18 +60,18 @@
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, ...) \
|
|
||||||
bool operator COMPARATOR(const MY_TYPE& other) const { \
|
|
||||||
__VA_OPT__(const MY_TYPE* me = this;) \
|
|
||||||
auto fields1 = std::make_tuple( __VA_ARGS__ ); \
|
|
||||||
__VA_OPT__(me = &other;) \
|
|
||||||
auto fields2 = std::make_tuple( __VA_ARGS__ ); \
|
|
||||||
return fields1 COMPARATOR fields2; \
|
|
||||||
}
|
|
||||||
#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args)
|
|
||||||
#define GENERATE_LEQ(args...) GENERATE_ONE_CMP(<, args)
|
|
||||||
#define GENERATE_NEQ(args...) GENERATE_ONE_CMP(!=, args)
|
|
||||||
#define GENERATE_CMP(args...) \
|
#define GENERATE_CMP(args...) \
|
||||||
GENERATE_EQUAL(args) \
|
GENERATE_EQUAL(,,args) \
|
||||||
GENERATE_LEQ(args) \
|
GENERATE_LEQ(,,args) \
|
||||||
GENERATE_NEQ(args)
|
GENERATE_NEQ(,,args)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param prefix This is for something before each declaration like
|
||||||
|
* `template<classname Foo>`.
|
||||||
|
*
|
||||||
|
* @param my_type the type are defining operators for.
|
||||||
|
*/
|
||||||
|
#define GENERATE_CMP_EXT(prefix, my_type, args...) \
|
||||||
|
GENERATE_EQUAL(prefix, my_type ::, my_type, args) \
|
||||||
|
GENERATE_LEQ(prefix, my_type ::, my_type, args) \
|
||||||
|
GENERATE_NEQ(prefix, my_type ::, my_type, args)
|
||||||
|
|
|
@ -36,8 +36,8 @@ namespace nix {
|
||||||
*
|
*
|
||||||
* std::map<std::string, Config::SettingInfo> settings;
|
* std::map<std::string, Config::SettingInfo> settings;
|
||||||
* config.getSettings(settings);
|
* config.getSettings(settings);
|
||||||
* config["system"].description == "the current system"
|
* settings["system"].description == "the current system"
|
||||||
* config["system"].value == "x86_64-linux"
|
* settings["system"].value == "x86_64-linux"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The above retrieves all currently known settings from the `Config` object
|
* The above retrieves all currently known settings from the `Config` object
|
||||||
|
|
|
@ -14,6 +14,11 @@ void BaseError::addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint, boo
|
||||||
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame });
|
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void throwExceptionSelfCheck(){
|
||||||
|
// This is meant to be caught in initLibUtil()
|
||||||
|
throw SysError("C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||||
// This stringifies the error and caches it for use by what(), or similarly by msg().
|
// This stringifies the error and caches it for use by what(), or similarly by msg().
|
||||||
const std::string & BaseError::calcWhat() const
|
const std::string & BaseError::calcWhat() const
|
||||||
|
|
|
@ -214,4 +214,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Throw an exception for the purpose of checking that exception handling works; see 'initLibUtil()'.
|
||||||
|
*/
|
||||||
|
void throwExceptionSelfCheck();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ constexpr std::array<ExperimentalFeatureDetails, 14> xpFeatureDetails = {{
|
||||||
.name = "auto-allocate-uids",
|
.name = "auto-allocate-uids",
|
||||||
.description = R"(
|
.description = R"(
|
||||||
Allows Nix to automatically pick UIDs for builds, rather than creating
|
Allows Nix to automatically pick UIDs for builds, rather than creating
|
||||||
`nixbld*` user accounts. See the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting for details.
|
`nixbld*` user accounts. See the [`auto-allocate-uids`](@docroot@/command-ref/conf-file.md#conf-auto-allocate-uids) setting for details.
|
||||||
)",
|
)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -179,7 +179,7 @@ constexpr std::array<ExperimentalFeatureDetails, 14> xpFeatureDetails = {{
|
||||||
.name = "cgroups",
|
.name = "cgroups",
|
||||||
.description = R"(
|
.description = R"(
|
||||||
Allows Nix to execute builds inside cgroups. See
|
Allows Nix to execute builds inside cgroups. See
|
||||||
the [`use-cgroups`](#conf-use-cgroups) setting for details.
|
the [`use-cgroups`](@docroot@/command-ref/conf-file.md#conf-use-cgroups) setting for details.
|
||||||
)",
|
)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -220,8 +220,8 @@ struct JSONLogger : Logger {
|
||||||
json["level"] = lvl;
|
json["level"] = lvl;
|
||||||
json["type"] = type;
|
json["type"] = type;
|
||||||
json["text"] = s;
|
json["text"] = s;
|
||||||
|
json["parent"] = parent;
|
||||||
addFields(json, fields);
|
addFields(json, fields);
|
||||||
// FIXME: handle parent
|
|
||||||
write(json);
|
write(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,23 @@ extern char * * environ __attribute__((weak));
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void initLibUtil() {
|
void initLibUtil() {
|
||||||
|
// Check that exception handling works. Exception handling has been observed
|
||||||
|
// not to work on darwin when the linker flags aren't quite right.
|
||||||
|
// In this case we don't want to expose the user to some unrelated uncaught
|
||||||
|
// exception, but rather tell them exactly that exception handling is
|
||||||
|
// broken.
|
||||||
|
// When exception handling fails, the message tends to be printed by the
|
||||||
|
// C++ runtime, followed by an abort.
|
||||||
|
// For example on macOS we might see an error such as
|
||||||
|
// libc++abi: terminating with uncaught exception of type nix::SysError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
|
||||||
|
bool caught = false;
|
||||||
|
try {
|
||||||
|
throwExceptionSelfCheck();
|
||||||
|
} catch (const nix::Error & _e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
// This is not actually the main point of this check, but let's make sure anyway:
|
||||||
|
assert(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> getEnv(const std::string & key)
|
std::optional<std::string> getEnv(const std::string & key)
|
||||||
|
|
|
@ -406,8 +406,22 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::function<void(ref<SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)> accumDerivedPath;
|
||||||
|
|
||||||
|
accumDerivedPath = [&](ref<SingleDerivedPath> inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
if (!inputNode.value.empty())
|
||||||
|
pathsToBuild.push_back(DerivedPath::Built {
|
||||||
|
.drvPath = inputDrv,
|
||||||
|
.outputs = OutputsSpec::Names { inputNode.value },
|
||||||
|
});
|
||||||
|
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
accumDerivedPath(
|
||||||
|
make_ref<SingleDerivedPath>(SingleDerivedPath::Built { inputDrv, outputName }),
|
||||||
|
childNode);
|
||||||
|
};
|
||||||
|
|
||||||
// Build or fetch all dependencies of the derivation.
|
// Build or fetch all dependencies of the derivation.
|
||||||
for (const auto & [inputDrv0, inputOutputs] : drv.inputDrvs) {
|
for (const auto & [inputDrv0, inputNode] : drv.inputDrvs.map) {
|
||||||
// To get around lambda capturing restrictions in the
|
// To get around lambda capturing restrictions in the
|
||||||
// standard.
|
// standard.
|
||||||
const auto & inputDrv = inputDrv0;
|
const auto & inputDrv = inputDrv0;
|
||||||
|
@ -416,10 +430,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
return !std::regex_search(store->printStorePath(inputDrv), std::regex(exclude));
|
return !std::regex_search(store->printStorePath(inputDrv), std::regex(exclude));
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
pathsToBuild.push_back(DerivedPath::Built {
|
accumDerivedPath(makeConstantStorePathRef(inputDrv), inputNode);
|
||||||
.drvPath = makeConstantStorePathRef(inputDrv),
|
|
||||||
.outputs = OutputsSpec::Names { inputOutputs },
|
|
||||||
});
|
|
||||||
pathsToCopy.insert(inputDrv);
|
pathsToCopy.insert(inputDrv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,13 +493,21 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
|
|
||||||
if (env.count("__json")) {
|
if (env.count("__json")) {
|
||||||
StorePathSet inputs;
|
StorePathSet inputs;
|
||||||
for (auto & [depDrvPath, wantedDepOutputs] : drv.inputDrvs) {
|
|
||||||
auto outputs = evalStore->queryPartialDerivationOutputMap(depDrvPath);
|
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputClosure;
|
||||||
for (auto & i : wantedDepOutputs) {
|
|
||||||
|
accumInputClosure = [&](const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
auto outputs = evalStore->queryPartialDerivationOutputMap(inputDrv);
|
||||||
|
for (auto & i : inputNode.value) {
|
||||||
auto o = outputs.at(i);
|
auto o = outputs.at(i);
|
||||||
store->computeFSClosure(*o, inputs);
|
store->computeFSClosure(*o, inputs);
|
||||||
}
|
}
|
||||||
}
|
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
accumInputClosure(*outputs.at(outputName), childNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map)
|
||||||
|
accumInputClosure(inputDrv, inputNode);
|
||||||
|
|
||||||
ParsedDerivation parsedDrv(drvInfo.requireDrvPath(), drv);
|
ParsedDerivation parsedDrv(drvInfo.requireDrvPath(), drv);
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
enum QueryType
|
enum QueryType
|
||||||
{ qOutputs, qRequisites, qReferences, qReferrers
|
{ qOutputs, qRequisites, qReferences, qReferrers
|
||||||
, qReferrersClosure, qDeriver, qBinding, qHash, qSize
|
, qReferrersClosure, qDeriver, qValidDerivers, qBinding, qHash, qSize
|
||||||
, qTree, qGraph, qGraphML, qResolve, qRoots };
|
, qTree, qGraph, qGraphML, qResolve, qRoots };
|
||||||
std::optional<QueryType> query;
|
std::optional<QueryType> query;
|
||||||
bool useOutput = false;
|
bool useOutput = false;
|
||||||
|
@ -299,6 +299,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
else if (i == "--referrers" || i == "--referers") query = qReferrers;
|
else if (i == "--referrers" || i == "--referers") query = qReferrers;
|
||||||
else if (i == "--referrers-closure" || i == "--referers-closure") query = qReferrersClosure;
|
else if (i == "--referrers-closure" || i == "--referers-closure") query = qReferrersClosure;
|
||||||
else if (i == "--deriver" || i == "-d") query = qDeriver;
|
else if (i == "--deriver" || i == "-d") query = qDeriver;
|
||||||
|
else if (i == "--valid-derivers") query = qValidDerivers;
|
||||||
else if (i == "--binding" || i == "-b") {
|
else if (i == "--binding" || i == "-b") {
|
||||||
if (opArgs.size() == 0)
|
if (opArgs.size() == 0)
|
||||||
throw UsageError("expected binding name");
|
throw UsageError("expected binding name");
|
||||||
|
@ -372,6 +373,21 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case qValidDerivers: {
|
||||||
|
StorePathSet result;
|
||||||
|
for (auto & i : opArgs) {
|
||||||
|
auto derivers = store->queryValidDerivers(store->followLinksToStorePath(i));
|
||||||
|
for (const auto &i: derivers) {
|
||||||
|
result.insert(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto sorted = store->topoSortPaths(result);
|
||||||
|
for (StorePaths::reverse_iterator i = sorted.rbegin();
|
||||||
|
i != sorted.rend(); ++i)
|
||||||
|
cout << fmt("%s\n", store->printStorePath(*i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case qBinding:
|
case qBinding:
|
||||||
for (auto & i : opArgs) {
|
for (auto & i : opArgs) {
|
||||||
auto path = useDeriver(store->followLinksToStorePath(i));
|
auto path = useDeriver(store->followLinksToStorePath(i));
|
||||||
|
|
|
@ -20,15 +20,22 @@ StringPairs resolveRewrites(
|
||||||
const std::vector<BuiltPathWithResult> & dependencies)
|
const std::vector<BuiltPathWithResult> & dependencies)
|
||||||
{
|
{
|
||||||
StringPairs res;
|
StringPairs res;
|
||||||
for (auto & dep : dependencies)
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
|
for (auto & dep : dependencies) {
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path)) {
|
||||||
for (auto & [ outputName, outputPath ] : drvDep->outputs)
|
for (auto & [ outputName, outputPath ] : drvDep->outputs) {
|
||||||
res.emplace(
|
res.emplace(
|
||||||
DownstreamPlaceholder::unknownCaOutput(
|
DownstreamPlaceholder::fromSingleDerivedPathBuilt(
|
||||||
drvDep->drvPath->outPath(), outputName).render(),
|
SingleDerivedPath::Built {
|
||||||
|
.drvPath = make_ref<SingleDerivedPath>(drvDep->drvPath->discardOutputPath()),
|
||||||
|
.output = outputName,
|
||||||
|
}).render(),
|
||||||
store.printStorePath(outputPath)
|
store.printStorePath(outputPath)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -500,6 +500,45 @@ static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon);
|
||||||
|
|
||||||
struct CmdDaemon : StoreCommand
|
struct CmdDaemon : StoreCommand
|
||||||
{
|
{
|
||||||
|
bool stdio = false;
|
||||||
|
std::optional<TrustedFlag> isTrustedOpt = std::nullopt;
|
||||||
|
|
||||||
|
CmdDaemon()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "stdio",
|
||||||
|
.description = "Attach to standard I/O, instead of trying to bind to a UNIX socket.",
|
||||||
|
.handler = {&stdio, true},
|
||||||
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "force-trusted",
|
||||||
|
.description = "Force the daemon to trust connecting clients.",
|
||||||
|
.handler = {[&]() {
|
||||||
|
isTrustedOpt = Trusted;
|
||||||
|
}},
|
||||||
|
.experimentalFeature = Xp::DaemonTrustOverride,
|
||||||
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "force-untrusted",
|
||||||
|
.description = "Force the daemon to not trust connecting clients. The connection will be processed by the receiving daemon before forwarding commands.",
|
||||||
|
.handler = {[&]() {
|
||||||
|
isTrustedOpt = NotTrusted;
|
||||||
|
}},
|
||||||
|
.experimentalFeature = Xp::DaemonTrustOverride,
|
||||||
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "default-trust",
|
||||||
|
.description = "Use Nix's default trust.",
|
||||||
|
.handler = {[&]() {
|
||||||
|
isTrustedOpt = std::nullopt;
|
||||||
|
}},
|
||||||
|
.experimentalFeature = Xp::DaemonTrustOverride,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "daemon to perform store operations on behalf of non-root clients";
|
return "daemon to perform store operations on behalf of non-root clients";
|
||||||
|
@ -516,7 +555,7 @@ struct CmdDaemon : StoreCommand
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
void run(ref<Store> store) override
|
||||||
{
|
{
|
||||||
runDaemon(false, std::nullopt);
|
runDaemon(stdio, isTrustedOpt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,44 @@
|
||||||
R""(
|
R""(
|
||||||
|
|
||||||
# Example
|
# Examples
|
||||||
|
|
||||||
* Run the daemon in the foreground:
|
* Run the daemon:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix daemon
|
# nix daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Run the daemon and listen on standard I/O instead of binding to a UNIX socket:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix daemon --stdio
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the daemon and force all connections to be trusted:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix daemon --force-trusted
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the daemon and force all connections to be untrusted:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix daemon --force-untrusted
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the daemon, listen on standard I/O, and force all connections to use Nix's default trust:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix daemon --stdio --default-trust
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command runs the Nix daemon, which is a required component in
|
This command runs the Nix daemon, which is a required component in
|
||||||
multi-user Nix installations. It runs build tasks and other
|
multi-user Nix installations. It runs build tasks and other
|
||||||
operations on the Nix store on behalf of non-root users. Usually you
|
operations on the Nix store on behalf of non-root users. Usually you
|
||||||
don't run the daemon directly; instead it's managed by a service
|
don't run the daemon directly; instead it's managed by a service
|
||||||
management framework such as `systemd`.
|
management framework such as `systemd` on Linux, or `launchctl` on Darwin.
|
||||||
|
|
||||||
Note that this daemon does not fork into the background.
|
Note that this daemon does not fork into the background.
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ R""(
|
||||||
* Run a series of script commands:
|
* Run a series of script commands:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix develop --command bash --command "mkdir build && cmake .. && make"
|
# nix develop --command bash -c "mkdir build && cmake .. && make"
|
||||||
```
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
|
@ -26,7 +26,7 @@ R""(
|
||||||
* Run multiple commands in a shell environment:
|
* Run multiple commands in a shell environment:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix shell nixpkgs#gnumake --command sh --command "cd src && make"
|
# nix shell nixpkgs#gnumake --command sh -c "cd src && make"
|
||||||
```
|
```
|
||||||
|
|
||||||
* Run GNU Hello in a chroot store:
|
* Run GNU Hello in a chroot store:
|
||||||
|
|
|
@ -2,7 +2,7 @@ with import ./config.nix;
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
|
|
||||||
dep = import ./dependencies.nix;
|
dep = import ./dependencies.nix {};
|
||||||
|
|
||||||
makeTest = nr: args: mkDerivation ({
|
makeTest = nr: args: mkDerivation ({
|
||||||
name = "check-refs-" + toString nr;
|
name = "check-refs-" + toString nr;
|
||||||
|
|
|
@ -195,7 +195,7 @@ expect() {
|
||||||
shift
|
shift
|
||||||
"$@" && res=0 || res="$?"
|
"$@" && res=0 || res="$?"
|
||||||
if [[ $res -ne $expected ]]; then
|
if [[ $res -ne $expected ]]; then
|
||||||
echo "Expected '$expected' but got '$res' while running '${*@Q}'" >&2
|
echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
|
@ -209,7 +209,7 @@ expectStderr() {
|
||||||
shift
|
shift
|
||||||
"$@" 2>&1 && res=0 || res="$?"
|
"$@" 2>&1 && res=0 || res="$?"
|
||||||
if [[ $res -ne $expected ]]; then
|
if [[ $res -ne $expected ]]; then
|
||||||
echo "Expected '$expected' but got '$res' while running '${*@Q}'" >&2
|
echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{ hashInvalidator ? "" }:
|
||||||
with import ./config.nix;
|
with import ./config.nix;
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
@ -21,6 +22,17 @@ let {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fod_input = mkDerivation {
|
||||||
|
name = "fod-input";
|
||||||
|
buildCommand = ''
|
||||||
|
echo ${hashInvalidator}
|
||||||
|
echo FOD > $out
|
||||||
|
'';
|
||||||
|
outputHashMode = "flat";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
outputHash = "1dq9p0hnm1y75q2x40fws5887bq1r840hzdxak0a9djbwvx0b16d";
|
||||||
|
};
|
||||||
|
|
||||||
body = mkDerivation {
|
body = mkDerivation {
|
||||||
name = "dependencies-top";
|
name = "dependencies-top";
|
||||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||||
|
@ -29,6 +41,7 @@ let {
|
||||||
input1_drv = input1;
|
input1_drv = input1;
|
||||||
input2_drv = input2;
|
input2_drv = input2;
|
||||||
input0_drv = input0;
|
input0_drv = input0;
|
||||||
|
fod_input_drv = fod_input;
|
||||||
meta.description = "Random test package";
|
meta.description = "Random test package";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,3 +53,20 @@ nix-store -q --referrers-closure "$input2OutPath" | grep "$outPath"
|
||||||
# Check that the derivers are set properly.
|
# Check that the derivers are set properly.
|
||||||
test $(nix-store -q --deriver "$outPath") = "$drvPath"
|
test $(nix-store -q --deriver "$outPath") = "$drvPath"
|
||||||
nix-store -q --deriver "$input2OutPath" | grepQuiet -- "-input-2.drv"
|
nix-store -q --deriver "$input2OutPath" | grepQuiet -- "-input-2.drv"
|
||||||
|
|
||||||
|
# --valid-derivers returns the currently single valid .drv file
|
||||||
|
test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath"
|
||||||
|
|
||||||
|
# instantiate a different drv with the same output
|
||||||
|
drvPath2=$(nix-instantiate dependencies.nix --argstr hashInvalidator yay)
|
||||||
|
|
||||||
|
# now --valid-derivers returns both
|
||||||
|
test "$(nix-store -q --valid-derivers "$outPath" | sort)" = "$(sort <<< "$drvPath"$'\n'"$drvPath2")"
|
||||||
|
|
||||||
|
# check that nix-store --valid-derivers only returns existing drv
|
||||||
|
nix-store --delete "$drvPath"
|
||||||
|
test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath2"
|
||||||
|
|
||||||
|
# check that --valid-derivers returns nothing when there are no valid derivers
|
||||||
|
nix-store --delete "$drvPath2"
|
||||||
|
test -z "$(nix-store -q --valid-derivers "$outPath")"
|
||||||
|
|
|
@ -18,4 +18,6 @@ clearStore
|
||||||
|
|
||||||
drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv)
|
drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv)
|
||||||
|
|
||||||
expectStderr 1 nix build "${drvDep}^out^out" --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
|
out2=$(nix build "${drvDep}^out^out" --no-link)
|
||||||
|
|
||||||
|
test $out1 == $out2
|
||||||
|
|
|
@ -6,4 +6,6 @@ out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link)
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
expectStderr 1 nix-build ./text-hashed-output.nix -A wrapper --no-out-link | grepQuiet "Dependencies on the outputs of dynamic derivations are not yet supported"
|
out2=$(nix-build ./text-hashed-output.nix -A wrapper --no-out-link)
|
||||||
|
|
||||||
|
diff -r $out1 $out2
|
||||||
|
|
|
@ -13,14 +13,14 @@ nix --experimental-features 'nix-command' eval --impure --expr \
|
||||||
# the future so it does work, but there are some design questions to
|
# the future so it does work, but there are some design questions to
|
||||||
# resolve first. Adding a test so we don't liberalise it by accident.
|
# resolve first. Adding a test so we don't liberalise it by accident.
|
||||||
expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \
|
expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \
|
||||||
'builtins.outputOf (import ../dependencies.nix) "out"' \
|
'builtins.outputOf (import ../dependencies.nix {}) "out"' \
|
||||||
| grepQuiet "value is a set while a string was expected"
|
| grepQuiet "value is a set while a string was expected"
|
||||||
|
|
||||||
# Test that "DrvDeep" string contexts are not supported at this time
|
# Test that "DrvDeep" string contexts are not supported at this time
|
||||||
#
|
#
|
||||||
# Like the above, this is a restriction we could relax later.
|
# Like the above, this is a restriction we could relax later.
|
||||||
expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \
|
expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \
|
||||||
'builtins.outputOf (import ../dependencies.nix).drvPath "out"' \
|
'builtins.outputOf (import ../dependencies.nix {}).drvPath "out"' \
|
||||||
| grepQuiet "has a context which refers to a complete source and binary closure. This is not supported at this time"
|
| grepQuiet "has a context which refers to a complete source and binary closure. This is not supported at this time"
|
||||||
|
|
||||||
# Test using `builtins.outputOf` with static derivations
|
# Test using `builtins.outputOf` with static derivations
|
||||||
|
|
|
@ -3,7 +3,8 @@ dyn-drv-tests := \
|
||||||
$(d)/recursive-mod-json.sh \
|
$(d)/recursive-mod-json.sh \
|
||||||
$(d)/build-built-drv.sh \
|
$(d)/build-built-drv.sh \
|
||||||
$(d)/eval-outputOf.sh \
|
$(d)/eval-outputOf.sh \
|
||||||
$(d)/dep-built-drv.sh
|
$(d)/dep-built-drv.sh \
|
||||||
|
$(d)/old-daemon-error-hack.sh
|
||||||
|
|
||||||
install-tests-groups += dyn-drv
|
install-tests-groups += dyn-drv
|
||||||
|
|
||||||
|
|
20
tests/dyn-drv/old-daemon-error-hack.nix
Normal file
20
tests/dyn-drv/old-daemon-error-hack.nix
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
with import ./config.nix;
|
||||||
|
|
||||||
|
# A simple content-addressed derivation.
|
||||||
|
# The derivation can be arbitrarily modified by passing a different `seed`,
|
||||||
|
# but the output will always be the same
|
||||||
|
rec {
|
||||||
|
stub = mkDerivation {
|
||||||
|
name = "stub";
|
||||||
|
buildCommand = ''
|
||||||
|
echo stub > $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
wrapper = mkDerivation {
|
||||||
|
name = "has-dynamic-drv-dep";
|
||||||
|
buildCommand = ''
|
||||||
|
exit 1 # we're not building this derivation
|
||||||
|
${builtins.outputOf stub.outPath "out"}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
11
tests/dyn-drv/old-daemon-error-hack.sh
Normal file
11
tests/dyn-drv/old-daemon-error-hack.sh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Purposely bypassing our usual common for this subgroup
|
||||||
|
source ../common.sh
|
||||||
|
|
||||||
|
# Need backend to support text-hashing too
|
||||||
|
isDaemonNewer "2.18.0pre20230906" && skipTest "Daemon is too new"
|
||||||
|
|
||||||
|
enableFeatures "ca-derivations dynamic-derivations"
|
||||||
|
|
||||||
|
restartDaemon
|
||||||
|
|
||||||
|
expectStderr 1 nix-instantiate --read-write-mode ./old-daemon-error-hack.nix | grepQuiet "the daemon is too old to understand dependencies on dynamic derivations"
|
|
@ -17,13 +17,13 @@ rec {
|
||||||
foo."bar.runtimeGraph" = mkDerivation {
|
foo."bar.runtimeGraph" = mkDerivation {
|
||||||
name = "dependencies";
|
name = "dependencies";
|
||||||
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
||||||
exportReferencesGraph = ["refs" (import ./dependencies.nix)];
|
exportReferencesGraph = ["refs" (import ./dependencies.nix {})];
|
||||||
};
|
};
|
||||||
|
|
||||||
foo."bar.buildGraph" = mkDerivation {
|
foo."bar.buildGraph" = mkDerivation {
|
||||||
name = "dependencies";
|
name = "dependencies";
|
||||||
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
||||||
exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
|
exportReferencesGraph = ["refs" (import ./dependencies.nix {}).drvPath];
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,5 +146,87 @@ EOF
|
||||||
|
|
||||||
git -C $flakeFollowsA add flake.nix
|
git -C $flakeFollowsA add flake.nix
|
||||||
|
|
||||||
nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'"
|
nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'"
|
||||||
nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'"
|
nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'"
|
||||||
|
|
||||||
|
# Now test follow path overloading
|
||||||
|
# This tests a lockfile checking regression https://github.com/NixOS/nix/pull/8819
|
||||||
|
#
|
||||||
|
# We construct the following graph, where p->q means p has input q.
|
||||||
|
# A double edge means that the edge gets overridden using `follows`.
|
||||||
|
#
|
||||||
|
# A
|
||||||
|
# / \
|
||||||
|
# / \
|
||||||
|
# v v
|
||||||
|
# B ==> C --- follows declared in A
|
||||||
|
# \\ /
|
||||||
|
# \\/ --- follows declared in B
|
||||||
|
# v
|
||||||
|
# D
|
||||||
|
#
|
||||||
|
# The message was
|
||||||
|
# error: input 'B/D' follows a non-existent input 'B/C/D'
|
||||||
|
#
|
||||||
|
# Note that for `B` to resolve its follow for `D`, it needs `C/D`, for which it needs to resolve the follow on `C` first.
|
||||||
|
flakeFollowsOverloadA="$TEST_ROOT/follows/overload/flakeA"
|
||||||
|
flakeFollowsOverloadB="$TEST_ROOT/follows/overload/flakeA/flakeB"
|
||||||
|
flakeFollowsOverloadC="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC"
|
||||||
|
flakeFollowsOverloadD="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC/flakeD"
|
||||||
|
|
||||||
|
# Test following path flakerefs.
|
||||||
|
createGitRepo "$flakeFollowsOverloadA"
|
||||||
|
mkdir -p "$flakeFollowsOverloadB"
|
||||||
|
mkdir -p "$flakeFollowsOverloadC"
|
||||||
|
mkdir -p "$flakeFollowsOverloadD"
|
||||||
|
|
||||||
|
cat > "$flakeFollowsOverloadD/flake.nix" <<EOF
|
||||||
|
{
|
||||||
|
description = "Flake D";
|
||||||
|
inputs = {};
|
||||||
|
outputs = { ... }: {};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$flakeFollowsOverloadC/flake.nix" <<EOF
|
||||||
|
{
|
||||||
|
description = "Flake C";
|
||||||
|
inputs.D.url = "path:./flakeD";
|
||||||
|
outputs = { ... }: {};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$flakeFollowsOverloadB/flake.nix" <<EOF
|
||||||
|
{
|
||||||
|
description = "Flake B";
|
||||||
|
inputs = {
|
||||||
|
C = {
|
||||||
|
url = "path:./flakeC";
|
||||||
|
};
|
||||||
|
D.follows = "C/D";
|
||||||
|
};
|
||||||
|
outputs = { ... }: {};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# input B/D should be able to be found...
|
||||||
|
cat > "$flakeFollowsOverloadA/flake.nix" <<EOF
|
||||||
|
{
|
||||||
|
description = "Flake A";
|
||||||
|
inputs = {
|
||||||
|
B = {
|
||||||
|
url = "path:./flakeB";
|
||||||
|
inputs.C.follows = "C";
|
||||||
|
};
|
||||||
|
C.url = "path:./flakeB/flakeC";
|
||||||
|
};
|
||||||
|
outputs = { ... }: {};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git -C "$flakeFollowsOverloadA" add flake.nix flakeB/flake.nix \
|
||||||
|
flakeB/flakeC/flake.nix flakeB/flakeC/flakeD/flake.nix
|
||||||
|
|
||||||
|
nix flake metadata "$flakeFollowsOverloadA"
|
||||||
|
nix flake update "$flakeFollowsOverloadA"
|
||||||
|
nix flake lock "$flakeFollowsOverloadA"
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
builtins.pathExists (builtins.toPath ./lib.nix)
|
builtins.pathExists (./lib.nix)
|
||||||
|
&& builtins.pathExists (builtins.toPath ./lib.nix)
|
||||||
|
&& builtins.pathExists (builtins.toString ./lib.nix)
|
||||||
|
&& !builtins.pathExists (builtins.toString ./lib.nix + "/")
|
||||||
&& builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix))
|
&& builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix))
|
||||||
&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
|
&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
|
||||||
&& builtins.pathExists ./lib.nix
|
&& builtins.pathExists ./lib.nix
|
||||||
|
|
|
@ -73,6 +73,8 @@ runCommand "test"
|
||||||
--resolv-conf=off \
|
--resolv-conf=off \
|
||||||
--bind-ro=/nix/store \
|
--bind-ro=/nix/store \
|
||||||
--bind=$out \
|
--bind=$out \
|
||||||
|
--bind=/proc:/run/host/proc \
|
||||||
|
--bind=/sys:/run/host/sys \
|
||||||
--private-network \
|
--private-network \
|
||||||
$toplevel/init
|
$toplevel/init
|
||||||
''
|
''
|
||||||
|
|
|
@ -82,7 +82,7 @@ let
|
||||||
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
|
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
|
||||||
cp -prd ${nixpkgs} $dir
|
cp -prd ${nixpkgs} $dir
|
||||||
# Set the correct timestamp in the tarball.
|
# Set the correct timestamp in the tarball.
|
||||||
find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
||||||
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
# Test that ‘nix copy’ works over ssh.
|
# Test that ‘nix copy’ works over ssh.
|
||||||
|
# Run interactively with:
|
||||||
|
# rm key key.pub; nix run .#hydraJobs.tests.nix-copy.driverInteractive
|
||||||
|
|
||||||
{ lib, config, nixpkgs, hostPkgs, ... }:
|
{ lib, config, nixpkgs, hostPkgs, ... }:
|
||||||
|
|
||||||
|
@ -55,7 +57,9 @@ in {
|
||||||
server.wait_for_unit("sshd")
|
server.wait_for_unit("sshd")
|
||||||
client.wait_for_unit("network.target")
|
client.wait_for_unit("network.target")
|
||||||
client.wait_for_unit("getty@tty1.service")
|
client.wait_for_unit("getty@tty1.service")
|
||||||
client.wait_for_text("]#")
|
# Either the prompt: ]#
|
||||||
|
# or an OCR misreading of it: 1#
|
||||||
|
client.wait_for_text("[]1]#")
|
||||||
|
|
||||||
# Copy the closure of package A from the client to the server using password authentication,
|
# Copy the closure of package A from the client to the server using password authentication,
|
||||||
# and check that all prompts are visible
|
# and check that all prompts are visible
|
||||||
|
|
|
@ -47,7 +47,7 @@ let
|
||||||
cp -prd ${nixpkgs} $dir
|
cp -prd ${nixpkgs} $dir
|
||||||
|
|
||||||
# Set the correct timestamp in the tarball.
|
# Set the correct timestamp in the tarball.
|
||||||
find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
||||||
|
|
||||||
mkdir -p $out/archive
|
mkdir -p $out/archive
|
||||||
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue