Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/manual/source/language/advanced-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions src/libstore/derivation-options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
Expand All @@ -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)
Expand Down Expand Up @@ -215,6 +225,7 @@ DerivationOptions adl_serializer<DerivationOptions>::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")),
};
Expand Down Expand Up @@ -247,6 +258,7 @@ void adl_serializer<DerivationOptions>::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;
}
Expand Down
10 changes: 10 additions & 0 deletions src/libstore/derivation-options.hh
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ struct DerivationOptions
*/
StringSet requiredSystemFeatures = {};

/**
* env: rejectSystemFeatures
*/
StringSet rejectSystemFeatures = {};

/**
* env: preferLocalBuild
*/
Expand Down Expand Up @@ -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`
*/
Expand Down
14 changes: 14 additions & 0 deletions src/libstore/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include <sys/sysctl.h>
#endif

#ifdef __linux__
#include <sys/auxv.h>
#endif

#include "strings.hh"

namespace nix {
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/libstore/globals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
3 changes: 2 additions & 1 deletion src/libstore/unix/build/local-derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<StringSet>(", ", worker.store.systemFeatures));
Expand Down
21 changes: 20 additions & 1 deletion tests/functional/build-hook.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
7 changes: 7 additions & 0 deletions tests/functional/build-remote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't obvious from the code below. Could you add assertions that simply check what you're saying here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried getting a test here which would reject builds in the proper way but I'm not confident with the testing framework so I might need guidance.

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
Expand Down