mirror of
https://github.com/NixOS/nix
synced 2025-06-25 10:41:16 +02:00
Merge remote-tracking branch 'upstream/master' into messages-present-tense
This commit is contained in:
commit
d6710b4c04
43 changed files with 984 additions and 170 deletions
|
@ -25,7 +25,7 @@ This performs the default type of installation for your platform:
|
||||||
|
|
||||||
We recommend the multi-user installation if it supports your platform and you can authenticate with `sudo`.
|
We recommend the multi-user installation if it supports your platform and you can authenticate with `sudo`.
|
||||||
|
|
||||||
The installer can configured with various command line arguments and environment variables.
|
The installer can be configured with various command line arguments and environment variables.
|
||||||
To show available command line flags:
|
To show available command line flags:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
72
docker.nix
72
docker.nix
|
@ -1,6 +1,11 @@
|
||||||
{
|
{
|
||||||
pkgs ? import <nixpkgs> { },
|
# Core dependencies
|
||||||
lib ? pkgs.lib,
|
pkgs,
|
||||||
|
lib,
|
||||||
|
dockerTools,
|
||||||
|
runCommand,
|
||||||
|
buildPackages,
|
||||||
|
# Image configuration
|
||||||
name ? "nix",
|
name ? "nix",
|
||||||
tag ? "latest",
|
tag ? "latest",
|
||||||
bundleNixpkgs ? true,
|
bundleNixpkgs ? true,
|
||||||
|
@ -14,11 +19,36 @@
|
||||||
gid ? 0,
|
gid ? 0,
|
||||||
uname ? "root",
|
uname ? "root",
|
||||||
gname ? "root",
|
gname ? "root",
|
||||||
|
Labels ? {
|
||||||
|
"org.opencontainers.image.title" = "Nix";
|
||||||
|
"org.opencontainers.image.source" = "https://github.com/NixOS/nix";
|
||||||
|
"org.opencontainers.image.vendor" = "Nix project";
|
||||||
|
"org.opencontainers.image.version" = nix.version;
|
||||||
|
"org.opencontainers.image.description" = "Nix container image";
|
||||||
|
},
|
||||||
|
Cmd ? [ (lib.getExe bashInteractive) ],
|
||||||
|
# Default Packages
|
||||||
|
nix,
|
||||||
|
bashInteractive,
|
||||||
|
coreutils-full,
|
||||||
|
gnutar,
|
||||||
|
gzip,
|
||||||
|
gnugrep,
|
||||||
|
which,
|
||||||
|
curl,
|
||||||
|
less,
|
||||||
|
wget,
|
||||||
|
man,
|
||||||
|
cacert,
|
||||||
|
findutils,
|
||||||
|
iana-etc,
|
||||||
|
gitMinimal,
|
||||||
|
openssh,
|
||||||
|
# Other dependencies
|
||||||
|
shadow,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
defaultPkgs =
|
defaultPkgs = [
|
||||||
with pkgs;
|
|
||||||
[
|
|
||||||
nix
|
nix
|
||||||
bashInteractive
|
bashInteractive
|
||||||
coreutils-full
|
coreutils-full
|
||||||
|
@ -33,17 +63,16 @@ let
|
||||||
cacert.out
|
cacert.out
|
||||||
findutils
|
findutils
|
||||||
iana-etc
|
iana-etc
|
||||||
git
|
gitMinimal
|
||||||
openssh
|
openssh
|
||||||
]
|
] ++ extraPkgs;
|
||||||
++ extraPkgs;
|
|
||||||
|
|
||||||
users =
|
users =
|
||||||
{
|
{
|
||||||
|
|
||||||
root = {
|
root = {
|
||||||
uid = 0;
|
uid = 0;
|
||||||
shell = "${pkgs.bashInteractive}/bin/bash";
|
shell = lib.getExe bashInteractive;
|
||||||
home = "/root";
|
home = "/root";
|
||||||
gid = 0;
|
gid = 0;
|
||||||
groups = [ "root" ];
|
groups = [ "root" ];
|
||||||
|
@ -52,7 +81,7 @@ let
|
||||||
|
|
||||||
nobody = {
|
nobody = {
|
||||||
uid = 65534;
|
uid = 65534;
|
||||||
shell = "${pkgs.shadow}/bin/nologin";
|
shell = lib.getExe' shadow "nologin";
|
||||||
home = "/var/empty";
|
home = "/var/empty";
|
||||||
gid = 65534;
|
gid = 65534;
|
||||||
groups = [ "nobody" ];
|
groups = [ "nobody" ];
|
||||||
|
@ -63,7 +92,7 @@ let
|
||||||
// lib.optionalAttrs (uid != 0) {
|
// lib.optionalAttrs (uid != 0) {
|
||||||
"${uname}" = {
|
"${uname}" = {
|
||||||
uid = uid;
|
uid = uid;
|
||||||
shell = "${pkgs.bashInteractive}/bin/bash";
|
shell = lib.getExe bashInteractive;
|
||||||
home = "/home/${uname}";
|
home = "/home/${uname}";
|
||||||
gid = gid;
|
gid = gid;
|
||||||
groups = [ "${gname}" ];
|
groups = [ "${gname}" ];
|
||||||
|
@ -170,7 +199,7 @@ let
|
||||||
baseSystem =
|
baseSystem =
|
||||||
let
|
let
|
||||||
nixpkgs = pkgs.path;
|
nixpkgs = pkgs.path;
|
||||||
channel = pkgs.runCommand "channel-nixos" { inherit bundleNixpkgs; } ''
|
channel = runCommand "channel-nixos" { inherit bundleNixpkgs; } ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
if [ "$bundleNixpkgs" ]; then
|
if [ "$bundleNixpkgs" ]; then
|
||||||
ln -s ${
|
ln -s ${
|
||||||
|
@ -182,11 +211,11 @@ let
|
||||||
echo "[]" > $out/manifest.nix
|
echo "[]" > $out/manifest.nix
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
rootEnv = pkgs.buildPackages.buildEnv {
|
rootEnv = buildPackages.buildEnv {
|
||||||
name = "root-profile-env";
|
name = "root-profile-env";
|
||||||
paths = defaultPkgs;
|
paths = defaultPkgs;
|
||||||
};
|
};
|
||||||
manifest = pkgs.buildPackages.runCommand "manifest.nix" { } ''
|
manifest = buildPackages.runCommand "manifest.nix" { } ''
|
||||||
cat > $out <<EOF
|
cat > $out <<EOF
|
||||||
[
|
[
|
||||||
${lib.concatStringsSep "\n" (
|
${lib.concatStringsSep "\n" (
|
||||||
|
@ -215,7 +244,7 @@ let
|
||||||
]
|
]
|
||||||
EOF
|
EOF
|
||||||
'';
|
'';
|
||||||
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
|
profile = buildPackages.runCommand "user-environment" { } ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
cp -a ${rootEnv}/* $out/
|
cp -a ${rootEnv}/* $out/
|
||||||
ln -s ${manifest} $out/manifest.nix
|
ln -s ${manifest} $out/manifest.nix
|
||||||
|
@ -228,7 +257,7 @@ let
|
||||||
else
|
else
|
||||||
flake-registry;
|
flake-registry;
|
||||||
in
|
in
|
||||||
pkgs.runCommand "base-system"
|
runCommand "base-system"
|
||||||
{
|
{
|
||||||
inherit
|
inherit
|
||||||
passwdContents
|
passwdContents
|
||||||
|
@ -280,7 +309,6 @@ let
|
||||||
|
|
||||||
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
|
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
|
||||||
ln -s /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 ${channel} $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link
|
||||||
ln -s /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
|
||||||
|
@ -290,8 +318,8 @@ let
|
||||||
echo "${channelURL} ${channelName}" > $out${userHome}/.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 ${lib.getExe' coreutils-full "env"} $out/usr/bin/env
|
||||||
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
|
ln -s ${lib.getExe bashInteractive} $out/bin/sh
|
||||||
|
|
||||||
''
|
''
|
||||||
+ (lib.optionalString (flake-registry-path != null) ''
|
+ (lib.optionalString (flake-registry-path != null) ''
|
||||||
|
@ -300,13 +328,13 @@ let
|
||||||
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
|
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
|
||||||
ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
|
ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
|
||||||
mkdir -p $out/nix/var/nix/gcroots/auto
|
mkdir -p $out/nix/var/nix/gcroots/auto
|
||||||
rootName=$(${pkgs.nix}/bin/nix --extra-experimental-features nix-command hash file --type sha1 --base32 <(echo -n $globalFlakeRegistryPath))
|
rootName=$(${lib.getExe' nix "nix"} --extra-experimental-features nix-command hash file --type sha1 --base32 <(echo -n $globalFlakeRegistryPath))
|
||||||
ln -s $globalFlakeRegistryPath $out/nix/var/nix/gcroots/auto/$rootName
|
ln -s $globalFlakeRegistryPath $out/nix/var/nix/gcroots/auto/$rootName
|
||||||
'')
|
'')
|
||||||
);
|
);
|
||||||
|
|
||||||
in
|
in
|
||||||
pkgs.dockerTools.buildLayeredImageWithNixDb {
|
dockerTools.buildLayeredImageWithNixDb {
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
name
|
name
|
||||||
|
@ -332,7 +360,7 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
|
||||||
'';
|
'';
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
Cmd = [ "${userHome}/.nix-profile/bin/bash" ];
|
inherit Cmd Labels;
|
||||||
User = "${toString uid}:${toString gid}";
|
User = "${toString uid}:${toString gid}";
|
||||||
Env = [
|
Env = [
|
||||||
"USER=${uname}"
|
"USER=${uname}"
|
||||||
|
|
|
@ -404,8 +404,7 @@
|
||||||
dockerImage =
|
dockerImage =
|
||||||
let
|
let
|
||||||
pkgs = nixpkgsFor.${system}.native;
|
pkgs = nixpkgsFor.${system}.native;
|
||||||
image = import ./docker.nix {
|
image = pkgs.callPackage ./docker.nix {
|
||||||
inherit pkgs;
|
|
||||||
tag = pkgs.nix.version;
|
tag = pkgs.nix.version;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
@ -37,6 +37,118 @@
|
||||||
fi
|
fi
|
||||||
''}";
|
''}";
|
||||||
};
|
};
|
||||||
|
meson-format = {
|
||||||
|
enable = true;
|
||||||
|
files = "(meson.build|meson.options)$";
|
||||||
|
entry = "${pkgs.writeScript "format-meson" ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
for file in "$@"; do
|
||||||
|
${lib.getExe pkgs.meson} format -ic ${../meson.format} "$file"
|
||||||
|
done
|
||||||
|
''}";
|
||||||
|
excludes = [
|
||||||
|
# We haven't applied formatting to these files yet
|
||||||
|
''^doc/manual/meson.build$''
|
||||||
|
''^doc/manual/source/command-ref/meson.build$''
|
||||||
|
''^doc/manual/source/development/meson.build$''
|
||||||
|
''^doc/manual/source/language/meson.build$''
|
||||||
|
''^doc/manual/source/meson.build$''
|
||||||
|
''^doc/manual/source/release-notes/meson.build$''
|
||||||
|
''^doc/manual/source/store/meson.build$''
|
||||||
|
''^misc/bash/meson.build$''
|
||||||
|
''^misc/fish/meson.build$''
|
||||||
|
''^misc/launchd/meson.build$''
|
||||||
|
''^misc/meson.build$''
|
||||||
|
''^misc/systemd/meson.build$''
|
||||||
|
''^misc/zsh/meson.build$''
|
||||||
|
''^nix-meson-build-support/$''
|
||||||
|
''^nix-meson-build-support/big-objs/meson.build$''
|
||||||
|
''^nix-meson-build-support/common/meson.build$''
|
||||||
|
''^nix-meson-build-support/deps-lists/meson.build$''
|
||||||
|
''^nix-meson-build-support/export/meson.build$''
|
||||||
|
''^nix-meson-build-support/export-all-symbols/meson.build$''
|
||||||
|
''^nix-meson-build-support/generate-header/meson.build$''
|
||||||
|
''^nix-meson-build-support/libatomic/meson.build$''
|
||||||
|
''^nix-meson-build-support/subprojects/meson.build$''
|
||||||
|
''^scripts/meson.build$''
|
||||||
|
''^src/external-api-docs/meson.build$''
|
||||||
|
''^src/internal-api-docs/meson.build$''
|
||||||
|
''^src/libcmd/include/nix/cmd/meson.build$''
|
||||||
|
''^src/libcmd/meson.build$''
|
||||||
|
''^src/libcmd/nix-meson-build-support$''
|
||||||
|
''^src/libexpr/include/nix/expr/meson.build$''
|
||||||
|
''^src/libexpr/meson.build$''
|
||||||
|
''^src/libexpr/nix-meson-build-support$''
|
||||||
|
''^src/libexpr-c/meson.build$''
|
||||||
|
''^src/libexpr-c/nix-meson-build-support$''
|
||||||
|
''^src/libexpr-test-support/meson.build$''
|
||||||
|
''^src/libexpr-test-support/nix-meson-build-support$''
|
||||||
|
''^src/libexpr-tests/meson.build$''
|
||||||
|
''^src/libexpr-tests/nix-meson-build-support$''
|
||||||
|
''^src/libfetchers/include/nix/fetchers/meson.build$''
|
||||||
|
''^src/libfetchers/meson.build$''
|
||||||
|
''^src/libfetchers/nix-meson-build-support$''
|
||||||
|
''^src/libfetchers-c/meson.build$''
|
||||||
|
''^src/libfetchers-c/nix-meson-build-support$''
|
||||||
|
''^src/libfetchers-tests/meson.build$''
|
||||||
|
''^src/libfetchers-tests/nix-meson-build-support$''
|
||||||
|
''^src/libflake/include/nix/flake/meson.build$''
|
||||||
|
''^src/libflake/meson.build$''
|
||||||
|
''^src/libflake/nix-meson-build-support$''
|
||||||
|
''^src/libflake-c/meson.build$''
|
||||||
|
''^src/libflake-c/nix-meson-build-support$''
|
||||||
|
''^src/libflake-tests/meson.build$''
|
||||||
|
''^src/libflake-tests/nix-meson-build-support$''
|
||||||
|
''^src/libmain/include/nix/main/meson.build$''
|
||||||
|
''^src/libmain/meson.build$''
|
||||||
|
''^src/libmain/nix-meson-build-support$''
|
||||||
|
''^src/libmain-c/meson.build$''
|
||||||
|
''^src/libmain-c/nix-meson-build-support$''
|
||||||
|
''^src/libstore/include/nix/store/meson.build$''
|
||||||
|
''^src/libstore/meson.build$''
|
||||||
|
''^src/libstore/nix-meson-build-support$''
|
||||||
|
''^src/libstore/unix/include/nix/store/meson.build$''
|
||||||
|
''^src/libstore/unix/meson.build$''
|
||||||
|
''^src/libstore/windows/meson.build$''
|
||||||
|
''^src/libstore-c/meson.build$''
|
||||||
|
''^src/libstore-c/nix-meson-build-support$''
|
||||||
|
''^src/libstore-test-support/include/nix/store/tests/meson.build$''
|
||||||
|
''^src/libstore-test-support/meson.build$''
|
||||||
|
''^src/libstore-test-support/nix-meson-build-support$''
|
||||||
|
''^src/libstore-tests/meson.build$''
|
||||||
|
''^src/libstore-tests/nix-meson-build-support$''
|
||||||
|
''^src/libutil/meson.build$''
|
||||||
|
''^src/libutil/nix-meson-build-support$''
|
||||||
|
''^src/libutil/unix/include/nix/util/meson.build$''
|
||||||
|
''^src/libutil/unix/meson.build$''
|
||||||
|
''^src/libutil/windows/meson.build$''
|
||||||
|
''^src/libutil-c/meson.build$''
|
||||||
|
''^src/libutil-c/nix-meson-build-support$''
|
||||||
|
''^src/libutil-test-support/include/nix/util/tests/meson.build$''
|
||||||
|
''^src/libutil-test-support/meson.build$''
|
||||||
|
''^src/libutil-test-support/nix-meson-build-support$''
|
||||||
|
''^src/libutil-tests/meson.build$''
|
||||||
|
''^src/libutil-tests/nix-meson-build-support$''
|
||||||
|
''^src/nix/meson.build$''
|
||||||
|
''^src/nix/nix-meson-build-support$''
|
||||||
|
''^src/perl/lib/Nix/meson.build$''
|
||||||
|
''^src/perl/meson.build$''
|
||||||
|
''^tests/functional/ca/meson.build$''
|
||||||
|
''^tests/functional/common/meson.build$''
|
||||||
|
''^tests/functional/dyn-drv/meson.build$''
|
||||||
|
''^tests/functional/flakes/meson.build$''
|
||||||
|
''^tests/functional/git-hashing/meson.build$''
|
||||||
|
''^tests/functional/local-overlay-store/meson.build$''
|
||||||
|
''^tests/functional/meson.build$''
|
||||||
|
''^src/libcmd/meson.options$''
|
||||||
|
''^src/libexpr/meson.options$''
|
||||||
|
''^src/libstore/meson.options$''
|
||||||
|
''^src/libutil/meson.options$''
|
||||||
|
''^src/libutil-c/meson.options$''
|
||||||
|
''^src/nix/meson.options$''
|
||||||
|
''^src/perl/meson.options$''
|
||||||
|
];
|
||||||
|
};
|
||||||
nixfmt-rfc-style = {
|
nixfmt-rfc-style = {
|
||||||
enable = true;
|
enable = true;
|
||||||
excludes = [
|
excludes = [
|
||||||
|
|
10
meson.build
10
meson.build
|
@ -1,13 +1,13 @@
|
||||||
# This is just a stub project to include all the others as subprojects
|
# This is just a stub project to include all the others as subprojects
|
||||||
# for development shell purposes
|
# for development shell purposes
|
||||||
|
|
||||||
project('nix-dev-shell', 'cpp',
|
project(
|
||||||
|
'nix-dev-shell',
|
||||||
|
'cpp',
|
||||||
version : files('.version'),
|
version : files('.version'),
|
||||||
subproject_dir : 'src',
|
subproject_dir : 'src',
|
||||||
default_options : [
|
default_options : [ 'localstatedir=/nix/var' ],
|
||||||
'localstatedir=/nix/var',
|
meson_version : '>= 1.1',
|
||||||
],
|
|
||||||
meson_version : '>= 1.1'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Internal Libraries
|
# Internal Libraries
|
||||||
|
|
7
meson.format
Normal file
7
meson.format
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
indent_by = ' '
|
||||||
|
space_array = true
|
||||||
|
kwargs_force_multiline = false
|
||||||
|
wide_colon = true
|
||||||
|
group_arg_value = true
|
||||||
|
indent_before_comments = ' '
|
||||||
|
use_editor_config = true
|
|
@ -1,13 +1,22 @@
|
||||||
# vim: filetype=meson
|
# vim: filetype=meson
|
||||||
|
|
||||||
option('doc-gen', type : 'boolean', value : false,
|
option(
|
||||||
|
'doc-gen',
|
||||||
|
type : 'boolean',
|
||||||
|
value : false,
|
||||||
description : 'Generate documentation',
|
description : 'Generate documentation',
|
||||||
)
|
)
|
||||||
|
|
||||||
option('unit-tests', type : 'boolean', value : true,
|
option(
|
||||||
|
'unit-tests',
|
||||||
|
type : 'boolean',
|
||||||
|
value : true,
|
||||||
description : 'Build unit tests',
|
description : 'Build unit tests',
|
||||||
)
|
)
|
||||||
|
|
||||||
option('bindings', type : 'boolean', value : true,
|
option(
|
||||||
|
'bindings',
|
||||||
|
type : 'boolean',
|
||||||
|
value : true,
|
||||||
description : 'Build language bindings (e.g. Perl)',
|
description : 'Build language bindings (e.g. Perl)',
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,5 +2,5 @@ if host_machine.system() == 'windows'
|
||||||
# https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170
|
# https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170
|
||||||
# #define _WIN32_WINNT_WIN8 0x0602
|
# #define _WIN32_WINNT_WIN8 0x0602
|
||||||
# We currently don't use any API which requires higher than this.
|
# We currently don't use any API which requires higher than this.
|
||||||
add_project_arguments([ '-D_WIN32_WINNT=0x0602' ], language: 'cpp')
|
add_project_arguments([ '-D_WIN32_WINNT=0x0602' ], language : 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -484,7 +484,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||||
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
||||||
return {path, 0};
|
return {path, 0};
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
auto pos = state->positions[v.payload.lambda.fun->pos];
|
auto pos = state->positions[v.lambda().fun->pos];
|
||||||
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
||||||
return {*path, pos.line};
|
return {*path, pos.line};
|
||||||
else
|
else
|
||||||
|
|
|
@ -252,7 +252,7 @@ const char * nix_get_path_string(nix_c_context * context, const nix_value * valu
|
||||||
// We could use v.path().to_string().c_str(), but I'm concerned this
|
// We could use v.path().to_string().c_str(), but I'm concerned this
|
||||||
// crashes. Looks like .path() allocates a CanonPath with a copy of the
|
// crashes. Looks like .path() allocates a CanonPath with a copy of the
|
||||||
// string, then it gets the underlying data from that.
|
// string, then it gets the underlying data from that.
|
||||||
return v.payload.path.path;
|
return v.pathStr();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS_NULL
|
NIXC_CATCH_ERRS_NULL
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
# Public headers directory
|
# Public headers directory
|
||||||
|
|
||||||
include_dirs = [include_directories('../../..')]
|
include_dirs = [ include_directories('../../..') ]
|
||||||
|
|
||||||
headers = files(
|
headers = files('libexpr.hh', 'nix_api_expr.hh', 'value/context.hh')
|
||||||
'libexpr.hh',
|
|
||||||
'nix_api_expr.hh',
|
|
||||||
'value/context.hh',
|
|
||||||
)
|
|
||||||
|
|
|
@ -667,8 +667,8 @@ namespace nix {
|
||||||
auto v = eval("derivation");
|
auto v = eval("derivation");
|
||||||
ASSERT_EQ(v.type(), nFunction);
|
ASSERT_EQ(v.type(), nFunction);
|
||||||
ASSERT_TRUE(v.isLambda());
|
ASSERT_TRUE(v.isLambda());
|
||||||
ASSERT_NE(v.payload.lambda.fun, nullptr);
|
ASSERT_NE(v.lambda().fun, nullptr);
|
||||||
ASSERT_TRUE(v.payload.lambda.fun->hasFormals());
|
ASSERT_TRUE(v.lambda().fun->hasFormals());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PrimOpTest, currentTime) {
|
TEST_F(PrimOpTest, currentTime) {
|
||||||
|
|
|
@ -204,7 +204,7 @@ FrameInfo SampleStack::getFrameInfoFromValueAndPos(const Value & v, std::span<Va
|
||||||
/* NOTE: No actual references to garbage collected values are not held in
|
/* NOTE: No actual references to garbage collected values are not held in
|
||||||
the profiler. */
|
the profiler. */
|
||||||
if (v.isLambda())
|
if (v.isLambda())
|
||||||
return LambdaFrameInfo{.expr = v.payload.lambda.fun, .callPos = pos};
|
return LambdaFrameInfo{.expr = v.lambda().fun, .callPos = pos};
|
||||||
else if (v.isPrimOp()) {
|
else if (v.isPrimOp()) {
|
||||||
return getPrimOpFrameInfo(*v.primOp(), args, pos);
|
return getPrimOpFrameInfo(*v.primOp(), args, pos);
|
||||||
} else if (v.isPrimOpApp())
|
} else if (v.isPrimOpApp())
|
||||||
|
|
|
@ -147,8 +147,8 @@ PosIdx Value::determinePos(const PosIdx pos) const
|
||||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tAttrs: return attrs()->pos;
|
case tAttrs: return attrs()->pos;
|
||||||
case tLambda: return payload.lambda.fun->pos;
|
case tLambda: return lambda().fun->pos;
|
||||||
case tApp: return payload.app.left->determinePos(pos);
|
case tApp: return app().left->determinePos(pos);
|
||||||
default: return pos;
|
default: return pos;
|
||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
@ -160,10 +160,10 @@ bool Value::isTrivial() const
|
||||||
internalType != tApp
|
internalType != tApp
|
||||||
&& internalType != tPrimOpApp
|
&& internalType != tPrimOpApp
|
||||||
&& (internalType != tThunk
|
&& (internalType != tThunk
|
||||||
|| (dynamic_cast<ExprAttrs *>(payload.thunk.expr)
|
|| (dynamic_cast<ExprAttrs *>(thunk().expr)
|
||||||
&& ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty())
|
&& ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty())
|
||||||
|| dynamic_cast<ExprLambda *>(payload.thunk.expr)
|
|| dynamic_cast<ExprLambda *>(thunk().expr)
|
||||||
|| dynamic_cast<ExprList *>(payload.thunk.expr));
|
|| dynamic_cast<ExprList *>(thunk().expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -525,9 +525,9 @@ std::ostream & operator<<(std::ostream & output, const PrimOp & primOp)
|
||||||
|
|
||||||
const PrimOp * Value::primOpAppPrimOp() const
|
const PrimOp * Value::primOpAppPrimOp() const
|
||||||
{
|
{
|
||||||
Value * left = payload.primOpApp.left;
|
Value * left = primOpApp().left;
|
||||||
while (left && !left->isPrimOp()) {
|
while (left && !left->isPrimOp()) {
|
||||||
left = left->payload.primOpApp.left;
|
left = left->primOpApp().left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!left)
|
if (!left)
|
||||||
|
@ -610,7 +610,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
auto exprLambda = v.payload.lambda.fun;
|
auto exprLambda = v.lambda().fun;
|
||||||
|
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -1567,13 +1567,13 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||||
|
|
||||||
if (vCur.isLambda()) {
|
if (vCur.isLambda()) {
|
||||||
|
|
||||||
ExprLambda & lambda(*vCur.payload.lambda.fun);
|
ExprLambda & lambda(*vCur.lambda().fun);
|
||||||
|
|
||||||
auto size =
|
auto size =
|
||||||
(!lambda.arg ? 0 : 1) +
|
(!lambda.arg ? 0 : 1) +
|
||||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||||
Env & env2(allocEnv(size));
|
Env & env2(allocEnv(size));
|
||||||
env2.up = vCur.payload.lambda.env;
|
env2.up = vCur.lambda().env;
|
||||||
|
|
||||||
Displacement displ = 0;
|
Displacement displ = 0;
|
||||||
|
|
||||||
|
@ -1603,7 +1603,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||||
symbols[i.name])
|
symbols[i.name])
|
||||||
.atPos(lambda.pos)
|
.atPos(lambda.pos)
|
||||||
.withTrace(pos, "from call site")
|
.withTrace(pos, "from call site")
|
||||||
.withFrame(*fun.payload.lambda.env, lambda)
|
.withFrame(*fun.lambda().env, lambda)
|
||||||
.debugThrow();
|
.debugThrow();
|
||||||
}
|
}
|
||||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||||
|
@ -1630,7 +1630,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||||
.atPos(lambda.pos)
|
.atPos(lambda.pos)
|
||||||
.withTrace(pos, "from call site")
|
.withTrace(pos, "from call site")
|
||||||
.withSuggestions(suggestions)
|
.withSuggestions(suggestions)
|
||||||
.withFrame(*fun.payload.lambda.env, lambda)
|
.withFrame(*fun.lambda().env, lambda)
|
||||||
.debugThrow();
|
.debugThrow();
|
||||||
}
|
}
|
||||||
unreachable();
|
unreachable();
|
||||||
|
@ -1702,7 +1702,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||||
Value * primOp = &vCur;
|
Value * primOp = &vCur;
|
||||||
while (primOp->isPrimOpApp()) {
|
while (primOp->isPrimOpApp()) {
|
||||||
argsDone++;
|
argsDone++;
|
||||||
primOp = primOp->payload.primOpApp.left;
|
primOp = primOp->primOpApp().left;
|
||||||
}
|
}
|
||||||
assert(primOp->isPrimOp());
|
assert(primOp->isPrimOp());
|
||||||
auto arity = primOp->primOp()->arity;
|
auto arity = primOp->primOp()->arity;
|
||||||
|
@ -1718,8 +1718,8 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||||
|
|
||||||
Value * vArgs[maxPrimOpArity];
|
Value * vArgs[maxPrimOpArity];
|
||||||
auto n = argsDone;
|
auto n = argsDone;
|
||||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left)
|
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp().left)
|
||||||
vArgs[--n] = arg->payload.primOpApp.right;
|
vArgs[--n] = arg->primOpApp().right;
|
||||||
|
|
||||||
for (size_t i = 0; i < argsLeft; ++i)
|
for (size_t i = 0; i < argsLeft; ++i)
|
||||||
vArgs[argsDone + i] = args[i];
|
vArgs[argsDone + i] = args[i];
|
||||||
|
@ -1825,14 +1825,14 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) {
|
if (!fun.isLambda() || !fun.lambda().fun->hasFormals()) {
|
||||||
res = fun;
|
res = fun;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.payload.lambda.fun->formals->formals.size()), args.size()));
|
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda().fun->formals->formals.size()), args.size()));
|
||||||
|
|
||||||
if (fun.payload.lambda.fun->formals->ellipsis) {
|
if (fun.lambda().fun->formals->ellipsis) {
|
||||||
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
||||||
// all available automatic arguments (which includes arguments specified on
|
// all available automatic arguments (which includes arguments specified on
|
||||||
// the command line via --arg/--argstr)
|
// the command line via --arg/--argstr)
|
||||||
|
@ -1840,7 +1840,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
|
||||||
attrs.insert(v);
|
attrs.insert(v);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, only pass the arguments that the function accepts
|
// Otherwise, only pass the arguments that the function accepts
|
||||||
for (auto & i : fun.payload.lambda.fun->formals->formals) {
|
for (auto & i : fun.lambda().fun->formals->formals) {
|
||||||
auto j = args.get(i.name);
|
auto j = args.get(i.name);
|
||||||
if (j) {
|
if (j) {
|
||||||
attrs.insert(*j);
|
attrs.insert(*j);
|
||||||
|
@ -1850,7 +1850,7 @@ Nix attempted to evaluate a function as a top level expression; in
|
||||||
this case it must have its arguments supplied either by default
|
this case it must have its arguments supplied either by default
|
||||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||||
.atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow();
|
.atPos(i.pos).withFrame(*fun.lambda().env, *fun.lambda().fun).debugThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2163,7 +2163,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
try {
|
try {
|
||||||
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
|
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
|
||||||
auto dts = debugRepl && i.value->isThunk()
|
auto dts = debugRepl && i.value->isThunk()
|
||||||
? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, i.pos,
|
? makeDebugTraceStacker(*this, *i.value->thunk().expr, *i.value->thunk().env, i.pos,
|
||||||
"while evaluating the attribute '%1%'", symbols[i.name])
|
"while evaluating the attribute '%1%'", symbols[i.name])
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
|
@ -2368,7 +2368,7 @@ BackedStringView EvalState::coerceToString(
|
||||||
!canonicalizePath && !copyToStore
|
!canonicalizePath && !copyToStore
|
||||||
? // FIXME: hack to preserve path literals that end in a
|
? // FIXME: hack to preserve path literals that end in a
|
||||||
// slash, as in /foo/${x}.
|
// slash, as in /foo/${x}.
|
||||||
v.payload.path.path
|
v.pathStr()
|
||||||
: copyToStore
|
: copyToStore
|
||||||
? store->printStorePath(copyPathToStore(context, v.path()))
|
? store->printStorePath(copyPathToStore(context, v.path()))
|
||||||
: std::string(v.path().path.abs());
|
: std::string(v.path().path.abs());
|
||||||
|
@ -2636,14 +2636,14 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case nPath:
|
case nPath:
|
||||||
if (v1.payload.path.accessor != v2.payload.path.accessor) {
|
if (v1.pathAccessor() != v2.pathAccessor()) {
|
||||||
error<AssertionError>(
|
error<AssertionError>(
|
||||||
"path '%s' is not equal to path '%s' because their accessors are different",
|
"path '%s' is not equal to path '%s' because their accessors are different",
|
||||||
ValuePrinter(*this, v1, errorPrintOptions),
|
ValuePrinter(*this, v1, errorPrintOptions),
|
||||||
ValuePrinter(*this, v2, errorPrintOptions))
|
ValuePrinter(*this, v2, errorPrintOptions))
|
||||||
.debugThrow();
|
.debugThrow();
|
||||||
}
|
}
|
||||||
if (strcmp(v1.payload.path.path, v2.payload.path.path) != 0) {
|
if (strcmp(v1.pathStr(), v2.pathStr()) != 0) {
|
||||||
error<AssertionError>(
|
error<AssertionError>(
|
||||||
"path '%s' is not equal to path '%s'",
|
"path '%s' is not equal to path '%s'",
|
||||||
ValuePrinter(*this, v1, errorPrintOptions),
|
ValuePrinter(*this, v1, errorPrintOptions),
|
||||||
|
@ -2810,8 +2810,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||||
case nPath:
|
case nPath:
|
||||||
return
|
return
|
||||||
// FIXME: compare accessors by their fingerprint.
|
// FIXME: compare accessors by their fingerprint.
|
||||||
v1.payload.path.accessor == v2.payload.path.accessor
|
v1.pathAccessor() == v2.pathAccessor()
|
||||||
&& strcmp(v1.payload.path.path, v2.payload.path.path) == 0;
|
&& strcmp(v1.pathStr(), v2.pathStr()) == 0;
|
||||||
|
|
||||||
case nNull:
|
case nNull:
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -89,9 +89,9 @@ Env & EvalState::allocEnv(size_t size)
|
||||||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
if (v.isThunk()) {
|
if (v.isThunk()) {
|
||||||
Env * env = v.payload.thunk.env;
|
Env * env = v.thunk().env;
|
||||||
assert(env || v.isBlackhole());
|
assert(env || v.isBlackhole());
|
||||||
Expr * expr = v.payload.thunk.expr;
|
Expr * expr = v.thunk().expr;
|
||||||
try {
|
try {
|
||||||
v.mkBlackhole();
|
v.mkBlackhole();
|
||||||
//checkInterrupt();
|
//checkInterrupt();
|
||||||
|
@ -106,7 +106,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (v.isApp())
|
else if (v.isApp())
|
||||||
callFunction(*v.payload.app.left, *v.payload.app.right, v, pos);
|
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -410,11 +410,6 @@ public:
|
||||||
return internalType == tList1 || internalType == tList2 || internalType == tListN;
|
return internalType == tList1 || internalType == tList2 || internalType == tListN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * const * listElems()
|
|
||||||
{
|
|
||||||
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<Value * const> listItems() const
|
std::span<Value * const> listItems() const
|
||||||
{
|
{
|
||||||
assert(isList());
|
assert(isList());
|
||||||
|
@ -444,8 +439,8 @@ public:
|
||||||
{
|
{
|
||||||
assert(internalType == tPath);
|
assert(internalType == tPath);
|
||||||
return SourcePath(
|
return SourcePath(
|
||||||
ref(payload.path.accessor->shared_from_this()),
|
ref(pathAccessor()->shared_from_this()),
|
||||||
CanonPath(CanonPath::unchecked_t(), payload.path.path));
|
CanonPath(CanonPath::unchecked_t(), pathStr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view string_view() const
|
std::string_view string_view() const
|
||||||
|
@ -482,6 +477,24 @@ public:
|
||||||
|
|
||||||
NixFloat fpoint() const
|
NixFloat fpoint() const
|
||||||
{ return payload.fpoint; }
|
{ return payload.fpoint; }
|
||||||
|
|
||||||
|
Lambda lambda() const
|
||||||
|
{ return payload.lambda; }
|
||||||
|
|
||||||
|
ClosureThunk thunk() const
|
||||||
|
{ return payload.thunk; }
|
||||||
|
|
||||||
|
FunctionApplicationThunk primOpApp() const
|
||||||
|
{ return payload.primOpApp; }
|
||||||
|
|
||||||
|
FunctionApplicationThunk app() const
|
||||||
|
{ return payload.app; }
|
||||||
|
|
||||||
|
const char * pathStr() const
|
||||||
|
{ return payload.path.path; }
|
||||||
|
|
||||||
|
SourceAccessor * pathAccessor() const
|
||||||
|
{ return payload.path.accessor; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -489,7 +502,7 @@ extern ExprBlackHole eBlackHole;
|
||||||
|
|
||||||
bool Value::isBlackhole() const
|
bool Value::isBlackhole() const
|
||||||
{
|
{
|
||||||
return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole;
|
return internalType == tThunk && thunk().expr == (Expr*) &eBlackHole;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::mkBlackhole()
|
void Value::mkBlackhole()
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "nix/expr/value-to-xml.hh"
|
#include "nix/expr/value-to-xml.hh"
|
||||||
#include "nix/expr/primops.hh"
|
#include "nix/expr/primops.hh"
|
||||||
#include "nix/fetchers/fetch-to-store.hh"
|
#include "nix/fetchers/fetch-to-store.hh"
|
||||||
|
#include "nix/util/sort.hh"
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
@ -651,7 +652,7 @@ struct CompareValues
|
||||||
// Note: we don't take the accessor into account
|
// Note: we don't take the accessor into account
|
||||||
// since it's not obvious how to compare them in a
|
// since it's not obvious how to compare them in a
|
||||||
// reproducible way.
|
// reproducible way.
|
||||||
return strcmp(v1->payload.path.path, v2->payload.path.path) < 0;
|
return strcmp(v1->pathStr(), v2->pathStr()) < 0;
|
||||||
case nList:
|
case nList:
|
||||||
// Lexicographic comparison
|
// Lexicographic comparison
|
||||||
for (size_t i = 0;; i++) {
|
for (size_t i = 0;; i++) {
|
||||||
|
@ -3138,12 +3139,12 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
|
||||||
if (!args[0]->isLambda())
|
if (!args[0]->isLambda())
|
||||||
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
|
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
|
||||||
|
|
||||||
if (!args[0]->payload.lambda.fun->hasFormals()) {
|
if (!args[0]->lambda().fun->hasFormals()) {
|
||||||
v.mkAttrs(&state.emptyBindings);
|
v.mkAttrs(&state.emptyBindings);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &formals = args[0]->payload.lambda.fun->formals->formals;
|
const auto &formals = args[0]->lambda().fun->formals->formals;
|
||||||
auto attrs = state.buildBindings(formals.size());
|
auto attrs = state.buildBindings(formals.size());
|
||||||
for (auto & i : formals)
|
for (auto & i : formals)
|
||||||
attrs.insert(i.name, state.getBool(i.def), i.pos);
|
attrs.insert(i.name, state.getBool(i.def), i.pos);
|
||||||
|
@ -3695,10 +3696,14 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
|
||||||
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");
|
||||||
};
|
};
|
||||||
|
|
||||||
/* FIXME: std::sort can segfault if the comparator is not a strict
|
/* NOTE: Using custom implementation because std::sort and std::stable_sort
|
||||||
weak ordering. What to do? std::stable_sort() seems more
|
are not resilient to comparators that violate strict weak ordering. Diagnosing
|
||||||
resilient, but no guarantees... */
|
incorrect implementations is a O(n^3) problem, so doing the checks is much more
|
||||||
std::stable_sort(list.begin(), list.end(), comparator);
|
expensive that doing the sorting. For this reason we choose to use sorting algorithms
|
||||||
|
that are can't be broken by invalid comprators. peeksort (mergesort)
|
||||||
|
doesn't misbehave when any of the strict weak order properties is
|
||||||
|
violated - output is always a reordering of the input. */
|
||||||
|
peeksort(list.begin(), list.end(), comparator);
|
||||||
|
|
||||||
v.mkList(list);
|
v.mkList(list);
|
||||||
}
|
}
|
||||||
|
@ -3720,6 +3725,32 @@ static RegisterPrimOp primop_sort({
|
||||||
|
|
||||||
This is a stable sort: it preserves the relative order of elements
|
This is a stable sort: it preserves the relative order of elements
|
||||||
deemed equal by the comparator.
|
deemed equal by the comparator.
|
||||||
|
|
||||||
|
*comparator* must impose a strict weak ordering on the set of values
|
||||||
|
in the *list*. This means that for any elements *a*, *b* and *c* from the
|
||||||
|
*list*, *comparator* must satisfy the following relations:
|
||||||
|
|
||||||
|
1. Transitivity
|
||||||
|
|
||||||
|
```nix
|
||||||
|
comparator a b && comparator b c -> comparator a c
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Irreflexivity
|
||||||
|
|
||||||
|
```nix
|
||||||
|
comparator a a == false
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Transitivity of equivalence
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let equiv = a: b: (!comparator a b && !comparator b a); in
|
||||||
|
equiv a b && equiv b c -> equiv a c
|
||||||
|
```
|
||||||
|
|
||||||
|
If the *comparator* violates any of these properties, then `builtins.sort`
|
||||||
|
reorders elements in an unspecified manner.
|
||||||
)",
|
)",
|
||||||
.fun = prim_sort,
|
.fun = prim_sort,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
generated_headers += gen_header.process(
|
generated_headers += gen_header.process(
|
||||||
'derivation.nix',
|
'derivation.nix',
|
||||||
preserve_path_from: meson.project_source_root(),
|
preserve_path_from : meson.project_source_root(),
|
||||||
)
|
)
|
||||||
|
|
||||||
sources += files(
|
sources += files(
|
||||||
|
|
|
@ -453,13 +453,13 @@ private:
|
||||||
|
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
output << "lambda";
|
output << "lambda";
|
||||||
if (v.payload.lambda.fun) {
|
if (v.lambda().fun) {
|
||||||
if (v.payload.lambda.fun->name) {
|
if (v.lambda().fun->name) {
|
||||||
output << " " << state.symbols[v.payload.lambda.fun->name];
|
output << " " << state.symbols[v.lambda().fun->name];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << state.positions[v.payload.lambda.fun->pos];
|
s << state.positions[v.lambda().fun->pos];
|
||||||
output << " @ " << filterANSIEscapes(toView(s));
|
output << " @ " << filterANSIEscapes(toView(s));
|
||||||
}
|
}
|
||||||
} else if (v.isPrimOp()) {
|
} else if (v.isPrimOp()) {
|
||||||
|
|
|
@ -126,18 +126,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
|
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]);
|
||||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||||
|
|
||||||
if (v.payload.lambda.fun->hasFormals()) {
|
if (v.lambda().fun->hasFormals()) {
|
||||||
XMLAttrs attrs;
|
XMLAttrs attrs;
|
||||||
if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg];
|
if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg];
|
||||||
if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda().fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
XMLOpenElement _(doc, "attrspat", attrs);
|
XMLOpenElement _(doc, "attrspat", attrs);
|
||||||
for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols))
|
for (auto & i : v.lambda().fun->formals->lexicographicOrder(state.symbols))
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg]));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,8 +252,8 @@ static Flake readFlake(
|
||||||
if (auto outputs = vInfo.attrs()->get(sOutputs)) {
|
if (auto outputs = vInfo.attrs()->get(sOutputs)) {
|
||||||
expectType(state, nFunction, *outputs->value, outputs->pos);
|
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||||
|
|
||||||
if (outputs->value->isLambda() && outputs->value->payload.lambda.fun->hasFormals()) {
|
if (outputs->value->isLambda() && outputs->value->lambda().fun->hasFormals()) {
|
||||||
for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) {
|
for (auto & formal : outputs->value->lambda().fun->formals->formals) {
|
||||||
if (formal.name != state.sSelf)
|
if (formal.name != state.sSelf)
|
||||||
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
||||||
.ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name]))
|
.ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name]))
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
include_dirs += include_directories('../..')
|
include_dirs += include_directories('../..')
|
||||||
|
|
||||||
headers += files(
|
headers += files('personality.hh')
|
||||||
'personality.hh',
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
sources += files(
|
sources += files('personality.cc')
|
||||||
'personality.cc',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('include/nix/store')
|
subdir('include/nix/store')
|
||||||
|
|
|
@ -133,7 +133,7 @@ LocalStore::LocalStore(ref<const Config> config)
|
||||||
Path gcRootsDir = config->stateDir + "/gcroots";
|
Path gcRootsDir = config->stateDir + "/gcroots";
|
||||||
if (!pathExists(gcRootsDir)) {
|
if (!pathExists(gcRootsDir)) {
|
||||||
createDirs(gcRootsDir);
|
createDirs(gcRootsDir);
|
||||||
createSymlink(profilesDir, gcRootsDir + "/profiles");
|
replaceSymlink(profilesDir, gcRootsDir + "/profiles");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
|
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
|
||||||
|
|
|
@ -65,6 +65,7 @@ sources = files(
|
||||||
'position.cc',
|
'position.cc',
|
||||||
'processes.cc',
|
'processes.cc',
|
||||||
'references.cc',
|
'references.cc',
|
||||||
|
'sort.cc',
|
||||||
'spawn.cc',
|
'spawn.cc',
|
||||||
'strings.cc',
|
'strings.cc',
|
||||||
'suggestions.cc',
|
'suggestions.cc',
|
||||||
|
|
274
src/libutil-tests/sort.cc
Normal file
274
src/libutil-tests/sort.cc
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <rapidcheck/gtest.h>
|
||||||
|
#include "nix/util/sort.hh"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct MonotonicSubranges : public ::testing::Test
|
||||||
|
{
|
||||||
|
std::vector<int> empty_;
|
||||||
|
std::vector<int> basic_ = {1, 0, -1, -100, 10, 10, 20, 40, 5, 5, 20, 10, 10, 1, -5};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(MonotonicSubranges, empty)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(weaklyIncreasingPrefix(empty_.begin(), empty_.end()), empty_.begin());
|
||||||
|
ASSERT_EQ(weaklyIncreasingSuffix(empty_.begin(), empty_.end()), empty_.begin());
|
||||||
|
ASSERT_EQ(strictlyDecreasingPrefix(empty_.begin(), empty_.end()), empty_.begin());
|
||||||
|
ASSERT_EQ(strictlyDecreasingSuffix(empty_.begin(), empty_.end()), empty_.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MonotonicSubranges, basic)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(strictlyDecreasingPrefix(basic_.begin(), basic_.end()), basic_.begin() + 4);
|
||||||
|
ASSERT_EQ(strictlyDecreasingSuffix(basic_.begin(), basic_.end()), basic_.begin() + 12);
|
||||||
|
std::reverse(basic_.begin(), basic_.end());
|
||||||
|
ASSERT_EQ(weaklyIncreasingPrefix(basic_.begin(), basic_.end()), basic_.begin() + 5);
|
||||||
|
ASSERT_EQ(weaklyIncreasingSuffix(basic_.begin(), basic_.end()), basic_.begin() + 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class SortTestPermutations : public ::testing::Test
|
||||||
|
{
|
||||||
|
std::vector<T> initialData = {std::numeric_limits<T>::max(), std::numeric_limits<T>::min(), 0, 0, 42, 126, 36};
|
||||||
|
std::vector<T> vectorData;
|
||||||
|
std::list<T> listData;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<T> scratchVector;
|
||||||
|
std::list<T> scratchList;
|
||||||
|
std::vector<T> empty;
|
||||||
|
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
vectorData = initialData;
|
||||||
|
std::sort(vectorData.begin(), vectorData.end());
|
||||||
|
listData = std::list(vectorData.begin(), vectorData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nextPermutation()
|
||||||
|
{
|
||||||
|
std::next_permutation(vectorData.begin(), vectorData.end());
|
||||||
|
std::next_permutation(listData.begin(), listData.end());
|
||||||
|
scratchList = listData;
|
||||||
|
scratchVector = vectorData;
|
||||||
|
return vectorData == initialData;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using SortPermutationsTypes = ::testing::Types<int, long long, short, unsigned, unsigned long>;
|
||||||
|
|
||||||
|
TYPED_TEST_SUITE(SortTestPermutations, SortPermutationsTypes);
|
||||||
|
|
||||||
|
TYPED_TEST(SortTestPermutations, insertionsort)
|
||||||
|
{
|
||||||
|
while (!this->nextPermutation()) {
|
||||||
|
auto & list = this->scratchList;
|
||||||
|
insertionsort(list.begin(), list.end());
|
||||||
|
ASSERT_TRUE(std::is_sorted(list.begin(), list.end()));
|
||||||
|
auto & vector = this->scratchVector;
|
||||||
|
insertionsort(vector.begin(), vector.end());
|
||||||
|
ASSERT_TRUE(std::is_sorted(vector.begin(), vector.end()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(SortTestPermutations, peeksort)
|
||||||
|
{
|
||||||
|
while (!this->nextPermutation()) {
|
||||||
|
auto & vector = this->scratchVector;
|
||||||
|
peeksort(vector.begin(), vector.end());
|
||||||
|
ASSERT_TRUE(std::is_sorted(vector.begin(), vector.end()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(InsertionSort, empty)
|
||||||
|
{
|
||||||
|
std::vector<int> empty;
|
||||||
|
insertionsort(empty.begin(), empty.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RandomPeekSort : public ::testing::TestWithParam<
|
||||||
|
std::tuple</*maxSize*/ std::size_t, /*min*/ int, /*max*/ int, /*iterations*/ std::size_t>>
|
||||||
|
{
|
||||||
|
using ValueType = int;
|
||||||
|
std::vector<ValueType> data_;
|
||||||
|
std::mt19937 urng_;
|
||||||
|
std::uniform_int_distribution<int> distribution_;
|
||||||
|
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
urng_ = std::mt19937(GTEST_FLAG_GET(random_seed));
|
||||||
|
distribution_ = std::uniform_int_distribution<int>(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto regenerate()
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
std::size_t dataSize = std::uniform_int_distribution<std::size_t>(0, maxSize)(urng_);
|
||||||
|
data_.resize(dataSize);
|
||||||
|
std::generate(data_.begin(), data_.end(), [&]() { return distribution_(urng_); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(RandomPeekSort, defaultComparator)
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < iterations; ++i) {
|
||||||
|
regenerate();
|
||||||
|
peeksort(data_.begin(), data_.end());
|
||||||
|
ASSERT_TRUE(std::is_sorted(data_.begin(), data_.end()));
|
||||||
|
/* Sorting is idempotent */
|
||||||
|
peeksort(data_.begin(), data_.end());
|
||||||
|
ASSERT_TRUE(std::is_sorted(data_.begin(), data_.end()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(RandomPeekSort, greater)
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < iterations; ++i) {
|
||||||
|
regenerate();
|
||||||
|
peeksort(data_.begin(), data_.end(), std::greater<int>{});
|
||||||
|
ASSERT_TRUE(std::is_sorted(data_.begin(), data_.end(), std::greater<int>{}));
|
||||||
|
/* Sorting is idempotent */
|
||||||
|
peeksort(data_.begin(), data_.end(), std::greater<int>{});
|
||||||
|
ASSERT_TRUE(std::is_sorted(data_.begin(), data_.end(), std::greater<int>{}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(RandomPeekSort, brokenComparator)
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
|
||||||
|
/* This is a pretty nice way of modeling a worst-case scenario for a broken comparator.
|
||||||
|
If the sorting algorithm doesn't break in such case, then surely all deterministic
|
||||||
|
predicates won't break it. */
|
||||||
|
auto comp = [&]([[maybe_unused]] const auto & lhs, [[maybe_unused]] const auto & rhs) -> bool {
|
||||||
|
return std::uniform_int_distribution<unsigned>(0, 1)(urng_);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < iterations; ++i) {
|
||||||
|
regenerate();
|
||||||
|
auto originalData = data_;
|
||||||
|
peeksort(data_.begin(), data_.end(), comp);
|
||||||
|
/* Check that the output is just a reordering of the input. This is the
|
||||||
|
contract of the implementation in regard to comparators that don't
|
||||||
|
define a strict weak order. */
|
||||||
|
std::sort(data_.begin(), data_.end());
|
||||||
|
std::sort(originalData.begin(), originalData.end());
|
||||||
|
ASSERT_EQ(originalData, data_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(RandomPeekSort, stability)
|
||||||
|
{
|
||||||
|
auto [maxSize, min, max, iterations] = GetParam();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < iterations; ++i) {
|
||||||
|
regenerate();
|
||||||
|
std::vector<std::pair<int, int>> pairs;
|
||||||
|
|
||||||
|
/* Assign sequential ids to objects. After the sort ids for equivalent
|
||||||
|
elements should be in ascending order. */
|
||||||
|
std::transform(
|
||||||
|
data_.begin(), data_.end(), std::back_inserter(pairs), [id = std::size_t{0}](auto && val) mutable {
|
||||||
|
return std::pair{val, ++id};
|
||||||
|
});
|
||||||
|
|
||||||
|
auto comp = [&]([[maybe_unused]] const auto & lhs, [[maybe_unused]] const auto & rhs) -> bool {
|
||||||
|
return lhs.first > rhs.first;
|
||||||
|
};
|
||||||
|
|
||||||
|
peeksort(pairs.begin(), pairs.end(), comp);
|
||||||
|
ASSERT_TRUE(std::is_sorted(pairs.begin(), pairs.end(), comp));
|
||||||
|
|
||||||
|
for (auto begin = pairs.begin(), end = pairs.end(); begin < end; ++begin) {
|
||||||
|
auto key = begin->first;
|
||||||
|
auto innerEnd = std::find_if_not(begin, end, [key](const auto & lhs) { return lhs.first == key; });
|
||||||
|
ASSERT_TRUE(std::is_sorted(begin, innerEnd, [](const auto & lhs, const auto & rhs) {
|
||||||
|
return lhs.second < rhs.second;
|
||||||
|
}));
|
||||||
|
begin = innerEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using RandomPeekSortParamType = RandomPeekSort::ParamType;
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
PeekSort,
|
||||||
|
RandomPeekSort,
|
||||||
|
::testing::Values(
|
||||||
|
RandomPeekSortParamType{128, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 1024},
|
||||||
|
RandomPeekSortParamType{7753, -32, 32, 128},
|
||||||
|
RandomPeekSortParamType{11719, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 64},
|
||||||
|
RandomPeekSortParamType{4063, 0, 32, 256},
|
||||||
|
RandomPeekSortParamType{771, -8, 8, 2048},
|
||||||
|
RandomPeekSortParamType{433, 0, 1, 2048},
|
||||||
|
RandomPeekSortParamType{0, 0, 0, 1}, /* empty case */
|
||||||
|
RandomPeekSortParamType{
|
||||||
|
1, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 1}, /* single element */
|
||||||
|
RandomPeekSortParamType{
|
||||||
|
2, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 2}, /* two elements */
|
||||||
|
RandomPeekSortParamType{55425, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), 128}));
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct SortProperty : public ::testing::Test
|
||||||
|
{};
|
||||||
|
|
||||||
|
using SortPropertyTypes = ::testing::Types<int, unsigned, long long, short, std::string>;
|
||||||
|
TYPED_TEST_SUITE(SortProperty, SortPropertyTypes);
|
||||||
|
|
||||||
|
RC_GTEST_TYPED_FIXTURE_PROP(SortProperty, peeksortSorted, (std::vector<TypeParam> vec))
|
||||||
|
{
|
||||||
|
peeksort(vec.begin(), vec.end());
|
||||||
|
RC_ASSERT(std::is_sorted(vec.begin(), vec.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RC_GTEST_TYPED_FIXTURE_PROP(SortProperty, peeksortSortedGreater, (std::vector<TypeParam> vec))
|
||||||
|
{
|
||||||
|
auto comp = std::greater<TypeParam>();
|
||||||
|
peeksort(vec.begin(), vec.end(), comp);
|
||||||
|
RC_ASSERT(std::is_sorted(vec.begin(), vec.end(), comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
RC_GTEST_TYPED_FIXTURE_PROP(SortProperty, insertionsortSorted, (std::vector<TypeParam> vec))
|
||||||
|
{
|
||||||
|
insertionsort(vec.begin(), vec.end());
|
||||||
|
RC_ASSERT(std::is_sorted(vec.begin(), vec.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RC_GTEST_PROP(SortProperty, peeksortStability, (std::vector<std::pair<char, char>> vec))
|
||||||
|
{
|
||||||
|
auto comp = [](auto lhs, auto rhs) { return lhs.first < rhs.first; };
|
||||||
|
auto copy = vec;
|
||||||
|
std::stable_sort(copy.begin(), copy.end(), comp);
|
||||||
|
peeksort(vec.begin(), vec.end(), comp);
|
||||||
|
RC_ASSERT(copy == vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
RC_GTEST_TYPED_FIXTURE_PROP(SortProperty, peeksortSortedLinearComparisonComplexity, (std::vector<TypeParam> vec))
|
||||||
|
{
|
||||||
|
peeksort(vec.begin(), vec.end());
|
||||||
|
RC_ASSERT(std::is_sorted(vec.begin(), vec.end()));
|
||||||
|
std::size_t comparisonCount = 0;
|
||||||
|
auto countingComp = [&](auto lhs, auto rhs) {
|
||||||
|
++comparisonCount;
|
||||||
|
return lhs < rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
peeksort(vec.begin(), vec.end(), countingComp);
|
||||||
|
|
||||||
|
/* In the sorted case comparison complexify should be linear. */
|
||||||
|
RC_ASSERT(comparisonCount <= vec.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
|
@ -2,6 +2,4 @@
|
||||||
|
|
||||||
include_dirs += include_directories('../..')
|
include_dirs += include_directories('../..')
|
||||||
|
|
||||||
headers += files(
|
headers += files('freebsd-jail.hh')
|
||||||
'freebsd-jail.hh',
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
sources += files(
|
sources += files('freebsd-jail.cc')
|
||||||
'freebsd-jail.cc',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('include/nix/util')
|
subdir('include/nix/util')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Public headers directory
|
# Public headers directory
|
||||||
|
|
||||||
include_dirs = [include_directories('../..')]
|
include_dirs = [ include_directories('../..') ]
|
||||||
|
|
||||||
headers = files(
|
headers = files(
|
||||||
'abstract-setting-to-json.hh',
|
'abstract-setting-to-json.hh',
|
||||||
|
@ -59,12 +59,13 @@ headers = files(
|
||||||
'signals.hh',
|
'signals.hh',
|
||||||
'signature/local-keys.hh',
|
'signature/local-keys.hh',
|
||||||
'signature/signer.hh',
|
'signature/signer.hh',
|
||||||
|
'sort.hh',
|
||||||
'source-accessor.hh',
|
'source-accessor.hh',
|
||||||
'source-path.hh',
|
'source-path.hh',
|
||||||
'split.hh',
|
'split.hh',
|
||||||
'std-hash.hh',
|
'std-hash.hh',
|
||||||
'strings.hh',
|
|
||||||
'strings-inline.hh',
|
'strings-inline.hh',
|
||||||
|
'strings.hh',
|
||||||
'suggestions.hh',
|
'suggestions.hh',
|
||||||
'sync.hh',
|
'sync.hh',
|
||||||
'tarfile.hh',
|
'tarfile.hh',
|
||||||
|
|
299
src/libutil/include/nix/util/sort.hh
Normal file
299
src/libutil/include/nix/util/sort.hh
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <concepts>
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* In-house implementation of sorting algorithms. Used for cases when several properties
|
||||||
|
* need to be upheld regardless of the stdlib implementation of std::sort or
|
||||||
|
* std::stable_sort.
|
||||||
|
*
|
||||||
|
* PeekSort implementation is adapted from reference implementation
|
||||||
|
* https://github.com/sebawild/powersort licensed under the MIT License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* PeekSort attribution:
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Sebastian Wild
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge sorted runs [begin, middle) with [middle, end) in-place [begin, end).
|
||||||
|
* Uses a temporary working buffer by first copying [begin, end) to it.
|
||||||
|
*
|
||||||
|
* @param begin Start of the first subrange to be sorted.
|
||||||
|
* @param middle End of the first sorted subrange and the start of the second.
|
||||||
|
* @param end End of the second sorted subrange.
|
||||||
|
* @param workingBegin Start of the working buffer.
|
||||||
|
* @param comp Comparator implementing an operator()(const ValueType& lhs, const ValueType& rhs).
|
||||||
|
*
|
||||||
|
* @pre workingBegin buffer must have at least std::distance(begin, end) elements.
|
||||||
|
*
|
||||||
|
* @note We can't use std::inplace_merge or std::merge, because their behavior
|
||||||
|
* is undefined if the comparator is not strict weak ordering.
|
||||||
|
*/
|
||||||
|
template<
|
||||||
|
std::forward_iterator Iter,
|
||||||
|
std::random_access_iterator BufIter,
|
||||||
|
typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
void mergeSortedRunsInPlace(Iter begin, Iter middle, Iter end, BufIter workingBegin, Comparator comp = {})
|
||||||
|
{
|
||||||
|
const BufIter workingMiddle = std::move(begin, middle, workingBegin);
|
||||||
|
const BufIter workingEnd = std::move(middle, end, workingMiddle);
|
||||||
|
|
||||||
|
Iter output = begin;
|
||||||
|
BufIter workingLeft = workingBegin;
|
||||||
|
BufIter workingRight = workingMiddle;
|
||||||
|
|
||||||
|
while (workingLeft != workingMiddle && workingRight != workingEnd) {
|
||||||
|
/* Note the inversion here !comp(...., ....). This is required for the merge to be stable.
|
||||||
|
If a == b where a if from the left part and b is the the right, then we have to pick
|
||||||
|
a. */
|
||||||
|
*output++ = !comp(*workingRight, *workingLeft) ? std::move(*workingLeft++) : std::move(*workingRight++);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::move(workingLeft, workingMiddle, output);
|
||||||
|
std::move(workingRight, workingEnd, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple insertion sort.
|
||||||
|
*
|
||||||
|
* Does not require that the std::iter_value_t<Iter> is copyable.
|
||||||
|
*
|
||||||
|
* @param begin Start of the range to sort.
|
||||||
|
* @param end End of the range to sort.
|
||||||
|
* @comp Comparator the defines the ordering. Order of elements if the comp is not strict weak ordering
|
||||||
|
* is not specified.
|
||||||
|
* @throws Nothing.
|
||||||
|
*
|
||||||
|
* Note on exception safety: this function provides weak exception safety
|
||||||
|
* guarantees. To elaborate: if the comparator throws or move assignment
|
||||||
|
* throws (value type is not nothrow_move_assignable) then the range is left in
|
||||||
|
* a consistent, but unspecified state.
|
||||||
|
*
|
||||||
|
* @note This can't be implemented in terms of binary search if the strict weak ordering
|
||||||
|
* needs to be handled in a well-defined but unspecified manner.
|
||||||
|
*/
|
||||||
|
template<std::bidirectional_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
void insertionsort(Iter begin, Iter end, Comparator comp = {})
|
||||||
|
{
|
||||||
|
if (begin == end)
|
||||||
|
return;
|
||||||
|
for (Iter current = std::next(begin); current != end; ++current) {
|
||||||
|
for (Iter insertionPoint = current;
|
||||||
|
insertionPoint != begin && comp(*insertionPoint, *std::prev(insertionPoint));
|
||||||
|
--insertionPoint) {
|
||||||
|
std::swap(*insertionPoint, *std::prev(insertionPoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find maximal i <= end such that [begin, i) is strictly decreasing according
|
||||||
|
* to the specified comparator.
|
||||||
|
*/
|
||||||
|
template<std::forward_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
Iter strictlyDecreasingPrefix(Iter begin, Iter end, Comparator && comp = {})
|
||||||
|
{
|
||||||
|
if (begin == end)
|
||||||
|
return begin;
|
||||||
|
while (std::next(begin) != end && /* *std::next(begin) < begin */
|
||||||
|
comp(*std::next(begin), *begin))
|
||||||
|
++begin;
|
||||||
|
return std::next(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find minimal i >= start such that [i, end) is strictly decreasing according
|
||||||
|
* to the specified comparator.
|
||||||
|
*/
|
||||||
|
template<std::bidirectional_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
Iter strictlyDecreasingSuffix(Iter begin, Iter end, Comparator && comp = {})
|
||||||
|
{
|
||||||
|
if (begin == end)
|
||||||
|
return end;
|
||||||
|
while (std::prev(end) > begin && /* *std::prev(end) < *std::prev(end, 2) */
|
||||||
|
comp(*std::prev(end), *std::prev(end, 2)))
|
||||||
|
--end;
|
||||||
|
return std::prev(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find maximal i <= end such that [begin, i) is weakly increasing according
|
||||||
|
* to the specified comparator.
|
||||||
|
*/
|
||||||
|
template<std::bidirectional_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
Iter weaklyIncreasingPrefix(Iter begin, Iter end, Comparator && comp = {})
|
||||||
|
{
|
||||||
|
return strictlyDecreasingPrefix(begin, end, std::not_fn(std::forward<Comparator>(comp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find minimal i >= start such that [i, end) is weakly increasing according
|
||||||
|
* to the specified comparator.
|
||||||
|
*/
|
||||||
|
template<std::bidirectional_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
Iter weaklyIncreasingSuffix(Iter begin, Iter end, Comparator && comp = {})
|
||||||
|
{
|
||||||
|
return strictlyDecreasingSuffix(begin, end, std::not_fn(std::forward<Comparator>(comp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeksort stable sorting algorithm. Sorts elements in-place.
|
||||||
|
* Allocates additional memory as needed.
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* PeekSort is a stable, near-optimal natural mergesort. Most importantly, like any
|
||||||
|
* other mergesort it upholds the "Ord safety" property. Meaning that even for
|
||||||
|
* comparator predicates that don't satisfy strict weak ordering it can't result
|
||||||
|
* in infinite loops/out of bounds memory accesses or other undefined behavior.
|
||||||
|
*
|
||||||
|
* As a quick reminder, strict weak ordering relation operator< must satisfy
|
||||||
|
* the following properties. Keep in mind that in C++ an equvalence relation
|
||||||
|
* is specified in terms of operator< like so: a ~ b iff !(a < b) && !(b < a).
|
||||||
|
*
|
||||||
|
* 1. a < a === false - relation is irreflexive
|
||||||
|
* 2. a < b, b < c => a < c - transitivity
|
||||||
|
* 3. a ~ b, a ~ b, b ~ c => a ~ c, transitivity of equivalence
|
||||||
|
*
|
||||||
|
* @see https://www.wild-inter.net/publications/munro-wild-2018
|
||||||
|
* @see https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md#property-analysis
|
||||||
|
*
|
||||||
|
* The order of elements when comp is not strict weak ordering is not specified, but
|
||||||
|
* is not undefined. The output is always some permutation of the input, regardless
|
||||||
|
* of the comparator provided.
|
||||||
|
* Relying on ordering in such cases is erroneous, but this implementation
|
||||||
|
* will happily accept broken comparators and will not crash.
|
||||||
|
*
|
||||||
|
* @param begin Start of the range to be sorted.
|
||||||
|
* @param end End of the range to be sorted.
|
||||||
|
* @comp comp Comparator implementing an operator()(const ValueType& lhs, const ValueType& rhs).
|
||||||
|
*
|
||||||
|
* @throws std::bad_alloc if the temporary buffer can't be allocated.
|
||||||
|
*
|
||||||
|
* @return Nothing.
|
||||||
|
*
|
||||||
|
* Note on exception safety: this function provides weak exception safety
|
||||||
|
* guarantees. To elaborate: if the comparator throws or move assignment
|
||||||
|
* throws (value type is not nothrow_move_assignable) then the range is left in
|
||||||
|
* a consistent, but unspecified state.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<std::random_access_iterator Iter, typename Comparator = std::less<std::iter_value_t<Iter>>>
|
||||||
|
/* ValueType must be default constructible to create the temporary buffer */
|
||||||
|
requires std::is_default_constructible_v<std::iter_value_t<Iter>>
|
||||||
|
void peeksort(Iter begin, Iter end, Comparator comp = {})
|
||||||
|
{
|
||||||
|
auto length = std::distance(begin, end);
|
||||||
|
|
||||||
|
/* Special-case very simple inputs. This is identical to how libc++ does it. */
|
||||||
|
switch (length) {
|
||||||
|
case 0:
|
||||||
|
[[fallthrough]];
|
||||||
|
case 1:
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
if (comp(*--end, *begin)) /* [a, b], b < a */
|
||||||
|
std::swap(*begin, *end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using ValueType = std::iter_value_t<Iter>;
|
||||||
|
auto workingBuffer = std::vector<ValueType>(length);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sorts [begin, end), assuming that [begin, leftRunEnd) and
|
||||||
|
* [rightRunBegin, end) are sorted.
|
||||||
|
* Modified implementation from:
|
||||||
|
* https://github.com/sebawild/powersort/blob/1d078b6be9023e134c4f8f6de88e2406dc681e89/src/sorts/peeksort.h
|
||||||
|
*/
|
||||||
|
auto peeksortImpl = [&workingBuffer,
|
||||||
|
&comp](auto & peeksortImpl, Iter begin, Iter end, Iter leftRunEnd, Iter rightRunBegin) {
|
||||||
|
if (leftRunEnd == end || rightRunBegin == begin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Dispatch to simpler insertion sort implementation for smaller cases
|
||||||
|
Cut-off limit is the same as in libstdc++
|
||||||
|
https://github.com/gcc-mirror/gcc/blob/d9375e490072d1aae73a93949aa158fcd2a27018/libstdc%2B%2B-v3/include/bits/stl_algo.h#L4977
|
||||||
|
*/
|
||||||
|
static constexpr std::size_t insertionsortThreshold = 16;
|
||||||
|
size_t length = std::distance(begin, end);
|
||||||
|
if (length <= insertionsortThreshold)
|
||||||
|
return insertionsort(begin, end, comp);
|
||||||
|
|
||||||
|
Iter middle = std::next(begin, (length / 2)); /* Middle split between m and m - 1 */
|
||||||
|
|
||||||
|
if (middle <= leftRunEnd) {
|
||||||
|
/* |XXXXXXXX|XX X| */
|
||||||
|
peeksortImpl(peeksortImpl, leftRunEnd, end, std::next(leftRunEnd), rightRunBegin);
|
||||||
|
mergeSortedRunsInPlace(begin, leftRunEnd, end, workingBuffer.begin(), comp);
|
||||||
|
return;
|
||||||
|
} else if (middle >= rightRunBegin) {
|
||||||
|
/* |XX X|XXXXXXXX| */
|
||||||
|
peeksortImpl(peeksortImpl, begin, rightRunBegin, leftRunEnd, std::prev(rightRunBegin));
|
||||||
|
mergeSortedRunsInPlace(begin, rightRunBegin, end, workingBuffer.begin(), comp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find middle run, i.e., run containing m - 1 */
|
||||||
|
Iter i, j;
|
||||||
|
|
||||||
|
if (!comp(*middle, *std::prev(middle)) /* *std::prev(middle) <= *middle */) {
|
||||||
|
i = weaklyIncreasingSuffix(leftRunEnd, middle, comp);
|
||||||
|
j = weaklyIncreasingPrefix(std::prev(middle), rightRunBegin, comp);
|
||||||
|
} else {
|
||||||
|
i = strictlyDecreasingSuffix(leftRunEnd, middle, comp);
|
||||||
|
j = strictlyDecreasingPrefix(std::prev(middle), rightRunBegin, comp);
|
||||||
|
std::reverse(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == begin && j == end)
|
||||||
|
return; /* single run */
|
||||||
|
|
||||||
|
if (middle - i < j - middle) {
|
||||||
|
/* |XX x|xxxx X| */
|
||||||
|
peeksortImpl(peeksortImpl, begin, i, leftRunEnd, std::prev(i));
|
||||||
|
peeksortImpl(peeksortImpl, i, end, j, rightRunBegin);
|
||||||
|
mergeSortedRunsInPlace(begin, i, end, workingBuffer.begin(), comp);
|
||||||
|
} else {
|
||||||
|
/* |XX xxx|x X| */
|
||||||
|
peeksortImpl(peeksortImpl, begin, j, leftRunEnd, i);
|
||||||
|
peeksortImpl(peeksortImpl, j, end, std::next(j), rightRunBegin);
|
||||||
|
mergeSortedRunsInPlace(begin, j, end, workingBuffer.begin(), comp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
peeksortImpl(peeksortImpl, begin, end, /*leftRunEnd=*/begin, /*rightRunBegin=*/end);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,4 @@
|
||||||
|
|
||||||
include_dirs += include_directories('../..')
|
include_dirs += include_directories('../..')
|
||||||
|
|
||||||
headers += files(
|
headers += files('cgroup.hh', 'linux-namespaces.hh')
|
||||||
'cgroup.hh',
|
|
||||||
'linux-namespaces.hh',
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
sources += files(
|
sources += files('cgroup.cc', 'linux-namespaces.cc')
|
||||||
'cgroup.cc',
|
|
||||||
'linux-namespaces.cc',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('include/nix/util')
|
subdir('include/nix/util')
|
||||||
|
|
|
@ -2,8 +2,4 @@
|
||||||
|
|
||||||
include_dirs += include_directories('../..')
|
include_dirs += include_directories('../..')
|
||||||
|
|
||||||
headers += files(
|
headers += files('signals-impl.hh', 'windows-async-pipe.hh', 'windows-error.hh')
|
||||||
'signals-impl.hh',
|
|
||||||
'windows-async-pipe.hh',
|
|
||||||
'windows-error.hh',
|
|
||||||
)
|
|
||||||
|
|
|
@ -387,8 +387,8 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool add = false;
|
bool add = false;
|
||||||
if (v.type() == nFunction && v.payload.lambda.fun->hasFormals()) {
|
if (v.type() == nFunction && v.lambda().fun->hasFormals()) {
|
||||||
for (auto & i : v.payload.lambda.fun->formals->formals) {
|
for (auto & i : v.lambda().fun->formals->formals) {
|
||||||
if (state->symbols[i.name] == "inNixShell") {
|
if (state->symbols[i.name] == "inNixShell") {
|
||||||
add = true;
|
add = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -492,8 +492,8 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (!v.isLambda()) {
|
if (!v.isLambda()) {
|
||||||
throw Error("overlay is not a function, but %s instead", showType(v));
|
throw Error("overlay is not a function, but %s instead", showType(v));
|
||||||
}
|
}
|
||||||
if (v.payload.lambda.fun->hasFormals()
|
if (v.lambda().fun->hasFormals()
|
||||||
|| !argHasName(v.payload.lambda.fun->arg, "final"))
|
|| !argHasName(v.lambda().fun->arg, "final"))
|
||||||
throw Error("overlay does not take an argument named 'final'");
|
throw Error("overlay does not take an argument named 'final'");
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// evaluate the overlay.
|
// evaluate the overlay.
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
# src
|
# src
|
||||||
#---------------------------------------------------
|
#---------------------------------------------------
|
||||||
|
|
||||||
nix_perl_tests = files(
|
nix_perl_tests = files('init.t')
|
||||||
'init.t',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
foreach f : nix_perl_tests
|
foreach f : nix_perl_tests
|
||||||
|
|
|
@ -160,7 +160,7 @@ expect 1 nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --no-update-lock-file
|
||||||
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --commit-lock-file
|
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --commit-lock-file
|
||||||
[[ -e "$flake2Dir/flake.lock" ]]
|
[[ -e "$flake2Dir/flake.lock" ]]
|
||||||
[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]]
|
[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]]
|
||||||
[[ $(jq --indent 0 . < "$flake2Dir/flake.lock") =~ ^'{"nodes":{"flake1":{"locked":{"lastModified":'.*',"narHash":"sha256-'.*'","ref":"refs/heads/master","rev":"'.*'","revCount":2,"type":"git","url":"file:///'.*'"},"original":{"id":"flake1","type":"indirect"}},"root":{"inputs":{"flake1":"flake1"}}},"root":"root","version":7}'$ ]]
|
[[ $(jq --indent 0 --compact-output . < "$flake2Dir/flake.lock") =~ ^'{"nodes":{"flake1":{"locked":{"lastModified":'.*',"narHash":"sha256-'.*'","ref":"refs/heads/master","rev":"'.*'","revCount":2,"type":"git","url":"file:///'.*'"},"original":{"id":"flake1","type":"indirect"}},"root":{"inputs":{"flake1":"flake1"}}},"root":"root","version":7}'$ ]]
|
||||||
|
|
||||||
# Rerunning the build should not change the lockfile.
|
# Rerunning the build should not change the lockfile.
|
||||||
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar"
|
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar"
|
||||||
|
|
|
@ -69,7 +69,7 @@ git -C "$rootFlake" add flake.nix sub2/flake.nix
|
||||||
git -C "$rootFlake" add sub2/flake.lock
|
git -C "$rootFlake" add sub2/flake.lock
|
||||||
[[ $(nix eval "$subflake2#y") = 15 ]]
|
[[ $(nix eval "$subflake2#y") = 15 ]]
|
||||||
|
|
||||||
[[ $(jq --indent 0 . < "$subflake2/flake.lock") =~ ^'{"nodes":{"root":{"inputs":{"root":"root_2","sub1":"sub1"}},"root_2":{"inputs":{"sub0":"sub0"},"locked":{"path":"..","type":"path"},"original":{"path":"..","type":"path"},"parent":[]},"root_3":{"inputs":{"sub0":"sub0_2"},"locked":{"path":"../","type":"path"},"original":{"path":"../","type":"path"},"parent":["sub1"]},"sub0":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["root"]},"sub0_2":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["sub1","root"]},"sub1":{"inputs":{"root":"root_3"},"locked":{"path":"../sub1","type":"path"},"original":{"path":"../sub1","type":"path"},"parent":[]}},"root":"root","version":7}'$ ]]
|
[[ $(jq --indent 0 --compact-output . < "$subflake2/flake.lock") =~ ^'{"nodes":{"root":{"inputs":{"root":"root_2","sub1":"sub1"}},"root_2":{"inputs":{"sub0":"sub0"},"locked":{"path":"..","type":"path"},"original":{"path":"..","type":"path"},"parent":[]},"root_3":{"inputs":{"sub0":"sub0_2"},"locked":{"path":"../","type":"path"},"original":{"path":"../","type":"path"},"parent":["sub1"]},"sub0":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["root"]},"sub0_2":{"locked":{"path":"sub0","type":"path"},"original":{"path":"sub0","type":"path"},"parent":["sub1","root"]},"sub1":{"inputs":{"root":"root_3"},"locked":{"path":"../sub1","type":"path"},"original":{"path":"../sub1","type":"path"},"parent":[]}},"root":"root","version":7}'$ ]]
|
||||||
|
|
||||||
# Make sure there are no content locks for relative path flakes.
|
# Make sure there are no content locks for relative path flakes.
|
||||||
(! grep "$TEST_ROOT" "$subflake2/flake.lock")
|
(! grep "$TEST_ROOT" "$subflake2/flake.lock")
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] [ [ ] [ ] [ 1 ] [ 1 4 ] [ 1 5 ] [ 1 6 ] [ 2 ] [ 2 3 ] [ 3 ] [ 3 ] ] ]
|
[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] [ { key = 1; value = "foo"; } { key = 1; value = "foo2"; } { key = 1; value = "foo3"; } { key = 1; value = "foo4"; } { key = 1; value = "foo5"; } { key = 1; value = "foo6"; } { key = 1; value = "foo7"; } { key = 1; value = "foo8"; } { key = 2; value = "bar"; } { key = 2; value = "bar2"; } { key = 2; value = "bar3"; } { key = 2; value = "bar4"; } { key = 2; value = "bar5"; } { key = 3; value = "baz"; } { key = 3; value = "baz2"; } { key = 3; value = "baz3"; } { key = 3; value = "baz4"; } { key = 4; value = "biz1"; } ] [ [ ] [ ] [ 1 ] [ 1 4 ] [ 1 5 ] [ 1 6 ] [ 2 ] [ 2 3 ] [ 3 ] [ 3 ] ] ]
|
||||||
|
|
|
@ -37,6 +37,80 @@ with builtins;
|
||||||
value = "fnord";
|
value = "fnord";
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
(sort (x: y: x.key < y.key) [
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 2;
|
||||||
|
value = "bar";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo2";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 2;
|
||||||
|
value = "bar2";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 2;
|
||||||
|
value = "bar3";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 2;
|
||||||
|
value = "bar4";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo3";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 3;
|
||||||
|
value = "baz";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 3;
|
||||||
|
value = "baz2";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo4";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 3;
|
||||||
|
value = "baz3";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo5";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo6";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 2;
|
||||||
|
value = "bar5";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 3;
|
||||||
|
value = "baz4";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo7";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 4;
|
||||||
|
value = "biz1";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = 1;
|
||||||
|
value = "foo8";
|
||||||
|
}
|
||||||
|
])
|
||||||
(sort lessThan [
|
(sort lessThan [
|
||||||
[
|
[
|
||||||
1
|
1
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
libplugintest = shared_module(
|
libplugintest = shared_module(
|
||||||
'plugintest',
|
'plugintest',
|
||||||
'plugintest.cc',
|
'plugintest.cc',
|
||||||
dependencies : [
|
dependencies : [ dependency('nix-expr') ],
|
||||||
dependency('nix-expr'),
|
|
||||||
],
|
|
||||||
build_by_default : false,
|
build_by_default : false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
libstoreconsumer_tester = executable(
|
libstoreconsumer_tester = executable(
|
||||||
'test-libstoreconsumer',
|
'test-libstoreconsumer',
|
||||||
'main.cc',
|
'main.cc',
|
||||||
dependencies : [
|
dependencies : [ dependency('nix-store') ],
|
||||||
dependency('nix-store'),
|
|
||||||
],
|
|
||||||
build_by_default : false,
|
build_by_default : false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
# Test the container built by ../../docker.nix.
|
# Test the container built by ../../docker.nix.
|
||||||
|
|
||||||
{
|
{
|
||||||
lib,
|
|
||||||
config,
|
config,
|
||||||
nixpkgs,
|
|
||||||
hostPkgs,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||||
|
|
||||||
nixImage = import ../../docker.nix {
|
nixImage = pkgs.callPackage ../../docker.nix { };
|
||||||
inherit (config.nodes.machine.nixpkgs) pkgs;
|
nixUserImage = pkgs.callPackage ../../docker.nix {
|
||||||
};
|
|
||||||
nixUserImage = import ../../docker.nix {
|
|
||||||
inherit (config.nodes.machine.nixpkgs) pkgs;
|
|
||||||
name = "nix-user";
|
name = "nix-user";
|
||||||
uid = 1000;
|
uid = 1000;
|
||||||
gid = 1000;
|
gid = 1000;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue