diff --git a/src/libstore-tests/data/derivation/ca/all_set.json b/src/libstore-tests/data/derivation/ca/all_set.json index 198356c64..8086c752c 100644 --- a/src/libstore-tests/data/derivation/ca/all_set.json +++ b/src/libstore-tests/data/derivation/ca/all_set.json @@ -4,10 +4,13 @@ "allowSubstitutes": false, "exportReferencesGraph": { "refs1": [ - "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "out" + } ], "refs2": [ - "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" + "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" ] }, "impureEnvVars": [ @@ -20,18 +23,36 @@ "outputChecks": { "forAllOutputs": { "allowedReferences": [ - "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "out" + } ], "allowedRequisites": [ - "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", - "bin" + { + "drvPath": "self", + "output": "bin" + }, + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "dev" + } ], "disallowedReferences": [ - "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", - "dev" + { + "drvPath": "self", + "output": "dev" + }, + { + "drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", + "output": "out" + } ], "disallowedRequisites": [ - "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8" + { + "drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", + "output": "dev" + } ], "ignoreSelfRefs": true, "maxClosureSize": null, diff --git a/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json b/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json index f566c48dd..e29447b6a 100644 --- a/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json +++ b/src/libstore-tests/data/derivation/ca/structuredAttrs_all_set.json @@ -4,10 +4,13 @@ "allowSubstitutes": false, "exportReferencesGraph": { "refs1": [ - "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "out" + } ], "refs2": [ - "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" + "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv" ] }, "impureEnvVars": [ @@ -23,11 +26,20 @@ "allowedReferences": null, "allowedRequisites": null, "disallowedReferences": [ - "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", - "dev" + { + "drvPath": "self", + "output": "dev" + }, + { + "drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", + "output": "out" + } ], "disallowedRequisites": [ - "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8" + { + "drvPath": "qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", + "output": "dev" + } ], "ignoreSelfRefs": false, "maxClosureSize": null, @@ -44,11 +56,20 @@ }, "out": { "allowedReferences": [ - "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9" + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "out" + } ], "allowedRequisites": [ - "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", - "bin" + { + "drvPath": "self", + "output": "bin" + }, + { + "drvPath": "j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv", + "output": "dev" + } ], "disallowedReferences": [], "disallowedRequisites": [], diff --git a/src/libstore-tests/data/derivation/ia/all_set.json b/src/libstore-tests/data/derivation/ia/all_set.json index 8731ca3a2..2e1c848da 100644 --- a/src/libstore-tests/data/derivation/ia/all_set.json +++ b/src/libstore-tests/data/derivation/ia/all_set.json @@ -4,10 +4,10 @@ "allowSubstitutes": false, "exportReferencesGraph": { "refs1": [ - "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" + "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" ], "refs2": [ - "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" + "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" ] }, "impureEnvVars": [ @@ -20,18 +20,24 @@ "outputChecks": { "forAllOutputs": { "allowedReferences": [ - "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" + "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" ], "allowedRequisites": [ - "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", - "bin" + { + "drvPath": "self", + "output": "bin" + }, + "z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev" ], "disallowedReferences": [ - "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", - "dev" + { + "drvPath": "self", + "output": "dev" + }, + "r5cff30838majxk5mp3ip2diffi8vpaj-bar" ], "disallowedRequisites": [ - "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" + "9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" ], "ignoreSelfRefs": true, "maxClosureSize": null, diff --git a/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json b/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json index 67fa634cf..a29699b5d 100644 --- a/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json +++ b/src/libstore-tests/data/derivation/ia/structuredAttrs_all_set.json @@ -4,10 +4,10 @@ "allowSubstitutes": false, "exportReferencesGraph": { "refs1": [ - "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" + "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" ], "refs2": [ - "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" + "vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv" ] }, "impureEnvVars": [ @@ -23,11 +23,14 @@ "allowedReferences": null, "allowedRequisites": null, "disallowedReferences": [ - "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", - "dev" + { + "drvPath": "self", + "output": "dev" + }, + "r5cff30838majxk5mp3ip2diffi8vpaj-bar" ], "disallowedRequisites": [ - "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" + "9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev" ], "ignoreSelfRefs": false, "maxClosureSize": null, @@ -44,11 +47,14 @@ }, "out": { "allowedReferences": [ - "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" + "p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo" ], "allowedRequisites": [ - "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", - "bin" + { + "drvPath": "self", + "output": "bin" + }, + "z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev" ], "disallowedReferences": [], "disallowedRequisites": [], diff --git a/src/libstore-tests/derivation-advanced-attrs.cc b/src/libstore-tests/derivation-advanced-attrs.cc index f44e96cdd..27b8aba16 100644 --- a/src/libstore-tests/derivation-advanced-attrs.cc +++ b/src/libstore-tests/derivation-advanced-attrs.cc @@ -3,7 +3,7 @@ #include "nix/util/experimental-features.hh" #include "nix/store/derivations.hh" -#include "nix/store/derivations.hh" +#include "nix/store/derived-path.hh" #include "nix/store/derivation-options.hh" #include "nix/store/parsed-derivations.hh" #include "nix/util/types.hh" @@ -17,7 +17,7 @@ namespace nix { using namespace nlohmann; class DerivationAdvancedAttrsTest : public JsonCharacterizationTest, - public JsonCharacterizationTest, + public JsonCharacterizationTest>, public LibStoreTest { protected: @@ -42,7 +42,8 @@ public: { this->readTest(fileName, [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedFeatures); }); } @@ -51,11 +52,14 @@ public: * Helper function to test DerivationOptions parsing and comparison */ void testDerivationOptions( - const std::string & fileName, const DerivationOptions & expected, const StringSet & expectedSystemFeatures) + const std::string & fileName, + const DerivationOptions & expected, + const StringSet & expectedSystemFeatures) { this->readTest(fileName, [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_EQ(options, expected); EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedSystemFeatures); @@ -131,22 +135,38 @@ TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attribute * Since these are both repeated and sensative opaque values, it makes * sense to give them names in this file. */ -static std::string pathFoo = "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo", - pathFooDev = "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev", - pathBar = "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar", - pathBarDev = "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev", - pathBarDrvIA = "/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv", - pathBarDrvCA = "/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv", - placeholderFoo = "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9", - placeholderFooDev = "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z", - placeholderBar = "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g", - placeholderBarDev = "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"; +static SingleDerivedPath + pathFoo = SingleDerivedPath::Opaque{StorePath{"p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}}, + pathFooDev = SingleDerivedPath::Opaque{StorePath{"z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"}}, + pathBar = SingleDerivedPath::Opaque{StorePath{"r5cff30838majxk5mp3ip2diffi8vpaj-bar"}}, + pathBarDev = SingleDerivedPath::Opaque{StorePath{"9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"}}, + pathBarDrvIA = SingleDerivedPath::Opaque{StorePath{"vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}}, + pathBarDrvCA = SingleDerivedPath::Opaque{StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}}, + placeholderFoo = + SingleDerivedPath::Built{ + .drvPath = makeConstantStorePathRef(StorePath{"j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv"}), + .output = "out", + }, + placeholderFooDev = + SingleDerivedPath::Built{ + .drvPath = makeConstantStorePathRef(StorePath{"j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv"}), + .output = "dev", + }, + placeholderBar = + SingleDerivedPath::Built{ + .drvPath = makeConstantStorePathRef(StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}), + .output = "out", + }, + placeholderBarDev = SingleDerivedPath::Built{ + .drvPath = makeConstantStorePathRef(StorePath{"qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}), + .output = "dev", + }; -using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph); +using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph); -static const DerivationOptions advancedAttributes_defaults = { +static const DerivationOptions advancedAttributes_defaults = { .outputChecks = - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .ignoreSelfRefs = true, }, .unsafeDiscardReferences = {}, @@ -167,7 +187,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults) this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_TRUE(!got.structuredAttrs); @@ -192,9 +213,9 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) { - DerivationOptions expected = { + DerivationOptions expected = { .outputChecks = - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .ignoreSelfRefs = true, }, .unsafeDiscardReferences = {}, @@ -212,12 +233,13 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) this->readTest("advanced-attributes.drv", [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_TRUE(!got.structuredAttrs); // Reset fields that vary between test cases to enable whole-object comparison - options.outputChecks = DerivationOptions::OutputChecks{.ignoreSelfRefs = true}; + options.outputChecks = DerivationOptions::OutputChecks{.ignoreSelfRefs = true}; options.exportReferencesGraph = {}; EXPECT_EQ(options, expected); @@ -227,14 +249,14 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) }); }; -DerivationOptions advancedAttributes_ia = { +DerivationOptions advancedAttributes_ia = { .outputChecks = - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .ignoreSelfRefs = true, - .allowedReferences = StringSet{pathFoo}, - .disallowedReferences = StringSet{pathBar, "dev"}, - .allowedRequisites = StringSet{pathFooDev, "bin"}, - .disallowedRequisites = StringSet{pathBarDev}, + .allowedReferences = std::set>{pathFoo}, + .disallowedReferences = std::set>{pathBar, OutputName{"dev"}}, + .allowedRequisites = std::set>{pathFooDev, OutputName{"bin"}}, + .disallowedRequisites = std::set>{pathBarDev}, }, .unsafeDiscardReferences = {}, .passAsFile = {}, @@ -257,14 +279,14 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_ia) testDerivationOptions("advanced-attributes.drv", advancedAttributes_ia, {"rainbow", "uid-range"}); }; -DerivationOptions advancedAttributes_ca = { +DerivationOptions advancedAttributes_ca = { .outputChecks = - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .ignoreSelfRefs = true, - .allowedReferences = StringSet{placeholderFoo}, - .disallowedReferences = StringSet{placeholderBar, "dev"}, - .allowedRequisites = StringSet{placeholderFooDev, "bin"}, - .disallowedRequisites = StringSet{placeholderBarDev}, + .allowedReferences = std::set>{placeholderFoo}, + .disallowedReferences = std::set>{placeholderBar, OutputName{"dev"}}, + .allowedRequisites = std::set>{placeholderFooDev, OutputName{"bin"}}, + .disallowedRequisites = std::set>{placeholderBarDev}, }, .unsafeDiscardReferences = {}, .passAsFile = {}, @@ -287,8 +309,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes) testDerivationOptions("advanced-attributes.drv", advancedAttributes_ca, {"rainbow", "uid-range", "ca-derivations"}); }; -DerivationOptions advancedAttributes_structuredAttrs_defaults = { - .outputChecks = std::map{}, +DerivationOptions advancedAttributes_structuredAttrs_defaults = { + .outputChecks = std::map::OutputChecks>{}, .unsafeDiscardReferences = {}, .passAsFile = {}, .exportReferencesGraph = {}, @@ -307,7 +329,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_TRUE(got.structuredAttrs); @@ -332,11 +355,11 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_default TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) { - DerivationOptions expected = { + DerivationOptions expected = { .outputChecks = - std::map{ + std::map::OutputChecks>{ {"dev", - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .maxSize = 789, .maxClosureSize = 5909, }}, @@ -357,7 +380,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); - DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); + auto options = derivationOptionsFromStructuredAttrs( + *this->store, got.inputDrvs, got.env, got.structuredAttrs, true, this->mockXpSettings); EXPECT_TRUE(got.structuredAttrs); @@ -365,7 +389,8 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) { // Delete all keys but "dev" in options.outputChecks auto * outputChecksMapP = - std::get_if>(&options.outputChecks); + std::get_if::OutputChecks>>( + &options.outputChecks); ASSERT_TRUE(outputChecksMapP); auto & outputChecksMap = *outputChecksMapP; auto devEntry = outputChecksMap.find("dev"); @@ -385,21 +410,21 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) }); }; -DerivationOptions advancedAttributes_structuredAttrs_ia = { +DerivationOptions advancedAttributes_structuredAttrs_ia = { .outputChecks = - std::map{ + std::map::OutputChecks>{ {"out", - DerivationOptions::OutputChecks{ - .allowedReferences = StringSet{pathFoo}, - .allowedRequisites = StringSet{pathFooDev, "bin"}, + DerivationOptions::OutputChecks{ + .allowedReferences = std::set>{pathFoo}, + .allowedRequisites = std::set>{pathFooDev, OutputName{"bin"}}, }}, {"bin", - DerivationOptions::OutputChecks{ - .disallowedReferences = StringSet{pathBar, "dev"}, - .disallowedRequisites = StringSet{pathBarDev}, + DerivationOptions::OutputChecks{ + .disallowedReferences = std::set>{pathBar, OutputName{"dev"}}, + .disallowedRequisites = std::set>{pathBarDev}, }}, {"dev", - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .maxSize = 789, .maxClosureSize = 5909, }}, @@ -427,21 +452,21 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) "advanced-attributes-structured-attrs.drv", advancedAttributes_structuredAttrs_ia, {"rainbow", "uid-range"}); }; -DerivationOptions advancedAttributes_structuredAttrs_ca = { +DerivationOptions advancedAttributes_structuredAttrs_ca = { .outputChecks = - std::map{ + std::map::OutputChecks>{ {"out", - DerivationOptions::OutputChecks{ - .allowedReferences = StringSet{placeholderFoo}, - .allowedRequisites = StringSet{placeholderFooDev, "bin"}, + DerivationOptions::OutputChecks{ + .allowedReferences = std::set>{placeholderFoo}, + .allowedRequisites = std::set>{placeholderFooDev, OutputName{"bin"}}, }}, {"bin", - DerivationOptions::OutputChecks{ - .disallowedReferences = StringSet{placeholderBar, "dev"}, - .disallowedRequisites = StringSet{placeholderBarDev}, + DerivationOptions::OutputChecks{ + .disallowedReferences = std::set>{placeholderBar, OutputName{"dev"}}, + .disallowedRequisites = std::set>{placeholderBarDev}, }}, {"dev", - DerivationOptions::OutputChecks{ + DerivationOptions::OutputChecks{ .maxSize = 789, .maxClosureSize = 5909, }}, @@ -471,14 +496,16 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) {"rainbow", "uid-range", "ca-derivations"}); }; -#define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \ - TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \ - { \ - this->JsonCharacterizationTest::readJsonTest(#VAR, advancedAttributes_##VAR2); \ - } \ - TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \ - { \ - this->JsonCharacterizationTest::writeJsonTest(#VAR, advancedAttributes_##VAR2); \ +#define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \ + TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \ + { \ + this->JsonCharacterizationTest>::readJsonTest( \ + #VAR, advancedAttributes_##VAR2); \ + } \ + TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \ + { \ + this->JsonCharacterizationTest>::writeJsonTest( \ + #VAR, advancedAttributes_##VAR2); \ } TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, defaults, defaults) diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index c72130142..41d76fbba 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -32,14 +32,27 @@ DerivationBuildingGoal::DerivationBuildingGoal( , drv{std::make_unique(drv)} , buildMode(buildMode) { + DerivationOptions temp; try { - drvOptions = - std::make_unique(DerivationOptions::fromStructuredAttrs(drv.env, drv.structuredAttrs)); + temp = derivationOptionsFromStructuredAttrs(worker.store, drv.inputDrvs, drv.env, drv.structuredAttrs); } catch (Error & e) { e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath)); throw; } + auto x = tryResolve( + temp, [&](ref drvPath, const std::string & outputName) -> std::optional { + try { + return resolveDerivedPath( + worker.store, SingleDerivedPath::Built{drvPath, outputName}, &worker.evalStore); + } catch (Error &) { + return std::nullopt; + } + }); + + assert(x); + drvOptions = std::make_unique>(*x); + name = fmt("building derivation '%s'", worker.store.printStorePath(drvPath)); trace("created"); diff --git a/src/libstore/build/derivation-check.cc b/src/libstore/build/derivation-check.cc index 181221ba5..e56b9fe49 100644 --- a/src/libstore/build/derivation-check.cc +++ b/src/libstore/build/derivation-check.cc @@ -11,7 +11,7 @@ void checkOutputs( Store & store, const StorePath & drvPath, const decltype(Derivation::outputs) & drvOutputs, - const decltype(DerivationOptions::outputChecks) & outputChecks, + const decltype(DerivationOptions::outputChecks) & outputChecks, const std::map & outputs) { std::map outputsByPath; @@ -85,7 +85,7 @@ void checkOutputs( return std::make_pair(std::move(pathsDone), closureSize); }; - auto applyChecks = [&](const DerivationOptions::OutputChecks & checks) { + auto applyChecks = [&](const DerivationOptions::OutputChecks & checks) { if (checks.maxSize && info.narSize > *checks.maxSize) throw BuildError( BuildResult::Failure::OutputRejected, @@ -105,28 +105,33 @@ void checkOutputs( *checks.maxClosureSize); } - auto checkRefs = [&](const StringSet & value, bool allowed, bool recursive) { + auto checkRefs = [&](const std::set> & value, bool allowed, bool recursive) { /* Parse a list of reference specifiers. Each element must either be a store path, or the symbolic name of the output of the derivation (such as `out'). */ StorePathSet spec; for (auto & i : value) { - if (store.isStorePath(i)) - spec.insert(store.parseStorePath(i)); - else if (auto output = get(outputs, i)) - spec.insert(output->path); - else { - std::string outputsListing = - concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; }); - throw BuildError( - BuildResult::Failure::OutputRejected, - "derivation '%s' output check for '%s' contains an illegal reference specifier '%s'," - " expected store path or output name (one of [%s])", - store.printStorePath(drvPath), - outputName, - i, - outputsListing); - } + std::visit( + overloaded{ + [&](const StorePath & path) { spec.insert(path); }, + [&](const OutputName & refOutputName) { + if (auto output = get(outputs, refOutputName)) + spec.insert(output->path); + else { + std::string outputsListing = + concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; }); + throw BuildError( + BuildResult::Failure::OutputRejected, + "derivation '%s' output check for '%s' contains output name '%s'," + " but this is not a valid output of this derivation." + " (Valid outputs are [%s].)", + store.printStorePath(drvPath), + outputName, + refOutputName, + outputsListing); + } + }}, + i); } auto used = recursive ? getClosure(info.path).first : info.references; @@ -180,8 +185,8 @@ void checkOutputs( std::visit( overloaded{ - [&](const DerivationOptions::OutputChecks & checks) { applyChecks(checks); }, - [&](const std::map & checksPerOutput) { + [&](const DerivationOptions::OutputChecks & checks) { applyChecks(checks); }, + [&](const std::map::OutputChecks> & checksPerOutput) { if (auto outputChecks = get(checksPerOutput, outputName)) applyChecks(*outputChecks); diff --git a/src/libstore/build/derivation-check.hh b/src/libstore/build/derivation-check.hh index b425c2ac5..01e6c5d56 100644 --- a/src/libstore/build/derivation-check.hh +++ b/src/libstore/build/derivation-check.hh @@ -21,7 +21,7 @@ void checkOutputs( Store & store, const StorePath & drvPath, const decltype(Derivation::outputs) & drvOutputs, - const decltype(DerivationOptions::outputChecks) & drvOptions, + const decltype(DerivationOptions::outputChecks) & drvOptions, const std::map & outputs); } // namespace nix diff --git a/src/libstore/build/derivation-env-desugar.cc b/src/libstore/build/derivation-env-desugar.cc index 8d552fc4d..75b62c116 100644 --- a/src/libstore/build/derivation-env-desugar.cc +++ b/src/libstore/build/derivation-env-desugar.cc @@ -18,7 +18,10 @@ std::string & DesugaredEnv::atFileEnvPair(std::string_view name, std::string fil } DesugaredEnv DesugaredEnv::create( - Store & store, const Derivation & drv, const DerivationOptions & drvOptions, const StorePathSet & inputPaths) + Store & store, + const Derivation & drv, + const DerivationOptions & drvOptions, + const StorePathSet & inputPaths) { DesugaredEnv res; @@ -46,7 +49,7 @@ DesugaredEnv DesugaredEnv::create( } /* Handle exportReferencesGraph(), if set. */ - for (auto & [fileName, storePaths] : drvOptions.getParsedExportReferencesGraph(store)) { + for (auto & [fileName, storePaths] : drvOptions.exportReferencesGraph) { /* Write closure info to . */ res.extraFiles.insert_or_assign( fileName, store.makeValidityRegistration(store.exportReferences(storePaths, inputPaths), false, false)); diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 14aa044ea..a44b79f57 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -64,9 +64,9 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation) { trace("have derivation"); - auto drvOptions = [&]() -> DerivationOptions { + auto drvOptions = [&]() -> DerivationOptions { try { - return DerivationOptions::fromStructuredAttrs(drv->env, drv->structuredAttrs); + return derivationOptionsFromStructuredAttrs(worker.store, drv->inputDrvs, drv->env, drv->structuredAttrs); } catch (Error & e) { e.addTrace({}, "while parsing derivation '%s'", worker.store.printStorePath(drvPath)); throw; diff --git a/src/libstore/derivation-options.cc b/src/libstore/derivation-options.cc index 75313841c..8c71ef4b1 100644 --- a/src/libstore/derivation-options.cc +++ b/src/libstore/derivation-options.cc @@ -2,15 +2,18 @@ #include "nix/util/json-utils.hh" #include "nix/store/parsed-derivations.hh" #include "nix/store/derivations.hh" +#include "nix/store/derived-path.hh" #include "nix/store/store-api.hh" #include "nix/util/types.hh" #include "nix/util/util.hh" #include "nix/store/globals.hh" +#include "nix/util/variant-wrapper.hh" #include #include #include #include +#include namespace nix { @@ -90,14 +93,60 @@ getStringSetAttr(const StringMap & env, const StructuredAttrs * parsed, const st return ss ? (std::optional{StringSet{ss->begin(), ss->end()}}) : (std::optional{}); } -using OutputChecks = DerivationOptions::OutputChecks; +template +using OutputChecks = DerivationOptions::OutputChecks; -using OutputChecksVariant = std::variant>; +template +using OutputChecksVariant = std::variant, std::map>>; -DerivationOptions DerivationOptions::fromStructuredAttrs( - const StringMap & env, const std::optional & parsed, bool shouldWarn) +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const StringMap & env, + const StructuredAttrs * parsed, + bool shouldWarn, + const ExperimentalFeatureSettings & mockXpSettings) { - return fromStructuredAttrs(env, parsed ? &*parsed : nullptr); + /* Use the SingleDerivedPath version with empty inputDrvs, then + resolve. */ + DerivedPathMap emptyInputDrvs{}; + auto singleDerivedPathOptions = + derivationOptionsFromStructuredAttrs(store, emptyInputDrvs, env, parsed, shouldWarn, mockXpSettings); + + /* "Resolve" all SingleDerivedPath inputs to StorePath. */ + auto resolved = tryResolve( + singleDerivedPathOptions, + [&](ref drvPath, const std::string & outputName) -> std::optional { + // there should be nothing to resolve + assert(false); + }); + + /* Since we should never need to call the call back, there should be + no way it fails. */ + assert(resolved); + + return *resolved; +} + +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const StringMap & env, + const std::optional & parsed, + bool shouldWarn, + const ExperimentalFeatureSettings & mockXpSettings) +{ + return derivationOptionsFromStructuredAttrs(store, env, parsed ? &*parsed : nullptr, shouldWarn, mockXpSettings); +} + +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const DerivedPathMap & inputDrvs, + const StringMap & env, + const std::optional & parsed, + bool shouldWarn, + const ExperimentalFeatureSettings & mockXpSettings) +{ + return derivationOptionsFromStructuredAttrs( + store, inputDrvs, env, parsed ? &*parsed : nullptr, shouldWarn, mockXpSettings); } static void flatten(const nlohmann::json & value, StringSet & res) @@ -111,10 +160,63 @@ static void flatten(const nlohmann::json & value, StringSet & res) throw Error("'exportReferencesGraph' value is not an array or a string"); } -DerivationOptions -DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn) +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const DerivedPathMap & inputDrvs, + const StringMap & env, + const StructuredAttrs * parsed, + bool shouldWarn, + const ExperimentalFeatureSettings & mockXpSettings) { - DerivationOptions defaults = {}; + DerivationOptions defaults = {}; + + std::map placeholders; + if (mockXpSettings.isEnabled(Xp::CaDerivations)) { + /* Initialize placeholder map from inputDrvs */ + auto initPlaceholders = [&](this const auto & initPlaceholders, + ref basePath, + const DerivedPathMap::ChildNode & node) -> void { + for (const auto & outputName : node.value) { + auto built = SingleDerivedPath::Built{ + .drvPath = basePath, + .output = outputName, + }; + placeholders.insert_or_assign( + DownstreamPlaceholder::fromSingleDerivedPathBuilt(built, mockXpSettings).render(), + std::move(built)); + } + + for (const auto & [outputName, childNode] : node.childMap) { + initPlaceholders( + make_ref(SingleDerivedPath::Built{ + .drvPath = basePath, + .output = outputName, + }), + childNode); + } + }; + + for (const auto & [drvPath, outputs] : inputDrvs.map) { + auto basePath = make_ref(SingleDerivedPath::Opaque{drvPath}); + initPlaceholders(basePath, outputs); + } + } + + auto parseSingleDerivedPath = [&](const std::string & pathS) -> SingleDerivedPath { + if (auto it = placeholders.find(pathS); it != placeholders.end()) + return it->second; + else + return SingleDerivedPath::Opaque{store.toStorePath(pathS).first}; + }; + + auto parseRef = [&](const std::string & pathS) -> DrvRef { + if (auto it = placeholders.find(pathS); it != placeholders.end()) + return it->second; + if (store.isStorePath(pathS)) + return SingleDerivedPath::Opaque{store.toStorePath(pathS).first}; + else + return pathS; + }; if (shouldWarn && parsed) { auto & structuredAttrs = parsed->structuredAttrs; @@ -146,14 +248,14 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt } return { - .outputChecks = [&]() -> OutputChecksVariant { + .outputChecks = [&]() -> OutputChecksVariant { if (parsed) { auto & structuredAttrs = parsed->structuredAttrs; - std::map res; + std::map> res; if (auto * outputChecks = get(structuredAttrs, "outputChecks")) { for (auto & [outputName, output_] : getObject(*outputChecks)) { - OutputChecks checks; + OutputChecks checks; auto & output = getObject(output_); @@ -163,13 +265,14 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt if (auto maxClosureSize = get(output, "maxClosureSize")) checks.maxClosureSize = maxClosureSize->get(); - auto get_ = [&output = output](const std::string & name) -> std::optional { + auto get_ = + [&](const std::string & name) -> std::optional>> { if (auto i = get(output, name)) { - StringSet res; + std::set> res; for (auto j = i->begin(); j != i->end(); ++j) { if (!j->is_string()) throw Error("attribute '%s' must be a list of strings", name); - res.insert(j->get()); + res.insert(parseRef(j->get())); } return res; } @@ -178,7 +281,7 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt res.insert_or_assign( outputName, - OutputChecks{ + OutputChecks{ .maxSize = [&]() -> std::optional { if (auto maxSize = get(output, "maxSize")) return maxSize->get(); @@ -192,21 +295,32 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt return std::nullopt; }(), .allowedReferences = get_("allowedReferences"), - .disallowedReferences = get_("disallowedReferences").value_or(StringSet{}), + .disallowedReferences = + get_("disallowedReferences").value_or(std::set>{}), .allowedRequisites = get_("allowedRequisites"), - .disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{}), + .disallowedRequisites = + get_("disallowedRequisites").value_or(std::set>{}), }); } } return res; } else { - return OutputChecks{ + auto parseRefSet = [&](const std::optional optionalStringSet) + -> std::optional>> { + if (!optionalStringSet) + return std::nullopt; + auto range = *optionalStringSet | std::views::transform(parseRef); + return std::set>(range.begin(), range.end()); + }; + return OutputChecks{ // legacy non-structured-attributes case .ignoreSelfRefs = true, - .allowedReferences = getStringSetAttr(env, parsed, "allowedReferences"), - .disallowedReferences = getStringSetAttr(env, parsed, "disallowedReferences").value_or(StringSet{}), - .allowedRequisites = getStringSetAttr(env, parsed, "allowedRequisites"), - .disallowedRequisites = getStringSetAttr(env, parsed, "disallowedRequisites").value_or(StringSet{}), + .allowedReferences = parseRefSet(getStringSetAttr(env, parsed, "allowedReferences")), + .disallowedReferences = parseRefSet(getStringSetAttr(env, parsed, "disallowedReferences")) + .value_or(std::set>{}), + .allowedRequisites = parseRefSet(getStringSetAttr(env, parsed, "allowedRequisites")), + .disallowedRequisites = parseRefSet(getStringSetAttr(env, parsed, "disallowedRequisites")) + .value_or(std::set>{}), }; } }(), @@ -245,16 +359,19 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt }(), .exportReferencesGraph = [&] { - std::map ret; + std::map> ret; if (parsed) { auto * e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph"); if (!e || !e->is_object()) return ret; - for (auto & [key, value] : getObject(*e)) { + for (auto & [key, storePathsJson] : getObject(*e)) { StringSet ss; - flatten(value, ss); - ret.insert_or_assign(key, std::move(ss)); + flatten(storePathsJson, ss); + std::set storePaths; + for (auto & s : ss) + storePaths.insert(parseSingleDerivedPath(s)); + ret.insert_or_assign(key, std::move(storePaths)); } } else { auto s = getOr(env, "exportReferencesGraph", ""); @@ -268,7 +385,7 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName); auto & storePathS = *i++; - ret.insert_or_assign(std::move(fileName), StringSet{storePathS}); + ret.insert_or_assign(std::move(fileName), std::set{parseSingleDerivedPath(storePathS)}); } } return ret; @@ -286,28 +403,8 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt }; } -std::map -DerivationOptions::getParsedExportReferencesGraph(const StoreDirConfig & store) const -{ - std::map res; - - for (auto & [fileName, ss] : exportReferencesGraph) { - StorePathSet storePaths; - for (auto & storePathS : ss) { - if (!store.isInStore(storePathS)) - throw BuildError( - BuildResult::Failure::InputRejected, - "'exportReferencesGraph' contains a non-store path '%1%'", - storePathS); - storePaths.insert(store.toStorePath(storePathS).first); - } - res.insert_or_assign(fileName, storePaths); - } - - return res; -} - -StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & drv) const +template +StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & drv) const { // FIXME: cache this? StringSet res; @@ -318,7 +415,8 @@ StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & d return res; } -bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivation & drv) const +template +bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivation & drv) const { if (drv.platform != settings.thisSystem.get() && !settings.extraPlatforms.get().count(drv.platform) && !drv.isBuiltin()) @@ -334,42 +432,194 @@ bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivatio return true; } -bool DerivationOptions::willBuildLocally(Store & localStore, const BasicDerivation & drv) const +template +bool DerivationOptions::willBuildLocally(Store & localStore, const BasicDerivation & drv) const { return preferLocalBuild && canBuildLocally(localStore, drv); } -bool DerivationOptions::substitutesAllowed() const +template +bool DerivationOptions::substitutesAllowed() const { return settings.alwaysAllowSubstitutes ? true : allowSubstitutes; } -bool DerivationOptions::useUidRange(const BasicDerivation & drv) const +template +bool DerivationOptions::useUidRange(const BasicDerivation & drv) const { return getRequiredSystemFeatures(drv).count("uid-range"); } +std::optional> tryResolve( + const DerivationOptions & drvOptions, + std::function(ref drvPath, const std::string & outputName)> + queryResolutionChain) +{ + auto tryResolvePath = [&](const SingleDerivedPath & input) -> std::optional { + return std::visit( + overloaded{ + [](const SingleDerivedPath::Opaque & p) -> std::optional { return p.path; }, + [&](const SingleDerivedPath::Built & p) -> std::optional { + return queryResolutionChain(p.drvPath, p.output); + }}, + input.raw()); + }; + + auto tryResolveRef = [&](const DrvRef & ref) -> std::optional> { + return std::visit( + overloaded{ + [](const OutputName & outputName) -> std::optional> { return outputName; }, + [&](const SingleDerivedPath & input) -> std::optional> { + return tryResolvePath(input); + }}, + ref); + }; + + auto tryResolveRefSet = + [&](const std::set> & refSet) -> std::optional>> { + std::set> resolvedSet; + for (const auto & ref : refSet) { + auto resolvedRef = tryResolveRef(ref); + if (!resolvedRef) + return std::nullopt; + resolvedSet.insert(*resolvedRef); + } + return resolvedSet; + }; + + // Helper function to try resolving OutputChecks using functional style + auto tryResolveOutputChecks = [&](const DerivationOptions::OutputChecks & checks) + -> std::optional::OutputChecks> { + std::optional>> resolvedAllowedReferences; + if (checks.allowedReferences) { + resolvedAllowedReferences = tryResolveRefSet(*checks.allowedReferences); + if (!resolvedAllowedReferences) + return std::nullopt; + } + + std::optional>> resolvedAllowedRequisites; + if (checks.allowedRequisites) { + resolvedAllowedRequisites = tryResolveRefSet(*checks.allowedRequisites); + if (!resolvedAllowedRequisites) + return std::nullopt; + } + + auto resolvedDisallowedReferences = tryResolveRefSet(checks.disallowedReferences); + if (!resolvedDisallowedReferences) + return std::nullopt; + + auto resolvedDisallowedRequisites = tryResolveRefSet(checks.disallowedRequisites); + if (!resolvedDisallowedRequisites) + return std::nullopt; + + return DerivationOptions::OutputChecks{ + .ignoreSelfRefs = checks.ignoreSelfRefs, + .maxSize = checks.maxSize, + .maxClosureSize = checks.maxClosureSize, + .allowedReferences = resolvedAllowedReferences, + .disallowedReferences = *resolvedDisallowedReferences, + .allowedRequisites = resolvedAllowedRequisites, + .disallowedRequisites = *resolvedDisallowedRequisites, + }; + }; + + // Helper function to resolve exportReferencesGraph using functional style + auto tryResolveExportReferencesGraph = [&](const std::map> & exportGraph) + -> std::optional>> { + std::map> resolved; + for (const auto & [name, inputPaths] : exportGraph) { + std::set resolvedPaths; + for (const auto & inputPath : inputPaths) { + auto resolvedPath = tryResolvePath(inputPath); + if (!resolvedPath) + return std::nullopt; + resolvedPaths.insert(*resolvedPath); + } + resolved.emplace(name, std::move(resolvedPaths)); + } + return resolved; + }; + + // Resolve outputChecks using functional style with std::visit + auto resolvedOutputChecks = std::visit( + overloaded{ + [&](const DerivationOptions::OutputChecks & checks) + -> std::optional::OutputChecks, + std::map::OutputChecks>>> { + auto resolved = tryResolveOutputChecks(checks); + if (!resolved) + return std::nullopt; + return std::variant< + DerivationOptions::OutputChecks, + std::map::OutputChecks>>(*resolved); + }, + [&](const std::map::OutputChecks> & checksMap) + -> std::optional::OutputChecks, + std::map::OutputChecks>>> { + std::map::OutputChecks> resolvedMap; + for (const auto & [outputName, checks] : checksMap) { + auto resolved = tryResolveOutputChecks(checks); + if (!resolved) + return std::nullopt; + resolvedMap.emplace(outputName, *resolved); + } + return std::variant< + DerivationOptions::OutputChecks, + std::map::OutputChecks>>(resolvedMap); + }}, + drvOptions.outputChecks); + + if (!resolvedOutputChecks) + return std::nullopt; + + // Resolve exportReferencesGraph + auto resolvedExportGraph = tryResolveExportReferencesGraph(drvOptions.exportReferencesGraph); + if (!resolvedExportGraph) + return std::nullopt; + + // Return resolved DerivationOptions using designated initializers + return DerivationOptions{ + .outputChecks = *resolvedOutputChecks, + .unsafeDiscardReferences = drvOptions.unsafeDiscardReferences, + .passAsFile = drvOptions.passAsFile, + .exportReferencesGraph = *resolvedExportGraph, + .additionalSandboxProfile = drvOptions.additionalSandboxProfile, + .noChroot = drvOptions.noChroot, + .impureHostDeps = drvOptions.impureHostDeps, + .impureEnvVars = drvOptions.impureEnvVars, + .allowLocalNetworking = drvOptions.allowLocalNetworking, + .requiredSystemFeatures = drvOptions.requiredSystemFeatures, + .preferLocalBuild = drvOptions.preferLocalBuild, + .allowSubstitutes = drvOptions.allowSubstitutes, + }; +} + +template struct DerivationOptions; +template struct DerivationOptions; + } // namespace nix namespace nlohmann { using namespace nix; -DerivationOptions adl_serializer::from_json(const json & json_) +DerivationOptions adl_serializer>::from_json(const json & json_) { auto & json = getObject(json_); return { - .outputChecks = [&]() -> OutputChecksVariant { + .outputChecks = [&]() -> OutputChecksVariant { auto outputChecks = getObject(valueAt(json, "outputChecks")); auto forAllOutputsOpt = optionalValueAt(outputChecks, "forAllOutputs"); auto perOutputOpt = optionalValueAt(outputChecks, "perOutput"); if (forAllOutputsOpt && !perOutputOpt) { - return static_cast(*forAllOutputsOpt); + return static_cast>(*forAllOutputsOpt); } else if (perOutputOpt && !forAllOutputsOpt) { - return static_cast>(*perOutputOpt); + return static_cast>>(*perOutputOpt); } else { throw Error("Exactly one of 'perOutput' or 'forAllOutputs' is required"); } @@ -377,7 +627,7 @@ DerivationOptions adl_serializer::from_json(const json & json .unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"), .passAsFile = getStringSet(valueAt(json, "passAsFile")), - .exportReferencesGraph = getMap(getObject(valueAt(json, "exportReferencesGraph")), getStringSet), + .exportReferencesGraph = valueAt(json, "exportReferencesGraph"), .additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")), .noChroot = getBoolean(valueAt(json, "noChroot")), @@ -391,16 +641,17 @@ DerivationOptions adl_serializer::from_json(const json & json }; } -void adl_serializer::to_json(json & json, const DerivationOptions & o) +void adl_serializer>::to_json( + json & json, const DerivationOptions & o) { json["outputChecks"] = std::visit( overloaded{ - [&](const OutputChecks & checks) { + [&](const OutputChecks & checks) { nlohmann::json outputChecks; outputChecks["forAllOutputs"] = checks; return outputChecks; }, - [&](const std::map & checksPerOutput) { + [&](const std::map> & checksPerOutput) { nlohmann::json outputChecks; outputChecks["perOutput"] = checksPerOutput; return outputChecks; @@ -432,7 +683,7 @@ static inline std::optional ptrToOwned(const json * ptr) return std::nullopt; } -DerivationOptions::OutputChecks adl_serializer::from_json(const json & json_) +OutputChecks adl_serializer>::from_json(const json & json_) { auto & json = getObject(json_); @@ -440,14 +691,16 @@ DerivationOptions::OutputChecks adl_serializer: .ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")), .maxSize = ptrToOwned(getNullable(valueAt(json, "maxSize"))), .maxClosureSize = ptrToOwned(getNullable(valueAt(json, "maxClosureSize"))), - .allowedReferences = ptrToOwned(getNullable(valueAt(json, "allowedReferences"))), - .disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")), - .allowedRequisites = ptrToOwned(getNullable(valueAt(json, "allowedRequisites"))), - .disallowedRequisites = getStringSet(valueAt(json, "disallowedRequisites")), + .allowedReferences = + ptrToOwned>>(getNullable(valueAt(json, "allowedReferences"))), + .disallowedReferences = valueAt(json, "disallowedReferences"), + .allowedRequisites = + ptrToOwned>>(getNullable(valueAt(json, "allowedRequisites"))), + .disallowedRequisites = valueAt(json, "disallowedRequisites"), }; } -void adl_serializer::to_json(json & json, const DerivationOptions::OutputChecks & c) +void adl_serializer>::to_json(json & json, const OutputChecks & c) { json["ignoreSelfRefs"] = c.ignoreSelfRefs; json["maxSize"] = c.maxSize; diff --git a/src/libstore/downstream-placeholder.cc b/src/libstore/downstream-placeholder.cc index 780717a62..73ed2b74a 100644 --- a/src/libstore/downstream-placeholder.cc +++ b/src/libstore/downstream-placeholder.cc @@ -1,5 +1,6 @@ #include "nix/store/downstream-placeholder.hh" #include "nix/store/derivations.hh" +#include "nix/util/json-utils.hh" namespace nix { @@ -49,3 +50,45 @@ DownstreamPlaceholder DownstreamPlaceholder::fromSingleDerivedPathBuilt( } } // namespace nix + +namespace nlohmann { + +using namespace nix; + +template +DrvRef adl_serializer>::from_json(const json & json) +{ + // OutputName case: { "drvPath": "self", "output": } + if (json.type() == nlohmann::json::value_t::object) { + auto & obj = getObject(json); + if (auto * drvPath_ = get(obj, "drvPath")) { + auto & drvPath = *drvPath_; + if (drvPath.type() == nlohmann::json::value_t::string && getString(drvPath) == "self") { + return getString(valueAt(obj, "output")); + } + } + } + + // Input case + return adl_serializer::from_json(json); +} + +template +void adl_serializer>::to_json(json & json, const DrvRef & ref) +{ + std::visit( + overloaded{ + [&](const OutputName & outputName) { + json = nlohmann::json::object(); + json["drvPath"] = "self"; + json["output"] = outputName; + }, + [&](const Item & item) { json = item; }, + }, + ref); +} + +template struct adl_serializer>; +template struct adl_serializer>; + +} // namespace nlohmann diff --git a/src/libstore/include/nix/store/build/derivation-builder.hh b/src/libstore/include/nix/store/build/derivation-builder.hh index 5eed38462..af84661e2 100644 --- a/src/libstore/include/nix/store/build/derivation-builder.hh +++ b/src/libstore/include/nix/store/build/derivation-builder.hh @@ -69,7 +69,7 @@ struct DerivationBuilderParams * * @todo this should be part of `Derivation`. */ - const DerivationOptions & drvOptions; + const DerivationOptions & drvOptions; // The remainder is state held during the build. diff --git a/src/libstore/include/nix/store/build/derivation-building-goal.hh b/src/libstore/include/nix/store/build/derivation-building-goal.hh index 547e533e2..8277d6b57 100644 --- a/src/libstore/include/nix/store/build/derivation-building-goal.hh +++ b/src/libstore/include/nix/store/build/derivation-building-goal.hh @@ -52,7 +52,7 @@ private: */ std::unique_ptr drv; - std::unique_ptr drvOptions; + std::unique_ptr> drvOptions; /** * The remainder is state held during the build. diff --git a/src/libstore/include/nix/store/build/derivation-env-desugar.hh b/src/libstore/include/nix/store/build/derivation-env-desugar.hh index 6e2efa6bb..a10ec9fa8 100644 --- a/src/libstore/include/nix/store/build/derivation-env-desugar.hh +++ b/src/libstore/include/nix/store/build/derivation-env-desugar.hh @@ -8,6 +8,7 @@ namespace nix { class Store; struct Derivation; +template struct DerivationOptions; /** @@ -77,7 +78,10 @@ struct DesugaredEnv * just part of `Derivation`. */ static DesugaredEnv create( - Store & store, const Derivation & drv, const DerivationOptions & drvOptions, const StorePathSet & inputPaths); + Store & store, + const Derivation & drv, + const DerivationOptions & drvOptions, + const StorePathSet & inputPaths); }; } // namespace nix diff --git a/src/libstore/include/nix/store/derivation-options.hh b/src/libstore/include/nix/store/derivation-options.hh index 88694f730..0b79af85d 100644 --- a/src/libstore/include/nix/store/derivation-options.hh +++ b/src/libstore/include/nix/store/derivation-options.hh @@ -8,7 +8,8 @@ #include "nix/util/types.hh" #include "nix/util/json-impls.hh" -#include "nix/store/path.hh" +#include "nix/store/store-dir-config.hh" +#include "nix/store/downstream-placeholder.hh" namespace nix { @@ -17,6 +18,9 @@ struct StoreDirConfig; struct BasicDerivation; struct StructuredAttrs; +template +struct DerivedPathMap; + /** * This represents all the special options on a `Derivation`. * @@ -34,6 +38,7 @@ struct StructuredAttrs; * separately. That would be nice to separate concerns, and not make any * environment variable names magical. */ +template struct DerivationOptions { struct OutputChecks @@ -41,13 +46,15 @@ struct DerivationOptions bool ignoreSelfRefs = false; std::optional maxSize, maxClosureSize; + using DrvRef = nix::DrvRef; + /** * env: allowedReferences * * A value of `nullopt` indicates that the check is skipped. * This means that all references are allowed. */ - std::optional allowedReferences; + std::optional> allowedReferences; /** * env: disallowedReferences @@ -55,21 +62,21 @@ struct DerivationOptions * No needed for `std::optional`, because skipping the check is * the same as disallowing the references. */ - StringSet disallowedReferences; + std::set disallowedReferences; /** * env: allowedRequisites * * See `allowedReferences` */ - std::optional allowedRequisites; + std::optional> allowedRequisites; /** * env: disallowedRequisites * * See `disallowedReferences` */ - StringSet disallowedRequisites; + std::set disallowedRequisites; bool operator==(const OutputChecks &) const = default; }; @@ -116,23 +123,7 @@ struct DerivationOptions * attributes give to the builder. The set of paths in the original JSON * is replaced with a list of `PathInfo` in JSON format. */ - std::map exportReferencesGraph; - - /** - * Once a derivations is resolved, the strings in in - * `exportReferencesGraph` should all be store paths (with possible - * suffix paths, but those are discarded). - * - * @return The parsed path set for for each key in the map. - * - * @todo Ideally, `exportReferencesGraph` would just store - * `StorePath`s for this, but we can't just do that, because for CA - * derivations they is actually in general `DerivedPath`s (via - * placeholder strings) until the derivation is resolved and exact - * inputs store paths are known. We can use better types for that - * too, but that is a longer project. - */ - std::map getParsedExportReferencesGraph(const StoreDirConfig & store) const; + std::map> exportReferencesGraph; /** * env: __sandboxProfile @@ -185,18 +176,6 @@ struct DerivationOptions bool operator==(const DerivationOptions &) const = default; - /** - * Parse this information from its legacy encoding as part of the - * environment. This should not be used with nice greenfield formats - * (e.g. JSON) but is necessary for supporting old formats (e.g. - * ATerm). - */ - static DerivationOptions - fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn = true); - - static DerivationOptions - fromStructuredAttrs(const StringMap & env, const std::optional & parsed, bool shouldWarn = true); - /** * @param drv Must be the same derivation we parsed this from. In * the future we'll flip things around so a `BasicDerivation` has @@ -222,7 +201,55 @@ struct DerivationOptions bool useUidRange(const BasicDerivation & drv) const; }; +extern template struct DerivationOptions; +extern template struct DerivationOptions; + +struct DerivationOutput; + +/** + * Parse this information from its legacy encoding as part of the + * environment. This should not be used with nice greenfield formats + * (e.g. JSON) but is necessary for supporting old formats (e.g. + * ATerm). + */ +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const DerivedPathMap & inputDrvs, + const StringMap & env, + const StructuredAttrs * parsed, + bool shouldWarn = true, + const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings); + +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const DerivedPathMap & inputDrvs, + const StringMap & env, + const std::optional & parsed, + bool shouldWarn = true, + const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings); + +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const StringMap & env, + const StructuredAttrs * parsed, + bool shouldWarn = true, + const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings); + +DerivationOptions derivationOptionsFromStructuredAttrs( + const StoreDirConfig & store, + const StringMap & env, + const std::optional & parsed, + bool shouldWarn = true, + const ExperimentalFeatureSettings & mockXpSettings = experimentalFeatureSettings); + +std::optional> tryResolve( + const DerivationOptions & drvOptions, + std::function(ref drvPath, const std::string & outputName)> + queryResolutionChain); + }; // namespace nix -JSON_IMPL(DerivationOptions); -JSON_IMPL(DerivationOptions::OutputChecks) +JSON_IMPL(nix::DerivationOptions); +JSON_IMPL(nix::DerivationOptions); +JSON_IMPL(nix::DerivationOptions::OutputChecks) +JSON_IMPL(nix::DerivationOptions::OutputChecks) diff --git a/src/libstore/include/nix/store/downstream-placeholder.hh b/src/libstore/include/nix/store/downstream-placeholder.hh index ee4d9e3c2..ba3e9faef 100644 --- a/src/libstore/include/nix/store/downstream-placeholder.hh +++ b/src/libstore/include/nix/store/downstream-placeholder.hh @@ -2,11 +2,23 @@ ///@file #include "nix/util/hash.hh" +#include "nix/util/json-impls.hh" #include "nix/store/path.hh" #include "nix/store/derived-path.hh" namespace nix { +/** + * A reference is either to a to-be-registered output (by name), + * or to an already-registered store object (by `Input`). + * + * `Ref +using DrvRef = std::variant; + /** * Downstream Placeholders are opaque and almost certainly unique values * used to allow derivations to refer to store objects which are yet to @@ -92,3 +104,17 @@ public: }; } // namespace nix + +namespace nlohmann { + +template +struct adl_serializer> +{ + static nix::DrvRef from_json(const json & json); + static void to_json(json & json, const nix::DrvRef & t); +}; + +extern template struct adl_serializer>; +extern template struct adl_serializer>; + +} // namespace nlohmann diff --git a/src/libstore/include/nix/store/parsed-derivations.hh b/src/libstore/include/nix/store/parsed-derivations.hh index 52e97b0e7..098591310 100644 --- a/src/libstore/include/nix/store/parsed-derivations.hh +++ b/src/libstore/include/nix/store/parsed-derivations.hh @@ -9,6 +9,7 @@ namespace nix { class Store; +template struct DerivationOptions; struct DerivationOutput; @@ -47,7 +48,7 @@ struct StructuredAttrs nlohmann::json::object_t prepareStructuredAttrs( Store & store, - const DerivationOptions & drvOptions, + const DerivationOptions & drvOptions, const StorePathSet & inputPaths, const DerivationOutputs & outputs) const; diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 8b2a7287e..642ac0978 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -224,11 +224,12 @@ MissingPaths Store::queryMissing(const std::vector & targets) return; auto drv = make_ref(derivationFromPath(drvPath)); - DerivationOptions drvOptions; + DerivationOptions drvOptions; try { // FIXME: this is a lot of work just to get the value // of `allowSubstitutes`. - drvOptions = DerivationOptions::fromStructuredAttrs(drv->env, drv->structuredAttrs); + drvOptions = + derivationOptionsFromStructuredAttrs(*this, drv->inputDrvs, drv->env, drv->structuredAttrs); } catch (Error & e) { e.addTrace({}, "while parsing derivation '%s'", printStorePath(drvPath)); throw; diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index 8d147f65f..95434bd20 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -100,7 +100,7 @@ static nlohmann::json pathInfoToJSON(Store & store, const StorePathSet & storePa nlohmann::json::object_t StructuredAttrs::prepareStructuredAttrs( Store & store, - const DerivationOptions & drvOptions, + const DerivationOptions & drvOptions, const StorePathSet & inputPaths, const DerivationOutputs & outputs) const { @@ -114,8 +114,8 @@ nlohmann::json::object_t StructuredAttrs::prepareStructuredAttrs( json["outputs"] = std::move(outputsJson); /* Handle exportReferencesGraph. */ - for (auto & [key, storePaths] : drvOptions.getParsedExportReferencesGraph(store)) { - json[key] = pathInfoToJSON(store, store.exportReferences(storePaths, storePaths)); + for (auto & [key, storePaths] : drvOptions.exportReferencesGraph) { + json[key] = pathInfoToJSON(store, store.exportReferences(storePaths, inputPaths)); } return json; diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index 4d876c9eb..00ef32dfa 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -554,9 +554,9 @@ static void main_nix_build(int argc, char ** argv) env["NIX_STORE"] = store->storeDir; env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores ? settings.buildCores : settings.getDefaultCores()); - DerivationOptions drvOptions; + DerivationOptions drvOptions; try { - drvOptions = DerivationOptions::fromStructuredAttrs(drv.env, drv.structuredAttrs); + drvOptions = derivationOptionsFromStructuredAttrs(*store, drv.env, drv.structuredAttrs); } catch (Error & e) { e.addTrace({}, "while parsing derivation '%s'", store->printStorePath(packageInfo.requireDrvPath())); throw; diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 590c3fb53..8eb6aaf68 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -64,5 +64,5 @@ fi if isDaemonNewer "2.28pre20241225"; then # test12 should fail (syntactically invalid). expectStderr 1 nix-build -vvv -o "$RESULT" check-refs.nix -A test12 >"$TEST_ROOT/test12.stderr" - grepQuiet -F "output check for 'lib' contains an illegal reference specifier 'dev', expected store path or output name (one of [lib, out])" < "$TEST_ROOT/test12.stderr" + grepQuiet -F "output check for 'lib' contains output name 'dev', but this is not a valid output of this derivation. (Valid outputs are [lib, out].)" < "$TEST_ROOT/test12.stderr" fi