1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 06:31:14 +02:00

Merge branch 'master' into lfs

This commit is contained in:
Brian Camacho 2024-12-02 15:06:28 -05:00
commit 169d62a382
154 changed files with 1859 additions and 844 deletions

View file

@ -1,36 +1,54 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Report unexpected or incorrect behaviour
title: '' title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** ## Describe the bug
A clear and concise description of what the bug is. <!--
A clear and concise description of what the bug is.
If you have a problem with a specific package or NixOS, If you have a problem with a specific package or NixOS,
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues. you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
-->
**Steps To Reproduce** ## Steps To Reproduce
1. Go to '...' <!--
2. Click on '....' Example:
3. Scroll down to '....'
4. See error
**Expected behavior** 1. Clone this repository: ...
2. Run `nix-... ...`
3. Observe unexpected behaviour
-->
A clear and concise description of what you expected to happen. ## Expected behavior
**`nix-env --version` output** <!-- A clear and concise description of what you expected to happen. -->
**Additional context** ## Metadata
Add any other context about the problem here. <!-- Please insert the output of running `nix-env --version` below this line -->
**Priorities** ## Additional context
<!-- Add any other context about the problem here. -->
## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open bug issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open bug issues and pull requests]: https://github.com/NixOS/nix/labels/bug
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -1,24 +1,39 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest a new feature
title: '' title: ''
labels: feature labels: feature
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** ## Is your feature request related to a problem?
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like** <!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
A clear and concise description of what you want to happen.
**Describe alternatives you've considered** ## Proposed solution
A clear and concise description of any alternative solutions or features you've considered.
**Additional context** <!-- A clear and concise description of what you want to happen. -->
Add any other context or screenshots about the feature request here.
**Priorities** ## Alternative solutions
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!-- Add any other context or screenshots about the feature request here. -->
## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open feature issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open feature issues and pull requests]: https://github.com/NixOS/nix/labels/feature
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -23,14 +23,25 @@ assignees: ''
<details><summary>Output</summary> <details><summary>Output</summary>
```log <!-- paste console output inside the below code block -->
<!-- paste console output here and remove this comment --> ```log
``` ```
</details> </details>
## Priorities ## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open installer issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open installer issues and pull requests]: https://github.com/NixOS/nix/labels/installer
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

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

View file

@ -17,10 +17,12 @@ so you understand the process and the expectations.
--> -->
# Motivation ## Motivation
<!-- Briefly explain what the change is about and why it is desirable. --> <!-- Briefly explain what the change is about and why it is desirable. -->
# Context ## Context
<!-- Provide context. Reference open issues if available. --> <!-- Provide context. Reference open issues if available. -->
<!-- Non-trivial change: Briefly outline the implementation strategy. --> <!-- Non-trivial change: Briefly outline the implementation strategy. -->
@ -29,7 +31,7 @@ so you understand the process and the expectations.
<!-- Large change: Provide instructions to reviewers how to read the diff. --> <!-- Large change: Provide instructions to reviewers how to read the diff. -->
# Priorities and Process ---
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).

View file

@ -23,7 +23,9 @@ jobs:
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
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
max-jobs = 1
- 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/cachix-action@v15 - uses: cachix/cachix-action@v15
if: needs.check_secrets.outputs.cachix == 'true' if: needs.check_secrets.outputs.cachix == 'true'
@ -128,7 +130,7 @@ jobs:
- run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello" - run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"
docker_push_image: docker_push_image:
needs: [check_secrets, tests] needs: [check_secrets, tests, vm_tests]
permissions: permissions:
contents: read contents: read
packages: write packages: write
@ -194,7 +196,13 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main - uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user - run: |
nix build -L \
.#hydraJobs.tests.functional_user \
.#hydraJobs.tests.githubFlakes \
.#hydraJobs.tests.nix-docker \
.#hydraJobs.tests.tarballFlakes \
;
flake_regressions: flake_regressions:
needs: vm_tests needs: vm_tests
@ -214,4 +222,4 @@ jobs:
path: flake-regressions/tests path: flake-regressions/tests
- uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main - uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix build --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH MAX_FLAKES=25 flake-regressions/eval-all.sh - run: nix build -L --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH MAX_FLAKES=25 flake-regressions/eval-all.sh

View file

@ -2,9 +2,6 @@ queue_rules:
- name: default - name: default
# all required tests need to go here # all required tests need to go here
merge_conditions: merge_conditions:
- check-success=installer
- check-success=installer_test (macos-latest)
- check-success=installer_test (ubuntu-latest)
- check-success=tests (macos-latest) - check-success=tests (macos-latest)
- check-success=tests (ubuntu-latest) - check-success=tests (ubuntu-latest)
- check-success=vm_tests - check-success=vm_tests
@ -90,3 +87,13 @@ pull_request_rules:
- "2.24-maintenance" - "2.24-maintenance"
labels: labels:
- merge-queue - merge-queue
- name: backport patches to 2.25
conditions:
- label=backport 2.25-maintenance
actions:
backport:
branches:
- "2.25-maintenance"
labels:
- merge-queue

View file

@ -0,0 +1,22 @@
# This is only conditional to work around
# https://github.com/mesonbuild/meson/issues/13293. It should be
# unconditional.
if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc')
deps_private += dependency('threads')
endif
add_project_arguments(
'-Wdeprecated-copy',
'-Werror=suggest-override',
'-Werror=switch',
'-Werror=switch-enum',
'-Werror=unused-result',
'-Wignored-qualifiers',
'-Wimplicit-fallthrough',
'-Wno-deprecated-declarations',
language : 'cpp',
)
if get_option('buildtype') not in ['debug']
add_project_arguments('-O3', language : 'cpp')
endif

View file

@ -1,11 +0,0 @@
add_project_arguments(
'-Wdeprecated-copy',
'-Werror=suggest-override',
'-Werror=switch',
'-Werror=switch-enum',
'-Werror=unused-result',
'-Wignored-qualifiers',
'-Wimplicit-fallthrough',
'-Wno-deprecated-declarations',
language : 'cpp',
)

View file

@ -1,6 +0,0 @@
# This is only conditional to work around
# https://github.com/mesonbuild/meson/issues/13293. It should be
# unconditional.
if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc')
deps_private += dependency('threads')
endif

View file

@ -0,0 +1,18 @@
---
synopsis: "`nix copy` supports `--profile` and `--out-link`"
prs: [11657]
---
The `nix copy` command now has flags `--profile` and `--out-link`, similar to `nix build`. `--profile` makes a profile point to the
top-level store path, while `--out-link` create symlinks to the top-level store paths.
For example, when updating the local NixOS system profile from a NixOS system closure on a remote machine, instead of
```
# nix copy --from ssh://server $path
# nix build --profile /nix/var/nix/profiles/system $path
```
you can now do
```
# nix copy --from ssh://server --profile /nix/var/nix/profiles/system $path
```
The advantage is that this avoids a time window where *path* is not a garbage collector root, and so could be deleted by a concurrent `nix store gc` process.

View file

@ -88,7 +88,9 @@ All options not listed here are passed to `nix-store
cleared before the interactive shell is started, so you get an cleared before the interactive shell is started, so you get an
environment that more closely corresponds to the “real” Nix build. A environment that more closely corresponds to the “real” Nix build. A
few variables, in particular `HOME`, `USER` and `DISPLAY`, are few variables, in particular `HOME`, `USER` and `DISPLAY`, are
retained. retained. Note that the shell used to run commands is obtained from
[`NIX_BUILD_SHELL`](#env-NIX_BUILD_SHELL) / `<nixpkgs>` from
`NIX_PATH`, and therefore not affected by `--pure`.
- `--packages` / `-p` *packages* - `--packages` / `-p` *packages*
@ -112,11 +114,30 @@ All options not listed here are passed to `nix-store
# Environment variables # Environment variables
- `NIX_BUILD_SHELL` - <span id="env-NIX_BUILD_SHELL">[`NIX_BUILD_SHELL`](#env-NIX_BUILD_SHELL)</span>
Shell used to start the interactive environment. Defaults to the Shell used to start the interactive environment.
`bash` found in `<nixpkgs>`, falling back to the `bash` found in Defaults to the `bash` from `bashInteractive` found in `<nixpkgs>`, falling back to the `bash` found in `PATH` if not found.
`PATH` if not found.
> **Note**
>
> The shell obtained using this method may not necessarily be the same as any shells requested in *path*.
<!-- -->
> **Example
>
> Despite `--pure`, this invocation will not result in a fully reproducible shell environment:
>
> ```nix
> #!/usr/bin/env -S nix-shell --pure
> let
> pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/854fdc68881791812eddd33b2fed94b954979a8e.tar.gz") {};
> in
> pkgs.mkShell {
> buildInputs = pkgs.bashInteractive;
> }
> ```
{{#include ./env-common.md}} {{#include ./env-common.md}}

View file

@ -57,3 +57,21 @@ $ nix build ./\#hydraJobs.dockerImage.x86_64-linux
$ docker load -i ./result/image.tar.gz $ docker load -i ./result/image.tar.gz
$ docker run -ti nix:2.5pre20211105 $ docker run -ti nix:2.5pre20211105
``` ```
# Docker image with non-root Nix
If you would like to run Nix in a container under a user other than `root`,
you can build an image with a non-root single-user installation of Nix
by specifying the `uid`, `gid`, `uname`, and `gname` arguments to `docker.nix`:
```console
$ nix build --file docker.nix \
--arg uid 1000 \
--arg gid 1000 \
--argstr uname user \
--argstr gname user \
--argstr name nix-user \
--out-link nix-user.tar.gz
$ docker load -i nix-user.tar.gz
$ docker run -ti nix-user
```

View file

@ -9,6 +9,10 @@
, maxLayers ? 100 , maxLayers ? 100
, nixConf ? {} , nixConf ? {}
, flake-registry ? null , flake-registry ? null
, uid ? 0
, gid ? 0
, uname ? "root"
, gname ? "root"
}: }:
let let
defaultPkgs = with pkgs; [ defaultPkgs = with pkgs; [
@ -50,6 +54,15 @@ let
description = "Unprivileged account (don't use!)"; description = "Unprivileged account (don't use!)";
}; };
} // lib.optionalAttrs (uid != 0) {
"${uname}" = {
uid = uid;
shell = "${pkgs.bashInteractive}/bin/bash";
home = "/home/${uname}";
gid = gid;
groups = [ "${gname}" ];
description = "Nix user";
};
} // lib.listToAttrs ( } // lib.listToAttrs (
map map
( (
@ -70,6 +83,8 @@ let
root.gid = 0; root.gid = 0;
nixbld.gid = 30000; nixbld.gid = 30000;
nobody.gid = 65534; nobody.gid = 65534;
} // lib.optionalAttrs (gid != 0) {
"${gname}".gid = gid;
}; };
userToPasswd = ( userToPasswd = (
@ -150,6 +165,8 @@ let
in in
"${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n"; "${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n";
userHome = if uid == 0 then "/root" else "/home/${uname}";
baseSystem = baseSystem =
let let
nixpkgs = pkgs.path; nixpkgs = pkgs.path;
@ -237,26 +254,26 @@ let
mkdir -p $out/etc/nix mkdir -p $out/etc/nix
cat $nixConfContentsPath > $out/etc/nix/nix.conf cat $nixConfContentsPath > $out/etc/nix/nix.conf
mkdir -p $out/root mkdir -p $out${userHome}
mkdir -p $out/nix/var/nix/profiles/per-user/root mkdir -p $out/nix/var/nix/profiles/per-user/${uname}
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
ln -s $out/nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default ln -s /nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default
ln -s /nix/var/nix/profiles/default $out/root/.nix-profile ln -s /nix/var/nix/profiles/default $out${userHome}/.nix-profile
ln -s ${channel} $out/nix/var/nix/profiles/per-user/root/channels-1-link ln -s ${channel} $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/root/channels-1-link $out/nix/var/nix/profiles/per-user/root/channels ln -s /nix/var/nix/profiles/per-user/${uname}/channels-1-link $out/nix/var/nix/profiles/per-user/${uname}/channels
mkdir -p $out/root/.nix-defexpr mkdir -p $out${userHome}/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/root/channels $out/root/.nix-defexpr/channels ln -s /nix/var/nix/profiles/per-user/${uname}/channels $out${userHome}/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out/root/.nix-channels echo "${channelURL} ${channelName}" > $out${userHome}/.nix-channels
mkdir -p $out/bin $out/usr/bin mkdir -p $out/bin $out/usr/bin
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
'' + (lib.optionalString (flake-registry-path != null) '' '' + (lib.optionalString (flake-registry-path != null) ''
nixCacheDir="/root/.cache/nix" nixCacheDir="${userHome}/.cache/nix"
mkdir -p $out$nixCacheDir mkdir -p $out$nixCacheDir
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json" globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
ln -s ${flake-registry-path} $out$globalFlakeRegistryPath ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
@ -268,7 +285,7 @@ let
in in
pkgs.dockerTools.buildLayeredImageWithNixDb { pkgs.dockerTools.buildLayeredImageWithNixDb {
inherit name tag maxLayers; inherit name tag maxLayers uid gid uname gname;
contents = [ baseSystem ]; contents = [ baseSystem ];
@ -279,25 +296,28 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
fakeRootCommands = '' fakeRootCommands = ''
chmod 1777 tmp chmod 1777 tmp
chmod 1777 var/tmp chmod 1777 var/tmp
chown -R ${toString uid}:${toString gid} .${userHome}
chown -R ${toString uid}:${toString gid} nix
''; '';
config = { config = {
Cmd = [ "/root/.nix-profile/bin/bash" ]; Cmd = [ "${userHome}/.nix-profile/bin/bash" ];
User = "${toString uid}:${toString gid}";
Env = [ Env = [
"USER=root" "USER=${uname}"
"PATH=${lib.concatStringsSep ":" [ "PATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/bin" "${userHome}/.nix-profile/bin"
"/nix/var/nix/profiles/default/bin" "/nix/var/nix/profiles/default/bin"
"/nix/var/nix/profiles/default/sbin" "/nix/var/nix/profiles/default/sbin"
]}" ]}"
"MANPATH=${lib.concatStringsSep ":" [ "MANPATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/share/man" "${userHome}/.nix-profile/share/man"
"/nix/var/nix/profiles/default/share/man" "/nix/var/nix/profiles/default/share/man"
]}" ]}"
"SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_PATH=/nix/var/nix/profiles/per-user/root/channels:/root/.nix-defexpr/channels" "NIX_PATH=/nix/var/nix/profiles/per-user/${uname}/channels:${userHome}/.nix-defexpr/channels"
]; ];
}; };

View file

@ -124,18 +124,36 @@
# without "polluting" the top level "`pkgs`" attrset. # without "polluting" the top level "`pkgs`" attrset.
# This also has the benefit of providing us with a distinct set of packages # This also has the benefit of providing us with a distinct set of packages
# we can iterate over. # we can iterate over.
nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { nixComponents =
inherit (final) lib; lib.makeScopeWithSplicing'
inherit officialRelease; {
src = self; inherit (final) splicePackages;
}); inherit (final.nixDependencies) newScope;
}
{
otherSplices = final.generateSplicesForMkScope "nixComponents";
f = import ./packaging/components.nix {
inherit (final) lib;
inherit officialRelease;
src = self;
};
};
# The dependencies are in their own scope, so that they don't have to be # The dependencies are in their own scope, so that they don't have to be
# in Nixpkgs top level `pkgs` or `nixComponents`. # in Nixpkgs top level `pkgs` or `nixComponents`.
nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix { nixDependencies =
inherit inputs stdenv; lib.makeScopeWithSplicing'
pkgs = final; {
}); inherit (final) splicePackages;
inherit (final) newScope; # layered directly on pkgs, unlike nixComponents above
}
{
otherSplices = final.generateSplicesForMkScope "nixDependencies";
f = import ./packaging/dependencies.nix {
inherit inputs stdenv;
pkgs = final;
};
};
nix = final.nixComponents.nix-cli; nix = final.nixComponents.nix-cli;

