diff --git a/doc/manual/source/language/advanced-attributes.md b/doc/manual/source/language/advanced-attributes.md index c384e956af67..1c5c24e564d3 100644 --- a/doc/manual/source/language/advanced-attributes.md +++ b/doc/manual/source/language/advanced-attributes.md @@ -370,6 +370,18 @@ Derivations can declare some infrequently used optional attributes. ensures that the derivation can only be built on a machine with the `kvm` feature. +- [`rejectSystemFeatures`]{#adv-attr-rejectSystemFeatures}\ + + If a derivation has the `rejectSystemFeatures` attribute, then Nix will only build it on a machine that does not have the corresponding features set in its [`system-features` configuration](@docroot@/command-ref/conf-file.md#conf-system-features). + + For example, setting + + ```nix + rejectSystemFeatures = [ "pages-16k" ]; + ``` + + ensures that the derivation can only be built on a machine which does not have the `pages-16k` feature. + [xp-feature-ca-derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations [xp-feature-dynamic-derivations]: @docroot@/development/experimental-features.md#xp-feature-dynamic-derivations [xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/src/libstore/derivation-options.cc b/src/libstore/derivation-options.cc index 1fc1718f7eb6..cb0d5e89c301 100644 --- a/src/libstore/derivation-options.cc +++ b/src/libstore/derivation-options.cc @@ -134,6 +134,7 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation .allowLocalNetworking = parsed.getBoolAttr("__darwinAllowLocalNetworking", defaults.allowLocalNetworking), .requiredSystemFeatures = parsed.getStringSetAttr("requiredSystemFeatures").value_or(defaults.requiredSystemFeatures), + .rejectSystemFeatures = parsed.getStringSetAttr("rejectSystemFeatures").value_or(defaults.rejectSystemFeatures), .preferLocalBuild = parsed.getBoolAttr("preferLocalBuild", defaults.preferLocalBuild), .allowSubstitutes = parsed.getBoolAttr("allowSubstitutes", defaults.allowSubstitutes), }; @@ -150,6 +151,15 @@ StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & d return res; } +StringSet DerivationOptions::getRejectSystemFeatures(const BasicDerivation & drv) const +{ + // FIXME: cache this? + StringSet res; + for (auto & i : rejectSystemFeatures) + res.insert(i); + return res; +} + bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivation & drv) const { if (drv.platform != settings.thisSystem.get() && !settings.extraPlatforms.get().count(drv.platform) @@ -215,6 +225,7 @@ DerivationOptions adl_serializer::from_json(const json & json .allowLocalNetworking = getBoolean(valueAt(json, "allowLocalNetworking")), .requiredSystemFeatures = getStringSet(valueAt(json, "requiredSystemFeatures")), + .rejectSystemFeatures = getStringSet(valueAt(json, "rejectSystemFeatures")), .preferLocalBuild = getBoolean(valueAt(json, "preferLocalBuild")), .allowSubstitutes = getBoolean(valueAt(json, "allowSubstitutes")), }; @@ -247,6 +258,7 @@ void adl_serializer::to_json(json & json, DerivationOptions o json["allowLocalNetworking"] = o.allowLocalNetworking; json["requiredSystemFeatures"] = o.requiredSystemFeatures; + json["rejectSystemFeatures"] = o.rejectSystemFeatures; json["preferLocalBuild"] = o.preferLocalBuild; json["allowSubstitutes"] = o.allowSubstitutes; } diff --git a/src/libstore/derivation-options.hh b/src/libstore/derivation-options.hh index 6e4ea5cd9fd0..e268b17708f8 100644 --- a/src/libstore/derivation-options.hh +++ b/src/libstore/derivation-options.hh @@ -134,6 +134,11 @@ struct DerivationOptions */ StringSet requiredSystemFeatures = {}; + /** + * env: rejectSystemFeatures + */ + StringSet rejectSystemFeatures = {}; + /** * env: preferLocalBuild */ @@ -161,6 +166,11 @@ struct DerivationOptions */ StringSet getRequiredSystemFeatures(const BasicDerivation & drv) const; + /** + * @param drv See note on `getRequiredSystemFeatures` + */ + StringSet getRejectSystemFeatures(const BasicDerivation & drv) const; + /** * @param drv See note on `getRequiredSystemFeatures` */ diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index d7c000dfab7a..1132e4592bcb 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -35,6 +35,10 @@ #include #endif +#ifdef __linux__ +#include +#endif + #include "strings.hh" namespace nix { @@ -195,6 +199,16 @@ StringSet Settings::getDefaultSystemFeatures() features.insert("apple-virt"); #endif + #if __linux__ + unsigned long pgsize = getauxval(AT_PAGESZ); + #else + unsigned long pgsize = getpagesize(); + #endif + + for (; pgsize <= (64 * 1024); pgsize *= 2) { + features.insert(fmt("pages-%1%k", pgsize / 1024)); + } + return features; } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index f62644ff1258..e019214ab659 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -304,7 +304,7 @@ public: 6. A comma-separated list of supported [system features](#conf-system-features). - A machine will only be used to build a derivation if all the features in the derivation's [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute are supported by that machine. + A machine will only be used to build a derivation if all the features in the derivation's [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute are supported by that machine and does not have any features in [`rejectSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-rejectSystemFeatures). 7. A comma-separated list of required [system features](#conf-system-features). diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 61a36dd51c32..010294f027d5 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -531,9 +531,10 @@ void LocalDerivationGoal::startBuilder() if (drv->platform == "x86_64-darwin" && settings.thisSystem == "aarch64-darwin") { throw Error("run `/usr/sbin/softwareupdate --install-rosetta` to enable your %s to run programs for %s", settings.thisSystem, drv->platform); } else { - throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", + throw Error("a '%s' with features {%s} & not {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, concatStringsSep(", ", drvOptions->getRequiredSystemFeatures(*drv)), + concatStringsSep(", ", drvOptions->getRejectSystemFeatures(*drv)), worker.store.printStorePath(drvPath), settings.thisSystem, concatStringsSep(", ", worker.store.systemFeatures)); diff --git a/tests/functional/build-hook.nix b/tests/functional/build-hook.nix index 45a2a84d6d4f..65328342ff81 100644 --- a/tests/functional/build-hook.nix +++ b/tests/functional/build-hook.nix @@ -69,12 +69,31 @@ let requiredSystemFeatures = [ "baz" ]; }; + input4 = mkDerivation { + shell = busybox; + name = "build-remote-input-4"; + buildCommand = '' + echo hi-input3 + read x < ${input3} + echo $x BAZ > $out + ''; + rejectSystemFeatures = [ "bar" ]; + requiredSystemFeatures = [ "foo" ]; + }; + in mkDerivation { shell = busybox; name = "build-remote"; - passthru = { inherit input1 input2 input3; }; + passthru = { + inherit + input1 + input2 + input3 + input4 + ; + }; buildCommand = '' read x < ${input1} read y < ${input3} diff --git a/tests/functional/build-remote.sh b/tests/functional/build-remote.sh index 3231341cbf6c..a74ef0070fa3 100644 --- a/tests/functional/build-remote.sh +++ b/tests/functional/build-remote.sh @@ -23,6 +23,7 @@ builders=( "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," foo "${EXTRA_SYSTEM_FEATURES[@]}")" "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar "${EXTRA_SYSTEM_FEATURES[@]}")" "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," baz "${EXTRA_SYSTEM_FEATURES[@]}")" + "$TEST_ROOT/machine4 - - 1 1 $(join_by "," bar foo "${EXTRA_SYSTEM_FEATURES[@]}")" ) chmod -R +w "$TEST_ROOT/machine"* || true @@ -70,6 +71,12 @@ echo "$output" | grepQuietInverse builder-build-remote-input-2.sh echo "$output" | grepQuiet builder-build-remote-input-3.sh unset output +# Ensure that input4 was built on store4 due to the required feature. +output=$(nix path-info --store "$TEST_ROOT/machine4" --all) +echo "$output" | grepQuietInverse builder-build-remote-input-1.sh +echo "$output" | grepQuietInverse builder-build-remote-input-2.sh +echo "$output" | grepQuietInverse builder-build-remote-input-3.sh +unset output for i in input1 input3; do nix log --store "$TEST_ROOT/machine0" --file "$file" --arg busybox "$busybox" "passthru.$i" | grep hi-$i