diff --git a/src/libstore-tests/derivation-advanced-attrs.cc b/src/libstore-tests/derivation-advanced-attrs.cc index 4408720e8..ce5804126 100644 --- a/src/libstore-tests/derivation-advanced-attrs.cc +++ b/src/libstore-tests/derivation-advanced-attrs.cc @@ -108,10 +108,10 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); - EXPECT_TRUE(!parsedDrv.hasStructuredAttrs()); + EXPECT_TRUE(!parsedDrv); EXPECT_EQ(options.additionalSandboxProfile, ""); EXPECT_EQ(options.noChroot, false); @@ -143,8 +143,8 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_defaults) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{}); }); @@ -157,8 +157,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"}); }); @@ -171,10 +171,10 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); - EXPECT_TRUE(!parsedDrv.hasStructuredAttrs()); + EXPECT_TRUE(!parsedDrv); EXPECT_EQ(options.additionalSandboxProfile, "sandcastle"); EXPECT_EQ(options.noChroot, true); @@ -195,8 +195,8 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ( options.exportReferencesGraph, @@ -243,8 +243,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ( options.exportReferencesGraph, @@ -296,10 +296,10 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); - EXPECT_TRUE(parsedDrv.hasStructuredAttrs()); + EXPECT_TRUE(parsedDrv); EXPECT_EQ(options.additionalSandboxProfile, ""); EXPECT_EQ(options.noChroot, false); @@ -330,8 +330,8 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{}); }); @@ -344,8 +344,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_default auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"}); }); @@ -358,10 +358,10 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); - EXPECT_TRUE(parsedDrv.hasStructuredAttrs()); + EXPECT_TRUE(parsedDrv); EXPECT_EQ(options.additionalSandboxProfile, "sandcastle"); EXPECT_EQ(options.noChroot, true); @@ -392,8 +392,8 @@ TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ( options.exportReferencesGraph, @@ -445,8 +445,8 @@ TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) auto drvPath = writeDerivation(*this->store, got, NoRepair, true); - ParsedDerivation parsedDrv(got); - DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); + auto parsedDrv = StructuredAttrs::tryParse(got.env); + DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, parsedDrv ? &*parsedDrv : nullptr); EXPECT_EQ( options.exportReferencesGraph, diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 8e43ab2ae..1a3d23c48 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -180,9 +180,12 @@ Goal::Co DerivationGoal::haveDerivation() { trace("have derivation"); - parsedDrv = std::make_unique(*drv); + if (auto parsedOpt = StructuredAttrs::tryParse(drv->env)) { + parsedDrv = std::make_unique(*parsedOpt); + } try { - drvOptions = std::make_unique(DerivationOptions::fromParsedDerivation(*parsedDrv)); + drvOptions = std::make_unique( + DerivationOptions::fromStructuredAttrs(drv->env, parsedDrv.get())); } 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 526e682f7..9872265e2 100644 --- a/src/libstore/derivation-options.cc +++ b/src/libstore/derivation-options.cc @@ -1,6 +1,8 @@ #include "nix/store/derivation-options.hh" #include "nix/util/json-utils.hh" #include "nix/store/parsed-derivations.hh" +#include "nix/store/derivations.hh" +#include "nix/store/store-api.hh" #include "nix/util/types.hh" #include "nix/util/util.hh" @@ -11,38 +13,112 @@ namespace nix { +static std::optional +getStringAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name) +{ + if (parsed) { + auto i = parsed->structuredAttrs.find(name); + if (i == parsed->structuredAttrs.end()) + return {}; + else { + if (!i->is_string()) + throw Error("attribute '%s' of must be a string", name); + return i->get(); + } + } else { + auto i = env.find(name); + if (i == env.end()) + return {}; + else + return i->second; + } +} + +static bool getBoolAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name, bool def) +{ + if (parsed) { + auto i = parsed->structuredAttrs.find(name); + if (i == parsed->structuredAttrs.end()) + return def; + else { + if (!i->is_boolean()) + throw Error("attribute '%s' must be a Boolean", name); + return i->get(); + } + } else { + auto i = env.find(name); + if (i == env.end()) + return def; + else + return i->second == "1"; + } +} + +static std::optional +getStringsAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name) +{ + if (parsed) { + auto i = parsed->structuredAttrs.find(name); + if (i == parsed->structuredAttrs.end()) + return {}; + else { + if (!i->is_array()) + throw Error("attribute '%s' must be a list of strings", name); + Strings 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.push_back(j->get()); + } + return res; + } + } else { + auto i = env.find(name); + if (i == env.end()) + return {}; + else + return tokenizeString(i->second); + } +} + +static std::optional +getStringSetAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name) +{ + auto ss = getStringsAttr(env, parsed, name); + return ss ? (std::optional{StringSet{ss->begin(), ss->end()}}) : (std::optional{}); +} + using OutputChecks = DerivationOptions::OutputChecks; using OutputChecksVariant = std::variant>; -DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn) +DerivationOptions +DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn) { DerivationOptions defaults = {}; - auto structuredAttrs = parsed.structuredAttrs.get(); - - if (shouldWarn && structuredAttrs) { - if (get(*structuredAttrs, "allowedReferences")) { + if (shouldWarn && parsed) { + if (get(parsed->structuredAttrs, "allowedReferences")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead"); } - if (get(*structuredAttrs, "allowedRequisites")) { + if (get(parsed->structuredAttrs, "allowedRequisites")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead"); } - if (get(*structuredAttrs, "disallowedRequisites")) { + if (get(parsed->structuredAttrs, "disallowedRequisites")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead"); } - if (get(*structuredAttrs, "disallowedReferences")) { + if (get(parsed->structuredAttrs, "disallowedReferences")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead"); } - if (get(*structuredAttrs, "maxSize")) { + if (get(parsed->structuredAttrs, "maxSize")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead"); } - if (get(*structuredAttrs, "maxClosureSize")) { + if (get(parsed->structuredAttrs, "maxClosureSize")) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead"); } @@ -50,9 +126,9 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation return { .outputChecks = [&]() -> OutputChecksVariant { - if (auto structuredAttrs = parsed.structuredAttrs.get()) { + if (parsed) { std::map res; - if (auto outputChecks = get(*structuredAttrs, "outputChecks")) { + if (auto outputChecks = get(parsed->structuredAttrs, "outputChecks")) { for (auto & [outputName, output] : getObject(*outputChecks)) { OutputChecks checks; @@ -90,10 +166,10 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation return OutputChecks{ // legacy non-structured-attributes case .ignoreSelfRefs = true, - .allowedReferences = parsed.getStringSetAttr("allowedReferences"), - .disallowedReferences = parsed.getStringSetAttr("disallowedReferences").value_or(StringSet{}), - .allowedRequisites = parsed.getStringSetAttr("allowedRequisites"), - .disallowedRequisites = parsed.getStringSetAttr("disallowedRequisites").value_or(StringSet{}), + .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{}), }; } }(), @@ -101,8 +177,8 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation [&] { std::map res; - if (auto structuredAttrs = parsed.structuredAttrs.get()) { - if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) { + if (parsed) { + if (auto udr = get(parsed->structuredAttrs, "unsafeDiscardReferences")) { for (auto & [outputName, output] : getObject(*udr)) { if (!output.is_boolean()) throw Error("attribute 'unsafeDiscardReferences.\"%s\"' must be a Boolean", outputName); @@ -116,8 +192,8 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation .passAsFile = [&] { StringSet res; - if (auto * passAsFileString = get(parsed.drv.env, "passAsFile")) { - if (parsed.hasStructuredAttrs()) { + if (auto * passAsFileString = get(env, "passAsFile")) { + if (parsed) { if (shouldWarn) { warn( "'structuredAttrs' disables the effect of the top-level attribute 'passAsFile'; because all JSON is always passed via file"); @@ -132,18 +208,18 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation [&] { std::map ret; - if (auto structuredAttrs = parsed.structuredAttrs.get()) { - auto e = optionalValueAt(*structuredAttrs, "exportReferencesGraph"); + if (parsed) { + auto e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph"); if (!e || !e->is_object()) return ret; for (auto & [key, storePathsJson] : getObject(*e)) { ret.insert_or_assign(key, storePathsJson); } } else { - auto s = getOr(parsed.drv.env, "exportReferencesGraph", ""); + auto s = getOr(env, "exportReferencesGraph", ""); Strings ss = tokenizeString(s); if (ss.size() % 2 != 0) - throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s); + throw Error("odd number of tokens in 'exportReferencesGraph': '%1%'", s); for (Strings::iterator i = ss.begin(); i != ss.end();) { auto fileName = std::move(*i++); static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*"); @@ -157,15 +233,15 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation return ret; }(), .additionalSandboxProfile = - parsed.getStringAttr("__sandboxProfile").value_or(defaults.additionalSandboxProfile), - .noChroot = parsed.getBoolAttr("__noChroot", defaults.noChroot), - .impureHostDeps = parsed.getStringSetAttr("__impureHostDeps").value_or(defaults.impureHostDeps), - .impureEnvVars = parsed.getStringSetAttr("impureEnvVars").value_or(defaults.impureEnvVars), - .allowLocalNetworking = parsed.getBoolAttr("__darwinAllowLocalNetworking", defaults.allowLocalNetworking), + getStringAttr(env, parsed, "__sandboxProfile").value_or(defaults.additionalSandboxProfile), + .noChroot = getBoolAttr(env, parsed, "__noChroot", defaults.noChroot), + .impureHostDeps = getStringSetAttr(env, parsed, "__impureHostDeps").value_or(defaults.impureHostDeps), + .impureEnvVars = getStringSetAttr(env, parsed, "impureEnvVars").value_or(defaults.impureEnvVars), + .allowLocalNetworking = getBoolAttr(env, parsed, "__darwinAllowLocalNetworking", defaults.allowLocalNetworking), .requiredSystemFeatures = - parsed.getStringSetAttr("requiredSystemFeatures").value_or(defaults.requiredSystemFeatures), - .preferLocalBuild = parsed.getBoolAttr("preferLocalBuild", defaults.preferLocalBuild), - .allowSubstitutes = parsed.getBoolAttr("allowSubstitutes", defaults.allowSubstitutes), + getStringSetAttr(env, parsed, "requiredSystemFeatures").value_or(defaults.requiredSystemFeatures), + .preferLocalBuild = getBoolAttr(env, parsed, "preferLocalBuild", defaults.preferLocalBuild), + .allowSubstitutes = getBoolAttr(env, parsed, "allowSubstitutes", defaults.allowSubstitutes), }; } diff --git a/src/libstore/include/nix/store/build/derivation-goal.hh b/src/libstore/include/nix/store/build/derivation-goal.hh index 3baf4babf..659cea444 100644 --- a/src/libstore/include/nix/store/build/derivation-goal.hh +++ b/src/libstore/include/nix/store/build/derivation-goal.hh @@ -2,6 +2,7 @@ ///@file #include "nix/store/parsed-derivations.hh" +#include "nix/store/derivations.hh" #include "nix/store/derivation-options.hh" #ifndef _WIN32 # include "nix/store/user-lock.hh" @@ -150,7 +151,7 @@ struct DerivationGoal : public Goal */ std::unique_ptr drv; - std::unique_ptr parsedDrv; + std::unique_ptr parsedDrv; std::unique_ptr drvOptions; /** diff --git a/src/libstore/include/nix/store/derivation-options.hh b/src/libstore/include/nix/store/derivation-options.hh index bc9ad3317..16730b5c9 100644 --- a/src/libstore/include/nix/store/derivation-options.hh +++ b/src/libstore/include/nix/store/derivation-options.hh @@ -13,16 +13,16 @@ namespace nix { class Store; struct BasicDerivation; -class ParsedDerivation; +struct StructuredAttrs; /** * This represents all the special options on a `Derivation`. * * Currently, these options are parsed from the environment variables - * with the aid of `ParsedDerivation`. + * with the aid of `StructuredAttrs`. * * The first goal of this data type is to make sure that no other code - * uses `ParsedDerivation` to ad-hoc parse some additional options. That + * uses `StructuredAttrs` to ad-hoc parse some additional options. That * ensures this data type is up to date and fully correct. * * The second goal of this data type is to allow an alternative to @@ -173,7 +173,8 @@ struct DerivationOptions * (e.g. JSON) but is necessary for supporing old formats (e.g. * ATerm). */ - static DerivationOptions fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn = true); + static DerivationOptions + fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn = true); /** * @param drv Must be the same derivation we parsed this from. In diff --git a/src/libstore/include/nix/store/parsed-derivations.hh b/src/libstore/include/nix/store/parsed-derivations.hh index e23eab1bb..a7c053a8f 100644 --- a/src/libstore/include/nix/store/parsed-derivations.hh +++ b/src/libstore/include/nix/store/parsed-derivations.hh @@ -1,51 +1,43 @@ #pragma once ///@file -#include "nix/store/derivations.hh" -#include "nix/store/store-api.hh" +#include -#include +#include "nix/util/types.hh" +#include "nix/store/path.hh" namespace nix { +class Store; struct DerivationOptions; +struct DerivationOutput; -class ParsedDerivation +typedef std::map DerivationOutputs; + +struct StructuredAttrs { - BasicDerivation & drv; - std::unique_ptr structuredAttrs; + nlohmann::json structuredAttrs; - std::optional getStringAttr(const std::string & name) const; + static std::optional tryParse(const StringPairs & env); - bool getBoolAttr(const std::string & name, bool def = false) const; - - std::optional getStringsAttr(const std::string & name) const; - - std::optional getStringSetAttr(const std::string & name) const; + nlohmann::json prepareStructuredAttrs( + Store & store, + const DerivationOptions & drvOptions, + const StorePathSet & inputPaths, + const DerivationOutputs & outputs) const; /** - * Only `DerivationOptions` is allowed to parse individual fields - * from `ParsedDerivation`. This ensure that it includes all - * derivation options, and, the likes of `LocalDerivationGoal` are - * incapable of more ad-hoc options. + * As a convenience to bash scripts, write a shell file that + * maps all attributes that are representable in bash - + * namely, strings, integers, nulls, Booleans, and arrays and + * objects consisting entirely of those values. (So nested + * arrays or objects are not supported.) + * + * @param prepared This should be the result of + * `prepareStructuredAttrs`, *not* the original `structuredAttrs` + * field. */ - friend struct DerivationOptions; - -public: - - ParsedDerivation(BasicDerivation & drv); - - ~ParsedDerivation(); - - bool hasStructuredAttrs() const - { - return static_cast(structuredAttrs); - } - - std::optional - prepareStructuredAttrs(Store & store, const DerivationOptions & drvOptions, const StorePathSet & inputPaths); + static std::string writeShell(const nlohmann::json & prepared); }; -std::string writeStructuredAttrsShell(const nlohmann::json & json); - } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index b9e729092..c56b2cfe3 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -222,10 +222,12 @@ void Store::queryMissing(const std::vector & targets, if (knownOutputPaths && invalid.empty()) return; auto drv = make_ref(derivationFromPath(drvPath)); - ParsedDerivation parsedDrv(*drv); + auto parsedDrv = StructuredAttrs::tryParse(drv->env); DerivationOptions drvOptions; try { - drvOptions = DerivationOptions::fromParsedDerivation(parsedDrv); + drvOptions = DerivationOptions::fromStructuredAttrs( + drv->env, + parsedDrv ? &*parsedDrv : nullptr); } 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 218a1cf35..61af101de 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -1,4 +1,6 @@ #include "nix/store/parsed-derivations.hh" +#include "nix/store/store-api.hh" +#include "nix/store/derivations.hh" #include "nix/store/derivation-options.hh" #include @@ -6,94 +8,20 @@ namespace nix { -ParsedDerivation::ParsedDerivation(BasicDerivation & drv) - : drv(drv) +std::optional StructuredAttrs::tryParse(const StringPairs & env) { /* Parse the __json attribute, if any. */ - auto jsonAttr = drv.env.find("__json"); - if (jsonAttr != drv.env.end()) { + auto jsonAttr = env.find("__json"); + if (jsonAttr != env.end()) { try { - structuredAttrs = std::make_unique(nlohmann::json::parse(jsonAttr->second)); + return StructuredAttrs { + .structuredAttrs = nlohmann::json::parse(jsonAttr->second), + }; } catch (std::exception & e) { throw Error("cannot process __json attribute: %s", e.what()); } } -} - -ParsedDerivation::~ParsedDerivation() { } - -std::optional ParsedDerivation::getStringAttr(const std::string & name) const -{ - if (structuredAttrs) { - auto i = structuredAttrs->find(name); - if (i == structuredAttrs->end()) - return {}; - else { - if (!i->is_string()) - throw Error("attribute '%s' of must be a string", name); - return i->get(); - } - } else { - auto i = drv.env.find(name); - if (i == drv.env.end()) - return {}; - else - return i->second; - } -} - -bool ParsedDerivation::getBoolAttr(const std::string & name, bool def) const -{ - if (structuredAttrs) { - auto i = structuredAttrs->find(name); - if (i == structuredAttrs->end()) - return def; - else { - if (!i->is_boolean()) - throw Error("attribute '%s' must be a Boolean", name); - return i->get(); - } - } else { - auto i = drv.env.find(name); - if (i == drv.env.end()) - return def; - else - return i->second == "1"; - } -} - -std::optional ParsedDerivation::getStringsAttr(const std::string & name) const -{ - if (structuredAttrs) { - auto i = structuredAttrs->find(name); - if (i == structuredAttrs->end()) - return {}; - else { - if (!i->is_array()) - throw Error("attribute '%s' must be a list of strings", name); - Strings 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.push_back(j->get()); - } - return res; - } - } else { - auto i = drv.env.find(name); - if (i == drv.env.end()) - return {}; - else - return tokenizeString(i->second); - } -} - -std::optional ParsedDerivation::getStringSetAttr(const std::string & name) const -{ - auto ss = getStringsAttr(name); - return ss - ? (std::optional{StringSet{ss->begin(), ss->end()}}) - : (std::optional{}); + return {}; } static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); @@ -152,20 +80,20 @@ static nlohmann::json pathInfoToJSON( return jsonList; } -std::optional ParsedDerivation::prepareStructuredAttrs( +nlohmann::json StructuredAttrs::prepareStructuredAttrs( Store & store, const DerivationOptions & drvOptions, - const StorePathSet & inputPaths) + const StorePathSet & inputPaths, + const DerivationOutputs & outputs) const { - if (!structuredAttrs) return std::nullopt; - - auto json = *structuredAttrs; + /* Copy to then modify */ + auto json = structuredAttrs; /* Add an "outputs" object containing the output paths. */ - nlohmann::json outputs; - for (auto & i : drv.outputs) - outputs[i.first] = hashPlaceholder(i.first); - json["outputs"] = outputs; + nlohmann::json outputsJson; + for (auto & i : outputs) + outputsJson[i.first] = hashPlaceholder(i.first); + json["outputs"] = std::move(outputsJson); /* Handle exportReferencesGraph. */ for (auto & [key, inputPaths] : drvOptions.exportReferencesGraph) { @@ -179,12 +107,7 @@ std::optional ParsedDerivation::prepareStructuredAttrs( return json; } -/* As a convenience to bash scripts, write a shell file that - maps all attributes that are representable in bash - - namely, strings, integers, nulls, Booleans, and arrays and - objects consisting entirely of those values. (So nested - arrays or objects are not supported.) */ -std::string writeStructuredAttrsShell(const nlohmann::json & json) +std::string StructuredAttrs::writeShell(const nlohmann::json & json) { auto handleSimpleType = [](const nlohmann::json & value) -> std::optional { diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 965ece136..7f921a23a 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -971,7 +971,7 @@ void LocalDerivationGoal::startBuilder() writeStructuredAttrs(); /* Handle exportReferencesGraph(), if set. */ - if (!parsedDrv->hasStructuredAttrs()) { + if (!parsedDrv) { for (auto & [fileName, ss] : drvOptions->exportReferencesGraph) { StorePathSet storePathSet; for (auto & storePathS : ss) { @@ -1475,7 +1475,7 @@ void LocalDerivationGoal::initTmpDir() /* In non-structured mode, set all bindings either directory in the environment or via a file, as specified by `DerivationOptions::passAsFile`. */ - if (!parsedDrv->hasStructuredAttrs()) { + if (!parsedDrv) { for (auto & i : drv->env) { if (drvOptions->passAsFile.find(i.first) == drvOptions->passAsFile.end()) { env[i.first] = i.second; @@ -1576,8 +1576,12 @@ void LocalDerivationGoal::initEnv() void LocalDerivationGoal::writeStructuredAttrs() { - if (auto structAttrsJson = parsedDrv->prepareStructuredAttrs(worker.store, *drvOptions, inputPaths)) { - auto json = structAttrsJson.value(); + if (parsedDrv) { + auto json = parsedDrv->prepareStructuredAttrs( + worker.store, + *drvOptions, + inputPaths, + drv->outputs); nlohmann::json rewritten; for (auto & [i, v] : json["outputs"].get()) { /* The placeholder must have a rewrite, so we use it to cover both the @@ -1587,7 +1591,7 @@ void LocalDerivationGoal::writeStructuredAttrs() json["outputs"] = rewritten; - auto jsonSh = writeStructuredAttrsShell(json); + auto jsonSh = StructuredAttrs::writeShell(json); writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites)); chownToBuilder(tmpDir + "/.attrs.sh"); diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index c2368dc5b..6815e0cdd 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -544,10 +544,12 @@ static void main_nix_build(int argc, char * * argv) env["NIX_STORE"] = store->storeDir; env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); - ParsedDerivation parsedDrv(drv); + auto parsedDrv = StructuredAttrs::tryParse(drv.env); DerivationOptions drvOptions; try { - drvOptions = DerivationOptions::fromParsedDerivation(parsedDrv); + drvOptions = DerivationOptions::fromStructuredAttrs( + drv.env, + parsedDrv ? &*parsedDrv : nullptr); } catch (Error & e) { e.addTrace({}, "while parsing derivation '%s'", store->printStorePath(packageInfo.requireDrvPath())); throw; @@ -566,7 +568,7 @@ static void main_nix_build(int argc, char * * argv) std::string structuredAttrsRC; - if (parsedDrv.hasStructuredAttrs()) { + if (parsedDrv) { StorePathSet inputs; std::function::ChildNode &)> accumInputClosure; @@ -584,19 +586,22 @@ static void main_nix_build(int argc, char * * argv) for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map) accumInputClosure(inputDrv, inputNode); - if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, drvOptions, inputs)) { - auto json = structAttrs.value(); - structuredAttrsRC = writeStructuredAttrsShell(json); + auto json = parsedDrv->prepareStructuredAttrs( + *store, + drvOptions, + inputs, + drv.outputs); - auto attrsJSON = (tmpDir.path() / ".attrs.json").string(); - writeFile(attrsJSON, json.dump()); + structuredAttrsRC = StructuredAttrs::writeShell(json); - auto attrsSH = (tmpDir.path() / ".attrs.sh").string(); - writeFile(attrsSH, structuredAttrsRC); + auto attrsJSON = (tmpDir.path() / ".attrs.json").string(); + writeFile(attrsJSON, json.dump()); - env["NIX_ATTRS_SH_FILE"] = attrsSH; - env["NIX_ATTRS_JSON_FILE"] = attrsJSON; - } + auto attrsSH = (tmpDir.path() / ".attrs.sh").string(); + writeFile(attrsSH, structuredAttrsRC); + + env["NIX_ATTRS_SH_FILE"] = attrsSH; + env["NIX_ATTRS_JSON_FILE"] = attrsJSON; } /* Run a shell using the derivation's environment. For