View file

@ -496,7 +496,6 @@
''^scripts/create-darwin-volume\.sh$'' ''^scripts/create-darwin-volume\.sh$''
''^scripts/install-darwin-multi-user\.sh$'' ''^scripts/install-darwin-multi-user\.sh$''
''^scripts/install-multi-user\.sh$'' ''^scripts/install-multi-user\.sh$''
''^scripts/install-nix-from-closure\.sh$''
''^scripts/install-systemd-multi-user\.sh$'' ''^scripts/install-systemd-multi-user\.sh$''
''^src/nix/get-env\.sh$'' ''^src/nix/get-env\.sh$''
''^tests/functional/ca/build-dry\.sh$'' ''^tests/functional/ca/build-dry\.sh$''

View file

@ -34,6 +34,7 @@ endif
subproject('libutil-c') subproject('libutil-c')
subproject('libstore-c') subproject('libstore-c')
subproject('libexpr-c') subproject('libexpr-c')
subproject('libflake-c')
subproject('libmain-c') subproject('libmain-c')
# Language Bindings # Language Bindings

View file

@ -44,6 +44,7 @@ in
nix-expr-tests = callPackage ../src/libexpr-tests/package.nix { }; nix-expr-tests = callPackage ../src/libexpr-tests/package.nix { };
nix-flake = callPackage ../src/libflake/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { };
nix-flake-c = callPackage ../src/libflake-c/package.nix { };
nix-flake-tests = callPackage ../src/libflake-tests/package.nix { }; nix-flake-tests = callPackage ../src/libflake-tests/package.nix { };
nix-main = callPackage ../src/libmain/package.nix { }; nix-main = callPackage ../src/libmain/package.nix { };

View file

@ -70,6 +70,9 @@ let
pkgs.buildPackages.meson pkgs.buildPackages.meson
pkgs.buildPackages.ninja pkgs.buildPackages.ninja
] ++ prevAttrs.nativeBuildInputs or []; ] ++ prevAttrs.nativeBuildInputs or [];
mesonCheckFlags = prevAttrs.mesonCheckFlags or [] ++ [
"--print-errorlogs"
];
}; };
mesonBuildLayer = finalAttrs: prevAttrs: mesonBuildLayer = finalAttrs: prevAttrs:

View file

@ -19,6 +19,7 @@
nix-expr-tests, nix-expr-tests,
nix-flake, nix-flake,
nix-flake-c,
nix-flake-tests, nix-flake-tests,
nix-main, nix-main,
@ -53,6 +54,7 @@ let
nix-expr-c nix-expr-c
nix-fetchers nix-fetchers
nix-flake nix-flake
nix-flake-c
nix-main nix-main
nix-main-c nix-main-c
nix-store nix-store
@ -86,6 +88,7 @@ let
"nix-expr-c" "nix-expr-c"
"nix-fetchers" "nix-fetchers"
"nix-flake" "nix-flake"
"nix-flake-c"
"nix-main" "nix-main"
"nix-main-c" "nix-main-c"
"nix-store" "nix-store"
@ -169,6 +172,7 @@ in
nix-expr nix-expr
nix-expr-c nix-expr-c
nix-flake nix-flake
nix-flake-c
nix-main nix-main
nix-main-c nix-main-c
; ;

View file

@ -23,7 +23,7 @@ in
runCommand "nix-binary-tarball-${version}" env '' runCommand "nix-binary-tarball-${version}" env ''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo cp ${installerClosureInfo}/registration $TMPDIR/reginfo
cp ${./create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh cp ${./create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
substitute ${./install-nix-from-closure.sh} $TMPDIR/install \ substitute ${./install-nix-from-tarball.sh} $TMPDIR/install \
--subst-var-by nix ${nix} \ --subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert} --subst-var-by cacert ${cacert}

View file

@ -48,15 +48,14 @@ case "$(uname -s)" in
INSTALL_MODE=no-daemon;; INSTALL_MODE=no-daemon;;
esac esac
# space-separated string ACTION=
ACTIONS=
# handle the command line flags # handle the command line flags
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case $1 in case $1 in
--daemon) --daemon)
INSTALL_MODE=daemon INSTALL_MODE=daemon
ACTIONS="${ACTIONS}install " ACTION=install
;; ;;
--no-daemon) --no-daemon)
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
@ -65,18 +64,14 @@ while [ $# -gt 0 ]; do
fi fi
INSTALL_MODE=no-daemon INSTALL_MODE=no-daemon
# intentional tail space # intentional tail space
ACTIONS="${ACTIONS}install " ACTION=install
;; ;;
# --uninstall)
# # intentional tail space
# ACTIONS="${ACTIONS}uninstall "
# ;;
--yes) --yes)
export NIX_INSTALLER_YES=1;; export NIX_INSTALLER_YES=1;;
--no-channel-add) --no-channel-add)
export NIX_INSTALLER_NO_CHANNEL_ADD=1;; export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
--daemon-user-count) --daemon-user-count)
export NIX_USER_COUNT=$2 export NIX_USER_COUNT="$2"
shift;; shift;;
--no-modify-profile) --no-modify-profile)
NIX_INSTALLER_NO_MODIFY_PROFILE=1;; NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
@ -128,7 +123,7 @@ done
if [ "$INSTALL_MODE" = "daemon" ]; then if [ "$INSTALL_MODE" = "daemon" ]; then
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n' printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split exec "$self/install-multi-user" $ACTION
exit 0 exit 0
fi fi

View file

@ -40,6 +40,7 @@ GENERATE_LATEX = NO
INPUT = \ INPUT = \
@src@/src/libutil-c \ @src@/src/libutil-c \
@src@/src/libexpr-c \ @src@/src/libexpr-c \
@src@/src/libflake-c \
@src@/src/libstore-c \ @src@/src/libstore-c \
@src@/src/external-api-docs/README.md @src@/src/external-api-docs/README.md

View file

@ -30,6 +30,7 @@ mkMesonDerivation (finalAttrs: {
# Source is not compiled, but still must be available for Doxygen # Source is not compiled, but still must be available for Doxygen
# to gather comments. # to gather comments.
(cpp ../libexpr-c) (cpp ../libexpr-c)
(cpp ../libflake-c)
(cpp ../libstore-c) (cpp ../libstore-c)
(cpp ../libutil-c) (cpp ../libutil-c)
]; ];

View file

@ -179,30 +179,34 @@ BuiltPathsCommand::BuiltPathsCommand(bool recursive)
void BuiltPathsCommand::run(ref<Store> store, Installables && installables) void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
{ {
BuiltPaths paths; BuiltPaths rootPaths, allPaths;
if (all) { if (all) {
if (installables.size()) if (installables.size())
throw UsageError("'--all' does not expect arguments"); throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations // XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : store->queryAllValidPaths()) for (auto & p : store->queryAllValidPaths())
paths.emplace_back(BuiltPath::Opaque{p}); rootPaths.emplace_back(BuiltPath::Opaque{p});
allPaths = rootPaths;
} else { } else {
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables); rootPaths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
allPaths = rootPaths;
if (recursive) { if (recursive) {
// XXX: This only computes the store path closure, ignoring // XXX: This only computes the store path closure, ignoring
// intermediate realisations // intermediate realisations
StorePathSet pathsRoots, pathsClosure; StorePathSet pathsRoots, pathsClosure;
for (auto & root : paths) { for (auto & root : rootPaths) {
auto rootFromThis = root.outPaths(); auto rootFromThis = root.outPaths();
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end()); pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
} }
store->computeFSClosure(pathsRoots, pathsClosure); store->computeFSClosure(pathsRoots, pathsClosure);
for (auto & path : pathsClosure) for (auto & path : pathsClosure)
paths.emplace_back(BuiltPath::Opaque{path}); allPaths.emplace_back(BuiltPath::Opaque{path});
} }
} }
run(store, std::move(paths)); run(store, std::move(allPaths), std::move(rootPaths));
} }
StorePathsCommand::StorePathsCommand(bool recursive) StorePathsCommand::StorePathsCommand(bool recursive)
@ -210,10 +214,10 @@ StorePathsCommand::StorePathsCommand(bool recursive)
{ {
} }
void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths) void StorePathsCommand::run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths)
{ {
StorePathSet storePaths; StorePathSet storePaths;
for (auto & builtPath : paths) for (auto & builtPath : allPaths)
for (auto & p : builtPath.outPaths()) for (auto & p : builtPath.outPaths())
storePaths.insert(p); storePaths.insert(p);
@ -245,7 +249,7 @@ void MixProfile::updateProfile(const StorePath & storePath)
{ {
if (!profile) if (!profile)
return; return;
auto store = getStore().dynamic_pointer_cast<LocalFSStore>(); auto store = getDstStore().dynamic_pointer_cast<LocalFSStore>();
if (!store) if (!store)
throw Error("'--profile' is not supported for this Nix store"); throw Error("'--profile' is not supported for this Nix store");
auto profile2 = absPath(*profile); auto profile2 = absPath(*profile);
@ -365,4 +369,31 @@ void MixEnvironment::setEnviron()
return; return;
} }
void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & buildables, LocalFSStore & store)
{
for (const auto & [_i, buildable] : enumerate(buildables)) {
auto i = _i;
std::visit(
overloaded{
[&](const BuiltPath::Opaque & bo) {
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
store.addPermRoot(bo.path, absPath(symlink.string()));
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
if (output.first != "out")
symlink += fmt("-%s", output.first);
store.addPermRoot(output.second, absPath(symlink.string()));
}
},
},
buildable.raw());
}
}
} }

View file

