From 2dd85bc5899979c2155d6ba99c0587f9a55d08dd Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Mon, 10 Feb 2025 21:35:37 -0800 Subject: [PATCH 1/4] feat(libstore): add page size system feature --- src/libstore/globals.cc | 10 ++++++++++ src/libstore/globals.zig | 23 +++++++++++++++++++++++ src/libstore/libstore.zig | 5 +++++ src/libstore/meson.build | 16 +++++++++++++++- src/libstore/package.nix | 1 + 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/libstore/globals.zig create mode 100644 src/libstore/libstore.zig diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index baadda63e..78d696463 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -174,6 +174,8 @@ static bool hasVirt() { } #endif +extern "C" const char** nix_libstore_get_default_system_features(); + StringSet Settings::getDefaultSystemFeatures() { /* For backwards compatibility, accept some "features" that are @@ -195,6 +197,14 @@ StringSet Settings::getDefaultSystemFeatures() features.insert("apple-virt"); #endif + const char** value = nix_libstore_get_default_system_features(); + if (value != nullptr) { + for (size_t i = 0; value[i] != nullptr; i++) { + features.insert(value[i]); + } + } + free(value); + return features; } diff --git a/src/libstore/globals.zig b/src/libstore/globals.zig new file mode 100644 index 000000000..3e587b9d1 --- /dev/null +++ b/src/libstore/globals.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn getDefaultSystemFeatures() callconv(.C) ?[*:null]const ?[*:0]const u8 { + var list = std.ArrayList(?[*:0]const u8).init(std.heap.c_allocator); + defer list.deinit(); + + const pgsize = std.heap.pageSize(); + + if (pgsize == std.heap.page_size_max) { + list.append(std.fmt.allocPrintZ(std.heap.c_allocator, "pages-{d}k", .{pgsize / 1024}) catch return null) catch return null; + } else { + var i = pgsize; + while (i < std.heap.page_size_max) : (i *= 2) { + list.append(std.fmt.allocPrintZ(std.heap.c_allocator, "pages-{d}k", .{i / 1024}) catch return null) catch return null; + } + } + + return list.toOwnedSliceSentinel(null) catch return null; +} + +comptime { + @export(&getDefaultSystemFeatures, .{ .name = "nix_libstore_get_default_system_features" }); +} diff --git a/src/libstore/libstore.zig b/src/libstore/libstore.zig new file mode 100644 index 000000000..099b1db12 --- /dev/null +++ b/src/libstore/libstore.zig @@ -0,0 +1,5 @@ +pub const globals = @import("globals.zig"); + +comptime { + _ = globals; +} diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 07c599a1c..f4dabbc38 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -1,4 +1,4 @@ -project('nix-store', 'cpp', +project('nix-store', 'cpp', 'c', version : files('.zix-version'), default_options : [ 'cpp_std=c++2a', @@ -132,6 +132,8 @@ if aws_s3.found() endif deps_other += aws_s3 +subdir('nix-meson-build-support/zig') + subdir('nix-meson-build-support/generate-header') generated_headers = [] @@ -424,6 +426,18 @@ endforeach subdir('nix-meson-build-support/export-all-symbols') subdir('nix-meson-build-support/windows-version') +sources += custom_target( + 'zig build-lib', + command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libstore-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', '-lc', zig_args, '@INPUT@'], + output: 'libstore-zig.c', + input: 'libstore.zig', + depend_files: [ + 'globals.zig', + ], +) + +include_dirs += fs.parent(fs.parent(zig.full_path())) / 'lib' / 'zig' + this_library = library( 'nixstore', generated_headers, diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 5f052b0d4..5ae6147c2 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -50,6 +50,7 @@ mkMesonLibrary (finalAttrs: { (fileset.fileFilter (file: file.hasExt "sb") ./.) (fileset.fileFilter (file: file.hasExt "md") ./.) (fileset.fileFilter (file: file.hasExt "sql") ./.) + (fileset.fileFilter (file: file.hasExt "zig") ./.) ]; nativeBuildInputs = lib.optional embeddedSandboxShell unixtools.hexdump; From e80c8207916e43a14ac70a071ef74bb279dc472e Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Mon, 10 Feb 2025 23:09:38 -0800 Subject: [PATCH 2/4] Add rejectSystemFeatures to derivations --- src/libstore/derivation-options.cc | 12 ++++++++++++ src/libstore/derivation-options.hh | 10 ++++++++++ src/libstore/unix/build/local-derivation-goal.cc | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libstore/derivation-options.cc b/src/libstore/derivation-options.cc index 1fc1718f7..cb0d5e89c 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 6e4ea5cd9..e268b1770 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/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 61a36dd51..010294f02 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)); From ce34aa633a067cb1aaaf8338b1eabebd2a519dd9 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Fri, 14 Feb 2025 12:27:52 -0800 Subject: [PATCH 3/4] tests/functional: add reject features test --- tests/functional/build-hook.nix | 21 ++++++++++++++++++++- tests/functional/build-remote.sh | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/functional/build-hook.nix b/tests/functional/build-hook.nix index 45a2a84d6..65328342f 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 3231341cb..a74ef0070 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 From ffd250c790698e025f9026a8046740e1ca1107ac Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Fri, 14 Feb 2025 12:40:19 -0800 Subject: [PATCH 4/4] Document the reject systems feature --- doc/manual/source/language/advanced-attributes.md | 12 ++++++++++++ src/libstore/globals.hh | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/manual/source/language/advanced-attributes.md b/doc/manual/source/language/advanced-attributes.md index c384e956a..1c5c24e56 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/globals.hh b/src/libstore/globals.hh index 684348477..c81b7b7fb 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).