Create Derivation::fromJSON

And test, of course
This commit is contained in:
John Ericson 2023-02-17 18:37:35 -05:00
parent 81dfc2b012
commit fe9cbe838c
5 changed files with 197 additions and 19 deletions

View file

@ -3,6 +3,7 @@
#include <variant> #include <variant>
#include "hash.hh" #include "hash.hh"
#include "comparator.hh"
namespace nix { namespace nix {
@ -30,6 +31,8 @@ struct TextHash {
* Hash of the contents of the text/file. * Hash of the contents of the text/file.
*/ */
Hash hash; Hash hash;
GENERATE_CMP(TextHash, me->hash);
}; };
/** /**
@ -46,6 +49,8 @@ struct FixedOutputHash {
Hash hash; Hash hash;
std::string printMethodAlgo() const; std::string printMethodAlgo() const;
GENERATE_CMP(FixedOutputHash, me->method, me->hash);
}; };
/** /**

View file

@ -889,6 +889,7 @@ std::optional<BasicDerivation> Derivation::tryResolve(
return resolved; return resolved;
} }
const Hash impureOutputHash = hashString(htSHA256, "impure"); const Hash impureOutputHash = hashString(htSHA256, "impure");
nlohmann::json DerivationOutput::toJSON( nlohmann::json DerivationOutput::toJSON(
@ -916,6 +917,73 @@ nlohmann::json DerivationOutput::toJSON(
return res; return res;
} }
DerivationOutput DerivationOutput::fromJSON(
const Store & store, std::string_view drvName, std::string_view outputName,
const nlohmann::json & _json)
{
std::set<std::string_view> keys;
auto json = (std::map<std::string, nlohmann::json>) _json;
for (const auto & [key, _] : json)
keys.insert(key);
auto methodAlgo = [&]() -> std::pair<FileIngestionMethod, HashType> {
std::string hashAlgo = json["hashAlgo"];
auto method = FileIngestionMethod::Flat;
if (hashAlgo.substr(0, 2) == "r:") {
method = FileIngestionMethod::Recursive;
hashAlgo = hashAlgo.substr(2);
}
auto hashType = parseHashType(hashAlgo);
return { method, hashType };
};
if (keys == (std::set<std::string_view> { "path" })) {
return DerivationOutput::InputAddressed {
.path = store.parseStorePath((std::string) json["path"]),
};
}
else if (keys == (std::set<std::string_view> { "path", "hashAlgo", "hash" })) {
auto [method, hashType] = methodAlgo();
auto dof = DerivationOutput::CAFixed {
.hash = {
.method = method,
.hash = Hash::parseNonSRIUnprefixed((std::string) json["hash"], hashType),
},
};
if (dof.path(store, drvName, outputName) != store.parseStorePath((std::string) json["path"]))
throw Error("Path doesn't match derivation output");
return dof;
}
else if (keys == (std::set<std::string_view> { "hashAlgo" })) {
auto [method, hashType] = methodAlgo();
return DerivationOutput::CAFloating {
.method = method,
.hashType = hashType,
};
}
else if (keys == (std::set<std::string_view> { })) {
return DerivationOutput::Deferred {};
}
else if (keys == (std::set<std::string_view> { "hashAlgo", "impure" })) {
auto [method, hashType] = methodAlgo();
return DerivationOutput::Impure {
.method = method,
.hashType = hashType,
};
}
else {
throw Error("invalid JSON for derivation output");
}
}
nlohmann::json Derivation::toJSON(const Store & store) const nlohmann::json Derivation::toJSON(const Store & store) const
{ {
nlohmann::json res = nlohmann::json::object(); nlohmann::json res = nlohmann::json::object();
@ -950,4 +1018,41 @@ nlohmann::json Derivation::toJSON(const Store & store) const
return res; return res;
} }
Derivation Derivation::fromJSON(
const Store & store, std::string_view drvName,
const nlohmann::json & json)
{
Derivation res;
{
auto & outputsObj = json["outputs"];
for (auto & [outputName, output] : outputsObj.items()) {
res.outputs.insert_or_assign(
outputName,
DerivationOutput::fromJSON(store, drvName, outputName, output));
}
}
{
auto & inputsList = json["inputSrcs"];
for (auto & input : inputsList)
res.inputSrcs.insert(store.parseStorePath(static_cast<const std::string &>(input)));
}
{
auto & inputDrvsObj = json["inputDrvs"];
for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items())
res.inputDrvs[store.parseStorePath(inputDrvPath)] =
static_cast<const StringSet &>(inputOutputs);
}
res.platform = json["system"];
res.builder = json["builder"];
res.args = json["args"];
res.env = json["env"];
return res;
}
} }

View file

@ -7,6 +7,7 @@
#include "content-address.hh" #include "content-address.hh"
#include "repair-flag.hh" #include "repair-flag.hh"
#include "sync.hh" #include "sync.hh"
#include "comparator.hh"
#include <map> #include <map>
#include <variant> #include <variant>
@ -24,6 +25,8 @@ class Store;
struct DerivationOutputInputAddressed struct DerivationOutputInputAddressed
{ {
StorePath path; StorePath path;
GENERATE_CMP(DerivationOutputInputAddressed, me->path);
}; };
/** /**
@ -44,6 +47,8 @@ struct DerivationOutputCAFixed
* @param outputName The name of this output. * @param outputName The name of this output.
*/ */
StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const; StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;
GENERATE_CMP(DerivationOutputCAFixed, me->hash);
}; };
/** /**
@ -62,13 +67,17 @@ struct DerivationOutputCAFloating
* How the serialization will be hashed * How the serialization will be hashed
*/ */
HashType hashType; HashType hashType;
GENERATE_CMP(DerivationOutputCAFloating, me->method, me->hashType);
}; };
/** /**
* Input-addressed output which depends on a (CA) derivation whose hash * Input-addressed output which depends on a (CA) derivation whose hash
* isn't known yet. * isn't known yet.
*/ */
struct DerivationOutputDeferred {}; struct DerivationOutputDeferred {
GENERATE_CMP(DerivationOutputDeferred);
};
/** /**
* Impure output which is moved to a content-addressed location (like * Impure output which is moved to a content-addressed location (like
@ -85,6 +94,8 @@ struct DerivationOutputImpure
* How the serialization will be hashed * How the serialization will be hashed
*/ */
HashType hashType; HashType hashType;
GENERATE_CMP(DerivationOutputImpure, me->method, me->hashType);
}; };
typedef std::variant< typedef std::variant<
@ -125,6 +136,11 @@ struct DerivationOutput : _DerivationOutputRaw
const Store & store, const Store & store,
std::string_view drvName, std::string_view drvName,
std::string_view outputName) const; std::string_view outputName) const;
static DerivationOutput fromJSON(
const Store & store,
std::string_view drvName,
std::string_view outputName,
const nlohmann::json & json);
}; };
typedef std::map<std::string, DerivationOutput> DerivationOutputs; typedef std::map<std::string, DerivationOutput> DerivationOutputs;
@ -273,6 +289,15 @@ struct BasicDerivation
DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const; DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const;
static std::string_view nameFromPath(const StorePath & storePath); static std::string_view nameFromPath(const StorePath & storePath);
GENERATE_CMP(BasicDerivation,
me->outputs,
me->inputSrcs,
me->platform,
me->builder,
me->args,
me->env,
me->name);
}; };
struct Derivation : BasicDerivation struct Derivation : BasicDerivation
@ -313,6 +338,14 @@ struct Derivation : BasicDerivation
Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { } Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
nlohmann::json toJSON(const Store & store) const; nlohmann::json toJSON(const Store & store) const;
static Derivation fromJSON(
const Store & store,
std::string_view drvName,
const nlohmann::json & json);
GENERATE_CMP(Derivation,
static_cast<const BasicDerivation &>(*me),
me->inputDrvs);
}; };