@ -18,6 +18,7 @@ extern char ** savedArgv;
class EvalState; class EvalState;
struct Pos; struct Pos;
class Store; class Store;
class LocalFSStore;
static constexpr Command::Category catHelp = -1; static constexpr Command::Category catHelp = -1;
static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catSecondary = 100;
@ -46,7 +47,20 @@ struct StoreCommand : virtual Command
{ {
StoreCommand(); StoreCommand();
void run() override; void run() override;
/**
* Return the default Nix store.
*/
ref<Store> getStore(); ref<Store> getStore();
/**
* Return the destination Nix store.
*/
virtual ref<Store> getDstStore()
{
return getStore();
}
virtual ref<Store> createStore(); virtual ref<Store> createStore();
/** /**
* Main entry point, with a `Store` provided * Main entry point, with a `Store` provided
@ -69,7 +83,7 @@ struct CopyCommand : virtual StoreCommand
ref<Store> createStore() override; ref<Store> createStore() override;
ref<Store> getDstStore(); ref<Store> getDstStore() override;
}; };
/** /**
@ -239,7 +253,7 @@ public:
BuiltPathsCommand(bool recursive = false); BuiltPathsCommand(bool recursive = false);
virtual void run(ref<Store> store, BuiltPaths && paths) = 0; virtual void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) = 0;
void run(ref<Store> store, Installables && installables) override; void run(ref<Store> store, Installables && installables) override;
@ -252,7 +266,7 @@ struct StorePathsCommand : public BuiltPathsCommand
virtual void run(ref<Store> store, StorePaths && storePaths) = 0; virtual void run(ref<Store> store, StorePaths && storePaths) = 0;
void run(ref<Store> store, BuiltPaths && paths) override; void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) override;
}; };
/** /**
@ -354,4 +368,10 @@ std::string showVersions(const std::set<std::string> & versions);
void printClosureDiff( void printClosureDiff(
ref<Store> store, const StorePath & beforePath, const StorePath & afterPath, std::string_view indent); ref<Store> store, const StorePath & beforePath, const StorePath & afterPath, std::string_view indent);
/**
* Create symlinks prefixed by `outLink` to the store paths in
* `buildables`.
*/
void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & buildables, LocalFSStore & store);
} }

View file

@ -29,13 +29,13 @@ EvalSettings evalSettings {
{ {
{ {
"flake", "flake",
[](ref<Store> store, std::string_view rest) { [](EvalState & state, std::string_view rest) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
// FIXME `parseFlakeRef` should take a `std::string_view`. // FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false);
debug("fetching flake search path element '%s''", rest); debug("fetching flake search path element '%s''", rest);
auto storePath = flakeRef.resolve(store).fetchTree(store).first; auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first;
return store->toRealPath(storePath); return state.rootPath(state.store->toRealPath(storePath));
}, },
}, },
}, },

View file

@ -858,7 +858,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
applyDefaultInstallables(rawInstallables); applyDefaultInstallables(rawInstallables);
std::vector<FlakeRef> res; std::vector<FlakeRef> res;
res.reserve(rawInstallables.size()); res.reserve(rawInstallables.size());
for (auto i : rawInstallables) for (const auto & i : rawInstallables)
res.push_back(parseFlakeRefWithFragment( res.push_back(parseFlakeRefWithFragment(
fetchSettings, fetchSettings,
expandTilde(i), expandTilde(i),
@ -918,4 +918,12 @@ void BuiltPathsCommand::applyDefaultInstallables(std::vector<std::string> & rawI
rawInstallables.push_back("."); rawInstallables.push_back(".");
} }
BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithResult)
{
BuiltPaths res;
for (auto & i : builtPathsWithResult)
res.push_back(i.path);
return res;
}
} }

View file

@ -86,6 +86,8 @@ struct BuiltPathWithResult
std::optional<BuildResult> result; std::optional<BuildResult> result;
}; };
BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithResult);
/** /**
* Shorthand, for less typing and helping us keep the choice of * Shorthand, for less typing and helping us keep the choice of
* collection in sync. * collection in sync.

View file

@ -30,8 +30,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json deps_public += nlohmann_json
@ -72,7 +70,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'built-path.cc', 'built-path.cc',

View file

@ -29,8 +29,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
# TODO rename, because it will conflict with downstream projects # TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -55,7 +53,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'nix_api_expr.cc', 'nix_api_expr.cc',

View file

@ -6,6 +6,7 @@
#include "eval-gc.hh" #include "eval-gc.hh"
#include "globals.hh" #include "globals.hh"
#include "eval-settings.hh" #include "eval-settings.hh"
#include "ref.hh"
#include "nix_api_expr.h" #include "nix_api_expr.h"
#include "nix_api_expr_internal.h" #include "nix_api_expr_internal.h"
@ -18,6 +19,29 @@
# include <mutex> # include <mutex>
#endif #endif
/**
* @brief Allocate and initialize using self-reference
*
* This allows a brace initializer to reference the object being constructed.
*
* @warning Use with care, as the pointer points to an object that is not fully constructed yet.
*
* @tparam T Type to allocate
* @tparam F A function type for `init`, taking a T* and returning the initializer for T
* @param init Function that takes a T* and returns the initializer for T
* @return Pointer to allocated and initialized object
*/
template <typename T, typename F>
static T * unsafe_new_with_self(F && init)
{
// Allocate
void * p = ::operator new(
sizeof(T),
static_cast<std::align_val_t>(alignof(T)));
// Initialize with placement new
return new (p) T(init(static_cast<T *>(p)));
}
nix_err nix_libexpr_init(nix_c_context * context) nix_err nix_libexpr_init(nix_c_context * context)
{ {
if (context) if (context)
@ -67,7 +91,7 @@ nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, nix_val
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
state->state.callFunction(fn->value, nargs, (nix::Value * *)args, value->value, nix::noPos); state->state.callFunction(fn->value, {(nix::Value * *) args, nargs}, value->value, nix::noPos);
state->state.forceValue(value->value, nix::noPos); state->state.forceValue(value->value, nix::noPos);
} }
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
@ -93,7 +117,42 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
} }
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store) nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Store * store)
{
if (context)
context->last_err_code = NIX_OK;
try {
return unsafe_new_with_self<nix_eval_state_builder>([&](auto * self) {
return nix_eval_state_builder{
.store = nix::ref<nix::Store>(store->ptr),
.settings = nix::EvalSettings{/* &bool */ self->readOnlyMode},
.fetchSettings = nix::fetchers::Settings{},
.readOnlyMode = true,
};
});
}
NIXC_CATCH_ERRS_NULL
}
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
{
delete builder;
}
nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_builder * builder)
{
if (context)
context->last_err_code = NIX_OK;
try {
// TODO: load in one go?
builder->settings.readOnlyMode = nix::settings.readOnlyMode;
loadConfFile(builder->settings);
loadConfFile(builder->fetchSettings);
}
NIXC_CATCH_ERRS
}
nix_err nix_eval_state_builder_set_lookup_path(nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath_c)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
@ -102,28 +161,47 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
if (lookupPath_c != nullptr) if (lookupPath_c != nullptr)
for (size_t i = 0; lookupPath_c[i] != nullptr; i++) for (size_t i = 0; lookupPath_c[i] != nullptr; i++)
lookupPath.push_back(lookupPath_c[i]); lookupPath.push_back(lookupPath_c[i]);
builder->lookupPath = nix::LookupPath::parse(lookupPath);
}
NIXC_CATCH_ERRS
}
void * p = ::operator new( EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder)
sizeof(EvalState), {
static_cast<std::align_val_t>(alignof(EvalState))); if (context)
auto * p2 = static_cast<EvalState *>(p); context->last_err_code = NIX_OK;
new (p) EvalState { try {
.fetchSettings = nix::fetchers::Settings{}, return unsafe_new_with_self<EvalState>([&](auto * self) {
.settings = nix::EvalSettings{ return EvalState{
nix::settings.readOnlyMode, .fetchSettings = std::move(builder->fetchSettings),
}, .settings = std::move(builder->settings),
.state = nix::EvalState( .state = nix::EvalState(
nix::LookupPath::parse(lookupPath), builder->lookupPath,
store->ptr, builder->store,
p2->fetchSettings, self->fetchSettings,
p2->settings), self->settings),
}; };
loadConfFile(p2->settings); });
return p2;
} }
NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_NULL
} }
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store)
{
auto builder = nix_eval_state_builder_new(context, store);
if (builder == nullptr)
return nullptr;
if (nix_eval_state_builder_load(context, builder) != NIX_OK)
return nullptr;
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c)
!= NIX_OK)
return nullptr;
return nix_eval_state_build(context, builder);
}
void nix_state_free(EvalState * state) void nix_state_free(EvalState * state)
{ {
delete state; delete state;

View file

@ -30,6 +30,11 @@ extern "C" {
// cffi start // cffi start
// Type definitions // Type definitions
/**
* @brief Builder for EvalState
*/
typedef struct nix_eval_state_builder nix_eval_state_builder;
/** /**
* @brief Represents a state of the Nix language evaluator. * @brief Represents a state of the Nix language evaluator.
* *
@ -174,12 +179,70 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value *
nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value); nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value);
/** /**
* @brief Create a new Nix language evaluator state. * @brief Create a new nix_eval_state_builder
*
* The settings are initialized to their default value.
* Values can be sourced elsewhere with nix_eval_state_builder_load.
*
* @param[out] context Optional, stores error information
* @param[in] store The Nix store to use.
* @return A new nix_eval_state_builder or NULL on failure.
*/
nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Store * store);
/**
* @brief Read settings from the ambient environment
*
* Settings are sourced from environment variables and configuration files,
* as documented in the Nix manual.
*
* @param[out] context Optional, stores error information
* @param[out] builder The builder to modify.
* @return NIX_OK if successful, an error code otherwise.
*/
nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_builder * builder);
/**
* @brief Set the lookup path for `<...>` expressions
*
* @param[in] context Optional, stores error information
* @param[in] builder The builder to modify.
* @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH.
*/
nix_err nix_eval_state_builder_set_lookup_path(
nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath);
/**
* @brief Create a new Nix language evaluator state
*
* Remember to nix_eval_state_builder_free after building the state.
*
* @param[out] context Optional, stores error information
* @param[in] builder The builder to use and free
* @return A new Nix state or NULL on failure.
* @see nix_eval_state_builder_new, nix_eval_state_builder_free
*/
EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder);
/**
* @brief Free a nix_eval_state_builder
*
* Does not fail.
*
* @param[in] builder The builder to free.
*/
void nix_eval_state_builder_free(nix_eval_state_builder * builder);
/**
* @brief Create a new Nix language evaluator state
*
* For more control, use nix_eval_state_builder
* *
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH. * @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH.
* @param[in] store The Nix store to use. * @param[in] store The Nix store to use.
* @return A new Nix state or NULL on failure. * @return A new Nix state or NULL on failure.
* @see nix_state_builder_new
*/ */
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath, Store * store); EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath, Store * store);

View file

@ -6,6 +6,17 @@
#include "eval-settings.hh" #include "eval-settings.hh"
#include "attr-set.hh" #include "attr-set.hh"
#include "nix_api_value.h" #include "nix_api_value.h"
#include "search-path.hh"
struct nix_eval_state_builder
{
nix::ref<nix::Store> store;
nix::EvalSettings settings;
nix::fetchers::Settings fetchSettings;
nix::LookupPath lookupPath;
// TODO: make an EvalSettings setting own this instead?
bool readOnlyMode;
};
struct EvalState struct EvalState
{ {

View file

@ -213,7 +213,7 @@ nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_
/** @brief Get path as string /** @brief Get path as string
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect * @param[in] value Nix value to inspect
* @return string * @return string, if the type is NIX_TYPE_PATH
* @return NULL in case of error. * @return NULL in case of error.
*/ */
const char * nix_get_path_string(nix_c_context * context, const nix_value * value); const char * nix_get_path_string(nix_c_context * context, const nix_value * value);

View file

@ -24,11 +24,10 @@ deps_public_maybe_subproject = [
dependency('nix-store'), dependency('nix-store'),
dependency('nix-store-test-support'), dependency('nix-store-test-support'),
dependency('nix-expr'), dependency('nix-expr'),
dependency('nix-expr-c'),
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
rapidcheck = dependency('rapidcheck') rapidcheck = dependency('rapidcheck')
deps_public += rapidcheck deps_public += rapidcheck
@ -41,7 +40,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'tests/value/context.cc', 'tests/value/context.cc',

View file

@ -4,6 +4,7 @@
, nix-store-test-support , nix-store-test-support
, nix-expr , nix-expr
, nix-expr-c
, rapidcheck , rapidcheck
@ -35,6 +36,7 @@ mkMesonLibrary (finalAttrs: {
propagatedBuildInputs = [ propagatedBuildInputs = [
nix-store-test-support nix-store-test-support
nix-expr nix-expr
nix-expr-c
rapidcheck rapidcheck
]; ];

View file

@ -40,6 +40,12 @@ namespace nix {
return v; return v;
} }
Value * maybeThunk(std::string input, bool forceValue = true) {
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
assert(e);
return e->maybeThunk(state, state.baseEnv);
}
Symbol createSymbol(const char * value) { Symbol createSymbol(const char * value) {
return state.symbols.create(value); return state.symbols.create(value);
} }

View file

@ -138,4 +138,27 @@ TEST(nix_isAllowedURI, non_scheme_colon) {
ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed)); ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed));
} }
} // namespace nix class EvalStateTest : public LibExprTest {};
TEST_F(EvalStateTest, getBuiltins_ok) {
auto evaled = maybeThunk("builtins");
auto & builtins = state.getBuiltins();
ASSERT_TRUE(builtins.type() == nAttrs);
ASSERT_EQ(evaled, &builtins);
}
TEST_F(EvalStateTest, getBuiltin_ok) {
auto & builtin = state.getBuiltin("toString");
ASSERT_TRUE(builtin.type() == nFunction);
// FIXME
// auto evaled = maybeThunk("builtins.toString");
// ASSERT_EQ(evaled, &builtin);
auto & builtin2 = state.getBuiltin("true");
ASSERT_EQ(state.forceBool(builtin2, noPos, "in unit test"), true);
}
TEST_F(EvalStateTest, getBuiltin_fail) {
ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError);
}
} // namespace nix

View file

@ -25,8 +25,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
subdir('build-utils-meson/export-all-symbols') subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('build-utils-meson/windows-version')
@ -51,7 +49,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'derived-path.cc', 'derived-path.cc',

View file

@ -7,12 +7,49 @@
#include "tests/nix_api_expr.hh" #include "tests/nix_api_expr.hh"
#include "tests/string_callback.hh" #include "tests/string_callback.hh"
#include "file-system.hh"
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace nixC { namespace nixC {
TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
{
auto tmpDir = nix::createTempDir();
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
auto nixpkgs = tmpDir + "/pkgs";
auto nixos = tmpDir + "/cfg";
std::filesystem::create_directories(nixpkgs);
std::filesystem::create_directories(nixos);
std::string nixpkgsEntry = "nixpkgs=" + nixpkgs;
std::string nixosEntry = "nixos-config=" + nixos;
const char * lookupPath[] = {nixpkgsEntry.c_str(), nixosEntry.c_str(), nullptr};
auto builder = nix_eval_state_builder_new(ctx, store);
assert_ctx_ok();
ASSERT_EQ(NIX_OK, nix_eval_state_builder_set_lookup_path(ctx, builder, lookupPath));
assert_ctx_ok();
auto state = nix_eval_state_build(ctx, builder);
assert_ctx_ok();
nix_eval_state_builder_free(builder);
Value * value = nix_alloc_value(ctx, state);
nix_expr_eval_from_string(ctx, state, "builtins.seq <nixos-config> <nixpkgs>", ".", value);
assert_ctx_ok();
ASSERT_EQ(nix_get_type(ctx, value), NIX_TYPE_PATH);
assert_ctx_ok();
auto pathStr = nix_get_path_string(ctx, value);
assert_ctx_ok();
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
}
TEST_F(nix_api_expr_test, nix_expr_eval_from_string) TEST_F(nix_api_expr_test, nix_expr_eval_from_string)
{ {
nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value);

View file

@ -177,6 +177,57 @@ namespace nix {
) )
); );
// The following macros ultimately define 48 tests (16 variations on three
// templates). Each template tests an expression that can be written in 2^4
// different ways, by making four choices about whether to write a particular
// attribute path segment as `x.y = ...;` (collapsed) or `x = { y = ...; };`
// (expanded).
//
// The nestedAttrsetMergeXXXX tests check that the expression
// `{ a.b.c = 1; a.b.d = 2; }` has the same value regardless of how it is
// expanded. (That exact expression is exercised in test
// nestedAttrsetMerge0000, because it is fully collapsed. The test
// nestedAttrsetMerge1001 would instead examine
// `{ a = { b.c = 1; }; a.b = { d = 2; }; }`.)
//
// The nestedAttrsetMergeDupXXXX tests check that the expression
// `{ a.b.c = 1; a.b.c = 2; }` throws a duplicate attribute error, again
// regardless of how it is expanded.
//
// The nestedAttrsetMergeLetXXXX tests check that the expression
// `let a.b.c = 1; a.b.d = 2; in a` has the same value regardless of how it is
// expanded.
#define X_EXPAND_IF0(k, v) k "." v
#define X_EXPAND_IF1(k, v) k " = { " v " };"
#define X4(w, x, y, z) \
TEST_F(TrivialExpressionTest, nestedAttrsetMerge##w##x##y##z) { \
auto v = eval("{ a.b = { c = 1; d = 2; }; } == { " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " }"); \
ASSERT_THAT(v, IsTrue()); \
}; \
TEST_F(TrivialExpressionTest, nestedAttrsetMergeDup##w##x##y##z) { \
ASSERT_THROW(eval("{ " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "c = 2;")) " }"), Error); \
}; \
TEST_F(TrivialExpressionTest, nestedAttrsetMergeLet##w##x##y##z) { \
auto v = eval("{ b = { c = 1; d = 2; }; } == (let " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " in a)"); \
ASSERT_THAT(v, IsTrue()); \
};
#define X3(...) X4(__VA_ARGS__, 0) X4(__VA_ARGS__, 1)
#define X2(...) X3(__VA_ARGS__, 0) X3(__VA_ARGS__, 1)
#define X1(...) X2(__VA_ARGS__, 0) X2(__VA_ARGS__, 1)
X1(0) X1(1)
#undef X_EXPAND_IF0
#undef X_EXPAND_IF1
#undef X1
#undef X2
#undef X3
#undef X4
TEST_F(TrivialExpressionTest, functor) { TEST_F(TrivialExpressionTest, functor) {
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5"); auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
ASSERT_THAT(v, IsIntEq(15)); ASSERT_THAT(v, IsIntEq(15));

View file

@ -129,7 +129,6 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
try { try {
auto colon = fn.rfind(':'); auto colon = fn.rfind(':');
if (colon == std::string::npos) fail(); if (colon == std::string::npos) fail();
std::string filename(fn, 0, colon);
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos)); auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno}; return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {

View file

@ -87,11 +87,15 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
{ {
if (v.isThunk()) { if (v.isThunk()) {
Env * env = v.payload.thunk.env; Env * env = v.payload.thunk.env;
assert(env || v.isBlackhole());
Expr * expr = v.payload.thunk.expr; Expr * expr = v.payload.thunk.expr;
try { try {
v.mkBlackhole(); v.mkBlackhole();
//checkInterrupt(); //checkInterrupt();
expr->eval(*this, *env, v); if (env) [[likely]]
expr->eval(*this, *env, v);
else
ExprBlackHole::throwInfiniteRecursionError(*this, v);
} catch (...) { } catch (...) {
v.mkThunk(env, expr); v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos); tryFixupBlackHolePos(v, pos);

View file

@ -3,10 +3,11 @@
#include "config.hh" #include "config.hh"
#include "ref.hh" #include "ref.hh"
#include "source-path.hh"
namespace nix { namespace nix {
class Store; class EvalState;
struct EvalSettings : Config struct EvalSettings : Config
{ {
@ -18,11 +19,8 @@ struct EvalSettings : Config
* *
* The return value is (a) whether the entry was valid, and, if so, * The return value is (a) whether the entry was valid, and, if so,
* what does it map to. * what does it map to.
*
* @todo Return (`std::optional` of) `SourceAccssor` or something
* more structured instead of mere `std::string`?
*/ */
using LookupPathHook = std::optional<std::string>(ref<Store> store, std::string_view); using LookupPathHook = std::optional<SourcePath>(EvalState & state, std::string_view);
/** /**
* Map from "scheme" to a `LookupPathHook`. * Map from "scheme" to a `LookupPathHook`.

View file

@ -448,7 +448,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
/* Install value the base environment. */ /* Install value the base environment. */
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v)); getBuiltins().payload.attrs->push_back(Attr(symbols.create(name2), v));
} }
} }
@ -516,16 +516,26 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
else { else {
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); getBuiltins().payload.attrs->push_back(Attr(symbols.create(primOp.name), v));
} }
return v; return v;
} }
Value & EvalState::getBuiltins()
{
return *baseEnv.values[0];
}
Value & EvalState::getBuiltin(const std::string & name) Value & EvalState::getBuiltin(const std::string & name)
{ {
return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value; auto it = getBuiltins().attrs()->get(symbols.create(name));
if (it)
return *it->value;
else
error<EvalError>("builtin '%1%' not found", name).debugThrow();
} }
@ -588,14 +598,14 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
if (isFunctor(v)) { if (isFunctor(v)) {
try { try {
Value & functor = *v.attrs()->find(sFunctor)->value; Value & functor = *v.attrs()->find(sFunctor)->value;
Value * vp = &v; Value * vp[] = {&v};
Value partiallyApplied; Value partiallyApplied;
// The first paramater is not user-provided, and may be // The first paramater is not user-provided, and may be
// handled by code that is opaque to the user, like lib.const = x: y: y; // handled by code that is opaque to the user, like lib.const = x: y: y;
// So preferably we show docs that are relevant to the // So preferably we show docs that are relevant to the
// "partially applied" function returned by e.g. `const`. // "partially applied" function returned by e.g. `const`.
// We apply the first argument: // We apply the first argument:
callFunction(functor, 1, &vp, partiallyApplied, noPos); callFunction(functor, vp, partiallyApplied, noPos);
auto _level = addCallDepth(noPos); auto _level = addCallDepth(noPos);
return getDoc(partiallyApplied); return getDoc(partiallyApplied);
} }
@ -1460,7 +1470,7 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
v.mkLambda(&env, this); v.mkLambda(&env, this);
} }
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos)
{ {
auto _level = addCallDepth(pos); auto _level = addCallDepth(pos);
@ -1475,16 +1485,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto makeAppChain = [&]() auto makeAppChain = [&]()
{ {
vRes = vCur; vRes = vCur;
for (size_t i = 0; i < nrArgs; ++i) { for (auto arg : args) {
auto fun2 = allocValue(); auto fun2 = allocValue();
*fun2 = vRes; *fun2 = vRes;
vRes.mkPrimOpApp(fun2, args[i]); vRes.mkPrimOpApp(fun2, arg);
} }
}; };
const Attr * functor; const Attr * functor;
while (nrArgs > 0) { while (args.size() > 0) {
if (vCur.isLambda()) { if (vCur.isLambda()) {
@ -1587,15 +1597,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
throw; throw;
} }
nrArgs--; args = args.subspan(1);
args += 1;
} }
else if (vCur.isPrimOp()) { else if (vCur.isPrimOp()) {
size_t argsLeft = vCur.primOp()->arity; size_t argsLeft = vCur.primOp()->arity;
if (nrArgs < argsLeft) { if (args.size() < argsLeft) {
/* We don't have enough arguments, so create a tPrimOpApp chain. */ /* We don't have enough arguments, so create a tPrimOpApp chain. */
makeAppChain(); makeAppChain();
return; return;
@ -1607,15 +1616,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (countCalls) primOpCalls[fn->name]++; if (countCalls) primOpCalls[fn->name]++;
try { try {
fn->fun(*this, vCur.determinePos(noPos), args, vCur); fn->fun(*this, vCur.determinePos(noPos), args.data(), vCur);
} catch (Error & e) { } catch (Error & e) {
if (fn->addTrace) if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
throw; throw;
} }
nrArgs -= argsLeft; args = args.subspan(argsLeft);
args += argsLeft;
} }
} }
@ -1631,7 +1639,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto arity = primOp->primOp()->arity; auto arity = primOp->primOp()->arity;
auto argsLeft = arity - argsDone; auto argsLeft = arity - argsDone;
if (nrArgs < argsLeft) { if (args.size() < argsLeft) {
/* We still don't have enough arguments, so extend the tPrimOpApp chain. */ /* We still don't have enough arguments, so extend the tPrimOpApp chain. */
makeAppChain(); makeAppChain();
return; return;
@ -1663,8 +1671,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
throw; throw;
} }
nrArgs -= argsLeft; args = args.subspan(argsLeft);
args += argsLeft;
} }
} }
@ -1675,13 +1682,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
Value * args2[] = {allocValue(), args[0]}; Value * args2[] = {allocValue(), args[0]};
*args2[0] = vCur; *args2[0] = vCur;
try { try {
callFunction(*functor->value, 2, args2, vCur, functor->pos); callFunction(*functor->value, args2, vCur, functor->pos);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)"); e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
throw; throw;
} }
nrArgs--; args = args.subspan(1);
args++;
} }
else else
@ -1724,7 +1730,7 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
for (size_t i = 0; i < args.size(); ++i) for (size_t i = 0; i < args.size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env); vArgs[i] = args[i]->maybeThunk(state, env);
state.callFunction(vFun, args.size(), vArgs.data(), v, pos); state.callFunction(vFun, vArgs, v, pos);
} }
@ -2046,9 +2052,12 @@ void ExprPos::eval(EvalState & state, Env & env, Value & v)
state.mkPos(v, pos); state.mkPos(v, pos);
} }
void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value & v)
void ExprBlackHole::eval(EvalState & state, Env & env, Value & v)
{ {
throwInfiniteRecursionError(state, v);
}
[[gnu::noinline]] [[noreturn]] void ExprBlackHole::throwInfiniteRecursionError(EvalState & state, Value &v) {
state.error<InfiniteRecursionError>("infinite recursion encountered") state.error<InfiniteRecursionError>("infinite recursion encountered")
.atPos(v.determinePos(noPos)) .atPos(v.determinePos(noPos))
.debugThrow(); .debugThrow();
@ -3029,8 +3038,8 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
if (!rOpt) continue; if (!rOpt) continue;
auto r = *rOpt; auto r = *rOpt;
Path res = suffix == "" ? r : concatStrings(r, "/", suffix); auto res = (r / CanonPath(suffix)).resolveSymlinks();
if (pathExists(res)) return rootPath(CanonPath(canonPath(res))); if (res.pathExists()) return res;
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
@ -3045,13 +3054,13 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
} }
std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl)
{ {
auto & value = value0.s; auto & value = value0.s;
auto i = lookupPathResolved.find(value); auto i = lookupPathResolved.find(value);
if (i != lookupPathResolved.end()) return i->second; if (i != lookupPathResolved.end()) return i->second;
auto finish = [&](std::string res) { auto finish = [&](SourcePath res) {
debug("resolved search path element '%s' to '%s'", value, res); debug("resolved search path element '%s' to '%s'", value, res);
lookupPathResolved.emplace(value, res); lookupPathResolved.emplace(value, res);
return res; return res;
@ -3064,7 +3073,7 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
fetchSettings, fetchSettings,
EvalSettings::resolvePseudoUrl(value)); EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
return finish(store->toRealPath(storePath)); return finish(rootPath(store->toRealPath(storePath)));
} catch (Error & e) { } catch (Error & e) {
logWarning({ logWarning({
.msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
@ -3076,29 +3085,29 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
auto scheme = value.substr(0, colPos); auto scheme = value.substr(0, colPos);
auto rest = value.substr(colPos + 1); auto rest = value.substr(colPos + 1);
if (auto * hook = get(settings.lookupPathHooks, scheme)) { if (auto * hook = get(settings.lookupPathHooks, scheme)) {
auto res = (*hook)(store, rest); auto res = (*hook)(*this, rest);
if (res) if (res)
return finish(std::move(*res)); return finish(std::move(*res));
} }
} }
{ {
auto path = absPath(value); auto path = rootPath(value);
/* Allow access to paths in the search path. */ /* Allow access to paths in the search path. */
if (initAccessControl) { if (initAccessControl) {
allowPath(path); allowPath(path.path.abs());
if (store->isInStore(path)) { if (store->isInStore(path.path.abs())) {
try { try {
StorePathSet closure; StorePathSet closure;
store->computeFSClosure(store->toStorePath(path).first, closure); store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure);
for (auto & p : closure) for (auto & p : closure)
allowPath(p); allowPath(p);
} catch (InvalidPath &) { } } catch (InvalidPath &) { }
} }
} }
if (pathExists(path)) if (path.pathExists())
return finish(std::move(path)); return finish(std::move(path));
else { else {
logWarning({ logWarning({
@ -3109,7 +3118,6 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
debug("failed to resolve search path element '%s'", value); debug("failed to resolve search path element '%s'", value);
return std::nullopt; return std::nullopt;
} }

View file

@ -347,7 +347,7 @@ private:
LookupPath lookupPath; LookupPath lookupPath;
std::map<std::string, std::optional<std::string>> lookupPathResolved; std::map<std::string, std::optional<SourcePath>> lookupPathResolved;
/** /**
* Cache used by prim_match(). * Cache used by prim_match().
@ -452,9 +452,9 @@ public:
* *
* If the specified search path element is a URI, download it. * If the specified search path element is a URI, download it.
* *
* If it is not found, return `std::nullopt` * If it is not found, return `std::nullopt`.
*/ */
std::optional<std::string> resolveLookupPathPath( std::optional<SourcePath> resolveLookupPathPath(
const LookupPath::Path & elem, const LookupPath::Path & elem,
bool initAccessControl = false); bool initAccessControl = false);
@ -623,8 +623,19 @@ private:
public: public:
/**
* Retrieve a specific builtin, equivalent to evaluating `builtins.${name}`.
* @param name The attribute name of the builtin to retrieve.
* @throws EvalError if the builtin does not exist.
*/
Value & getBuiltin(const std::string & name); Value & getBuiltin(const std::string & name);
/**
* Retrieve the `builtins` attrset, equivalent to evaluating the reference `builtins`.
* Always returns an attribute set value.
*/
Value & getBuiltins();
struct Doc struct Doc
{ {
Pos pos; Pos pos;
@ -690,13 +701,12 @@ public:
bool isFunctor(Value & fun); bool isFunctor(Value & fun);
// FIXME: use std::span void callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos) void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{ {
Value * args[] = {&arg}; Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos); callFunction(fun, args, vRes, pos);
} }
/** /**
@ -809,7 +819,6 @@ public:
bool callPathFilter( bool callPathFilter(
Value * filterFun, Value * filterFun,
const SourcePath & path, const SourcePath & path,
std::string_view pathArg,
PosIdx pos); PosIdx pos);
DocComment getDocCommentForPos(PosIdx pos); DocComment getDocCommentForPos(PosIdx pos);

View file

@ -27,8 +27,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
boost = dependency( boost = dependency(
'boost', 'boost',
modules : ['container', 'context'], modules : ['container', 'context'],
@ -79,7 +77,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
parser_tab = custom_target( parser_tab = custom_target(
input : 'parser.y', input : 'parser.y',

View file

@ -468,6 +468,7 @@ struct ExprBlackHole : Expr
void show(const SymbolTable & symbols, std::ostream & str) const override {} void show(const SymbolTable & symbols, std::ostream & str) const override {}
void eval(EvalState & state, Env & env, Value & v) override; void eval(EvalState & state, Env & env, Value & v) override;
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {} void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
[[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v);
}; };
extern ExprBlackHole eBlackHole; extern ExprBlackHole eBlackHole;

View file

@ -88,6 +88,7 @@ struct ParserState
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc); void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, Expr * stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es); std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
@ -120,64 +121,29 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
// Checking attrPath validity. // Checking attrPath validity.
// =========================== // ===========================
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
ExprAttrs * nested;
if (i->symbol) { if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { nested = dynamic_cast<ExprAttrs *>(j->second.e);
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); if (!nested) {
if (!attrs2) dupAttr(attrPath, pos, j->second.pos); attrPath.erase(i + 1, attrPath.end());
attrs = attrs2;
} else
dupAttr(attrPath, pos, j->second.pos); dupAttr(attrPath, pos, j->second.pos);
}
} else { } else {
ExprAttrs * nested = new ExprAttrs; nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs = nested;
} }
} else { } else {
ExprAttrs *nested = new ExprAttrs; nested = new ExprAttrs;
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
attrs = nested;
} }
attrs = nested;
} }
// Expr insertion. // Expr insertion.
// ========================== // ==========================
if (i->symbol) { if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
if (j != attrs->attrs.end()) {
// This attr path is already defined. However, if both
// e and the expr pointed by the attr path are two attribute sets,
// we want to merge them.
// Otherwise, throw an error.
auto ae = dynamic_cast<ExprAttrs *>(e);
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
from.displ += jAttrs->inheritFromExprs->size();
}
}
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
}
} else {
dupAttr(attrPath, pos, j->second.pos);
}
} else {
// This attr path is not defined. Let's create it.
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
e->setName(i->symbol);
}
} else { } else {
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
} }
@ -189,6 +155,60 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
} }
} }
/**
* Precondition: attrPath is used for error messages and should already contain
* symbol as its last element.
*/
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
{
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
if (j != attrs->attrs.end()) {
// This attr path is already defined. However, if both
// e and the expr pointed by the attr path are two attribute sets,
// we want to merge them.
// Otherwise, throw an error.
auto ae = dynamic_cast<ExprAttrs *>(def.e);
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
// N.B. In a world in which we are less bound by our past mistakes, we
// would also test that jAttrs and ae are not recursive. The effect of
// not doing so is that any `rec` marker on ae is discarded, and any
// `rec` marker on jAttrs will apply to the attributes in ae.
// See https://github.com/NixOS/nix/issues/9020.
if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
from.displ += jAttrs->inheritFromExprs->size();
}
attrPath.emplace_back(AttrName(ad.first));
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
attrPath.pop_back();
}
ae->attrs.clear();
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(),
std::make_move_iterator(ae->dynamicAttrs.begin()),
std::make_move_iterator(ae->dynamicAttrs.end()));
ae->dynamicAttrs.clear();
if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
std::make_move_iterator(ae->inheritFromExprs->begin()),
std::make_move_iterator(ae->inheritFromExprs->end()));
ae->inheritFromExprs = nullptr;
}
} else {
dupAttr(attrPath, def.pos, j->second.pos);
}
} else {
// This attr path is not defined. Let's create it.
attrs->attrs.emplace(symbol, def);
def.e->setName(symbol);
}
}
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
{ {
std::sort(formals->formals.begin(), formals->formals.end(), std::sort(formals->formals.begin(), formals->formals.end(),

View file

@ -66,14 +66,12 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
ensureValid(b.drvPath->getBaseStorePath()); ensureValid(b.drvPath->getBaseStorePath());
}, },
[&](const NixStringContextElem::Opaque & o) { [&](const NixStringContextElem::Opaque & o) {
auto ctxS = store->printStorePath(o.path);
ensureValid(o.path); ensureValid(o.path);
if (maybePathsOut) if (maybePathsOut)
maybePathsOut->emplace(o.path); maybePathsOut->emplace(o.path);
}, },
[&](const NixStringContextElem::DrvDeep & d) { [&](const NixStringContextElem::DrvDeep & d) {
/* Treat same as Opaque */ /* Treat same as Opaque */
auto ctxS = store->printStorePath(d.drvPath);
ensureValid(d.drvPath); ensureValid(d.drvPath);
if (maybePathsOut) if (maybePathsOut)
maybePathsOut->emplace(d.drvPath); maybePathsOut->emplace(d.drvPath);
@ -724,7 +722,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
/* Call the `operator' function with `e' as argument. */ /* Call the `operator' function with `e' as argument. */
Value newElements; Value newElements;
state.callFunction(*op->value, 1, &e, newElements, noPos); state.callFunction(*op->value, {&e, 1}, newElements, noPos);
state.forceList(newElements, noPos, "while evaluating the return value of the `operator` passed to builtins.genericClosure"); state.forceList(newElements, noPos, "while evaluating the return value of the `operator` passed to builtins.genericClosure");
/* Add the values returned by the operator to the work set. */ /* Add the values returned by the operator to the work set. */
@ -1603,7 +1601,8 @@ static RegisterPrimOp primop_placeholder({
*************************************************************/ *************************************************************/
/* Convert the argument to a path. !!! obsolete? */ /* Convert the argument to a path and then to a string (confusing,
eh?). !!! obsolete? */
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NixStringContext context; NixStringContext context;
@ -2437,7 +2436,6 @@ static RegisterPrimOp primop_toFile({
bool EvalState::callPathFilter( bool EvalState::callPathFilter(
Value * filterFun, Value * filterFun,
const SourcePath & path, const SourcePath & path,
std::string_view pathArg,
PosIdx pos) PosIdx pos)
{ {
auto st = path.lstat(); auto st = path.lstat();
@ -2445,12 +2443,12 @@ bool EvalState::callPathFilter(
/* Call the filter function. The first argument is the path, the /* Call the filter function. The first argument is the path, the
second is a string indicating the type of the file. */ second is a string indicating the type of the file. */
Value arg1; Value arg1;
arg1.mkString(pathArg); arg1.mkString(path.path.abs());
// assert that type is not "unknown" // assert that type is not "unknown"
Value * args []{&arg1, fileTypeToString(*this, st.type)}; Value * args []{&arg1, fileTypeToString(*this, st.type)};
Value res; Value res;
callFunction(*filterFun, 2, args, res, pos); callFunction(*filterFun, args, res, pos);
return forceBool(res, pos, "while evaluating the return value of the path filter function"); return forceBool(res, pos, "while evaluating the return value of the path filter function");
} }
@ -2488,7 +2486,7 @@ static void addPath(
if (filterFun) if (filterFun)
filter = std::make_unique<PathFilter>([&](const Path & p) { filter = std::make_unique<PathFilter>([&](const Path & p) {
auto p2 = CanonPath(p); auto p2 = CanonPath(p);
return state.callPathFilter(filterFun, {path.accessor, p2}, p2.abs(), pos); return state.callPathFilter(filterFun, {path.accessor, p2}, pos);
}); });
std::optional<StorePath> expectedStorePath; std::optional<StorePath> expectedStorePath;
@ -2614,13 +2612,13 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256);
else else
state.error<EvalError>( state.error<EvalError>(
"unsupported argument '%1%' to 'addPath'", "unsupported argument '%1%' to 'builtins.path'",
state.symbols[attr.name] state.symbols[attr.name]
).atPos(attr.pos).debugThrow(); ).atPos(attr.pos).debugThrow();
} }
if (!path) if (!path)
state.error<EvalError>( state.error<EvalError>(
"missing required 'path' attribute in the first argument to builtins.path" "missing required 'path' attribute in the first argument to 'builtins.path'"
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
if (name.empty()) if (name.empty())
name = path->baseName(); name = path->baseName();
@ -3487,7 +3485,7 @@ static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args
for (auto [n, elem] : enumerate(args[2]->listItems())) { for (auto [n, elem] : enumerate(args[2]->listItems())) {
Value * vs []{vCur, elem}; Value * vs []{vCur, elem};
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
state.callFunction(*args[0], 2, vs, *vCur, pos); state.callFunction(*args[0], vs, *vCur, pos);
} }
state.forceValue(v, pos); state.forceValue(v, pos);
} else { } else {
@ -3637,7 +3635,7 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
Value * vs[] = {a, b}; Value * vs[] = {a, b};
Value vBool; Value vBool;
state.callFunction(*args[0], 2, vs, vBool, noPos); state.callFunction(*args[0], vs, vBool, noPos);
return state.forceBool(vBool, pos, "while evaluating the return value of the sorting function passed to builtins.sort"); return state.forceBool(vBool, pos, "while evaluating the return value of the sorting function passed to builtins.sort");
}; };
@ -4385,7 +4383,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
for (auto i = begin; i != end; ++i) { for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3); assert(idx <= 2 * len + 1 - 3);
auto match = *i; const auto & match = *i;
// Add a string for non-matched characters. // Add a string for non-matched characters.
list[idx++] = mkString(state, match.prefix()); list[idx++] = mkString(state, match.prefix());
@ -4937,7 +4935,7 @@ void EvalState::createBaseEnv()
/* Now that we've added all primops, sort the `builtins' set, /* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */ because attribute lookups expect it to be sorted. */
baseEnv.values[0]->payload.attrs->sort(); getBuiltins().payload.attrs->sort();
staticBaseEnv->sort(); staticBaseEnv->sort();

View file

@ -132,6 +132,8 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
}, },
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep { [&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* Reuse original item because we want this to be idempotent. */ /* Reuse original item because we want this to be idempotent. */
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
above does not make much sense. */
return std::move(c); return std::move(c);
}, },
}, context.begin()->raw) }), }, context.begin()->raw) }),

