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

Merge remote-tracking branch 'origin/master' into relative-flakes

This commit is contained in:
Eelco Dolstra 2024-12-18 21:09:27 +01:00
commit 8534c4222c
263 changed files with 2129 additions and 805 deletions

View file

@ -7,9 +7,16 @@ on:
permissions: read-all
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v30
- run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json
tests:
needs: [check_secrets]
strategy:
fail-fast: false
matrix:
@ -23,87 +30,24 @@ jobs:
- uses: cachix/install-nix-action@v30
with:
# The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true"
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v15
if: needs.check_secrets.outputs.cachix == 'true'
extra_nix_config: |
sandbox = true
max-jobs = 1
- uses: DeterminateSystems/magic-nix-cache-action@main
# Since ubuntu 22.30, unprivileged usernamespaces are no longer allowed to map to the root user:
# https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
if: matrix.os == 'ubuntu-latest'
- run: scripts/build-checks
- run: scripts/prepare-installer-for-github-actions
- name: Upload installer tarball
uses: actions/upload-artifact@v4
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- if: matrix.os == 'ubuntu-latest'
run: |
free -h
swapon --show
swap=$(swapon --show --noheadings | head -n 1 | awk '{print $1}')
echo "Found swap: $swap"
sudo swapoff $swap
# resize it (fallocate)
sudo fallocate -l 10G $swap
sudo mkswap $swap
sudo swapon $swap
free -h
(
while sleep 60; do
free -h
done
) &
- run: nix --experimental-features 'nix-command flakes' flake check -L
- run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json
# Steps to test CI automation in your own fork.
# Cachix:
# 1. Sign-up for https://www.cachix.org/
# 2. Create a cache for $githubuser-nix-install-tests
# 3. Create a cachix auth token and save it in https://github.com/$githubuser/nix/settings/secrets/actions in "Repository secrets" as CACHIX_AUTH_TOKEN
# Dockerhub:
# 1. Sign-up for https://hub.docker.com/
# 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions)
# 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork
check_secrets:
permissions:
contents: none
name: Check Cachix and Docker secrets present for installer tests
runs-on: ubuntu-latest
outputs:
cachix: ${{ steps.secret.outputs.cachix }}
docker: ${{ steps.secret.outputs.docker }}
steps:
- name: Check for secrets
id: secret
env:
_CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }}
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=cachix::${{ env._CACHIX_SECRETS != '' }}"
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
installer:
needs: [tests, check_secrets]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
runs-on: ubuntu-latest
outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.20.3/install
- uses: cachix/cachix-action@v15
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
cachixArgs: '-v'
- id: prepare-installer
run: scripts/prepare-installer-for-github-actions
name: installer-${{matrix.os}}
path: out/*
installer_test:
needs: [installer, check_secrets]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
needs: [tests]
strategy:
fail-fast: false
matrix:
@ -111,11 +55,18 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- name: Download installer tarball
uses: actions/download-artifact@v4
with:
name: installer-${{matrix.os}}
path: out
- name: Serving installer
id: serving_installer
run: ./scripts/serve-installer-for-github-actions
- uses: cachix/install-nix-action@v30
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
install_url: 'http://localhost:8126/install'
install_options: "--tarball-url-prefix http://localhost:8126/"
- run: sudo apt install fish zsh
if: matrix.os == 'ubuntu-latest'
- run: brew install fish
@ -127,32 +78,50 @@ jobs:
- run: exec bash -c "nix-channel --add https://releases.nixos.org/nixos/unstable/nixos-23.05pre466020.60c1d71f2ba nixpkgs"
- run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"
# Steps to test CI automation in your own fork.
# 1. Sign-up for https://hub.docker.com/
# 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions)
# 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork
check_secrets:
permissions:
contents: none
name: Check Docker secrets present for installer tests
runs-on: ubuntu-latest
outputs:
docker: ${{ steps.secret.outputs.docker }}
steps:
- name: Check for secrets
id: secret
env:
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
docker_push_image:
needs: [check_secrets, tests, vm_tests]
needs: [tests, vm_tests, check_secrets]
permissions:
contents: read
packages: write
if: >-
needs.check_secrets.outputs.docker == 'true' &&
github.event_name == 'push' &&
github.ref_name == 'master' &&
needs.check_secrets.outputs.cachix == 'true' &&
needs.check_secrets.outputs.docker == 'true'
github.ref_name == 'master'
runs-on: ubuntu-latest
steps:
- name: Check for secrets
id: secret
env:
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.20.3/install
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: DeterminateSystems/magic-nix-cache-action@main
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#nix.version | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v15
if: needs.check_secrets.outputs.cachix == 'true'
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L
- run: docker load -i ./result/image.tar.gz
- run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION

122
.gitignore vendored
View file

@ -1,110 +1,12 @@
Makefile.config
perl/Makefile.config
# /
/aclocal.m4
/autom4te.cache
/precompiled-headers.h.gch
/config.*
/configure
/stamp-h1
/svn-revision
/libtool
/config/config.*
# Default meson build dir
/build
# /doc/manual/
/doc/manual/*.1
/doc/manual/*.5
/doc/manual/*.8
/doc/manual/generated/*
/doc/manual/nix.json
/doc/manual/conf-file.json
/doc/manual/language.json
/doc/manual/xp-features.json
/doc/manual/source/SUMMARY.md
/doc/manual/source/SUMMARY-rl-next.md
/doc/manual/source/store/types/*
!/doc/manual/source/store/types/index.md.in
/doc/manual/source/command-ref/new-cli
/doc/manual/source/command-ref/conf-file.md
/doc/manual/source/command-ref/experimental-features-shortlist.md
/doc/manual/source/contributing/experimental-feature-descriptions.md
/doc/manual/source/language/builtins.md
/doc/manual/source/language/builtin-constants.md
/doc/manual/source/release-notes/rl-next.md
# /scripts/
/scripts/nix-profile.sh
/scripts/nix-profile-daemon.sh
/scripts/nix-profile.fish
/scripts/nix-profile-daemon.fish
# /src/libexpr/
/src/libexpr/lexer-tab.cc
/src/libexpr/lexer-tab.hh
/src/libexpr/parser-tab.cc
/src/libexpr/parser-tab.hh
/src/libexpr/parser-tab.output
/src/libexpr/nix.tbl
/src/libexpr/tests
/src/libexpr-tests/libnixexpr-tests
# /src/libfetchers
/src/libfetchers-tests/libnixfetchers-tests
# /src/libflake
/src/libflake-tests/libnixflake-tests
# /src/libstore/
*.gen.*
/src/libstore/tests
/src/libstore-tests/libnixstore-tests
# /src/libutil/
/src/libutil/tests
/src/libutil-tests/libnixutil-tests
/src/nix/nix
/src/nix/generated-doc
# /src/nix-env/
/src/nix-env/nix-env
# /src/nix-instantiate/
/src/nix-instantiate/nix-instantiate
# /src/nix-store/
/src/nix-store/nix-store
/src/nix-prefetch-url/nix-prefetch-url
/src/nix-collect-garbage/nix-collect-garbage
# /src/nix-channel/
/src/nix-channel/nix-channel
# /src/nix-build/
/src/nix-build/nix-build
/src/nix-copy-closure/nix-copy-closure
/src/error-demo/error-demo
/src/build-remote/build-remote
# /tests/functional/
/tests/functional/test-tmp
/tests/functional/common/subst-vars.sh
/tests/functional/result*
/tests/functional/restricted-innocent
/tests/functional/shell
/tests/functional/shell.drv
/tests/functional/repl-result-out
/tests/functional/debugger-test-out
/tests/functional/test-libstoreconsumer/test-libstoreconsumer
/tests/functional/nix-shell
# /tests/functional/lang/
/tests/functional/lang/*.out
@ -112,27 +14,9 @@ perl/Makefile.config
/tests/functional/lang/*.err
/tests/functional/lang/*.ast
/perl/lib/Nix/Config.pm
/perl/lib/Nix/Store.cc
/misc/systemd/nix-daemon.service
/misc/systemd/nix-daemon.socket
/misc/systemd/nix-daemon.conf
/misc/upstart/nix-daemon.conf
outputs/
*.a
*.o
*.o.tmp
*.so
*.dylib
*.dll
*.exe
*.dep
*~
*.pc
*.plist
# GNU Global
GPATH
@ -147,8 +31,6 @@ GTAGS
compile_commands.json
*.compile_commands.json
nix-rust/target
result
result-*
@ -163,3 +45,5 @@ result-*
# Mac OS
.DS_Store
flake-regressions

View file

@ -2,13 +2,11 @@ queue_rules:
- name: default
# all required tests need to go here
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 (ubuntu-latest)
- check-success=installer_test (macos-latest)
- check-success=installer_test (ubuntu-latest)
- check-success=vm_tests
merge_method: rebase
batch_size: 5
pull_request_rules:
@ -90,3 +88,13 @@ pull_request_rules:
- "2.24-maintenance"
labels:
- 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

@ -199,6 +199,7 @@ nix3_manpages = [
'nix3-build',
'nix3-bundle',
'nix3-config',
'nix3-config-check',
'nix3-config-show',
'nix3-copy',
'nix3-daemon',
@ -206,8 +207,8 @@ nix3_manpages = [
'nix3-derivation',
'nix3-derivation-show',
'nix3-develop',
#'nix3-doctor',
'nix3-edit',
'nix3-env-shell',
'nix3-eval',
'nix3-flake-archive',
'nix3-flake-check',
@ -224,6 +225,7 @@ nix3_manpages = [
'nix3-fmt',
'nix3-hash-file',
'nix3-hash',
'nix3-hash-convert',
'nix3-hash-path',
'nix3-hash-to-base16',
'nix3-hash-to-base32',
@ -238,6 +240,7 @@ nix3_manpages = [
'nix3-nar-cat',
'nix3-nar-dump-path',
'nix3-nar-ls',
'nix3-nar-pack',
'nix3-nar',
'nix3-path-info',
'nix3-print-dev-env',
@ -260,7 +263,7 @@ nix3_manpages = [
'nix3-repl',
'nix3-run',
'nix3-search',
#'nix3-shell',
'nix3-store-add',
'nix3-store-add-file',
'nix3-store-add-path',
'nix3-store-cat',
@ -270,6 +273,7 @@ nix3_manpages = [
'nix3-store-diff-closures',
'nix3-store-dump-path',
'nix3-store-gc',
'nix3-store-info',
'nix3-store-ls',
'nix3-store-make-content-addressed',
'nix3-store',

View file

@ -0,0 +1,22 @@
---
synopsis: "Flake lock file generation now ignores local registries"
prs: [12019]
---
When resolving indirect flake references like `nixpkgs` in `flake.nix` files, Nix will no longer use the system and user flake registries. It will only use the global flake registry and overrides given on the command line via `--override-flake`.
This avoids accidents where users have local registry overrides that map `nixpkgs` to a `path:` flake in the local file system, which then end up in committed lock files pushed to other users.
In the future, we may remove the use of the registry during lock file generation altogether. It's better to explicitly specify the URL of a flake input. For example, instead of
```nix
{
outputs = { self, nixpkgs }: { ... };
}
```
write
```nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
outputs = { self, nixpkgs }: { ... };
}
```

View file

@ -0,0 +1,21 @@
---
synopsis: "Improved `NIX_SSHOPTS` parsing for better SSH option handling"
issues: [5181]
prs: [12020]
---
The parsing of the `NIX_SSHOPTS` environment variable has been improved to handle spaces and quotes correctly.
Previously, incorrectly split SSH options could cause failures in CLIs like `nix-copy-closure`,
especially when using complex ssh invocations such as `-o ProxyCommand="ssh -W %h:%p ..."`.
This change introduces a `shellSplitString` function to ensure
that `NIX_SSHOPTS` is parsed in a manner consistent with shell
behavior, addressing common parsing errors.
For example, the following now works as expected:
```bash
export NIX_SSHOPTS='-o ProxyCommand="ssh -W %h:%p ..."'
```
This update improves the reliability of SSH-related operations using `NIX_SSHOPTS` across Nix CLIs.

View file

@ -258,14 +258,14 @@ let
mkdir -p $out/nix/var/nix/profiles/per-user/${uname}
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${userHome}/.nix-profile
ln -s ${channel} $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link $out/nix/var/nix/profiles/per-user/${uname}/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${userHome}/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/${uname}/channels $out${userHome}/.nix-defexpr/channels
ln -s /nix/var/nix/profiles/per-user/${uname}/channels $out${userHome}/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out${userHome}/.nix-channels
mkdir -p $out/bin $out/usr/bin

View file

@ -124,18 +124,36 @@
# without "polluting" the top level "`pkgs`" attrset.
# This also has the benefit of providing us with a distinct set of packages
# we can iterate over.
nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix {
inherit (final) lib;
inherit officialRelease;
src = self;
});
nixComponents =
lib.makeScopeWithSplicing'
{
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
# in Nixpkgs top level `pkgs` or `nixComponents`.
nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix {
inherit inputs stdenv;
pkgs = final;
});
nixDependencies =
lib.makeScopeWithSplicing'
{
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;
@ -168,7 +186,7 @@
};
checks = forAllSystems (system: {
binaryTarball = self.hydraJobs.binaryTarball.${system};
installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
installTests = self.hydraJobs.installTests.${system};
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
rl-next =
@ -183,11 +201,7 @@
# Some perl dependencies are broken on i686-linux.
# Since the support is only best-effort there, disable the perl
# bindings
# Temporarily disabled because GitHub Actions OOM issues. Once
# the old build system is gone and we are back to one build
# system, we should reenable this.
#perlBindings = self.hydraJobs.perlBindings.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
}
# Add "passthru" tests
// flatMapAttrs ({
@ -219,6 +233,8 @@
inherit (nixpkgsFor.${system}.native)
changelog-d;
default = self.packages.${system}.nix;
installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
binaryTarball = self.hydraJobs.binaryTarball.${system};
# TODO probably should be `nix-cli`
nix = self.packages.${system}.nix-everything;
nix-manual = nixpkgsFor.${system}.native.nixComponents.nix-manual;

View file

@ -12,6 +12,8 @@
hooks = {
clang-format = {
enable = true;
# https://github.com/cachix/git-hooks.nix/pull/532
package = pkgs.llvmPackages_latest.clang-tools;
excludes = [
# We don't want to format test data
# ''tests/(?!nixos/).*\.nix''
@ -496,7 +498,6 @@
''^scripts/create-darwin-volume\.sh$''
''^scripts/install-darwin-multi-user\.sh$''
''^scripts/install-multi-user\.sh$''
''^scripts/install-nix-from-closure\.sh$''
''^scripts/install-systemd-multi-user\.sh$''
''^src/nix/get-env\.sh$''
''^tests/functional/ca/build-dry\.sh$''

View file

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

13
misc/launchd/meson.build Normal file
View file

@ -0,0 +1,13 @@
configure_file(
input : 'org.nixos.nix-daemon.plist.in',
output : 'org.nixos.nix-daemon.plist',
install : true,
install_dir : get_option('prefix') / 'Library/LaunchDaemons',
install_mode : 'rw-r--r--',
configuration : {
# TODO: unhardcode paths with something like:
# 'storedir' : store_dir,
# 'localstatedir' : localstatedir,
# 'bindir' : bindir,
},
)

View file

@ -2,4 +2,10 @@ subdir('bash')
subdir('fish')
subdir('zsh')
subdir('systemd')
if host_machine.system() == 'linux'
subdir('systemd')
endif
if host_machine.system() == 'darwin'
subdir('launchd')
endif

View file

@ -44,6 +44,7 @@ in
nix-expr-tests = callPackage ../src/libexpr-tests/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-main = callPackage ../src/libmain/package.nix { };

View file

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

View file

@ -18,12 +18,8 @@ let
testNixVersions = pkgs: daemon:
pkgs.nixComponents.nix-functional-tests.override {
pname =
"nix-tests"
+ lib.optionalString
(lib.versionAtLeast daemon.version "2.4pre20211005" &&
lib.versionAtLeast pkgs.nix.version "2.4pre20211005")
"-${pkgs.nix.version}-against-${daemon.version}";
pname = "nix-daemon-compat-tests";
version = "${pkgs.nix.version}-with-daemon-${daemon.version}";
test-daemon = daemon;
};
@ -127,15 +123,10 @@ in
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
];
installerScriptForGHA = installScriptFor [
# Native
self.hydraJobs.binaryTarball."x86_64-linux"
self.hydraJobs.binaryTarball."aarch64-darwin"
# Cross
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
];
installerScriptForGHA = forAllSystems (system: nixpkgsFor.${system}.native.callPackage ../scripts/installer.nix {
tarballs = [ self.hydraJobs.binaryTarball.${system} ];
});
# docker image with Nix inside
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);

View file

@ -23,7 +23,7 @@ in
runCommand "nix-binary-tarball-${version}" env ''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
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 cacert ${cacert}
@ -65,7 +65,7 @@ runCommand "nix-binary-tarball-${version}" env ''
fn=$out/$dir.tar.xz
mkdir -p $out/nix-support
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
tar cvfJ $fn \
tar cfJ $fn \
--owner=0 --group=0 --mode=u+rw,uga+r \
--mtime='1970-01-01' \
--absolute-names \

6
scripts/build-checks Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
system=$(nix eval --raw --impure --expr builtins.currentSystem)
nix eval --json ".#checks.$system" --apply builtins.attrNames | \
jq -r '.[]' | \
xargs -P0 -I '{}' sh -c "nix build -L .#checks.$system.{} || { echo 'FAILED: \033[0;31mnix build -L .#checks.$system.{}\\033[0m'; kill 0; }"

View file

@ -463,7 +463,7 @@ EOF
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
:a
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,nosuid,noatime,owners
.
:x
EOF

View file

@ -56,6 +56,9 @@ readonly NIX_INSTALLED_CACERT="@cacert@"
#readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2"
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
# allow to override identity change command
readonly NIX_BECOME=${NIX_BECOME:-sudo}
readonly ROOT_HOME=~root
if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then
@ -123,7 +126,7 @@ uninstall_directions() {
cat <<EOF
$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
$NIX_BECOME mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
(after this one, you may need to re-open any terminals that were
opened while it existed.)
@ -136,7 +139,7 @@ EOF
cat <<EOF
$step. Delete the files Nix added to your system:
sudo rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
$NIX_BECOME rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
and that is it.
@ -343,7 +346,7 @@ __sudo() {
echo "I am executing:"
echo ""
printf " $ sudo %s\\n" "$cmd"
printf " $ $NIX_BECOME %s\\n" "$cmd"
echo ""
echo "$expl"
echo ""
@ -361,7 +364,9 @@ _sudo() {
if is_root; then
env "$@"
else
sudo "$@"
# env sets environment variables for sudo alternatives
# that don't support "VAR=value command" syntax
$NIX_BECOME env "$@"
fi
}
@ -690,7 +695,7 @@ place_channel_configuration() {
if [ -z "${NIX_INSTALLER_NO_CHANNEL_ADD:-}" ]; then
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
_sudo "to set up the default system channel (part 1)" \
install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
install -m 0644 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
fi
}
@ -964,7 +969,7 @@ $NIX_EXTRA_CONF
build-users-group = $NIX_BUILD_GROUP_NAME
EOF
_sudo "to place the default nix daemon configuration (part 2)" \
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
install -m 0644 "$SCRATCH/nix.conf" /etc/nix/nix.conf
}

View file

@ -9,6 +9,8 @@ self="$(dirname "$0")"
nix="@nix@"
cacert="@cacert@"
# allow to override identity change command
readonly NIX_BECOME="${NIX_BECOME:-sudo}"
if ! [ -e "$self/.reginfo" ]; then
echo "$0: incomplete installer (.reginfo is missing)" >&2
@ -48,15 +50,14 @@ case "$(uname -s)" in
INSTALL_MODE=no-daemon;;
esac
# space-separated string
ACTIONS=
ACTION=
# handle the command line flags
while [ $# -gt 0 ]; do
case $1 in
--daemon)
INSTALL_MODE=daemon
ACTIONS="${ACTIONS}install "
ACTION=install
;;
--no-daemon)
if [ "$(uname -s)" = "Darwin" ]; then
@ -64,19 +65,14 @@ while [ $# -gt 0 ]; do
exit 1
fi
INSTALL_MODE=no-daemon
# intentional tail space
ACTIONS="${ACTIONS}install "
ACTION=install
;;
# --uninstall)
# # intentional tail space
# ACTIONS="${ACTIONS}uninstall "
# ;;
--yes)
export NIX_INSTALLER_YES=1;;
--no-channel-add)
export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
--daemon-user-count)
export NIX_USER_COUNT=$2
export NIX_USER_COUNT="$2"
shift;;
--no-modify-profile)
NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
@ -128,7 +124,7 @@ done
if [ "$INSTALL_MODE" = "daemon" ]; then
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split
exec "$self/install-multi-user" $ACTION
exit 0
fi
@ -140,8 +136,8 @@ echo "performing a single-user installation of Nix..." >&2
if ! [ -e "$dest" ]; then
cmd="mkdir -m 0755 $dest && chown $USER $dest"
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
if ! sudo sh -c "$cmd"; then
echo "directory $dest does not exist; creating it by running '$cmd' using $NIX_BECOME" >&2
if ! $NIX_BECOME sh -c "$cmd"; then
echo "$0: please manually run '$cmd' as root to create $dest" >&2
exit 1
fi

View file

@ -1,10 +1,11 @@
#!/usr/bin/env bash
set -e
set -euo pipefail
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
installerHash=$(echo "$script" | cut -b12-43 -)
nix build -L ".#installerScriptForGHA" ".#binaryTarball"
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
echo "::set-output name=installerURL::$installerURL"
mkdir -p out
cp ./result/install "out/install"
name="$(basename "$(realpath ./result-1)")"
# everything before the first dash
cp -r ./result-1 "out/${name%%-*}"

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ ! -d out ]]; then
echo "run prepare-installer-for-github-actions first"
exit 1
fi
cd out
PORT=${PORT:-8126}
nohup python -m http.server "$PORT" >/dev/null 2>&1 &
pid=$!
while ! curl -s "http://localhost:$PORT"; do
sleep 1
if ! kill -0 $pid; then
echo "Failed to start http server"
exit 1
fi
done
echo 'To install nix, run the following command:'
echo "sh <(curl http://localhost:$PORT/install) --tarball-url-prefix http://localhost:$PORT"

View file

@ -40,6 +40,7 @@ GENERATE_LATEX = NO
INPUT = \
@src@/src/libutil-c \
@src@/src/libexpr-c \
@src@/src/libflake-c \
@src@/src/libstore-c \
@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
# to gather comments.
(cpp ../libexpr-c)
(cpp ../libflake-c)
(cpp ../libstore-c)
(cpp ../libutil-c)
];

View file

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

View file

@ -858,7 +858,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
applyDefaultInstallables(rawInstallables);
std::vector<FlakeRef> res;
res.reserve(rawInstallables.size());
for (auto i : rawInstallables)
for (const auto & i : rawInstallables)
res.push_back(parseFlakeRefWithFragment(
fetchSettings,
expandTilde(i),

View file

@ -14,7 +14,7 @@ project('nix-cmd', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data()
@ -28,7 +28,7 @@ deps_public_maybe_subproject = [
dependency('nix-flake'),
dependency('nix-main'),
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json
@ -70,7 +70,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'built-path.cc',
@ -125,4 +125,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')
subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -39,8 +39,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

View file

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

View file

@ -14,7 +14,7 @@ project('nix-expr-c', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data()
@ -27,7 +27,7 @@ deps_public_maybe_subproject = [
dependency('nix-util-c'),
dependency('nix-store-c'),
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
# TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -53,7 +53,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'nix_api_expr.cc',
@ -72,8 +72,8 @@ headers = [config_h] + files(
# TODO move this header to libexpr, maybe don't use it in tests?
headers += files('nix_api_expr_internal.h')
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
subdir('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixexprc',
@ -89,4 +89,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')
subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -6,6 +6,7 @@
#include "eval-gc.hh"
#include "globals.hh"
#include "eval-settings.hh"
#include "ref.hh"
#include "nix_api_expr.h"
#include "nix_api_expr_internal.h"
@ -18,6 +19,29 @@
# include <mutex>
#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)
{
if (context)
@ -93,7 +117,42 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
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)
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)
for (size_t i = 0; lookupPath_c[i] != nullptr; i++)
lookupPath.push_back(lookupPath_c[i]);
builder->lookupPath = nix::LookupPath::parse(lookupPath);
}
NIXC_CATCH_ERRS
}
void * p = ::operator new(
sizeof(EvalState),
static_cast<std::align_val_t>(alignof(EvalState)));
auto * p2 = static_cast<EvalState *>(p);
new (p) EvalState {
.fetchSettings = nix::fetchers::Settings{},
.settings = nix::EvalSettings{
nix::settings.readOnlyMode,
},
.state = nix::EvalState(
nix::LookupPath::parse(lookupPath),
store->ptr,
p2->fetchSettings,
p2->settings),
};
loadConfFile(p2->settings);
return p2;
EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder)
{
if (context)
context->last_err_code = NIX_OK;
try {
return unsafe_new_with_self<EvalState>([&](auto * self) {
return EvalState{
.fetchSettings = std::move(builder->fetchSettings),
.settings = std::move(builder->settings),
.state = nix::EvalState(
builder->lookupPath,
builder->store,
self->fetchSettings,
self->settings),
};
});
}
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)
{
delete state;

View file

@ -30,6 +30,11 @@ extern "C" {
// cffi start
// 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.
*
@ -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);
/**
* @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[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH.
* @param[in] store The Nix store to use.
* @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);

View file

@ -6,6 +6,17 @@
#include "eval-settings.hh"
#include "attr-set.hh"
#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
{

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
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return string
* @return string, if the type is NIX_TYPE_PATH
* @return NULL in case of error.
*/
const char * nix_get_path_string(nix_c_context * context, const nix_value * value);

