1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-24 22:11:15 +02:00
nix/src/libstore/parsed-derivations.cc
John Ericson d850983f1d Store StructuredAttrs directly in Derivation
Instead of parsing a structured attrs at some later point, we parsed it
right away when parsing the A-Term format, and likewise serialize it to
`__json = <JSON dump>` when serializing a derivation to A-Term.

The JSON format can directly contain the JSON structured attrs without
so encoding it, so we just do that.
2025-06-12 20:46:38 -04:00

204 lines
5.9 KiB
C++

#include "nix/store/parsed-derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/derivations.hh"
#include "nix/store/derivation-options.hh"
#include <nlohmann/json.hpp>
#include <regex>
namespace nix {
static constexpr std::string_view envVarName = "__json";
StructuredAttrs StructuredAttrs::parse(const std::string & encoded)
{
try {
return StructuredAttrs {
.structuredAttrs = nlohmann::json::parse(encoded),
};
} catch (std::exception & e) {
throw Error("cannot process __json attribute: %s", e.what());
}
}
std::optional<StructuredAttrs> StructuredAttrs::tryParse(const StringPairs & env)
{
/* Parse the __json attribute, if any. */
auto jsonAttr = env.find(envVarName);
if (jsonAttr != env.end())
return parse(jsonAttr->second);
else
return {};
}
std::optional<StructuredAttrs> StructuredAttrs::tryExtract(StringPairs & env)
{
/* Parse the __json attribute, if any. */
auto jsonAttr = env.find(envVarName);
if (jsonAttr != env.end()) {
auto encoded = std::move(jsonAttr->second);
env.erase(jsonAttr);
return parse(encoded);
} else
return {};
}
std::pair<std::string_view, std::string> StructuredAttrs::unparse() const
{
return {envVarName, structuredAttrs.dump()};
}
void StructuredAttrs::checkKeyNotInUse(const StringPairs & env)
{
if (env.count(envVarName))
throw Error("Cannot have an environment variable named '__json'. This key is reserved for encoding structured attrs");
}
static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
/**
* Write a JSON representation of store object metadata, such as the
* hash and the references.
*
* @note Do *not* use `ValidPathInfo::toJSON` because this function is
* subject to stronger stability requirements since it is used to
* prepare build environments. Perhaps someday we'll have a versionining
* mechanism to allow this to evolve again and get back in sync, but for
* now we must not change - not even extend - the behavior.
*/
static nlohmann::json pathInfoToJSON(
Store & store,
const StorePathSet & storePaths)
{
using nlohmann::json;
nlohmann::json::array_t jsonList = json::array();
for (auto & storePath : storePaths) {
auto info = store.queryPathInfo(storePath);
auto & jsonPath = jsonList.emplace_back(json::object());
jsonPath["narHash"] = info->narHash.to_string(HashFormat::Nix32, true);
jsonPath["narSize"] = info->narSize;
{
auto & jsonRefs = jsonPath["references"] = json::array();
for (auto & ref : info->references)
jsonRefs.emplace_back(store.printStorePath(ref));
}
if (info->ca)
jsonPath["ca"] = renderContentAddress(info->ca);
// Add the path to the object whose metadata we are including.
jsonPath["path"] = store.printStorePath(storePath);
jsonPath["valid"] = true;
jsonPath["closureSize"] = ({
uint64_t totalNarSize = 0;
StorePathSet closure;
store.computeFSClosure(info->path, closure, false, false);
for (auto & p : closure) {
auto info = store.queryPathInfo(p);
totalNarSize += info->narSize;
}
totalNarSize;
});
}
return jsonList;
}
nlohmann::json StructuredAttrs::prepareStructuredAttrs(
Store & store,
const DerivationOptions & drvOptions,
const StorePathSet & inputPaths,
const DerivationOutputs & outputs) const
{
/* Copy to then modify */
auto json = structuredAttrs;
/* Add an "outputs" object containing the output paths. */
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) {
StorePathSet storePaths;
for (auto & p : inputPaths)
storePaths.insert(store.toStorePath(p).first);
json[key] = pathInfoToJSON(store,
store.exportReferences(storePaths, storePaths));
}
return json;
}
std::string StructuredAttrs::writeShell(const nlohmann::json & json)
{
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string())
return escapeShellArgAlways(value.get<std::string_view>());
if (value.is_number()) {
auto f = value.get<float>();
if (std::ceil(f) == f)
return std::to_string(value.get<int>());
}
if (value.is_null())
return std::string("''");
if (value.is_boolean())
return value.get<bool>() ? std::string("1") : std::string("");
return {};
};
std::string jsonSh;
for (auto & [key, value] : json.items()) {
if (!std::regex_match(key, shVarName)) continue;
auto s = handleSimpleType(value);
if (s)
jsonSh += fmt("declare %s=%s\n", key, *s);
else if (value.is_array()) {
std::string s2;
bool good = true;
for (auto & value2 : value) {
auto s3 = handleSimpleType(value2);
if (!s3) { good = false; break; }
s2 += *s3; s2 += ' ';
}
if (good)
jsonSh += fmt("declare -a %s=(%s)\n", key, s2);
}
else if (value.is_object()) {
std::string s2;
bool good = true;
for (auto & [key2, value2] : value.items()) {
auto s3 = handleSimpleType(value2);
if (!s3) { good = false; break; }
s2 += fmt("[%s]=%s ", escapeShellArgAlways(key2), *s3);
}
if (good)
jsonSh += fmt("declare -A %s=(%s)\n", key, s2);
}
}
return jsonSh;
}
}