Move structured attrs handling into a separate class

This is primarily because Derivation::{can,will}BuildLocally() depends
on attributes like preferLocalBuild and requiredSystemFeatures, but it
can't handle them properly because it doesn't have access to the
structured attributes.
This commit is contained in:
Eelco Dolstra 2018-09-28 14:31:16 +02:00
parent 99d4bb2d4c
commit 7ae7a38c9a
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
5 changed files with 145 additions and 116 deletions

View file

@ -11,6 +11,7 @@
#include "compression.hh" #include "compression.hh"
#include "json.hh" #include "json.hh"
#include "nar-info.hh" #include "nar-info.hh"
#include "parsed-derivations.hh"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -740,8 +741,7 @@ private:
/* The derivation stored at drvPath. */ /* The derivation stored at drvPath. */
std::unique_ptr<BasicDerivation> drv; std::unique_ptr<BasicDerivation> drv;
/* The contents of drv->env["__json"]. */ std::unique_ptr<ParsedDerivation> parsedDrv;
std::experimental::optional<nlohmann::json> structuredAttrs;
/* The remainder is state held during the build. */ /* The remainder is state held during the build. */
@ -923,13 +923,6 @@ private:
/* Fill in the environment for the builder. */ /* Fill in the environment for the builder. */
void initEnv(); void initEnv();
/* Get an attribute from drv->env or from drv->env["__json"]. */
std::experimental::optional<std::string> getAttr(const std::string & name);
bool getBoolAttr(const std::string & name, bool def = false);
std::experimental::optional<Strings> getStringsAttr(const std::string & name);
/* Write a JSON file containing the derivation attributes. */ /* Write a JSON file containing the derivation attributes. */
void writeStructuredAttrs(); void writeStructuredAttrs();
@ -1149,15 +1142,7 @@ void DerivationGoal::haveDerivation()
return; return;
} }
/* Parse the __json attribute, if any. */ parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
auto jsonAttr = drv->env.find("__json");
if (jsonAttr != drv->env.end()) {
try {
structuredAttrs = nlohmann::json::parse(jsonAttr->second);
} catch (std::exception & e) {
throw Error("cannot process __json attribute of '%s': %s", drvPath, e.what());
}
}
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build through substitutes. If that doesn't work, we'll build
@ -1415,7 +1400,7 @@ void DerivationGoal::tryToBuild()
/* Don't do a remote build if the derivation has the attribute /* Don't do a remote build if the derivation has the attribute
`preferLocalBuild' set. Also, check and repair modes are only `preferLocalBuild' set. Also, check and repair modes are only
supported for local builds. */ supported for local builds. */
bool buildLocally = buildMode != bmNormal || drv->willBuildLocally(); bool buildLocally = buildMode != bmNormal || parsedDrv->willBuildLocally();
auto started = [&]() { auto started = [&]() {
auto msg = fmt( auto msg = fmt(
@ -1664,7 +1649,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Tell the hook about system features (beyond the system type) /* Tell the hook about system features (beyond the system type)
required from the build machine. (The hook could parse the required from the build machine. (The hook could parse the
drv file itself, but this is easier.) */ drv file itself, but this is easier.) */
auto features = getStringsAttr("requiredSystemFeatures").value_or(Strings()); auto features = parsedDrv->getStringsAttr("requiredSystemFeatures").value_or(Strings());
/* Send the request to the hook. */ /* Send the request to the hook. */
worker.hook->sink worker.hook->sink
@ -1812,7 +1797,7 @@ static void preloadNSS() {
void DerivationGoal::startBuilder() void DerivationGoal::startBuilder()
{ {
/* Right platform? */ /* Right platform? */
if (!drv->canBuildLocally()) { if (!parsedDrv->canBuildLocally()) {
throw Error( throw Error(
format("a '%1%' is required to build '%3%', but I am a '%2%'") format("a '%1%' is required to build '%3%', but I am a '%2%'")
% drv->platform % settings.thisSystem % drvPath); % drv->platform % settings.thisSystem % drvPath);
@ -1822,12 +1807,12 @@ void DerivationGoal::startBuilder()
preloadNSS(); preloadNSS();
#if __APPLE__ #if __APPLE__
additionalSandboxProfile = getAttr("__sandboxProfile").value_or(""); additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
#endif #endif
/* Are we doing a chroot build? */ /* Are we doing a chroot build? */
{ {
auto noChroot = getBoolAttr("__noChroot"); auto noChroot = parsedDrv->getBoolAttr("__noChroot");
if (settings.sandboxMode == smEnabled) { if (settings.sandboxMode == smEnabled) {
if (noChroot) if (noChroot)
throw Error(format("derivation '%1%' has '__noChroot' set, " throw Error(format("derivation '%1%' has '__noChroot' set, "
@ -1893,7 +1878,7 @@ void DerivationGoal::startBuilder()
writeStructuredAttrs(); writeStructuredAttrs();
/* Handle exportReferencesGraph(), if set. */ /* Handle exportReferencesGraph(), if set. */
if (!structuredAttrs) { if (!parsedDrv->getStructuredAttrs()) {
/* The `exportReferencesGraph' feature allows the references graph /* The `exportReferencesGraph' feature allows the references graph
to be passed to a builder. This attribute should be a list of to be passed to a builder. This attribute should be a list of
pairs [name1 path1 name2 path2 ...]. The references graph of pairs [name1 path1 name2 path2 ...]. The references graph of
@ -1958,7 +1943,7 @@ void DerivationGoal::startBuilder()
PathSet allowedPaths = settings.allowedImpureHostPrefixes; PathSet allowedPaths = settings.allowedImpureHostPrefixes;
/* This works like the above, except on a per-derivation level */ /* This works like the above, except on a per-derivation level */
auto impurePaths = getStringsAttr("__impureHostDeps").value_or(Strings()); auto impurePaths = parsedDrv->getStringsAttr("__impureHostDeps").value_or(Strings());
for (auto & i : impurePaths) { for (auto & i : impurePaths) {
bool found = false; bool found = false;
@ -2326,7 +2311,7 @@ void DerivationGoal::initEnv()
passAsFile is ignored in structure mode because it's not passAsFile is ignored in structure mode because it's not
needed (attributes are not passed through the environment, so needed (attributes are not passed through the environment, so
there is no size constraint). */ there is no size constraint). */
if (!structuredAttrs) { if (!parsedDrv->getStructuredAttrs()) {
StringSet passAsFile = tokenizeString<StringSet>(get(drv->env, "passAsFile")); StringSet passAsFile = tokenizeString<StringSet>(get(drv->env, "passAsFile"));
int fileNr = 0; int fileNr = 0;
@ -2373,7 +2358,7 @@ void DerivationGoal::initEnv()
fixed-output derivations is by definition pure (since we fixed-output derivations is by definition pure (since we
already know the cryptographic hash of the output). */ already know the cryptographic hash of the output). */
if (fixedOutput) { if (fixedOutput) {
for (auto & i : getStringsAttr("impureEnvVars").value_or(Strings())) for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings()))
env[i] = getEnv(i); env[i] = getEnv(i);
} }
@ -2384,80 +2369,12 @@ void DerivationGoal::initEnv()
} }
std::experimental::optional<std::string> DerivationGoal::getAttr(const std::string & name)
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return {};
else {
if (!i->is_string())
throw Error("attribute '%s' of derivation '%s' must be a string", name, drvPath);
return i->get<std::string>();
}
} else {
auto i = drv->env.find(name);
if (i == drv->env.end())
return {};
else
return i->second;
}
}
bool DerivationGoal::getBoolAttr(const std::string & name, bool def)
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return def;
else {
if (!i->is_boolean())
throw Error("attribute '%s' of derivation '%s' must be a Boolean", name, drvPath);
return i->get<bool>();
}
} else {
auto i = drv->env.find(name);
if (i == drv->env.end())
return def;
else
return i->second == "1";
}
}
std::experimental::optional<Strings> DerivationGoal::getStringsAttr(const std::string & name)
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return {};
else {
if (!i->is_array())
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath);
Strings res;
for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string())
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath);
res.push_back(j->get<std::string>());
}
return res;
}
} else {
auto i = drv->env.find(name);
if (i == drv->env.end())
return {};
else
return tokenizeString<Strings>(i->second);
}
}
static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
void DerivationGoal::writeStructuredAttrs() void DerivationGoal::writeStructuredAttrs()
{ {
auto & structuredAttrs = parsedDrv->getStructuredAttrs();
if (!structuredAttrs) return; if (!structuredAttrs) return;
auto json = *structuredAttrs; auto json = *structuredAttrs;
@ -2997,7 +2914,7 @@ void DerivationGoal::runChild()
writeFile(sandboxFile, sandboxProfile); writeFile(sandboxFile, sandboxProfile);
bool allowLocalNetworking = getBoolAttr("__darwinAllowLocalNetworking"); bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */ to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
@ -3283,7 +3200,7 @@ void DerivationGoal::registerOutputs()
/* Enforce `allowedReferences' and friends. */ /* Enforce `allowedReferences' and friends. */
auto checkRefs = [&](const string & attrName, bool allowed, bool recursive) { auto checkRefs = [&](const string & attrName, bool allowed, bool recursive) {
auto value = getStringsAttr(attrName); auto value = parsedDrv->getStringsAttr(attrName);
if (!value) return; if (!value) return;
PathSet spec = parseReferenceSpecifiers(worker.store, *drv, *value); PathSet spec = parseReferenceSpecifiers(worker.store, *drv, *value);

View file

@ -36,12 +36,6 @@ Path BasicDerivation::findOutput(const string & id) const
} }
bool BasicDerivation::willBuildLocally() const
{
return get(env, "preferLocalBuild") == "1" && canBuildLocally();
}
bool BasicDerivation::substitutesAllowed() const bool BasicDerivation::substitutesAllowed() const
{ {
return get(env, "allowSubstitutes", "1") == "1"; return get(env, "allowSubstitutes", "1") == "1";
@ -54,14 +48,6 @@ bool BasicDerivation::isBuiltin() const
} }
bool BasicDerivation::canBuildLocally() const
{
return platform == settings.thisSystem
|| settings.extraPlatforms.get().count(platform) > 0
|| isBuiltin();
}
Path writeDerivation(ref<Store> store, Path writeDerivation(ref<Store> store,
const Derivation & drv, const string & name, RepairFlag repair) const Derivation & drv, const string & name, RepairFlag repair)
{ {

View file

@ -56,14 +56,10 @@ struct BasicDerivation
the given derivation. */ the given derivation. */
Path findOutput(const string & id) const; Path findOutput(const string & id) const;
bool willBuildLocally() const;
bool substitutesAllowed() const; bool substitutesAllowed() const;
bool isBuiltin() const; bool isBuiltin() const;
bool canBuildLocally() const;
/* Return true iff this is a fixed-output derivation. */ /* Return true iff this is a fixed-output derivation. */
bool isFixedOutput() const; bool isFixedOutput() const;

View file

@ -0,0 +1,97 @@
#include "parsed-derivations.hh"
namespace nix {
ParsedDerivation::ParsedDerivation(const Path & drvPath, BasicDerivation & drv)
: drvPath(drvPath), drv(drv)
{
/* Parse the __json attribute, if any. */
auto jsonAttr = drv.env.find("__json");
if (jsonAttr != drv.env.end()) {
try {
structuredAttrs = nlohmann::json::parse(jsonAttr->second);
} catch (std::exception & e) {
throw Error("cannot process __json attribute of '%s': %s", drvPath, e.what());
}
}
}
std::experimental::optional<std::string> 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 derivation '%s' must be a string", name, drvPath);
return i->get<std::string>();
}
} 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' of derivation '%s' must be a Boolean", name, drvPath);
return i->get<bool>();
}
} else {
auto i = drv.env.find(name);
if (i == drv.env.end())
return def;
else
return i->second == "1";
}
}
std::experimental::optional<Strings> 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' of derivation '%s' must be a list of strings", name, drvPath);
Strings res;
for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string())
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath);
res.push_back(j->get<std::string>());
}
return res;
}
} else {
auto i = drv.env.find(name);
if (i == drv.env.end())
return {};
else
return tokenizeString<Strings>(i->second);
}
}
bool ParsedDerivation::canBuildLocally() const
{
return drv.platform == settings.thisSystem
|| settings.extraPlatforms.get().count(drv.platform) > 0
|| drv.isBuiltin();
}
bool ParsedDerivation::willBuildLocally() const
{
return getBoolAttr("preferLocalBuild") && canBuildLocally();
}
}

View file

@ -0,0 +1,33 @@
#include "derivations.hh"
#include <nlohmann/json.hpp>
namespace nix {
class ParsedDerivation
{
Path drvPath;
BasicDerivation & drv;
std::experimental::optional<nlohmann::json> structuredAttrs;
public:
ParsedDerivation(const Path & drvPath, BasicDerivation & drv);
const std::experimental::optional<nlohmann::json> & getStructuredAttrs() const
{
return structuredAttrs;
}
std::experimental::optional<std::string> getStringAttr(const std::string & name) const;
bool getBoolAttr(const std::string & name, bool def = false) const;
std::experimental::optional<Strings> getStringsAttr(const std::string & name) const;
bool canBuildLocally() const;
bool willBuildLocally() const;
};
}