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

Merge remote-tracking branch 'upstream/master' into messages-present-tense

This commit is contained in:
Luc Perkins 2025-06-18 08:24:23 -07:00
commit d6710b4c04
No known key found for this signature in database
GPG key ID: C86EE5D85EE4DDA5
43 changed files with 984 additions and 170 deletions

View file

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

View file

@ -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,36 +19,60 @@
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
[ bashInteractive
nix coreutils-full
bashInteractive gnutar
coreutils-full gzip
gnutar gnugrep
gzip which
gnugrep curl
which less
curl wget
less man
wget cacert.out
man findutils
cacert.out iana-etc
findutils gitMinimal
iana-etc openssh
git ] ++ extraPkgs;
openssh
]
++ 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}"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,3 @@
include_dirs += include_directories('../..') include_dirs += include_directories('../..')
headers += files( headers += files('personality.hh')
'personality.hh',
)

View file

@ -1,5 +1,3 @@
sources += files( sources += files('personality.cc')
'personality.cc',
)
subdir('include/nix/store') subdir('include/nix/store')

View file

@ -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"}) {

View file

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

View file

@ -2,6 +2,4 @@
include_dirs += include_directories('../..') include_dirs += include_directories('../..')
headers += files( headers += files('freebsd-jail.hh')
'freebsd-jail.hh',
)

View file

@ -1,5 +1,3 @@
sources += files( sources += files('freebsd-jail.cc')
'freebsd-jail.cc',
)
subdir('include/nix/util') subdir('include/nix/util')

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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