View file

@ -40,7 +40,7 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
}); });
} }
auto toPath = *toPathMaybe; const auto & toPath = *toPathMaybe;
// check and return // check and return

View file

@ -33,9 +33,8 @@ void emitTreeAttrs(
// FIXME: support arbitrary input attributes. // FIXME: support arbitrary input attributes.
auto narHash = input.getNarHash(); if (auto narHash = input.getNarHash())
assert(narHash); attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
if (input.getType() == "git") if (input.getType() == "git")
attrs.alloc("submodules").mkBool( attrs.alloc("submodules").mkBool(

View file

@ -24,8 +24,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
subdir('build-utils-meson/export-all-symbols') subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('build-utils-meson/windows-version')
@ -44,7 +42,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'public-key.cc', 'public-key.cc',

View file

@ -36,7 +36,7 @@ struct CacheImpl : Cache
{ {
auto state(_state.lock()); auto state(_state.lock());
auto dbPath = getCacheDir() + "/fetcher-cache-v2.sqlite"; auto dbPath = getCacheDir() + "/fetcher-cache-v3.sqlite";
createDirs(dirOf(dbPath)); createDirs(dirOf(dbPath));
state->db = SQLite(dbPath); state->db = SQLite(dbPath);

View file

@ -44,6 +44,8 @@ StorePath fetchToStore(
: store.addToStore( : store.addToStore(
name, path, method, HashAlgorithm::SHA256, {}, filter2, repair); name, path, method, HashAlgorithm::SHA256, {}, filter2, repair);
debug(mode == FetchMode::DryRun ? "hashed '%s'" : "copied '%s' to '%s'", path, store.printStorePath(storePath));
if (cacheKey && mode == FetchMode::Copy) if (cacheKey && mode == FetchMode::Copy)
fetchers::getCache()->upsert(*cacheKey, store, {}, storePath); fetchers::getCache()->upsert(*cacheKey, store, {}, storePath);

View file

@ -522,8 +522,6 @@ struct GitInputScheme : InputScheme
auto origRev = input.getRev(); auto origRev = input.getRev();
std::string name = input.getName();
auto originalRef = input.getRef(); auto originalRef = input.getRef();
auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo); auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo);
input.attrs.insert_or_assign("ref", ref); input.attrs.insert_or_assign("ref", ref);

View file

@ -26,8 +26,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json deps_public += nlohmann_json
@ -46,7 +44,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'attrs.cc', 'attrs.cc',
@ -55,15 +53,15 @@ sources = files(
'fetch-to-store.cc', 'fetch-to-store.cc',
'fetchers.cc', 'fetchers.cc',
'filtering-source-accessor.cc', 'filtering-source-accessor.cc',
'git.cc',
'git-utils.cc', 'git-utils.cc',
'git.cc',
'github.cc', 'github.cc',
'indirect.cc', 'indirect.cc',
'mercurial.cc', 'mercurial.cc',
'mounted-source-accessor.cc', 'mounted-source-accessor.cc',
'path.cc', 'path.cc',
'store-path-accessor.cc',
'registry.cc', 'registry.cc',
'store-path-accessor.cc',
'tarball.cc', 'tarball.cc',
) )
@ -74,11 +72,11 @@ headers = files(
'cache.hh', 'cache.hh',
'fetch-settings.hh', 'fetch-settings.hh',
'fetch-to-store.hh', 'fetch-to-store.hh',
'fetchers.hh',
'filtering-source-accessor.hh', 'filtering-source-accessor.hh',
'git-utils.hh', 'git-utils.hh',
'git-lfs-fetch.hh', 'git-lfs-fetch.hh',
'mounted-source-accessor.hh', 'mounted-source-accessor.hh',
'fetchers.hh',
'registry.hh', 'registry.hh',
'store-path-accessor.hh', 'store-path-accessor.hh',
'tarball.hh', 'tarball.hh',

View file

@ -94,12 +94,9 @@ void Registry::add(
void Registry::remove(const Input & input) void Registry::remove(const Input & input)
{ {
// FIXME: use C++20 std::erase. entries.erase(
for (auto i = entries.begin(); i != entries.end(); ) std::remove_if(entries.begin(), entries.end(), [&](const Entry & entry) { return entry.from == input; }),
if (i->from == input) entries.end());
i = entries.erase(i);
else
++i;
} }
static Path getSystemRegistryPath() static Path getSystemRegistryPath()

1
src/libflake-c/.version Symbolic link
View file

@ -0,0 +1 @@
../../.version

View file

@ -0,0 +1 @@
../../build-utils-meson/

View file

@ -0,0 +1,93 @@
project('nix-flake-c', 'cpp',
version : files('.version'),
default_options : [
'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level
'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail
],
meson_version : '>= 1.1',
license : 'LGPL-2.1-or-later',
)
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
configdata = configuration_data()
deps_private_maybe_subproject = [
dependency('nix-util'),
dependency('nix-store'),
dependency('nix-expr'),
dependency('nix-flake'),
]
deps_public_maybe_subproject = [
dependency('nix-util-c'),
dependency('nix-store-c'),
dependency('nix-expr-c'),
]
subdir('build-utils-meson/subprojects')
# TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
config_h = configure_file(
configuration : configdata,
output : 'config-flake.h',
)
add_project_arguments(
# TODO(Qyriad): Yes this is how the autoconf+Make system did it.
# It would be nice for our headers to be idempotent instead.
# From C++ libraries, only for internals
'-include', 'config-util.hh',
'-include', 'config-store.hh',
'-include', 'config-expr.hh',
# not generated (yet?)
# '-include', 'config-flake.hh',
# From C libraries, for our public, installed headers too
'-include', 'config-util.h',
'-include', 'config-store.h',
'-include', 'config-expr.h',
'-include', 'config-flake.h',
language : 'cpp',
)
subdir('build-utils-meson/common')
sources = files(
'nix_api_flake.cc',
)
include_dirs = [include_directories('.')]
headers = [config_h] + files(
'nix_api_flake.h',
)
# TODO move this header to libexpr, maybe don't use it in tests?
headers += files('nix_api_flake.h')
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
this_library = library(
'nixflakec',
sources,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args: linker_export_flags,
prelink : true, # For C++ static initializers
install : true,
)
install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')

View file

@ -0,0 +1,32 @@
#include "nix_api_flake.h"
#include "nix_api_flake_internal.hh"
#include "nix_api_util_internal.h"
#include "flake/flake.hh"
nix_flake_settings * nix_flake_settings_new(nix_c_context * context)
{
try {
auto settings = nix::make_ref<nix::flake::Settings>();
return new nix_flake_settings{settings};
}
NIXC_CATCH_ERRS_NULL
}
void nix_flake_settings_free(nix_flake_settings * settings)
{
delete settings;
}
nix_err nix_flake_init_global(nix_c_context * context, nix_flake_settings * settings)
{
static std::shared_ptr<nix::flake::Settings> registeredSettings;
try {
if (registeredSettings)
throw nix::Error("nix_flake_init_global already initialized");
registeredSettings = settings->settings;
nix::flake::initLib(*registeredSettings);
}
NIXC_CATCH_ERRS
}

View file

@ -0,0 +1,46 @@
#ifndef NIX_API_FLAKE_H
#define NIX_API_FLAKE_H
/** @defgroup libflake libflake
* @brief Bindings to the Nix Flakes library
*
* @{
*/
/** @file
* @brief Main entry for the libflake C bindings
*/
#include "nix_api_store.h"
#include "nix_api_util.h"
#include "nix_api_expr.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
typedef struct nix_flake_settings nix_flake_settings;
// Function prototypes
/**
* Create a nix_flake_settings initialized with default values.
* @param[out] context Optional, stores error information
* @return A new nix_flake_settings or NULL on failure.
* @see nix_flake_settings_free
*/
nix_flake_settings * nix_flake_settings_new(nix_c_context * context);
/**
* @brief Release the resources associated with a nix_flake_settings.
*/
void nix_flake_settings_free(nix_flake_settings * settings);
/**
* @brief Register Flakes support process-wide.
*/
nix_err nix_flake_init_global(nix_c_context * context, nix_flake_settings * settings);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View file

@ -0,0 +1,9 @@
#pragma once
#include "ref.hh"
#include "flake/settings.hh"
struct nix_flake_settings
{
nix::ref<nix::flake::Settings> settings;
};

View file

@ -0,0 +1,60 @@
{ lib
, stdenv
, mkMesonLibrary
, nix-store-c
, nix-expr-c
, nix-flake
# Configuration Options
, version
}:
let
inherit (lib) fileset;
in
mkMesonLibrary (finalAttrs: {
pname = "nix-flake-c";
inherit version;
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../.version
./.version
./meson.build
# ./meson.options
(fileset.fileFilter (file: file.hasExt "cc") ./.)
(fileset.fileFilter (file: file.hasExt "hh") ./.)
(fileset.fileFilter (file: file.hasExt "h") ./.)
];
propagatedBuildInputs = [
nix-expr-c
nix-store-c
nix-flake
];
preConfigure =
# "Inline" .version so it's not a symlink, and includes the suffix.
# Do the meson utils, without modification.
''
chmod u+w ./.version
echo ${version} > ../../.version
'';
mesonFlags = [
];
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
LDFLAGS = "-fuse-ld=gold";
};
meta = {
platforms = lib.platforms.unix ++ lib.platforms.windows;
};
})

View file

@ -19,13 +19,12 @@ subdir('build-utils-meson/deps-lists')
deps_private_maybe_subproject = [ deps_private_maybe_subproject = [
dependency('nix-expr-test-support'), dependency('nix-expr-test-support'),
dependency('nix-flake'), dependency('nix-flake'),
dependency('nix-flake-c'),
] ]
deps_public_maybe_subproject = [ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
subdir('build-utils-meson/export-all-symbols') subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('build-utils-meson/windows-version')
@ -44,10 +43,11 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'flakeref.cc', 'flakeref.cc',
'nix_api_flake.cc',
'url-name.cc', 'url-name.cc',
) )
@ -70,6 +70,7 @@ test(
this_exe, this_exe,
env : { env : {
'_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data',
'NIX_CONFIG': 'extra-experimental-features = flakes',
}, },
protocol : 'gtest', protocol : 'gtest',
) )