View file

@ -11,15 +11,29 @@ class DerivationTest : public LibStoreTest
{ {
}; };
#define TEST_JSON(TYPE, NAME, STR, VAL, ...) \ #define TEST_JSON(NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
TEST_F(DerivationTest, TYPE ## _ ## NAME ## _to_json) { \ TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _to_json) { \
using nlohmann::literals::operator "" _json; \ using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \ ASSERT_EQ( \
STR ## _json, \ STR ## _json, \
(TYPE { VAL }).toJSON(*store __VA_OPT__(,) __VA_ARGS__)); \ (DerivationOutput { VAL }).toJSON( \
*store, \
DRV_NAME, \
OUTPUT_NAME)); \
} \
\
TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _from_json) { \
using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \
DerivationOutput { VAL }, \
DerivationOutput::fromJSON( \
*store, \
DRV_NAME, \
OUTPUT_NAME, \
STR ## _json)); \
} }
TEST_JSON(DerivationOutput, inputAddressed, TEST_JSON(inputAddressed,
R"({ R"({
"path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name" "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
})", })",
@ -28,7 +42,7 @@ TEST_JSON(DerivationOutput, inputAddressed,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(DerivationOutput, caFixed, TEST_JSON(caFixed,
R"({ R"({
"hashAlgo": "r:sha256", "hashAlgo": "r:sha256",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
@ -42,7 +56,7 @@ TEST_JSON(DerivationOutput, caFixed,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(DerivationOutput, caFloating, TEST_JSON(caFloating,
R"({ R"({
"hashAlgo": "r:sha256" "hashAlgo": "r:sha256"
})", })",
@ -52,12 +66,12 @@ TEST_JSON(DerivationOutput, caFloating,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(DerivationOutput, deferred, TEST_JSON(deferred,
R"({ })", R"({ })",
DerivationOutput::Deferred { }, DerivationOutput::Deferred { },
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(DerivationOutput, impure, TEST_JSON(impure,
R"({ R"({
"hashAlgo": "r:sha256", "hashAlgo": "r:sha256",
"impure": true "impure": true
@ -68,7 +82,27 @@ TEST_JSON(DerivationOutput, impure,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(Derivation, impure, #undef TEST_JSON
#define TEST_JSON(NAME, STR, VAL, DRV_NAME) \
TEST_F(DerivationTest, Derivation_ ## NAME ## _to_json) { \
using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \
STR ## _json, \
(Derivation { VAL }).toJSON(*store)); \
} \
\
TEST_F(DerivationTest, Derivation_ ## NAME ## _from_json) { \
using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \
Derivation { VAL }, \
Derivation::fromJSON( \
*store, \
DRV_NAME, \
STR ## _json)); \
}
TEST_JSON(simple,
R"({ R"({
"inputSrcs": [ "inputSrcs": [
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1" "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
@ -117,7 +151,8 @@ TEST_JSON(Derivation, impure,
}, },
}; };
drv; drv;
})) }),
"drv-name")
#undef TEST_JSON #undef TEST_JSON

View file

@ -17,12 +17,12 @@
* } * }
* ``` * ```
*/ */
#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, FIELDS...) \ #define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, ...) \
bool operator COMPARATOR(const MY_TYPE& other) const { \ bool operator COMPARATOR(const MY_TYPE& other) const { \
const MY_TYPE* me = this; \ __VA_OPT__(const MY_TYPE* me = this;) \
auto fields1 = std::make_tuple( FIELDS ); \ auto fields1 = std::make_tuple( __VA_ARGS__ ); \
me = &other; \ __VA_OPT__(me = &other;) \
auto fields2 = std::make_tuple( FIELDS ); \ auto fields2 = std::make_tuple( __VA_ARGS__ ); \
return fields1 COMPARATOR fields2; \ return fields1 COMPARATOR fields2; \
} }
#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args) #define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args)