aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/rl-next/inherit-from-by-need.md7
-rw-r--r--flake.nix437
-rw-r--r--package.nix261
-rw-r--r--src/libexpr/eval.cc40
-rw-r--r--src/libexpr/get-drvs.cc28
-rw-r--r--src/libexpr/nixexpr.cc113
-rw-r--r--src/libexpr/nixexpr.hh52
-rw-r--r--src/libexpr/parser-state.hh13
-rw-r--r--src/libexpr/parser.y16
-rw-r--r--src/nix/eval.cc13
-rw-r--r--tests/functional/lang/eval-okay-inherit-from.err.exp1
-rw-r--r--tests/functional/lang/eval-okay-inherit-from.exp1
-rw-r--r--tests/functional/lang/eval-okay-inherit-from.nix16
-rw-r--r--tests/functional/lang/parse-okay-inherits.exp1
-rw-r--r--tests/functional/lang/parse-okay-inherits.nix9
-rw-r--r--tests/functional/lang/parse-okay-subversion.exp2
-rw-r--r--tests/nixos/nix-copy-closure.nix10
-rw-r--r--tests/nixos/nix-copy.nix10
-rw-r--r--tests/nixos/remote-builds-ssh-ng.nix10
-rw-r--r--tests/nixos/remote-builds.nix12
20 files changed, 647 insertions, 405 deletions
diff --git a/doc/manual/rl-next/inherit-from-by-need.md b/doc/manual/rl-next/inherit-from-by-need.md
new file mode 100644
index 000000000..67c2cdedf
--- /dev/null
+++ b/doc/manual/rl-next/inherit-from-by-need.md
@@ -0,0 +1,7 @@
+---
+synopsis: "`inherit (x) ...` evaluates `x` only once"
+prs: 9847
+---
+
+`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
+This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
diff --git a/flake.nix b/flake.nix
index d9e16881f..8fde05179 100644
--- a/flake.nix
+++ b/flake.nix
@@ -48,50 +48,6 @@
})
stdenvs);
- baseFiles =
- # .gitignore has already been processed, so any changes in it are irrelevant
- # at this point. It is not represented verbatim for test purposes because
- # that would interfere with repo semantics.
- fileset.fileFilter (f: f.name != ".gitignore") ./.;
-
- configureFiles = fileset.unions [
- ./.version
- ./configure.ac
- ./m4
- # TODO: do we really need README.md? It doesn't seem used in the build.
- ./README.md
- ];
-
- topLevelBuildFiles = fileset.unions [
- ./local.mk
- ./Makefile
- ./Makefile.config.in
- ./mk
- ];
-
- functionalTestFiles = fileset.unions [
- ./tests/functional
- ./tests/unit
- (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
- ];
-
- nixSrc = fileset.toSource {
- root = ./.;
- fileset = fileset.intersection baseFiles (fileset.unions [
- configureFiles
- topLevelBuildFiles
- ./boehmgc-coroutine-sp-fallback.diff
- ./doc
- ./misc
- ./precompiled-headers.h
- ./src
- ./unit-test-data
- ./COPYING
- ./scripts/local.mk
- functionalTestFiles
- ]);
- };
-
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor = forAllSystems
(system: let
@@ -118,120 +74,6 @@
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
});
- commonDeps =
- { pkgs
- , isStatic ? pkgs.stdenv.hostPlatform.isStatic
- }:
- with pkgs; rec {
- # Use "busybox-sandbox-shell" if present,
- # if not (legacy) fallback and hope it's sufficient.
- sh = pkgs.busybox-sandbox-shell or (busybox.override {
- useMusl = true;
- enableStatic = true;
- enableMinimal = true;
- extraConfig = ''
- CONFIG_FEATURE_FANCY_ECHO y
- CONFIG_FEATURE_SH_MATH y
- CONFIG_FEATURE_SH_MATH_64 y
-
- CONFIG_ASH y
- CONFIG_ASH_OPTIMIZE_FOR_SIZE y
-
- CONFIG_ASH_ALIAS y
- CONFIG_ASH_BASH_COMPAT y
- CONFIG_ASH_CMDCMD y
- CONFIG_ASH_ECHO y
- CONFIG_ASH_GETOPTS y
- CONFIG_ASH_INTERNAL_GLOB y
- CONFIG_ASH_JOB_CONTROL y
- CONFIG_ASH_PRINTF y
- CONFIG_ASH_TEST y
- '';
- });
-
- configureFlags =
- lib.optionals stdenv.isLinux [
- "--with-boost=${boost}/lib"
- "--with-sandbox-shell=${sh}/bin/busybox"
- ]
- ++ lib.optionals (stdenv.isLinux && !(isStatic && stdenv.system == "aarch64-linux")) [
- "LDFLAGS=-fuse-ld=gold"
- ];
-
- testConfigureFlags = [
- "RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
- ];
-
- internalApiDocsConfigureFlags = [
- "--enable-internal-api-docs"
- ];
-
- changelog-d = pkgs.buildPackages.callPackage ./misc/changelog-d.nix { };
-
- nativeBuildDeps =
- [
- buildPackages.bison
- buildPackages.flex
- (lib.getBin buildPackages.lowdown)
- buildPackages.mdbook
- buildPackages.mdbook-linkcheck
- buildPackages.autoconf-archive
- buildPackages.autoreconfHook
- buildPackages.pkg-config
-
- # Tests
- buildPackages.git
- buildPackages.mercurial # FIXME: remove? only needed for tests
- buildPackages.jq # Also for custom mdBook preprocessor.
- ]
- ++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)]
- # Official releases don't have rl-next, so we don't need to compile a changelog
- ++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
- ;
-
- buildDeps =
- [ curl
- bzip2 xz brotli editline
- openssl sqlite
- libarchive
- boost
- lowdown
- libsodium
- ]
- ++ lib.optionals stdenv.isLinux [libseccomp]
- ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
-
- checkDeps = [
- gtest
- rapidcheck
- ];
-
- internalApiDocsDeps = [
- buildPackages.doxygen
- ];
-
- awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
- (aws-sdk-cpp.override {
- apis = ["s3" "transfer"];
- customMemoryManagement = false;
- });
-
- propagatedDeps =
- [ ((boehmgc.override {
- enableLargeConfig = true;
- }).overrideAttrs(o: {
- patches = (o.patches or []) ++ [
- ./boehmgc-coroutine-sp-fallback.diff
-
- # https://github.com/ivmai/bdwgc/pull/586
- ./boehmgc-traceable_allocator-public.diff
- ];
- })
- )
- nlohmann_json
- ];
- };
-
installScriptFor = systems:
with nixpkgsFor.x86_64-linux.native;
runCommand "installer-script"
@@ -266,51 +108,40 @@
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
'';
- testNixVersions = pkgs: client: daemon: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation {
- NIX_DAEMON_PACKAGE = daemon;
- NIX_CLIENT_PACKAGE = client;
- name =
- "nix-tests"
- + optionalString
- (versionAtLeast daemon.version "2.4pre20211005" &&
- versionAtLeast client.version "2.4pre20211005")
+ testNixVersions = pkgs: client: daemon: let
+ nix = pkgs.callPackage ./package.nix {
+ pname =
+ "nix-tests"
+ + lib.optionalString
+ (lib.versionAtLeast daemon.version "2.4pre20211005" &&
+ lib.versionAtLeast client.version "2.4pre20211005")
"-${client.version}-against-${daemon.version}";
- inherit version;
-
- src = fileset.toSource {
- root = ./.;
- fileset = fileset.intersection baseFiles (fileset.unions [
- configureFiles
- topLevelBuildFiles
- functionalTestFiles
- ]);
- };
- VERSION_SUFFIX = versionSuffix;
-
- nativeBuildInputs = nativeBuildDeps;
- buildInputs = buildDeps ++ awsDeps ++ checkDeps;
- propagatedBuildInputs = propagatedDeps;
-
- enableParallelBuilding = true;
+ inherit fileset;
+ };
+ in nix.overrideAttrs (prevAttrs: {
+ NIX_DAEMON_PACKAGE = daemon;
+ NIX_CLIENT_PACKAGE = client;
- configureFlags =
- testConfigureFlags # otherwise configure fails
- ++ [ "--disable-build" ];
dontBuild = true;
doInstallCheck = true;
+ configureFlags = prevAttrs.configureFlags ++ [
+ # We don't need the actual build here.
+ "--disable-build"
+ ];
+
installPhase = ''
mkdir -p $out
'';
- installCheckPhase = (optionalString pkgs.stdenv.hostPlatform.isDarwin ''
+ installCheckPhase = lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
- '') + ''
+ '' + ''
mkdir -p src/nix-channel
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
'';
- };
+ });
binaryTarball = nix: pkgs:
let
@@ -387,109 +218,60 @@
'';
overlayFor = getStdenv: final: prev:
- let currentStdenv = getStdenv final; in
- {
- nixStable = prev.nix;
-
- nix =
- with final;
- with commonDeps {
+ let
+ currentStdenv = getStdenv final;
+ comDeps = with final; commonDeps {
inherit pkgs;
inherit (currentStdenv.hostPlatform) isStatic;
};
- let
- canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
- in currentStdenv.mkDerivation (finalAttrs: {
- name = "nix-${version}";
- inherit version;
-
- src = nixSrc;
- VERSION_SUFFIX = versionSuffix;
-
- outputs = [ "out" "dev" "doc" ];
-
- nativeBuildInputs = nativeBuildDeps;
- buildInputs = buildDeps
- # There have been issues building these dependencies
- ++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps
- ++ lib.optionals finalAttrs.doCheck checkDeps;
-
- propagatedBuildInputs = propagatedDeps;
-
- disallowedReferences = [ boost ];
-
- preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
- ''
- # Copy libboost_context so we don't get all of Boost in our closure.
- # https://github.com/NixOS/nixpkgs/issues/45462
- mkdir -p $out/lib
- cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
- rm -f $out/lib/*.a
- ${lib.optionalString currentStdenv.hostPlatform.isLinux ''
- chmod u+w $out/lib/*.so.*
- patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
- ''}
- ${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
- for LIB in $out/lib/*.dylib; do
- chmod u+w $LIB
- install_name_tool -id $LIB $LIB
- install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
- done
- install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
- ''}
- '';
-
- configureFlags = configureFlags ++
- [ "--sysconfdir=/etc" ] ++
- lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
- [ (lib.enableFeature finalAttrs.doCheck "tests") ] ++
- lib.optionals finalAttrs.doCheck testConfigureFlags ++
- lib.optional (!canRunInstalled) "--disable-doc-gen";
-
- enableParallelBuilding = true;
-
- makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
-
- doCheck = true;
+ in {
+ nixStable = prev.nix;
- installFlags = "sysconfdir=$(out)/etc";
+ # Forward from the previous stage as we don’t want it to pick the lowdown override
+ nixUnstable = prev.nixUnstable;
- postInstall = ''
- mkdir -p $doc/nix-support
- echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
- ${lib.optionalString currentStdenv.hostPlatform.isStatic ''
- mkdir -p $out/nix-support
- echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
- ''}
- ${lib.optionalString currentStdenv.isDarwin ''
- install_name_tool \
- -change ${boost}/lib/libboost_context.dylib \
- $out/lib/libboost_context.dylib \
- $out/lib/libnixutil.dylib
- ''}
- '';
+ changelog-d = final.buildPackages.callPackage ./misc/changelog-d.nix { };
+ boehmgc-nix = (final.boehmgc.override {
+ enableLargeConfig = true;
+ }).overrideAttrs (o: {
+ patches = (o.patches or [ ]) ++ [
+ ./boehmgc-coroutine-sp-fallback.diff
- doInstallCheck = finalAttrs.doCheck;
- installCheckFlags = "sysconfdir=$(out)/etc";
- installCheckTarget = "installcheck"; # work around buggy detection in stdenv
+ # https://github.com/ivmai/bdwgc/pull/586
+ ./boehmgc-traceable_allocator-public.diff
+ ];
+ });
- preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
- export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
+ default-busybox-sandbox-shell = final.busybox.override {
+ useMusl = true;
+ enableStatic = true;
+ enableMinimal = true;
+ extraConfig = ''
+ CONFIG_FEATURE_FANCY_ECHO y
+ CONFIG_FEATURE_SH_MATH y
+ CONFIG_FEATURE_SH_MATH_64 y
+
+ CONFIG_ASH y
+ CONFIG_ASH_OPTIMIZE_FOR_SIZE y
+
+ CONFIG_ASH_ALIAS y
+ CONFIG_ASH_BASH_COMPAT y
+ CONFIG_ASH_CMDCMD y
+ CONFIG_ASH_ECHO y
+ CONFIG_ASH_GETOPTS y
+ CONFIG_ASH_INTERNAL_GLOB y
+ CONFIG_ASH_JOB_CONTROL y
+ CONFIG_ASH_PRINTF y
+ CONFIG_ASH_TEST y
'';
+ };
- separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
-
- strictDeps = true;
-
- hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
-
- passthru.perl-bindings = final.callPackage ./perl {
- inherit fileset;
- stdenv = currentStdenv;
- };
-
- meta.platforms = lib.platforms.unix;
- });
+ nix = final.callPackage ./package.nix {
+ inherit versionSuffix fileset;
+ stdenv = currentStdenv;
+ boehmgc = final.boehmgc-nix;
+ busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
+ };
};
in {
@@ -514,31 +296,25 @@
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
# API docs for Nix's unstable internal C++ interfaces.
- internal-api-docs =
- with nixpkgsFor.x86_64-linux.native;
- with commonDeps { inherit pkgs; };
-
- stdenv.mkDerivation {
- pname = "nix-internal-api-docs";
- inherit version;
-
- src = nixSrc;
-
- configureFlags = testConfigureFlags ++ internalApiDocsConfigureFlags;
-
- nativeBuildInputs = nativeBuildDeps;
- buildInputs = buildDeps ++ propagatedDeps
- ++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
-
- dontBuild = true;
-
- installTargets = [ "internal-api-html" ];
-
- postInstall = ''
- mkdir -p $out/nix-support
- echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> $out/nix-support/hydra-build-products
- '';
+ internal-api-docs = let
+ nixpkgs = nixpkgsFor.x86_64-linux.native;
+ inherit (nixpkgs) pkgs;
+
+ nix = pkgs.callPackage ./package.nix {
+ inherit versionSuffix fileset officialRelease buildUnreleasedNotes;
+ inherit (pkgs) changelog-d;
+ internalApiDocs = true;
+ boehmgc = pkgs.boehmgc-nix;
+ busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
};
+ in
+ nix.overrideAttrs (prev: {
+ # This Hydra job is just for the internal API docs.
+ # We don't need the build artifacts here.
+ dontBuild = true;
+ doCheck = false;
+ doInstallCheck = false;
+ });
# System tests.
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
@@ -552,7 +328,7 @@
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
- [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
+ [[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
mkdir $out
'';
@@ -588,7 +364,7 @@
rl-next =
let pkgs = nixpkgsFor.${system}.native;
in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } ''
- LANG=C.UTF-8 ${(commonDeps { inherit pkgs; }).changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
+ LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
'';
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
@@ -629,36 +405,25 @@
devShells = let
makeShell = pkgs: stdenv:
let
- canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
+ nix = pkgs.callPackage ./package.nix {
+ inherit stdenv versionSuffix fileset;
+ boehmgc = pkgs.boehmgc-nix;
+ busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
+ };
in
- with commonDeps { inherit pkgs; };
- stdenv.mkDerivation {
- name = "nix";
-
- outputs = [ "out" "dev" "doc" ];
+ nix.overrideAttrs (prev: {
+ nativeBuildInputs = prev.nativeBuildInputs
+ ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
+ ++ lib.optional
+ (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
+ pkgs.buildPackages.clang-tools;
- nativeBuildInputs = nativeBuildDeps
- ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
- ++ lib.optional
- (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
- pkgs.buildPackages.clang-tools
- # We want changelog-d in the shell even if the current build doesn't need it
- ++ lib.optional (officialRelease || ! buildUnreleasedNotes) changelog-d
- ;
+ src = null;
- buildInputs = buildDeps ++ propagatedDeps
- ++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
+ installFlags = "sysconfdir=$(out)/etc";
+ strictDeps = false;
- configureFlags = configureFlags
- ++ testConfigureFlags ++ internalApiDocsConfigureFlags
- ++ lib.optional (!canRunInstalled) "--disable-doc-gen";
-
- enableParallelBuilding = true;
-
- installFlags = "sysconfdir=$(out)/etc";
-
- shellHook =
- ''
+ shellHook = ''
PATH=$prefix/bin:$PATH
unset PYTHONPATH
export MANPATH=$out/share/man:$MANPATH
@@ -666,7 +431,7 @@
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
'';
- };
+ });
in
forAllSystems (system:
let
diff --git a/package.nix b/package.nix
new file mode 100644
index 000000000..06d644627
--- /dev/null
+++ b/package.nix
@@ -0,0 +1,261 @@
+{
+ pkgs,
+ lib,
+ stdenv,
+ autoconf-archive,
+ autoreconfHook,
+ aws-sdk-cpp,
+ boehmgc,
+ nlohmann_json,
+ bison,
+ changelog-d,
+ boost,
+ brotli,
+ bzip2,
+ curl,
+ doxygen,
+ editline,
+ fileset,
+ flex,
+ git,
+ gtest,
+ jq,
+ libarchive,
+ libcpuid,
+ libseccomp,
+ libsodium,
+ lowdown,
+ mdbook,
+ mdbook-linkcheck,
+ mercurial,
+ openssl,
+ pkg-config,
+ rapidcheck,
+ sqlite,
+ util-linuxMinimal ? utillinuxMinimal,
+ utillinuxMinimal ? null,
+ xz,
+
+ busybox-sandbox-shell,
+
+ pname ? "nix",
+ versionSuffix ? "",
+ officialRelease ? true,
+ # Set to true to build the release notes for the next release.
+ buildUnreleasedNotes ? false,
+ internalApiDocs ? false,
+
+ # Not a real argument, just the only way to approximate let-binding some
+ # stuff for argument defaults.
+ __forDefaults ? {
+ canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
+ },
+}: let
+ inherit (__forDefaults) canRunInstalled;
+
+ version = lib.fileContents ./.version + versionSuffix;
+
+ aws-sdk-cpp-nix = aws-sdk-cpp.override {
+ apis = [ "s3" "transfer" ];
+ customMemoryManagement = false;
+ };
+
+ testConfigureFlags = [
+ "RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
+ ];
+
+ # The internal API docs need these for the build, but if we're not building
+ # Nix itself, then these don't need to be propagated.
+ maybePropagatedInputs = [
+ boehmgc
+ nlohmann_json
+ ];
+
+ # .gitignore has already been processed, so any changes in it are irrelevant
+ # at this point. It is not represented verbatim for test purposes because
+ # that would interfere with repo semantics.
+ baseFiles = fileset.fileFilter (f: f.name != ".gitignore") ./.;
+
+ configureFiles = fileset.unions [
+ ./.version
+ ./configure.ac
+ ./m4
+ # TODO: do we really need README.md? It doesn't seem used in the build.
+ ./README.md
+ ];
+
+ topLevelBuildFiles = fileset.unions [
+ ./local.mk
+ ./Makefile
+ ./Makefile.config.in
+ ./mk
+ ];
+
+ functionalTestFiles = fileset.unions [
+ ./tests/functional
+ ./tests/unit
+ (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
+ ];
+
+in stdenv.mkDerivation (finalAttrs: {
+ inherit pname version;
+
+ src = fileset.toSource {
+ root = ./.;
+ fileset = fileset.intersection baseFiles (fileset.unions ([
+ configureFiles
+ topLevelBuildFiles
+ functionalTestFiles
+ ./unit-test-data
+ ] ++ lib.optionals (!finalAttrs.dontBuild || internalApiDocs) [
+ ./boehmgc-coroutine-sp-fallback.diff
+ ./doc
+ ./misc
+ ./precompiled-headers.h
+ ./src
+ ./COPYING
+ ./scripts/local.mk
+ ]));
+ };
+
+ VERSION_SUFFIX = versionSuffix;
+
+ outputs = [ "out" ]
+ ++ lib.optionals (!finalAttrs.dontBuild) [ "dev" "doc" ];
+
+ dontBuild = false;
+
+ nativeBuildInputs = [
+ bison
+ flex
+ ] ++ [
+ (lib.getBin lowdown)
+ mdbook
+ mdbook-linkcheck
+ autoconf-archive
+ autoreconfHook
+ pkg-config
+
+ # Tests
+ git
+ mercurial
+ jq
+ ] ++ lib.optional stdenv.hostPlatform.isLinux util-linuxMinimal
+ ++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
+ ++ lib.optional internalApiDocs doxygen
+ ;
+
+ buildInputs = [
+ curl
+ bzip2
+ xz
+ brotli
+ editline
+ openssl
+ sqlite
+ libarchive
+ boost
+ lowdown
+ libsodium
+ ]
+ ++ lib.optionals stdenv.isLinux [ libseccomp ]
+ ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
+ # There have been issues building these dependencies
+ ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) aws-sdk-cpp-nix
+ ++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs
+ ;
+
+ checkInputs = [
+ gtest
+ rapidcheck
+ ];
+
+ propagatedBuildInputs = lib.optionals (!finalAttrs.dontBuild) maybePropagatedInputs;
+
+ disallowedReferences = [
+ boost
+ ];
+
+ preConfigure = lib.optionalString (!finalAttrs.dontBuild && !stdenv.hostPlatform.isStatic) ''
+ # Copy libboost_context so we don't get all of Boost in our closure.
+ # https://github.com/NixOS/nixpkgs/issues/45462
+ mkdir -p $out/lib
+ cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
+ rm -f $out/lib/*.a
+ '' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isLinux) ''
+ chmod u+w $out/lib/*.so.*
+ patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
+ '' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isDarwin) ''
+ for LIB in $out/lib/*.dylib; do
+ chmod u+w $LIB
+ install_name_tool -id $LIB $LIB
+ install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
+ done
+ install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
+ '' + ''
+ # Workaround https://github.com/NixOS/nixpkgs/issues/294890.
+ if [[ -n "''${doCheck:-}" ]]; then
+ appendToVar configureFlags "--enable-tests"
+ else
+ appendToVar configureFlags "--disable-tests"
+ fi
+ '';
+
+ configureFlags = lib.optionals stdenv.isLinux [
+ "--with-boost=${boost}/lib"
+ "--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
+ ] ++ lib.optionals (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) [
+ "LDFLAGS=-fuse-ld=gold"
+ ] ++ [ "--sysconfdir=/etc" ]
+ ++ lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell"
+ ++ lib.optionals (finalAttrs.doCheck || internalApiDocs) testConfigureFlags
+ ++ lib.optional (!canRunInstalled) "--disable-doc-gen"
+ ++ [ (lib.enableFeature internalApiDocs "internal-api-docs") ]
+ ;
+
+ installTargets = lib.optional internalApiDocs "internal-api-html";
+
+ enableParallelBuilding = true;
+
+ makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
+
+ doCheck = true;
+
+ installFlags = "sysconfdir=$(out)/etc";
+
+ postInstall = lib.optionalString (!finalAttrs.dontBuild) ''
+ mkdir -p $doc/nix-support
+ echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
+ '' + lib.optionalString stdenv.hostPlatform.isStatic ''
+ mkdir -p $out/nix-support
+ echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
+ '' + lib.optionalString stdenv.isDarwin ''
+ install_name_tool \
+ -change ${boost}/lib/libboost_context.dylib \
+ $out/lib/libboost_context.dylib \
+ $out/lib/libnixutil.dylib
+ '' + lib.optionalString internalApiDocs ''
+ mkdir -p $out/nix-support
+ echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> "$out/nix-support/hydra-build-products"
+ '';
+
+ doInstallCheck = finalAttrs.doCheck;
+ installCheckFlags = "sysconfdir=$(out)/etc";
+ installCheckTarget = "installcheck"; # work around buggy detection in stdenv
+
+ preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
+ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
+ '';
+
+ separateDebugInfo = !stdenv.hostPlatform.isStatic && !finalAttrs.dontBuild;
+
+ strictDeps = true;
+
+ hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
+
+ meta.platforms = lib.platforms.unix;
+
+ passthru.perl-bindings = pkgs.callPackage ./perl {
+ inherit fileset stdenv;
+ };
+})
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index bb3e6f3bd..1739a04fa 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1217,6 +1217,18 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
}
+Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
+{
+ Env & inheritEnv = state.allocEnv(inheritFromExprs->size());
+ inheritEnv.up = &up;
+
+ Displacement displ = 0;
+ for (auto from : *inheritFromExprs)
+ inheritEnv.values[displ++] = from->maybeThunk(state, up);
+
+ return &inheritEnv;
+}
+
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
@@ -1228,6 +1240,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Env & env2(state.allocEnv(attrs.size()));
env2.up = &env;
dynamicEnv = &env2;
+ Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr;
AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end();
@@ -1238,11 +1251,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Displacement displ = 0;
for (auto & i : attrs) {
Value * vAttr;
- if (hasOverrides && !i.second.inherited) {
+ if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
vAttr = state.allocValue();
- mkThunk(*vAttr, env2, i.second.e);
+ mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), i.second.e);
} else
- vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
+ vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
env2.values[displ++] = vAttr;
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
}
@@ -1274,9 +1287,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
}
}
- else
- for (auto & i : attrs)
- v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
+ else {
+ Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
+ for (auto & i : attrs) {
+ v.attrs->push_back(Attr(
+ i.first,
+ i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
+ i.second.pos));
+ }
+ }
/* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) {
@@ -1308,12 +1327,17 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
Env & env2(state.allocEnv(attrs->attrs.size()));
env2.up = &env;
+ Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr;
+
/* The recursive attributes are evaluated in the new environment,
while the inherited attributes are evaluated in the original
environment. */
Displacement displ = 0;
- for (auto & i : attrs->attrs)
- env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
+ for (auto & i : attrs->attrs) {
+ env2.values[displ++] = i.second.e->maybeThunk(
+ state,
+ *i.second.chooseByKind(&env2, &env, inheritEnv));
+ }
auto dts = state.debugRepl
? makeDebugTraceStacker(
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 495407c39..e686ffe8c 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -296,22 +296,16 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
typedef std::set<Bindings *> Done;
-/* Evaluate value `v'. If it evaluates to a set of type `derivation',
- then put information about it in `drvs' (unless it's already in `done').
- The result boolean indicates whether it makes sense
+/* The result boolean indicates whether it makes sense
for the caller to recursively search for derivations in `v'. */
static bool getDerivation(EvalState & state, Value & v,
- const std::string & attrPath, DrvInfos & drvs, Done & done,
+ const std::string & attrPath, DrvInfos & drvs,
bool ignoreAssertionFailures)
{
try {
state.forceValue(v, v.determinePos(noPos));
if (!state.isDerivation(v)) return true;
- /* Remove spurious duplicates (e.g., a set like `rec { x =
- derivation {...}; y = x;}'. */
- if (!done.insert(v.attrs).second) return false;
-
DrvInfo drv(state, attrPath, v.attrs);
drv.queryName();
@@ -330,9 +324,8 @@ static bool getDerivation(EvalState & state, Value & v,
std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
bool ignoreAssertionFailures)
{
- Done done;
DrvInfos drvs;
- getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
+ getDerivation(state, v, "", drvs, ignoreAssertionFailures);
if (drvs.size() != 1) return {};
return std::move(drvs.front());
}
@@ -347,6 +340,9 @@ static std::string addToPath(const std::string & s1, const std::string & s2)
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
+/* Evaluate value `v'. If it evaluates to a set of type `derivation',
+ then put information about it in `drvs'. If it evaluates to a different
+ kind of set recurse (unless it's already in `done'). */
static void getDerivations(EvalState & state, Value & vIn,
const std::string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
@@ -356,10 +352,14 @@ static void getDerivations(EvalState & state, Value & vIn,
state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */
- if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
+ if (!getDerivation(state, v, pathPrefix, drvs, ignoreAssertionFailures)) ;
else if (v.type() == nAttrs) {
+ /* Dont consider sets we've already seen, e.g. y in
+ `rec { x.d = derivation {...}; y = x; }`. */
+ if (!done.insert(v.attrs).second) return;
+
/* !!! undocumented hackery to support combining channels in
nix-env.cc. */
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
@@ -376,7 +376,7 @@ static void getDerivations(EvalState & state, Value & vIn,
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
- else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
+ else if (getDerivation(state, *i->value, pathPrefix2, drvs, ignoreAssertionFailures)) {
/* If the value of this attribute is itself a set,
should we recurse into it? => Only if it has a
`recurseForDerivations = true' attribute. */
@@ -390,9 +390,11 @@ static void getDerivations(EvalState & state, Value & vIn,
}
else if (v.type() == nList) {
+ // NOTE we can't really deduplicate here because small lists don't have stable addresses
+ // and can cause spurious duplicate detections due to v being on the stack.
for (auto [n, elem] : enumerate(v.listItems())) {
std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
- if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
+ if (getDerivation(state, *elem, pathPrefix2, drvs, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 3525ea1a3..3bcd35fef 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -68,10 +68,8 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
}
-void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
+void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
{
- if (recursive) str << "rec ";
- str << "{ ";
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i);
@@ -79,10 +77,37 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
std::string_view sa = symbols[a->first], sb = symbols[b->first];
return sa < sb;
});
+ std::vector<Symbol> inherits;
+ std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
for (auto & i : sorted) {
- if (i->second.inherited)
- str << "inherit " << symbols[i->first] << " " << "; ";
- else {
+ switch (i->second.kind) {
+ case AttrDef::Kind::Plain:
+ break;
+ case AttrDef::Kind::Inherited:
+ inherits.push_back(i->first);
+ break;
+ case AttrDef::Kind::InheritedFrom: {
+ auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
+ auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
+ inheritsFrom[&from].push_back(i->first);
+ break;
+ }
+ }
+ }
+ if (!inherits.empty()) {
+ str << "inherit";
+ for (auto sym : inherits) str << " " << symbols[sym];
+ str << "; ";
+ }
+ for (const auto & [from, syms] : inheritsFrom) {
+ str << "inherit (";
+ (*inheritFromExprs)[from->displ]->show(symbols, str);
+ str << ")";
+ for (auto sym : syms) str << " " << symbols[sym];
+ str << "; ";
+ }
+ for (auto & i : sorted) {
+ if (i->second.kind == AttrDef::Kind::Plain) {
str << symbols[i->first] << " = ";
i->second.e->show(symbols, str);
str << "; ";
@@ -95,6 +120,13 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
i.valueExpr->show(symbols, str);
str << "; ";
}
+}
+
+void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
+{
+ if (recursive) str << "rec ";
+ str << "{ ";
+ showBindings(symbols, str);
str << "}";
}
@@ -150,15 +182,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(let ";
- for (auto & i : attrs->attrs)
- if (i.second.inherited) {
- str << "inherit " << symbols[i.first] << "; ";
- }
- else {
- str << symbols[i.first] << " = ";
- i.second.e->show(symbols, str);
- str << "; ";
- }
+ attrs->showBindings(symbols, str);
str << "in ";
body->show(symbols, str);
str << ")";
@@ -303,6 +327,12 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
this->level = withLevel;
}
+void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
+{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+}
+
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
@@ -326,22 +356,47 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
i.expr->bindVars(es, env);
}
+std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
+ EvalState & es, const std::shared_ptr<const StaticEnv> & env)
+{
+ if (!inheritFromExprs)
+ return nullptr;
+
+ // the inherit (from) source values are inserted into an env of its own, which
+ // does not introduce any variable names.
+ // analysis must see an empty env, or an env that contains only entries with
+ // otherwise unused names to not interfere with regular names. the parser
+ // has already filled all exprs that access this env with appropriate level
+ // and displacement, and nothing else is allowed to access it. ideally we'd
+ // not even *have* an expr that grabs anything from this env since it's fully
+ // invisible, but the evaluator does not allow for this yet.
+ auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
+ for (auto from : *inheritFromExprs)
+ from->bindVars(es, env);
+
+ return inner;
+}
+
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) {
- auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), recursive ? attrs.size() : 0);
+ auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
+ auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
- Displacement displ = 0;
- for (auto & i : attrs)
- newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
+ Displacement displ = 0;
+ for (auto & i : attrs)
+ newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
+ return newEnv;
+ }();
// No need to sort newEnv since attrs is in sorted order.
+ auto inheritFromEnv = bindInheritSources(es, newEnv);
for (auto & i : attrs)
- i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, newEnv);
@@ -349,8 +404,10 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
}
}
else {
+ auto inheritFromEnv = bindInheritSources(es, env);
+
for (auto & i : attrs)
- i.second.e->bindVars(es, env);
+ i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, env);
@@ -407,16 +464,20 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
- auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
+ auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
+ auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
- Displacement displ = 0;
- for (auto & i : attrs->attrs)
- newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
+ Displacement displ = 0;
+ for (auto & i : attrs->attrs)
+ newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
+ return newEnv;
+ }();
// No need to sort newEnv since attrs->attrs is in sorted order.
+ auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
for (auto & i : attrs->attrs)
- i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, newEnv));
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 6f34afaa7..f0265a896 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -129,6 +129,23 @@ struct ExprVar : Expr
COMMON_METHODS
};
+/**
+ * A pseudo-expression for the purpose of evaluating the `from` expression in `inherit (from)` syntax.
+ * Unlike normal variable references, the displacement is set during parsing, and always refers to
+ * `ExprAttrs::inheritFromExprs` (by itself or in `ExprLet`), whose values are put into their own `Env`.
+ */
+struct ExprInheritFrom : ExprVar
+{
+ ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
+ {
+ this->level = 0;
+ this->displ = displ;
+ this->fromWith = nullptr;
+ }
+
+ void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
+};
+
struct ExprSelect : Expr
{
PosIdx pos;
@@ -154,16 +171,40 @@ struct ExprAttrs : Expr
bool recursive;
PosIdx pos;
struct AttrDef {
- bool inherited;
+ enum class Kind {
+ /** `attr = expr;` */
+ Plain,
+ /** `inherit attr1 attrn;` */
+ Inherited,
+ /** `inherit (expr) attr1 attrn;` */
+ InheritedFrom,
+ };
+
+ Kind kind;
Expr * e;
PosIdx pos;
Displacement displ; // displacement
- AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
- : inherited(inherited), e(e), pos(pos) { };
+ AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
+ : kind(kind), e(e), pos(pos) { };
AttrDef() { };
+
+ template<typename T>
+ const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
+ {
+ switch (kind) {
+ case Kind::Plain:
+ return plain;
+ case Kind::Inherited:
+ return inherited;
+ default:
+ case Kind::InheritedFrom:
+ return inheritedFrom;
+ }
+ }
};
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
+ std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef {
Expr * nameExpr, * valueExpr;
PosIdx pos;
@@ -176,6 +217,11 @@ struct ExprAttrs : Expr
ExprAttrs() : recursive(false) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
+
+ std::shared_ptr<const StaticEnv> bindInheritSources(
+ EvalState & es, const std::shared_ptr<const StaticEnv> & env);
+ Env * buildInheritFromEnv(EvalState & state, Env & up);
+ void showBindings(const SymbolTable & symbols, std::ostream & str) const;
};
struct ExprList : Expr
diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh
index acadb676d..a83d8c8b2 100644
--- a/src/libexpr/parser-state.hh
+++ b/src/libexpr/parser-state.hh
@@ -88,7 +88,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
- if (!j->second.inherited) {
+ if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
attrs = attrs2;
@@ -117,13 +117,24 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
auto ae = dynamic_cast<ExprAttrs *>(e);
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
if (jAttrs && ae) {
+ if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
+ jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
+ if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
+ auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
+ auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
+ from.displ += jAttrs->inheritFromExprs->size();
+ }
}
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
+ if (ae->inheritFromExprs) {
+ jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
+ ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
+ }
} else {
dupAttr(attrPath, pos, j->second.pos);
}
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 447e4d61a..ee44a6d7a 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -311,17 +311,27 @@ binds
if ($$->attrs.find(i.symbol) != $$->attrs.end())
state->dupAttr(i.symbol, state->at(@3), $$->attrs[i.symbol].pos);
auto pos = state->at(@3);
- $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
+ $$->attrs.emplace(
+ i.symbol,
+ ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, ExprAttrs::AttrDef::Kind::Inherited));
}
delete $3;
}
| binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1;
- /* !!! Should ensure sharing of the expression in $4. */
+ if (!$$->inheritFromExprs)
+ $$->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
+ $$->inheritFromExprs->push_back($4);
+ auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
for (auto & i : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos);
- $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), state->at(@6)));
+ $$->attrs.emplace(
+ i.symbol,
+ ExprAttrs::AttrDef(
+ new ExprSelect(CUR_POS, from, i.symbol),
+ state->at(@6),
+ ExprAttrs::AttrDef::Kind::InheritedFrom));
}
delete $6;
}
diff --git a/src/nix/eval.cc b/src/nix/eval.cc
index 2698a6de8..a9e4c8968 100644
--- a/src/nix/eval.cc
+++ b/src/nix/eval.cc
@@ -120,8 +120,17 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
}
else {
- state->forceValueDeep(*v);
- logger->cout("%s", ValuePrinter(*state, *v, PrintOptions { .force = true }));
+ logger->cout(
+ "%s",
+ ValuePrinter(
+ *state,
+ *v,
+ PrintOptions {
+ .force = true,
+ .derivationPaths = true
+ }
+ )
+ );
}
}
};
diff --git a/tests/functional/lang/eval-okay-inherit-from.err.exp b/tests/functional/lang/eval-okay-inherit-from.err.exp
new file mode 100644
index 000000000..3227501f2
--- /dev/null
+++ b/tests/functional/lang/eval-okay-inherit-from.err.exp
@@ -0,0 +1 @@
+trace: used
diff --git a/tests/functional/lang/eval-okay-inherit-from.exp b/tests/functional/lang/eval-okay-inherit-from.exp
new file mode 100644
index 000000000..024daff6b
--- /dev/null
+++ b/tests/functional/lang/eval-okay-inherit-from.exp
@@ -0,0 +1 @@
+[ 1 2 { __overrides = { y = { d = [ ]; }; }; c = [ ]; d = 4; x = { c = [ ]; }; y = «repeated»; } { inner = { c = 3; d = 4; }; } ]
diff --git a/tests/functional/lang/eval-okay-inherit-from.nix b/tests/functional/lang/eval-okay-inherit-from.nix
new file mode 100644
index 000000000..b72a1c639
--- /dev/null
+++ b/tests/functional/lang/eval-okay-inherit-from.nix
@@ -0,0 +1,16 @@
+let
+ inherit (builtins.trace "used" { a = 1; b = 2; }) a b;
+ x.c = 3;
+ y.d = 4;
+
+ merged = {
+ inner = {
+ inherit (y) d;
+ };
+
+ inner = {
+ inherit (x) c;
+ };
+ };
+in
+ [ a b rec { x.c = []; inherit (x) c; inherit (y) d; __overrides.y.d = []; } merged ]
diff --git a/tests/functional/lang/parse-okay-inherits.exp b/tests/functional/lang/parse-okay-inherits.exp
new file mode 100644
index 000000000..1355527e6
--- /dev/null
+++ b/tests/functional/lang/parse-okay-inherits.exp
@@ -0,0 +1 @@
+(let b = 2; c = { }; in { inherit b; inherit (c) d e; a = 1; f = 3; })
diff --git a/tests/functional/lang/parse-okay-inherits.nix b/tests/functional/lang/parse-okay-inherits.nix
new file mode 100644
index 000000000..10596c8ad
--- /dev/null
+++ b/tests/functional/lang/parse-okay-inherits.nix
@@ -0,0 +1,9 @@
+let
+ c = {};
+ b = 2;
+in {
+ a = 1;
+ inherit b;
+ inherit (c) d e;
+ f = 3;
+}
diff --git a/tests/functional/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp
index 4168ee8bf..2303932c4 100644
--- a/tests/functional/lang/parse-okay-subversion.exp
+++ b/tests/functional/lang/parse-okay-subversion.exp
@@ -1 +1 @@
-({ fetchurl, localServer ? false, httpServer ? false, sslSupport ? false, pythonBindings ? false, javaSwigBindings ? false, javahlBindings ? false, stdenv, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { builder = /foo/bar; db4 = (if localServer then db4 else null); inherit expat ; inherit httpServer ; httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); inherit javaSwigBindings ; inherit javahlBindings ; inherit localServer ; name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); inherit pythonBindings ; src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); inherit sslSupport ; swig = (if (pythonBindings || javaSwigBindings) then swig else null); }))
+({ fetchurl, localServer ? false, httpServer ? false, sslSupport ? false, pythonBindings ? false, javaSwigBindings ? false, javahlBindings ? false, stdenv, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); }))
diff --git a/tests/nixos/nix-copy-closure.nix b/tests/nixos/nix-copy-closure.nix
index 66cbfb033..cd25fbbd0 100644
--- a/tests/nixos/nix-copy-closure.nix
+++ b/tests/nixos/nix-copy-closure.nix
@@ -40,6 +40,11 @@ in {
"${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
], capture_output=True, check=True)
+ server.succeed("systemctl start network-online.target")
+ client.succeed("systemctl start network-online.target")
+ server.wait_for_unit("network-online.target")
+ client.wait_for_unit("network-online.target")
+
client.succeed("mkdir -m 700 /root/.ssh")
client.copy_from_host("key", "/root/.ssh/id_ed25519")
client.succeed("chmod 600 /root/.ssh/id_ed25519")
@@ -47,9 +52,8 @@ in {
# Install the SSH key on the server.
server.succeed("mkdir -m 700 /root/.ssh")
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
- server.wait_for_unit("sshd")
- client.wait_for_unit("network.target")
- client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'")
+ server.wait_for_unit("sshd.service")
+ client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world' >&2")
# Copy the closure of package A from the client to the server.
server.fail("nix-store --check-validity ${pkgA}")
diff --git a/tests/nixos/nix-copy.nix b/tests/nixos/nix-copy.nix
index 7db5197aa..3bcc7a988 100644
--- a/tests/nixos/nix-copy.nix
+++ b/tests/nixos/nix-copy.nix
@@ -54,8 +54,12 @@ in {
start_all()
- server.wait_for_unit("sshd")
- client.wait_for_unit("network.target")
+ server.succeed("systemctl start network-online.target")
+ client.succeed("systemctl start network-online.target")
+ server.wait_for_unit("network-online.target")
+ client.wait_for_unit("network-online.target")
+
+ server.wait_for_unit("sshd.service")
client.wait_for_unit("getty@tty1.service")
# Either the prompt: ]#
# or an OCR misreading of it: 1#
@@ -82,7 +86,7 @@ in {
# Install the SSH key on the server.
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
server.succeed("systemctl restart sshd")
- client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'")
+ client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world' >&2")
client.succeed(f"ssh -O check {server.name}")
client.succeed(f"ssh -O exit {server.name}")
client.fail(f"ssh -O check {server.name}")
diff --git a/tests/nixos/remote-builds-ssh-ng.nix b/tests/nixos/remote-builds-ssh-ng.nix
index cca4066f3..5ff471607 100644
--- a/tests/nixos/remote-builds-ssh-ng.nix
+++ b/tests/nixos/remote-builds-ssh-ng.nix
@@ -78,6 +78,11 @@ in
start_all()
+ builder.succeed("systemctl start network-online.target")
+ client.succeed("systemctl start network-online.target")
+ builder.wait_for_unit("network-online.target")
+ client.wait_for_unit("network-online.target")
+
# Create an SSH key on the client.
subprocess.run([
"${hostPkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
@@ -87,11 +92,10 @@ in
client.succeed("chmod 600 /root/.ssh/id_ed25519")
# Install the SSH key on the builder.
- client.wait_for_unit("network.target")
builder.succeed("mkdir -p -m 700 /root/.ssh")
builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
- builder.wait_for_unit("sshd")
- client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'")
+ builder.wait_for_unit("sshd.service")
+ client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world' >&2")
# Perform a build
out = client.succeed("nix-build ${expr nodes.client 1} 2> build-output")
diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix
index 423b9d171..d2ed7853a 100644
--- a/tests/nixos/remote-builds.nix
+++ b/tests/nixos/remote-builds.nix
@@ -85,6 +85,13 @@ in
start_all()
+ builder1.succeed("systemctl start network-online.target")
+ builder2.succeed("systemctl start network-online.target")
+ client.succeed("systemctl start network-online.target")
+ builder1.wait_for_unit("network-online.target")
+ builder2.wait_for_unit("network-online.target")
+ client.wait_for_unit("network-online.target")
+
# Create an SSH key on the client.
subprocess.run([
"${hostPkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
@@ -94,12 +101,11 @@ in
client.succeed("chmod 600 /root/.ssh/id_ed25519")
# Install the SSH key on the builders.
- client.wait_for_unit("network.target")
for builder in [builder1, builder2]:
builder.succeed("mkdir -p -m 700 /root/.ssh")
builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
- builder.wait_for_unit("sshd")
- client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'")
+ builder.wait_for_unit("sshd.service")
+ client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world' >&2")
# Perform a build and check that it was performed on the builder.
out = client.succeed(