View file

@ -0,0 +1,51 @@
#include "nix_api_store.h"
#include "nix_api_store_internal.h"
#include "nix_api_util.h"
#include "nix_api_util_internal.h"
#include "nix_api_expr.h"
#include "nix_api_value.h"
#include "nix_api_flake.h"
#include "tests/nix_api_expr.hh"
#include "tests/string_callback.hh"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace nixC {
TEST_F(nix_api_store_test, nix_api_init_global_getFlake_exists)
{
nix_libstore_init(ctx);
assert_ctx_ok();
nix_libexpr_init(ctx);
assert_ctx_ok();
auto settings = nix_flake_settings_new(ctx);
assert_ctx_ok();
ASSERT_NE(nullptr, settings);
nix_flake_init_global(ctx, settings);
assert_ctx_ok();
nix_eval_state_builder * builder = nix_eval_state_builder_new(ctx, store);
ASSERT_NE(nullptr, builder);
assert_ctx_ok();
auto state = nix_eval_state_build(ctx, builder);
assert_ctx_ok();
ASSERT_NE(nullptr, state);
nix_eval_state_builder_free(builder);
auto value = nix_alloc_value(ctx, state);
assert_ctx_ok();
ASSERT_NE(nullptr, value);
nix_err err = nix_expr_eval_from_string(ctx, state, "builtins.getFlake", ".", value);
assert_ctx_ok();
ASSERT_EQ(NIX_OK, err);
ASSERT_EQ(NIX_TYPE_FUNCTION, nix_get_type(ctx, value));
}
} // namespace nixC