View file

@ -20,8 +20,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

View file

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

View file

@ -14,7 +14,7 @@ project('nix-expr-test-support', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
deps_private_maybe_subproject = [
]
@ -24,8 +24,9 @@ deps_public_maybe_subproject = [
dependency('nix-store'),
dependency('nix-store-test-support'),
dependency('nix-expr'),
dependency('nix-expr-c'),
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
rapidcheck = dependency('rapidcheck')
deps_public += rapidcheck
@ -39,7 +40,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'tests/value/context.cc',
@ -53,8 +54,8 @@ headers = files(
'tests/value/context.hh',
)
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
subdir('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/windows-version')
this_library = library(
'nix-expr-test-support',
@ -72,4 +73,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')
subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -4,6 +4,7 @@
, nix-store-test-support
, nix-expr
, nix-expr-c
, rapidcheck
@ -22,8 +23,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build
@ -35,6 +36,7 @@ mkMesonLibrary (finalAttrs: {
propagatedBuildInputs = [
nix-store-test-support
nix-expr
nix-expr-c
rapidcheck
];

View file

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

View file

@ -691,15 +691,15 @@ namespace nix {
ASSERT_TRACE2("elemAt \"foo\" (-1)",
TypeError,
HintFmt("expected a list but found %s: %s", "a string", Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.elemAt"));
HintFmt("while evaluating the first argument passed to 'builtins.elemAt'"));
ASSERT_TRACE1("elemAt [] (-1)",
Error,
HintFmt("list index %d is out of bounds", -1));
HintFmt("'builtins.elemAt' called with index %d on a list of size %d", -1, 0));
ASSERT_TRACE1("elemAt [\"foo\"] 3",
Error,
HintFmt("list index %d is out of bounds", 3));
HintFmt("'builtins.elemAt' called with index %d on a list of size %d", 3, 1));
}
@ -708,11 +708,11 @@ namespace nix {
ASSERT_TRACE2("head 1",
TypeError,
HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.elemAt"));
HintFmt("while evaluating the first argument passed to 'builtins.head'"));
ASSERT_TRACE1("head []",
Error,
HintFmt("list index %d is out of bounds", 0));
HintFmt("'builtins.head' called on an empty list"));
}
@ -721,11 +721,11 @@ namespace nix {
ASSERT_TRACE2("tail 1",
TypeError,
HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.tail"));
HintFmt("while evaluating the first argument passed to 'builtins.tail'"));
ASSERT_TRACE1("tail []",
Error,
HintFmt("'tail' called on an empty list"));
HintFmt("'builtins.tail' called on an empty list"));
}

View file

@ -14,7 +14,7 @@ project('nix-expr-tests', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
deps_private_maybe_subproject = [
dependency('nix-expr'),
@ -23,10 +23,10 @@ deps_private_maybe_subproject = [
]
deps_public_maybe_subproject = [
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
subdir('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/windows-version')
rapidcheck = dependency('rapidcheck')
deps_private += rapidcheck
@ -49,7 +49,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'derived-path.cc',

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -7,12 +7,49 @@
#include "tests/nix_api_expr.hh"
#include "tests/string_callback.hh"
#include "file-system.hh"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
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)
{
nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value);

View file

@ -27,8 +27,8 @@ mkMesonExecutable (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

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) {
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
ASSERT_THAT(v, IsIntEq(15));

View file

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

View file

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

View file

@ -347,6 +347,16 @@ void EvalState::allowPath(const StorePath & storePath)
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
}
void EvalState::allowClosure(const StorePath & storePath)
{
if (!rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) return;
StorePathSet closure;
store->computeFSClosure(storePath, closure);
for (auto & p : closure)
allowPath(p);
}
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
{
allowPath(storePath);
@ -3099,10 +3109,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
allowPath(path.path.abs());
if (store->isInStore(path.path.abs())) {
try {
StorePathSet closure;
store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure);
for (auto & p : closure)
allowPath(p);
allowClosure(store->toStorePath(path.path.abs()).first);
} catch (InvalidPath &) { }
}
}
@ -3178,5 +3185,14 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
return v.print(str);
}
void forceNoNullByte(std::string_view s)
{
if (s.find('\0') != s.npos) {
using namespace std::string_view_literals;
auto str = replaceStrings(std::string(s), "\0"sv, ""sv);
throw Error("input string '%s' cannot be represented as Nix string because it contains null bytes", str);
}
}
}

View file

@ -400,6 +400,11 @@ public:
*/
void allowPath(const StorePath & storePath);
/**
* Allow access to the closure of a store path.
*/
void allowClosure(const StorePath & storePath);
/**
* Allow access to a store path and return it as a string.
*/

View file

@ -50,6 +50,7 @@ class JSONSax : nlohmann::json_sax<json> {
public:
void key(string_t & name, EvalState & state)
{
forceNoNullByte(name);
attrs.insert_or_assign(state.symbols.create(name), &value(state));
}
};
@ -122,6 +123,7 @@ public:
bool string(string_t & val) override
{
forceNoNullByte(val);
rs->value(state).mkString(val);
rs->add();
return true;

View file

@ -1,5 +1,13 @@
#pragma once
#include <cstddef>
// inluding the generated headers twice leads to errors
#ifndef BISON_HEADER
# include "lexer-tab.hh"
# include "parser-tab.hh"
#endif
namespace nix::lexer::internal {
void initLoc(YYLTYPE * loc);

View file

@ -14,7 +14,7 @@ project('nix-expr', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data()
@ -25,7 +25,7 @@ deps_public_maybe_subproject = [
dependency('nix-store'),
dependency('nix-fetchers'),
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
boost = dependency(
'boost',
@ -77,7 +77,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
parser_tab = custom_target(
input : 'parser.y',
@ -121,7 +121,7 @@ lexer_tab = custom_target(
install_dir : get_option('includedir') / 'nix',
)
subdir('build-utils-meson/generate-header')
subdir('nix-meson-build-support/generate-header')
generated_headers = []
foreach header : [
@ -205,4 +205,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')
subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -168,7 +168,7 @@ struct ExprVar : Expr
the set stored in the environment that is `level` levels up
from the current one.*/
Level level;
Displacement displ;
Displacement displ = 0;
ExprVar(Symbol name) : name(name) { };
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
@ -242,7 +242,7 @@ struct ExprAttrs : Expr
Kind kind;
Expr * e;
PosIdx pos;
Displacement displ; // displacement
Displacement displ = 0; // displacement
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
: kind(kind), e(e), pos(pos) { };
AttrDef() { };

View file

@ -40,8 +40,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

View file

@ -88,6 +88,7 @@ struct ParserState
void dupAttr(const AttrPath & attrPath, 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 Symbol & symbol, ExprAttrs::AttrDef && def);
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos,
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.
// ===========================
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
ExprAttrs * nested;
if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
attrs = attrs2;
} else
nested = dynamic_cast<ExprAttrs *>(j->second.e);
if (!nested) {
attrPath.erase(i + 1, attrPath.end());
dupAttr(attrPath, pos, j->second.pos);
}
} else {
ExprAttrs * nested = new ExprAttrs;
nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs = nested;
}
} else {
ExprAttrs *nested = new ExprAttrs;
nested = new ExprAttrs;
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
attrs = nested;
}
attrs = nested;
}
// Expr insertion.
// ==========================
if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->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 *>(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);
}
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
} else {
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)
{
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());
},
[&](const NixStringContextElem::Opaque & o) {
auto ctxS = store->printStorePath(o.path);
ensureValid(o.path);
if (maybePathsOut)
maybePathsOut->emplace(o.path);
},
[&](const NixStringContextElem::DrvDeep & d) {
/* Treat same as Opaque */
auto ctxS = store->printStorePath(d.drvPath);
ensureValid(d.drvPath);
if (maybePathsOut)
maybePathsOut->emplace(d.drvPath);
@ -121,11 +119,9 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow);
if (isIFD) {
for (auto & outputPath : outputsToCopyAndAllow) {
/* Add the output of this derivations to the allowed
paths. */
allowPath(outputPath);
}
/* Allow access to the output closures of this derivation. */
for (auto & outputPath : outputsToCopyAndAllow)
allowClosure(outputPath);
}
return res;
@ -1102,7 +1098,7 @@ static RegisterPrimOp primop_warn({
.name = "__warn",
.args = {"e1", "e2"},
.doc = R"(
Evaluate *e1*, which must be a string and print iton standard error as a warning.
Evaluate *e1*, which must be a string, and print it on standard error as a warning.
Then return *e2*.
This function is useful for non-critical situations where attention is advisable.
@ -3261,23 +3257,19 @@ static RegisterPrimOp primop_isList({
.fun = prim_isList,
});
static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v)
{
state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt");
if (n < 0 || (unsigned int) n >= list.listSize())
state.error<EvalError>(
"list index %1% is out of bounds",
n
).atPos(pos).debugThrow();
state.forceValue(*list.listElems()[n], pos);
v = *list.listElems()[n];
}
/* Return the n-1'th element of a list. */
static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
NixInt::Inner elem = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt").value;
elemAt(state, pos, *args[0], elem, v);
NixInt::Inner n = state.forceInt(*args[1], pos, "while evaluating the second argument passed to 'builtins.elemAt'").value;
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.elemAt'");
if (n < 0 || (unsigned int) n >= args[0]->listSize())
state.error<EvalError>(
"'builtins.elemAt' called with index %d on a list of size %d",
n,
args[0]->listSize()
).atPos(pos).debugThrow();
state.forceValue(*args[0]->listElems()[n], pos);
v = *args[0]->listElems()[n];
}
static RegisterPrimOp primop_elemAt({
@ -3293,7 +3285,13 @@ static RegisterPrimOp primop_elemAt({
/* Return the first element of a list. */
static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
elemAt(state, pos, *args[0], 0, v);
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.head'");
if (args[0]->listSize() == 0)
state.error<EvalError>(
"'builtins.head' called on an empty list"
).atPos(pos).debugThrow();
state.forceValue(*args[0]->listElems()[0], pos);
v = *args[0]->listElems()[0];
}
static RegisterPrimOp primop_head({
@ -3312,9 +3310,9 @@ static RegisterPrimOp primop_head({
don't want to use it! */
static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail");
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.tail'");
if (args[0]->listSize() == 0)
state.error<EvalError>("'tail' called on an empty list").atPos(pos).debugThrow();
state.error<EvalError>("'builtins.tail' called on an empty list").atPos(pos).debugThrow();
auto list = state.buildList(args[0]->listSize() - 1);
for (const auto & [n, v] : enumerate(list))
@ -4385,7 +4383,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3);
auto match = *i;
const auto & match = *i;
// Add a string for non-matched characters.
list[idx++] = mkString(state, match.prefix());

View file

@ -132,6 +132,8 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
},
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* 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);
},
}, 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

View file

@ -28,8 +28,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
auto attrs = state.buildBindings(size);
for(auto & elem : table)
for(auto & elem : table) {
forceNoNullByte(elem.first);
visit(attrs.alloc(elem.first), elem.second);
}
v.mkAttrs(attrs);
}
@ -54,7 +56,11 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
v.mkFloat(toml::get<NixFloat>(t));
break;;
case toml::value_t::string:
v.mkString(toml::get<std::string>(t));
{
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s);
}
break;;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
@ -66,7 +72,9 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
attrs.alloc("value").mkString(toView(s));
auto str = toView(s);
forceNoNullByte(str);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");

View file

@ -5,6 +5,7 @@
*/
#include <limits>
#include <stddef.h>
namespace nix {

View file

@ -510,4 +510,6 @@ typedef std::shared_ptr<Value *> RootValue;
RootValue allocRootValue(Value * v);
void forceNoNullByte(std::string_view s);
}

View file

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

View file

@ -14,7 +14,7 @@ project('nix-fetchers-tests', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
deps_private_maybe_subproject = [
dependency('nix-store-test-support'),
@ -22,10 +22,10 @@ deps_private_maybe_subproject = [
]
deps_public_maybe_subproject = [
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
subdir('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/windows-version')
rapidcheck = dependency('rapidcheck')
deps_private += rapidcheck
@ -42,7 +42,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'public-key.cc',

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -26,8 +26,8 @@ mkMesonExecutable (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

View file

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

View file

@ -113,7 +113,15 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
std::optional<std::string> Input::getFingerprint(ref<Store> store) const
{
return scheme ? scheme->getFingerprint(store, *this) : std::nullopt;
if (!scheme) return std::nullopt;
if (cachedFingerprint) return *cachedFingerprint;
auto fingerprint = scheme->getFingerprint(store, *this);
cachedFingerprint = fingerprint;
return fingerprint;
}
ParsedURL Input::toURL() const
@ -313,7 +321,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
auto accessor = makeStorePathAccessor(store, storePath);
accessor->fingerprint = scheme->getFingerprint(store, *this);
accessor->fingerprint = getFingerprint(store);
return {accessor, *this};
} catch (Error & e) {
@ -324,7 +332,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
auto [accessor, result] = scheme->getAccessor(store, *this);
assert(!accessor->fingerprint);
accessor->fingerprint = scheme->getFingerprint(store, result);
accessor->fingerprint = result.getFingerprint(store);
return {accessor, std::move(result)};
}

View file

@ -41,6 +41,11 @@ struct Input
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;
/**
* Cached result of getFingerprint().
*/
mutable std::optional<std::optional<std::string>> cachedFingerprint;
public:
/**
* Create an `Input` from a URL.
@ -105,6 +110,11 @@ public:
bool operator ==(const Input & other) const noexcept;
bool operator <(const Input & other) const
{
return attrs < other.attrs;
}
bool contains(const Input & other) const;
/**

View file

@ -5,6 +5,7 @@
#include "signals.hh"
#include "users.hh"
#include "fs-sink.hh"
#include "sync.hh"
#include <git2/attr.h>
#include <git2/blob.h>
@ -437,7 +438,12 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{
if (!(statusFlags & GIT_STATUS_INDEX_DELETED) &&
!(statusFlags & GIT_STATUS_WT_DELETED))
{
info.files.insert(CanonPath(path));
if (statusFlags != GIT_STATUS_CURRENT)
info.dirtyFiles.insert(CanonPath(path));
} else
info.deletedFiles.insert(CanonPath(path));
if (statusFlags != GIT_STATUS_CURRENT)
info.isDirty = true;
return 0;
@ -1262,4 +1268,17 @@ ref<GitRepo> getTarballCache()
return GitRepo::openRepo(repoDir, true, true);
}
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
{
static Sync<std::map<std::filesystem::path, WorkdirInfo>> _cache;
{
auto cache(_cache.lock());
auto i = cache->find(path);
if (i != cache->end()) return i->second;
}
auto workdirInfo = GitRepo::openRepo(path)->getWorkdirInfo();
_cache.lock()->emplace(path, workdirInfo);
return workdirInfo;
}
}

View file

@ -59,12 +59,20 @@ struct GitRepo
modified or added, but excluding deleted files. */
std::set<CanonPath> files;
/* All modified or added files. */
std::set<CanonPath> dirtyFiles;
/* The deleted files. */
std::set<CanonPath> deletedFiles;
/* The submodules listed in .gitmodules of this workdir. */
std::vector<Submodule> submodules;
};
virtual WorkdirInfo getWorkdirInfo() = 0;
static WorkdirInfo getCachedWorkdirInfo(const std::filesystem::path & path);
/* Get the ref that HEAD points to. */
virtual std::optional<std::string> getWorkdirRef() = 0;

View file

@ -15,6 +15,7 @@
#include "finally.hh"
#include "fetch-settings.hh"
#include "json-utils.hh"
#include "archive.hh"
#include <regex>
#include <string.h>
@ -430,7 +431,7 @@ struct GitInputScheme : InputScheme
// If this is a local directory and no ref or revision is
// given, then allow the use of an unclean working tree.
if (!input.getRef() && !input.getRev() && repoInfo.isLocal)
repoInfo.workdirInfo = GitRepo::openRepo(repoInfo.url)->getWorkdirInfo();
repoInfo.workdirInfo = GitRepo::getCachedWorkdirInfo(repoInfo.url);
return repoInfo;
}
@ -514,8 +515,6 @@ struct GitInputScheme : InputScheme
auto origRev = input.getRev();
std::string name = input.getName();
auto originalRef = input.getRef();
auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo);
input.attrs.insert_or_assign("ref", ref);
@ -795,10 +794,33 @@ struct GitInputScheme : InputScheme
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
{
auto makeFingerprint = [&](const Hash & rev)
{
return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "");
};
if (auto rev = input.getRev())
return rev->gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "");
else
return makeFingerprint(*rev);
else {
auto repoInfo = getRepoInfo(input);
if (repoInfo.isLocal && repoInfo.workdirInfo.headRev && repoInfo.workdirInfo.submodules.empty()) {
/* Calculate a fingerprint that takes into account the
deleted and modified/added files. */
HashSink hashSink{HashAlgorithm::SHA512};
for (auto & file : repoInfo.workdirInfo.dirtyFiles) {
writeString("modified:", hashSink);
writeString(file.abs(), hashSink);
dumpPath(repoInfo.url + "/" + file.abs(), hashSink);
}
for (auto & file : repoInfo.workdirInfo.deletedFiles) {
writeString("deleted:", hashSink);
writeString(file.abs(), hashSink);
}
return makeFingerprint(*repoInfo.workdirInfo.headRev)
+ ";d=" + hashSink.finish().first.to_string(HashFormat::Base16, false);
}
return std::nullopt;
}
}
bool isLocked(const Input & input) const override

