From 7614aa97975f3e6e36b6ffbd9fec34462021ab39 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Mon, 4 Mar 2024 07:21:01 +0100 Subject: [PATCH] Merge pull request #4093 from matthewbauer/eval-system Add eval-system option (cherry picked from commit 071dbbee33af9f27338c3e53e4ea067dbfa14010) Change-Id: Ia81358c8cfb60241da07a4d0e84b9ee62a18a53f --- doc/manual/rl-next/eval-system.md | 12 +++++++++++ src/libexpr/eval-settings.cc | 6 ++++++ src/libexpr/eval-settings.hh | 20 ++++++++++++++++++ src/libexpr/primops.cc | 11 ++++++---- src/libstore/globals.hh | 6 +++++- tests/functional/impure-eval.sh | 35 +++++++++++++++++++++++++++++++ tests/functional/local.mk | 1 + tests/unit/libexpr/primops.cc | 4 +++- 8 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 doc/manual/rl-next/eval-system.md create mode 100644 tests/functional/impure-eval.sh diff --git a/doc/manual/rl-next/eval-system.md b/doc/manual/rl-next/eval-system.md new file mode 100644 index 000000000..a4696a56c --- /dev/null +++ b/doc/manual/rl-next/eval-system.md @@ -0,0 +1,12 @@ +--- +synopsis: Add new `eval-system` setting +prs: 4093 +--- + +Add a new `eval-system` option. +Unlike `system`, it just overrides the value of `builtins.currentSystem`. +This is more useful than overriding `system`, because you can build these derivations on remote builders which can work on the given system. +In contrast, `system` also effects scheduling which will cause Nix to build those derivations locally even if that doesn't make sense. + +`eval-system` only takes effect if it is non-empty. +If empty (the default) `system` is used as before, so there is no breakage. diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 93b4a5289..89ca198d8 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -88,6 +88,12 @@ std::string EvalSettings::resolvePseudoUrl(std::string_view url) return std::string(url); } +const std::string & EvalSettings::getCurrentSystem() +{ + const auto & evalSystem = currentSystem.get(); + return evalSystem != "" ? evalSystem : settings.thisSystem.get(); +} + EvalSettings evalSettings; static GlobalConfig::Register rEvalSettings(&evalSettings); diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 19122bc31..049f42e70 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -25,6 +25,26 @@ struct EvalSettings : Config [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). )"}; + Setting currentSystem{ + this, "", "eval-system", + R"( + This option defines + [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + in the Nix language if it is set as a non-empty string. + Otherwise, if it is defined as the empty string (the default), the value of the + [`system` ](#conf-system) + configuration setting is used instead. + + Unlike `system`, this setting does not change what kind of derivations can be built locally. + This is useful for evaluating Nix code on one system to produce derivations to be built on another type of system. + )"}; + + /** + * Implements the `eval-system` vs `system` defaulting logic + * described for `eval-system`. + */ + const std::string & getCurrentSystem(); + Setting restrictEval{ this, false, "restrict-eval", R"( diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b906e7747..005a38319 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4288,13 +4288,16 @@ void EvalState::createBaseEnv() .impureOnly = true, }); - if (!evalSettings.pureEval) { - v.mkString(settings.thisSystem.get()); - } + if (!evalSettings.pureEval) + v.mkString(evalSettings.getCurrentSystem()); addConstant("__currentSystem", v, { .type = nString, .doc = R"( - The value of the [`system` configuration option](@docroot@/command-ref/conf-file.md#conf-pure-eval). + The value of the + [`eval-system`](@docroot@/command-ref/conf-file.md#conf-eval-system) + or else + [`system`](@docroot@/command-ref/conf-file.md#conf-system) + configuration option. It can be used to set the `system` attribute for [`builtins.derivation`](@docroot@/language/derivations.md) such that the resulting derivation can be built on the same system that evaluates the Nix expression: diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 229f1a96a..1fb9ec746 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -210,7 +210,11 @@ public: In general, you do not have to modify this setting. While you can force Nix to run a Darwin-specific `builder` executable on a Linux machine, the result would obviously be wrong. - This value is available in the Nix language as [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem). + This value is available in the Nix language as + [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + if the + [`eval-system`](#conf-eval-system) + configuration option is set as the empty string. )"}; Setting maxSilentTime{ diff --git a/tests/functional/impure-eval.sh b/tests/functional/impure-eval.sh new file mode 100644 index 000000000..6c72f01d7 --- /dev/null +++ b/tests/functional/impure-eval.sh @@ -0,0 +1,35 @@ +source common.sh + +export REMOTE_STORE="dummy://" + +simpleTest () { + local expr=$1; shift + local result=$1; shift + # rest, extra args + + [[ "$(nix eval --impure --raw "$@" --expr "$expr")" == "$result" ]] +} + +# `builtins.storeDir` + +## Store dir follows `store` store setting +simpleTest 'builtins.storeDir' '/foo' --store "$REMOTE_STORE?store=/foo" +simpleTest 'builtins.storeDir' '/bar' --store "$REMOTE_STORE?store=/bar" + +# `builtins.currentSystem` + +## `system` alone affects by default +simpleTest 'builtins.currentSystem' 'foo' --system 'foo' +simpleTest 'builtins.currentSystem' 'bar' --system 'bar' + +## `system` affects if `eval-system` is an empty string +simpleTest 'builtins.currentSystem' 'foo' --system 'foo' --eval-system '' +simpleTest 'builtins.currentSystem' 'bar' --system 'bar' --eval-system '' + +## `eval-system` alone affects +simpleTest 'builtins.currentSystem' 'foo' --eval-system 'foo' +simpleTest 'builtins.currentSystem' 'bar' --eval-system 'bar' + +## `eval-system` overrides `system` +simpleTest 'builtins.currentSystem' 'bar' --system 'foo' --eval-system 'bar' +simpleTest 'builtins.currentSystem' 'baz' --system 'foo' --eval-system 'baz' diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 6b79020fc..9d69c925b 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -68,6 +68,7 @@ nix_tests = \ build-remote-trustless-should-pass-3.sh \ build-remote-trustless-should-fail-0.sh \ nar-access.sh \ + impure-eval.sh \ pure-eval.sh \ eval.sh \ repl.sh \ diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index ce3b5d11f..71e838b32 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -1,6 +1,8 @@ #include #include +#include "eval-settings.hh" + #include "tests/libexpr.hh" namespace nix { @@ -614,7 +616,7 @@ namespace nix { TEST_F(PrimOpTest, currentSystem) { auto v = eval("builtins.currentSystem"); - ASSERT_THAT(v, IsStringEq(settings.thisSystem.get())); + ASSERT_THAT(v, IsStringEq(evalSettings.getCurrentSystem())); } TEST_F(PrimOpTest, derivation) {