View file

@ -4,6 +4,7 @@
, mkMesonExecutable , mkMesonExecutable
, nix-flake , nix-flake
, nix-flake-c
, nix-expr-test-support , nix-expr-test-support
, rapidcheck , rapidcheck
@ -38,6 +39,7 @@ mkMesonExecutable (finalAttrs: {
buildInputs = [ buildInputs = [
nix-flake nix-flake
nix-flake-c
nix-expr-test-support nix-expr-test-support
rapidcheck rapidcheck
gtest gtest
@ -67,6 +69,7 @@ mkMesonExecutable (finalAttrs: {
mkdir -p "$HOME" mkdir -p "$HOME"
'' + '' '' + ''
export _NIX_TEST_UNIT_DATA=${resolvePath ./data} export _NIX_TEST_UNIT_DATA=${resolvePath ./data}
export NIX_CONFIG="extra-experimental-features = flakes"
${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage}
touch $out touch $out
''); '');

View file

@ -816,7 +816,7 @@ void callFlake(EvalState & state,
assert(vFetchFinalTree); assert(vFetchFinalTree);
Value * args[] = {vLocks, &vOverrides, *vFetchFinalTree}; Value * args[] = {vLocks, &vOverrides, *vFetchFinalTree};
state.callFunction(*vCallFlake, 3, args, vRes, noPos); state.callFunction(*vCallFlake, args, vRes, noPos);
} }
void initLib(const Settings & settings) void initLib(const Settings & settings)

View file

@ -67,6 +67,20 @@ std::optional<FlakeRef> maybeParseFlakeRef(
} }
} }
static std::pair<FlakeRef, std::string> fromParsedURL(
const fetchers::Settings & fetchSettings,
ParsedURL && parsedURL,
bool isFlake)
{
auto dir = getOr(parsedURL.query, "dir", "");
parsedURL.query.erase("dir");
std::string fragment;
std::swap(fragment, parsedURL.fragment);
return {FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake), dir), fragment};
}
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment( std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
const fetchers::Settings & fetchSettings, const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
@ -89,7 +103,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
fragment = percentDecode(url.substr(fragmentStart+1)); fragment = percentDecode(url.substr(fragmentStart+1));
} }
if (pathEnd != std::string::npos && fragmentStart != std::string::npos && url[pathEnd] == '?') { if (pathEnd != std::string::npos && fragmentStart != std::string::npos && url[pathEnd] == '?') {
query = decodeQuery(url.substr(pathEnd+1, fragmentStart-pathEnd-1)); query = decodeQuery(url.substr(pathEnd + 1, fragmentStart - pathEnd - 1));
} }
if (baseDir) { if (baseDir) {
@ -153,6 +167,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
.authority = "", .authority = "",
.path = flakeRoot, .path = flakeRoot,
.query = query, .query = query,
.fragment = fragment,
}; };
if (subdir != "") { if (subdir != "") {
@ -164,9 +179,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
if (pathExists(flakeRoot + "/.git/shallow")) if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1"); parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair( return fromParsedURL(fetchSettings, std::move(parsedURL), isFlake);
FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL), getOr(parsedURL.query, "dir", "")),
fragment);
} }
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
@ -185,11 +198,12 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
attrs.insert_or_assign("path", path); attrs.insert_or_assign("path", path);
return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment); return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment);
}; }
/**
/* Check if 'url' is a flake ID. This is an abbreviated syntax for * Check if `url` is a flake ID. This is an abbreviated syntax for
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */ * `flake:<flake-id>?ref=<ref>&rev=<rev>`.
*/
static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef( static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
const fetchers::Settings & fetchSettings, const fetchers::Settings & fetchSettings,
const std::string & url, const std::string & url,
@ -227,22 +241,11 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
bool isFlake bool isFlake
) )
{ {
ParsedURL parsedURL;
try { try {
parsedURL = parseURL(url); return fromParsedURL(fetchSettings, parseURL(url), isFlake);
} catch (BadURL &) { } catch (BadURL &) {
return std::nullopt; return std::nullopt;
} }
std::string fragment;
std::swap(fragment, parsedURL.fragment);
auto input = fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake);
input.parent = baseDir;
return std::make_pair(
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
fragment);
} }
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
@ -254,8 +257,6 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
{ {
using namespace fetchers; using namespace fetchers;
std::smatch match;
if (auto res = parseFlakeIdRef(fetchSettings, url, isFlake)) { if (auto res = parseFlakeIdRef(fetchSettings, url, isFlake)) {
return *res; return *res;
} else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) { } else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) {

View file

@ -26,8 +26,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json deps_public += nlohmann_json
@ -41,7 +39,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'flake/config.cc', 'flake/config.cc',

View file

@ -29,8 +29,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
# TODO rename, because it will conflict with downstream projects # TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -55,7 +53,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'nix_api_main.cc', 'nix_api_main.cc',

View file

@ -26,8 +26,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
pubsetbuf_test = ''' pubsetbuf_test = '''
#include <iostream> #include <iostream>
@ -60,7 +58,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'common-args.cc', 'common-args.cc',

View file

@ -27,8 +27,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
# TODO rename, because it will conflict with downstream projects # TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -51,7 +49,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'nix_api_store.cc', 'nix_api_store.cc',

View file

@ -22,11 +22,10 @@ deps_public_maybe_subproject = [
dependency('nix-util'), dependency('nix-util'),
dependency('nix-util-test-support'), dependency('nix-util-test-support'),
dependency('nix-store'), dependency('nix-store'),
dependency('nix-store-c'),
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
rapidcheck = dependency('rapidcheck') rapidcheck = dependency('rapidcheck')
deps_public += rapidcheck deps_public += rapidcheck
@ -38,7 +37,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'tests/derived-path.cc', 'tests/derived-path.cc',

View file

@ -4,6 +4,7 @@
, nix-util-test-support , nix-util-test-support
, nix-store , nix-store
, nix-store-c
, rapidcheck , rapidcheck
@ -35,6 +36,7 @@ mkMesonLibrary (finalAttrs: {
propagatedBuildInputs = [ propagatedBuildInputs = [
nix-util-test-support nix-util-test-support
nix-store nix-store
nix-store-c
rapidcheck rapidcheck
]; ];

View file

@ -25,8 +25,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
subdir('build-utils-meson/export-all-symbols') subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('build-utils-meson/windows-version')
@ -52,7 +50,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'common-protocol.cc', 'common-protocol.cc',

View file

@ -991,7 +991,10 @@ Goal::Co DerivationGoal::buildDone()
auto nixLogCommand = experimentalFeatureSettings.isEnabled(Xp::NixCommand) auto nixLogCommand = experimentalFeatureSettings.isEnabled(Xp::NixCommand)
? "nix log" ? "nix log"
: "nix-store -l"; : "nix-store -l";
msg += fmt("For full logs, run '" ANSI_BOLD "%s %s" ANSI_NORMAL "'.", // The command is on a separate line for easy copying, such as with triple click.
// This message will be indented elsewhere, so removing the indentation before the
// command will not put it at the start of the line unfortunately.
msg += fmt("For full logs, run:\n " ANSI_BOLD "%s %s" ANSI_NORMAL,
nixLogCommand, nixLogCommand,
worker.store.printStorePath(drvPath)); worker.store.printStorePath(drvPath));
} }
@ -1226,7 +1229,7 @@ HookReply DerivationGoal::tryBuildHook()
hook->toHook.writeSide.close(); hook->toHook.writeSide.close();
/* Create the log file and pipe. */ /* Create the log file and pipe. */
Path logFile = openLogFile(); [[maybe_unused]] Path logFile = openLogFile();
std::set<MuxablePipePollState::CommChannel> fds; std::set<MuxablePipePollState::CommChannel> fds;
fds.insert(hook->fromHook.readSide.get()); fds.insert(hook->fromHook.readSide.get());

View file

@ -32,7 +32,7 @@ Goal::Co DrvOutputSubstitutionGoal::init()
bool substituterFailed = false; bool substituterFailed = false;
for (auto sub : subs) { for (const auto & sub : subs) {
trace("trying next substituter"); trace("trying next substituter");
/* The callback of the curl download below can outlive `this` (if /* The callback of the curl download below can outlive `this` (if

View file

@ -57,7 +57,7 @@ Goal::Co PathSubstitutionGoal::init()
bool substituterFailed = false; bool substituterFailed = false;
for (auto sub : subs) { for (const auto & sub : subs) {
trace("trying next substituter"); trace("trying next substituter");
cleanup(); cleanup();

View file

@ -1017,29 +1017,31 @@ std::string hashPlaceholder(const OutputNameView outputName)
return "/" + hashString(HashAlgorithm::SHA256, concatStrings("nix-output:", outputName)).to_string(HashFormat::Nix32, false); return "/" + hashString(HashAlgorithm::SHA256, concatStrings("nix-output:", outputName)).to_string(HashFormat::Nix32, false);
} }
void BasicDerivation::applyRewrites(const StringMap & rewrites)
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
{ {
debug("Rewriting the derivation"); if (rewrites.empty()) return;
for (auto & rewrite : rewrites) { debug("rewriting the derivation");
for (auto & rewrite : rewrites)
debug("rewriting %s as %s", rewrite.first, rewrite.second); debug("rewriting %s as %s", rewrite.first, rewrite.second);
}
drv.builder = rewriteStrings(drv.builder, rewrites); builder = rewriteStrings(builder, rewrites);
for (auto & arg : drv.args) { for (auto & arg : args)
arg = rewriteStrings(arg, rewrites); arg = rewriteStrings(arg, rewrites);
}
StringPairs newEnv; StringPairs newEnv;
for (auto & envVar : drv.env) { for (auto & envVar : env) {
auto envName = rewriteStrings(envVar.first, rewrites); auto envName = rewriteStrings(envVar.first, rewrites);
auto envValue = rewriteStrings(envVar.second, rewrites); auto envValue = rewriteStrings(envVar.second, rewrites);
newEnv.emplace(envName, envValue); newEnv.emplace(envName, envValue);
} }
drv.env = newEnv; env = std::move(newEnv);
}
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
{
drv.applyRewrites(rewrites);
auto hashModulo = hashDerivationModulo(store, Derivation(drv), true); auto hashModulo = hashDerivationModulo(store, Derivation(drv), true);
for (auto & [outputName, output] : drv.outputs) { for (auto & [outputName, output] : drv.outputs) {

View file

@ -325,6 +325,12 @@ struct BasicDerivation
static std::string_view nameFromPath(const StorePath & storePath); static std::string_view nameFromPath(const StorePath & storePath);
/**
* Apply string rewrites to the `env`, `args` and `builder`
* fields.
*/
void applyRewrites(const StringMap & rewrites);
bool operator == (const BasicDerivation &) const = default; bool operator == (const BasicDerivation &) const = default;
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
//auto operator <=> (const BasicDerivation &) const = default; //auto operator <=> (const BasicDerivation &) const = default;

View file

@ -13,14 +13,9 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink)
auto sorted = topoSortPaths(paths); auto sorted = topoSortPaths(paths);
std::reverse(sorted.begin(), sorted.end()); std::reverse(sorted.begin(), sorted.end());
std::string doneLabel("paths exported");
//logger->incExpected(doneLabel, sorted.size());
for (auto & path : sorted) { for (auto & path : sorted) {
//Activity act(*logger, lvlInfo, "exporting path '%s'", path);
sink << 1; sink << 1;
exportPath(path, sink); exportPath(path, sink);
//logger->incProgress(doneLabel);
} }
sink << 0; sink << 0;

View file

@ -4,6 +4,7 @@
#include "finally.hh" #include "finally.hh"
#include "unix-domain-socket.hh" #include "unix-domain-socket.hh"
#include "signals.hh" #include "signals.hh"
#include "posix-fs-canonicalise.hh"
#if !defined(__linux__) #if !defined(__linux__)
// For shelling out to lsof // For shelling out to lsof
@ -763,13 +764,18 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
} }
} }
} }
for (auto & path : topoSortPaths(visited)) { for (auto & path : topoSortPaths(visited)) {
if (!dead.insert(path).second) continue; if (!dead.insert(path).second) continue;
if (shouldDelete) { if (shouldDelete) {
invalidatePathChecked(path); try {
deleteFromStore(path.to_string()); invalidatePathChecked(path);
referrersCache.erase(path); deleteFromStore(path.to_string());
referrersCache.erase(path);
} catch (PathInUse &e) {
// If we end up here, it's likely a new occurence
// of https://github.com/NixOS/nix/issues/11923
printError("BUG: %s", e.what());
}
} }
} }
}; };