View file

@ -14,7 +14,7 @@ project('nix-fetchers', 'cpp',
cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists')
subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data()
@ -24,7 +24,7 @@ deps_public_maybe_subproject = [
dependency('nix-util'),
dependency('nix-store'),
]
subdir('build-utils-meson/subprojects')
subdir('nix-meson-build-support/subprojects')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json
@ -41,7 +41,7 @@ add_project_arguments(
language : 'cpp',
)
subdir('build-utils-meson/common')
subdir('nix-meson-build-support/common')
sources = files(
'attrs.cc',
@ -90,4 +90,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = []
subdir('build-utils-meson/export')
subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -22,8 +22,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build

View file

@ -94,12 +94,9 @@ void Registry::add(
void Registry::remove(const Input & input)
{
// FIXME: use C++20 std::erase.
for (auto i = entries.begin(); i != entries.end(); )
if (i->from == input)
i = entries.erase(i);
else
++i;
entries.erase(
std::remove_if(entries.begin(), entries.end(), [&](const Entry & entry) { return entry.from == input; }),
entries.end());
}
static Path getSystemRegistryPath()
@ -181,7 +178,8 @@ Registries getRegistries(const Settings & settings, ref<Store> store)
std::pair<Input, Attrs> lookupInRegistries(
ref<Store> store,
const Input & _input)
const Input & _input,
const RegistryFilter & filter)
{
Attrs extraAttrs;
int n = 0;
@ -193,6 +191,7 @@ std::pair<Input, Attrs> lookupInRegistries(
if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string());
for (auto & registry : getRegistries(*input.settings, store)) {
if (filter && !filter(registry->type)) continue;
// FIXME: O(n)
for (auto & entry : registry->entries) {
if (entry.exact) {

View file

@ -65,8 +65,15 @@ void overrideRegistry(
const Input & to,
const Attrs & extraAttrs);
using RegistryFilter = std::function<bool(Registry::RegistryType)>;
/**
* Rewrite a flakeref using the registries. If `filter` is set, only
* use the registries for which the filter function returns true.
*/
std::pair<Input, Attrs> lookupInRegistries(
ref<Store> store,
const Input & input);
const Input & input,
const RegistryFilter & filter = {});
}

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

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

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('nix-meson-build-support/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('nix-meson-build-support/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('nix-meson-build-support/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('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/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('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

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 [
../../nix-meson-build-support
./nix-meson-build-support
../../.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

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

View file

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

View file

@ -0,0 +1 @@
../../nix-meson-build-support

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
, nix-flake
, nix-flake-c
, nix-expr-test-support
, rapidcheck
@ -26,8 +27,8 @@ mkMesonExecutable (finalAttrs: {
workDir = ./.;
fileset = fileset.unions [
../../build-utils-meson
./build-utils-meson
../../nix-meson-build-support
./nix-meson-build-support
../../.version
./.version
./meson.build
@ -38,6 +39,7 @@ mkMesonExecutable (finalAttrs: {
buildInputs = [
nix-flake
nix-flake-c
nix-expr-test-support
rapidcheck
gtest
@ -67,6 +69,7 @@ mkMesonExecutable (finalAttrs: {
mkdir -p "$HOME"
'' + ''
export _NIX_TEST_UNIT_DATA=${resolvePath ./data}
export NIX_CONFIG="extra-experimental-features = flakes"
${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage}
touch $out
'');

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