summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/build_engine/expression/TARGETS1
-rw-r--r--src/buildtool/build_engine/expression/target_result.cpp242
-rw-r--r--src/buildtool/build_engine/expression/target_result.hpp14
-rw-r--r--src/buildtool/common/artifact_description.hpp4
4 files changed, 261 insertions, 0 deletions
diff --git a/src/buildtool/build_engine/expression/TARGETS b/src/buildtool/build_engine/expression/TARGETS
index d6adc88c..ba030949 100644
--- a/src/buildtool/build_engine/expression/TARGETS
+++ b/src/buildtool/build_engine/expression/TARGETS
@@ -35,6 +35,7 @@
[ "expression_ptr.cpp"
, "expression.cpp"
, "evaluator.cpp"
+ , "target_result.cpp"
, "target_node.cpp"
]
, "deps":
diff --git a/src/buildtool/build_engine/expression/target_result.cpp b/src/buildtool/build_engine/expression/target_result.cpp
new file mode 100644
index 00000000..9be45a5f
--- /dev/null
+++ b/src/buildtool/build_engine/expression/target_result.cpp
@@ -0,0 +1,242 @@
+#include "src/buildtool/build_engine/expression/target_result.hpp"
+
+#include <unordered_map>
+#include <vector>
+
+#include "gsl-lite/gsl-lite.hpp"
+#include "src/buildtool/build_engine/expression/expression.hpp"
+#include "src/buildtool/logging/logger.hpp"
+
+namespace {
+
+// Serialize artifact description to JSON. If replacements is set, replace
+// non-known artifacts by known artifacts from replacement. Throws
+// bad_variant_access if expr is not an artifact or runtime_error if no
+// replacement is found.
+[[nodiscard]] auto SerializeArtifactDescription(
+ ExpressionPtr const& expr,
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) -> nlohmann::json {
+ if (not replacements.empty()) {
+ auto const& artifact = expr->Artifact();
+ if (not expr->Artifact().IsKnown()) {
+ if (auto it = replacements.find(artifact);
+ it != replacements.end()) {
+ auto const& info = it->second;
+ return ArtifactDescription{info.digest, info.type}.ToJson();
+ }
+ throw std::runtime_error{
+ "No replacement for non-known artifact found."};
+ }
+ }
+ return expr->ToJson();
+}
+
+// Serialize arbitrary expression to JSON. This and any sub-expressions will be
+// collected in `nodes`. Any possible duplicate will be collected only once. As
+// pure JSON values can coincide with our JSON encoding of artifacts, the hash
+// of artifact expressions is recorded in `provided_artifacts` to differentiate
+// them from non-artifacts. If replacements is set, replace any contained
+// non-known artifact by known artifact from replacement. Throws runtime_error
+// if no replacement is found.
+// NOLINTNEXTLINE(misc-no-recursion)
+[[nodiscard]] auto SerializeExpression(
+ gsl::not_null<std::unordered_map<std::string, nlohmann::json>*> const&
+ nodes,
+ gsl::not_null<std::vector<std::string>*> const& provided_artifacts,
+ ExpressionPtr const& expr,
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) -> std::string {
+ auto id = ToHexString(expr->ToHash());
+ if (not nodes->contains(id)) {
+ auto json = nlohmann::json();
+ if (expr->IsMap()) {
+ auto const& map = expr->Map();
+ std::unordered_map<std::string, std::string> hashes{};
+ hashes.reserve(map.size());
+ for (auto const& [key, val] : map) {
+ auto hash = SerializeExpression(
+ nodes, provided_artifacts, val, replacements);
+ hashes[key] = std::move(hash);
+ }
+ json = std::move(hashes);
+ }
+ else if (expr->IsList()) {
+ auto const& list = expr->List();
+ std::vector<std::string> hashes{};
+ hashes.reserve(list.size());
+ for (auto const& val : list) {
+ auto hash = SerializeExpression(
+ nodes, provided_artifacts, val, replacements);
+ hashes.emplace_back(std::move(hash));
+ }
+ json = std::move(hashes);
+ }
+ else if (expr->IsArtifact()) {
+ provided_artifacts->emplace_back(id);
+ json = SerializeArtifactDescription(expr, replacements);
+ }
+ else {
+ json = expr->ToJson();
+ }
+
+ (*nodes)[id] = std::move(json);
+ }
+ return id;
+}
+
+// NOLINTNEXTLINE(misc-no-recursion)
+[[nodiscard]] auto DeserializeExpression(
+ nlohmann::json const& entry,
+ nlohmann::json const& nodes,
+ nlohmann::json const& provided_artifacts) -> ExpressionPtr {
+ static auto contains = [](auto const& a, auto const& v) -> bool {
+ return std::find(a.begin(), a.end(), v) != a.end();
+ };
+ auto id = entry.get<std::string>();
+ auto const& json = nodes.at(id);
+ if (json.is_object()) {
+ if (contains(provided_artifacts, id)) {
+ if (auto artifact = ArtifactDescription::FromJson(json)) {
+ return ExpressionPtr{*artifact};
+ }
+ return ExpressionPtr{nullptr};
+ }
+
+ Expression::map_t::underlying_map_t map{};
+ for (auto const& [key, val] : json.items()) {
+ auto new_val = DeserializeExpression(
+ val.get<std::string>(), nodes, provided_artifacts);
+ if (not new_val) {
+ return new_val;
+ }
+ map.emplace(key, std::move(new_val));
+ }
+ return ExpressionPtr{Expression::map_t{map}};
+ }
+
+ if (json.is_array()) {
+ Expression::list_t list{};
+ list.reserve(json.size());
+ for (auto const& val : json) {
+ auto new_val = DeserializeExpression(
+ val.get<std::string>(), nodes, provided_artifacts);
+ if (not new_val) {
+ return new_val;
+ }
+ list.emplace_back(std::move(new_val));
+ }
+ return ExpressionPtr{list};
+ }
+
+ return Expression::FromJson(json);
+}
+
+// Serialize artifact map to JSON. If replacements is set, replace
+// non-known artifacts by known artifacts from replacement. Throws runtime_error
+// if no replacement is found.
+[[nodiscard]] auto SerializeArtifactMap(
+ ExpressionPtr const& expr,
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) -> nlohmann::json {
+ if (replacements.empty()) {
+ return expr->ToJson();
+ }
+
+ auto const& map = expr->Map();
+ std::unordered_map<std::string, nlohmann::json> artifacts{};
+ artifacts.reserve(map.size());
+ for (auto const& [key, val] : map) {
+ artifacts[key] = SerializeArtifactDescription(val, replacements);
+ }
+ return artifacts;
+}
+
+[[nodiscard]] auto DeserializeArtifactMap(nlohmann::json const& json)
+ -> ExpressionPtr {
+ if (json.is_object()) {
+ Expression::map_t::underlying_map_t map{};
+ for (auto const& [key, val] : json.items()) {
+ auto artifact = ArtifactDescription::FromJson(val);
+ if (not artifact) {
+ return ExpressionPtr{nullptr};
+ }
+ map.emplace(key, ExpressionPtr{std::move(*artifact)});
+ }
+ return ExpressionPtr{Expression::map_t{map}};
+ }
+ return ExpressionPtr{nullptr};
+}
+
+// Serialize provides map to JSON. If replacements is set, replace
+// non-known artifacts by known artifacts from replacement. Throws runtime_error
+// if no replacement is found.
+[[nodiscard]] auto SerializeProvidesMap(
+ ExpressionPtr const& expr,
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) -> nlohmann::json {
+ auto provided_artifacts = std::vector<std::string>{};
+ auto nodes = std::unordered_map<std::string, nlohmann::json>{};
+ auto entry =
+ SerializeExpression(&nodes, &provided_artifacts, expr, replacements);
+ return nlohmann::json{
+ {"entry", std::move(entry)},
+ {"nodes", std::move(nodes)},
+ {"provided_artifacts", std::move(provided_artifacts)}};
+}
+
+[[nodiscard]] auto DeserializeProvidesMap(nlohmann::json const& json)
+ -> ExpressionPtr {
+ return DeserializeExpression(
+ json["entry"], json["nodes"], json["provided_artifacts"]);
+}
+
+// Serialize TargetResult to JSON. If replacements is set, replace non-known
+// artifacts by known artifacts from replacement. Throws runtime_error if no
+// replacement is found.
+[[nodiscard]] auto SerializeTargetResultWithReplacement(
+ TargetResult const& result,
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements = {}) -> nlohmann::json {
+ return nlohmann::json{
+ {"artifacts",
+ SerializeArtifactMap(result.artifact_stage, replacements)},
+ {"runfiles", SerializeArtifactMap(result.runfiles, replacements)},
+ {"provides", SerializeProvidesMap(result.provides, replacements)}};
+}
+
+} // namespace
+
+auto TargetResult::ToJson() const -> nlohmann::json {
+ return SerializeTargetResultWithReplacement(*this /* no replacement */);
+}
+
+auto TargetResult::ReplaceNonKnownAndToJson(
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) const noexcept -> std::optional<nlohmann::json> {
+ try {
+ return SerializeTargetResultWithReplacement(*this, replacements);
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Serializing target result to JSON failed with:\n{}",
+ ex.what());
+ }
+ return std::nullopt;
+}
+
+auto TargetResult::FromJson(nlohmann::json const& json) noexcept
+ -> std::optional<TargetResult> {
+ try {
+ auto artifacts = DeserializeArtifactMap(json["artifacts"]);
+ auto runfiles = DeserializeArtifactMap(json["runfiles"]);
+ auto provides = DeserializeProvidesMap(json["provides"]);
+ if (artifacts and runfiles and provides) {
+ return TargetResult{artifacts, provides, runfiles};
+ }
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Deserializing target result failed with:\n{}",
+ ex.what());
+ }
+ return std::nullopt;
+}
diff --git a/src/buildtool/build_engine/expression/target_result.hpp b/src/buildtool/build_engine/expression/target_result.hpp
index 325d52fd..7df20000 100644
--- a/src/buildtool/build_engine/expression/target_result.hpp
+++ b/src/buildtool/build_engine/expression/target_result.hpp
@@ -1,7 +1,12 @@
#ifndef INCLUDED_SRC_BUILDTOOL_BUILDENGINE_EXPRESSION_TARGET_RESULT_HPP
#define INCLUDED_SRC_BUILDTOOL_BUILDENGINE_EXPRESSION_TARGET_RESULT_HPP
+#include <unordered_map>
+
+#include <nlohmann/json.hpp>
+
#include "src/buildtool/build_engine/expression/expression_ptr.hpp"
+#include "src/buildtool/common/artifact_description.hpp"
#include "src/utils/cpp/hash_combine.hpp"
struct TargetResult {
@@ -10,6 +15,15 @@ struct TargetResult {
ExpressionPtr runfiles{};
bool is_cacheable{provides.IsCacheable()};
+ [[nodiscard]] static auto FromJson(nlohmann::json const& json) noexcept
+ -> std::optional<TargetResult>;
+
+ [[nodiscard]] auto ToJson() const -> nlohmann::json;
+
+ [[nodiscard]] auto ReplaceNonKnownAndToJson(
+ std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> const&
+ replacements) const noexcept -> std::optional<nlohmann::json>;
+
[[nodiscard]] auto operator==(TargetResult const& other) const noexcept
-> bool {
return artifact_stage == other.artifact_stage and
diff --git a/src/buildtool/common/artifact_description.hpp b/src/buildtool/common/artifact_description.hpp
index af80cda0..2d361f8c 100644
--- a/src/buildtool/common/artifact_description.hpp
+++ b/src/buildtool/common/artifact_description.hpp
@@ -92,6 +92,10 @@ class ArtifactDescription {
return std::move(id_);
}
+ [[nodiscard]] auto IsKnown() const noexcept -> bool {
+ return std::holds_alternative<Known>(data_);
+ }
+
[[nodiscard]] auto IsTree() const noexcept -> bool {
return std::holds_alternative<Tree>(data_);
}