View file

@ -10,12 +10,12 @@ PublicKeys getDefaultPublicKeys()
// FIXME: filter duplicates // FIXME: filter duplicates
for (auto s : settings.trustedPublicKeys.get()) { for (const auto & s : settings.trustedPublicKeys.get()) {
PublicKey key(s); PublicKey key(s);
publicKeys.emplace(key.name, key); publicKeys.emplace(key.name, key);
} }
for (auto secretKeyFile : settings.secretKeyFiles.get()) { for (const auto & secretKeyFile : settings.secretKeyFiles.get()) {
try { try {
SecretKey secretKey(readFile(secretKeyFile)); SecretKey secretKey(readFile(secretKeyFile));
publicKeys.emplace(secretKey.name, secretKey.toPublicKey()); publicKeys.emplace(secretKey.name, secretKey.toPublicKey());

View file

@ -156,7 +156,7 @@ void LocalOverlayStore::queryGCReferrers(const StorePath & path, StorePathSet &
StorePathSet LocalOverlayStore::queryValidDerivers(const StorePath & path) StorePathSet LocalOverlayStore::queryValidDerivers(const StorePath & path)
{ {
auto res = LocalStore::queryValidDerivers(path); auto res = LocalStore::queryValidDerivers(path);
for (auto p : lowerStore->queryValidDerivers(path)) for (const auto & p : lowerStore->queryValidDerivers(path))
res.insert(p); res.insert(p);
return res; return res;
} }

View file

@ -95,51 +95,6 @@ struct LocalStore::State::Stmts {
SQLiteStmt AddRealisationReference; SQLiteStmt AddRealisationReference;
}; };
static int getSchema(Path schemaPath)
{
int curSchema = 0;
if (pathExists(schemaPath)) {
auto s = readFile(schemaPath);
auto n = string2Int<int>(s);
if (!n)
throw Error("'%1%' is corrupt", schemaPath);
curSchema = *n;
}
return curSchema;
}
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
{
const int nixCASchemaVersion = 4;
int curCASchema = getSchema(schemaPath);
if (curCASchema != nixCASchemaVersion) {
if (curCASchema > nixCASchemaVersion) {
throw Error("current Nix store ca-schema is version %1%, but I only support %2%",
curCASchema, nixCASchemaVersion);
}
if (!lockFile(lockFd.get(), ltWrite, false)) {
printInfo("waiting for exclusive access to the Nix store for ca drvs...");
lockFile(lockFd.get(), ltNone, false); // We have acquired a shared lock; release it to prevent deadlocks
lockFile(lockFd.get(), ltWrite, true);
}
if (curCASchema == 0) {
static const char schema[] =
#include "ca-specific-schema.sql.gen.hh"
;
db.exec(schema);
curCASchema = nixCASchemaVersion;
}
if (curCASchema < 4)
throw Error("experimental CA schema version %d is no longer supported", curCASchema);
writeFile(schemaPath, fmt("%d", nixCASchemaVersion), 0666, true);
lockFile(lockFd.get(), ltRead, true);
}
}
LocalStore::LocalStore( LocalStore::LocalStore(
std::string_view scheme, std::string_view scheme,
PathView path, PathView path,
@ -316,6 +271,10 @@ LocalStore::LocalStore(
openDB(*state, false); openDB(*state, false);
/* Legacy database schema migrations. Don't bump 'schema' for
new migrations; instead, add a migration to
upgradeDBSchema(). */
if (curSchema < 8) { if (curSchema < 8) {
SQLiteTxn txn(state->db); SQLiteTxn txn(state->db);
state->db.exec("alter table ValidPaths add column ultimate integer"); state->db.exec("alter table ValidPaths add column ultimate integer");
@ -342,13 +301,7 @@ LocalStore::LocalStore(
else openDB(*state, false); else openDB(*state, false);
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { upgradeDBSchema(*state);
if (!readOnly) {
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
} else {
throw Error("need to migrate to content-addressed schema, but this cannot be done in read-only mode");
}
}
/* Prepare SQL statements. */ /* Prepare SQL statements. */
state->stmts->RegisterValidPath.create(state->db, state->stmts->RegisterValidPath.create(state->db,
@ -483,7 +436,17 @@ std::string LocalStore::getUri()
int LocalStore::getSchema() int LocalStore::getSchema()
{ return nix::getSchema(schemaPath); } {
int curSchema = 0;
if (pathExists(schemaPath)) {
auto s = readFile(schemaPath);
auto n = string2Int<int>(s);
if (!n)
throw Error("'%1%' is corrupt", schemaPath);
curSchema = *n;
}
return curSchema;
}
void LocalStore::openDB(State & state, bool create) void LocalStore::openDB(State & state, bool create)
{ {
@ -566,6 +529,42 @@ void LocalStore::openDB(State & state, bool create)
} }
void LocalStore::upgradeDBSchema(State & state)
{
state.db.exec("create table if not exists SchemaMigrations (migration text primary key not null);");
std::set<std::string> schemaMigrations;
{
SQLiteStmt querySchemaMigrations;
querySchemaMigrations.create(state.db, "select migration from SchemaMigrations;");
auto useQuerySchemaMigrations(querySchemaMigrations.use());
while (useQuerySchemaMigrations.next())
schemaMigrations.insert(useQuerySchemaMigrations.getStr(0));
}
auto doUpgrade = [&](const std::string & migrationName, const std::string & stmt)
{
if (schemaMigrations.contains(migrationName))
return;
debug("executing Nix database schema migration '%s'...", migrationName);
SQLiteTxn txn(state.db);
state.db.exec(stmt + fmt(";\ninsert into SchemaMigrations values('%s')", migrationName));
txn.commit();
schemaMigrations.insert(migrationName);
};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
doUpgrade(
"20220326-ca-derivations",
#include "ca-specific-schema.sql.gen.hh"
);
}
/* To improve purity, users may want to make the Nix store a read-only /* To improve purity, users may want to make the Nix store a read-only
bind mount. So make the Nix store writable for this process. */ bind mount. So make the Nix store writable for this process. */
void LocalStore::makeStoreWritable() void LocalStore::makeStoreWritable()

View file

@ -356,6 +356,8 @@ private:
void openDB(State & state, bool create); void openDB(State & state, bool create);
void upgradeDBSchema(State & state);
void makeStoreWritable(); void makeStoreWritable();
uint64_t queryValidPathId(State & state, const StorePath & path); uint64_t queryValidPathId(State & state, const StorePath & path);

View file

@ -82,7 +82,6 @@ if host_machine.system() == 'windows'
endif endif
subdir('build-utils-meson/libatomic') subdir('build-utils-meson/libatomic')
subdir('build-utils-meson/threads')
boost = dependency( boost = dependency(
'boost', 'boost',
@ -180,7 +179,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'binary-cache-store.cc', 'binary-cache-store.cc',

View file

@ -118,7 +118,7 @@ std::string NarInfo::to_string(const Store & store) const
if (deriver) if (deriver)
res += "Deriver: " + std::string(deriver->to_string()) + "\n"; res += "Deriver: " + std::string(deriver->to_string()) + "\n";
for (auto sig : sigs) for (const auto & sig : sigs)
res += "Sig: " + sig + "\n"; res += "Sig: " + sig + "\n";
if (ca) if (ca)

View file

@ -454,7 +454,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
debug("got %d keys, next marker '%s'", debug("got %d keys, next marker '%s'",
contents.size(), res.GetNextMarker()); contents.size(), res.GetNextMarker());
for (auto object : contents) { for (const auto & object : contents) {
auto & key = object.GetKey(); auto & key = object.GetKey();
if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue; if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue;
paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-" + MissingName)); paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-" + MissingName));

View file

@ -1332,7 +1332,7 @@ ref<Store> openStore(StoreReference && storeURI)
return std::make_shared<LocalStore>(params); return std::make_shared<LocalStore>(params);
}, },
[&](const StoreReference::Specified & g) { [&](const StoreReference::Specified & g) {
for (auto implem : *Implementations::registered) for (const auto & implem : *Implementations::registered)
if (implem.uriSchemes.count(g.scheme)) if (implem.uriSchemes.count(g.scheme))
return implem.create(g.scheme, g.authority, params); return implem.create(g.scheme, g.authority, params);
@ -1363,7 +1363,7 @@ std::list<ref<Store>> getDefaultSubstituters()
} }
}; };
for (auto uri : settings.substituters.get()) for (const auto & uri : settings.substituters.get())
addStore(uri); addStore(uri);
stores.sort([](ref<Store> & a, ref<Store> & b) { stores.sort([](ref<Store> & a, ref<Store> & b) {

View file

@ -883,7 +883,7 @@ void LocalDerivationGoal::startBuilder()
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
/* Create the log file. */ /* Create the log file. */
Path logFile = openLogFile(); [[maybe_unused]] Path logFile = openLogFile();
/* Create a pseudoterminal to get the output of the builder. */ /* Create a pseudoterminal to get the output of the builder. */
builderOut = posix_openpt(O_RDWR | O_NOCTTY); builderOut = posix_openpt(O_RDWR | O_NOCTTY);

View file

@ -25,8 +25,6 @@ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
# TODO rename, because it will conflict with downstream projects # TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -47,7 +45,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'nix_api_util.cc', 'nix_api_util.cc',

View file

@ -20,11 +20,10 @@ deps_private_maybe_subproject = [
] ]
deps_public_maybe_subproject = [ deps_public_maybe_subproject = [
dependency('nix-util'), dependency('nix-util'),
dependency('nix-util-c'),
] ]
subdir('build-utils-meson/subprojects') subdir('build-utils-meson/subprojects')
subdir('build-utils-meson/threads')
rapidcheck = dependency('rapidcheck') rapidcheck = dependency('rapidcheck')
deps_public += rapidcheck deps_public += rapidcheck
@ -35,7 +34,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/common')
sources = files( sources = files(
'tests/hash.cc', 'tests/hash.cc',

View file

@ -3,6 +3,7 @@
, mkMesonLibrary , mkMesonLibrary
, nix-util , nix-util
, nix-util-c
, rapidcheck , rapidcheck
@ -33,6 +34,7 @@ mkMesonLibrary (finalAttrs: {
propagatedBuildInputs = [ propagatedBuildInputs = [
nix-util nix-util
nix-util-c
rapidcheck rapidcheck
]; ];

View file

@ -40,7 +40,7 @@ void checkGTestWith(Testable && testable, MakeTestParams makeTestParams)
} else { } else {
std::ostringstream ss; std::ostringstream ss;
printResultMessage(result, ss); printResultMessage(result, ss);
FAIL() << ss.str() << std::endl; throw std::runtime_error(ss.str());
} }
} }
} }

View file

@ -26,14 +26,13 @@ protected:
inline void assert_ctx_ok() inline void assert_ctx_ok()
{ {
if (nix_err_code(ctx) == NIX_OK) { if (nix_err_code(ctx) == NIX_OK) {
return; return;
} }
unsigned int n; unsigned int n;
const char * p = nix_err_msg(nullptr, ctx, &n); const char * p = nix_err_msg(nullptr, ctx, &n);
std::string msg(p, n); std::string msg(p, n);
FAIL() << "nix_err_code(ctx) != NIX_OK, message: " << msg; throw std::runtime_error(std::string("nix_err_code(ctx) != NIX_OK, message: ") + msg);
} }
inline void assert_ctx_err() inline void assert_ctx_err()
@ -41,7 +40,7 @@ protected:
if (nix_err_code(ctx) != NIX_OK) { if (nix_err_code(ctx) != NIX_OK) {
return; return;
} }
FAIL() << "Got NIX_OK, but expected an error!"; throw std::runtime_error("Got NIX_OK, but expected an error!");
} }
}; };

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