summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/execution_api/local/TARGETS10
-rw-r--r--src/buildtool/execution_api/local/local_api.cpp344
-rw-r--r--src/buildtool/execution_api/local/local_api.hpp305
3 files changed, 361 insertions, 298 deletions
diff --git a/src/buildtool/execution_api/local/TARGETS b/src/buildtool/execution_api/local/TARGETS
index ce12b788..da295681 100644
--- a/src/buildtool/execution_api/local/TARGETS
+++ b/src/buildtool/execution_api/local/TARGETS
@@ -14,11 +14,10 @@
, "local_response.hpp"
, "local_cas_reader.hpp"
]
- , "srcs": ["local_action.cpp", "local_cas_reader.cpp"]
+ , "srcs": ["local_api.cpp", "local_action.cpp", "local_cas_reader.cpp"]
, "deps":
[ "context"
, ["@", "fmt", "", "fmt"]
- , ["@", "grpc", "", "grpc++"]
, ["@", "gsl", "", "gsl"]
, ["@", "protoc", "", "libprotobuf"]
, ["src/buildtool/common", "artifact_digest_factory"]
@@ -28,11 +27,8 @@
, ["src/buildtool/common", "protocol_traits"]
, ["src/buildtool/crypto", "hash_function"]
, ["src/buildtool/execution_api/bazel_msg", "bazel_msg_factory"]
- , ["src/buildtool/execution_api/bazel_msg", "directory_tree"]
, ["src/buildtool/execution_api/common", "artifact_blob"]
, ["src/buildtool/execution_api/common", "common"]
- , ["src/buildtool/execution_api/common", "common_api"]
- , ["src/buildtool/execution_api/execution_service", "cas_utils"]
, ["src/buildtool/execution_api/git", "git_api"]
, ["src/buildtool/execution_engine/dag", "dag"]
, ["src/buildtool/file_system", "file_system_manager"]
@@ -48,7 +44,11 @@
, "stage": ["src", "buildtool", "execution_api", "local"]
, "private-deps":
[ "config"
+ , ["@", "grpc", "", "grpc++"]
, ["@", "json", "", "json"]
+ , ["src/buildtool/execution_api/bazel_msg", "directory_tree"]
+ , ["src/buildtool/execution_api/common", "common_api"]
+ , ["src/buildtool/execution_api/execution_service", "cas_utils"]
, ["src/buildtool/execution_api/utils", "outputscheck"]
, ["src/buildtool/file_system", "object_type"]
, ["src/buildtool/system", "system_command"]
diff --git a/src/buildtool/execution_api/local/local_api.cpp b/src/buildtool/execution_api/local/local_api.cpp
new file mode 100644
index 00000000..ae59d3bc
--- /dev/null
+++ b/src/buildtool/execution_api/local/local_api.cpp
@@ -0,0 +1,344 @@
+// Copyright 2025 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/buildtool/execution_api/local/local_api.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdio>
+#include <functional>
+#include <memory>
+#include <new> // std::nothrow
+#include <sstream>
+#include <unordered_map>
+#include <utility> // std::move
+
+#include <grpcpp/support/status.h>
+
+#include "src/buildtool/common/artifact_digest_factory.hpp"
+#include "src/buildtool/common/protocol_traits.hpp"
+#include "src/buildtool/crypto/hash_function.hpp"
+#include "src/buildtool/execution_api/bazel_msg/directory_tree.hpp"
+#include "src/buildtool/execution_api/common/common_api.hpp"
+#include "src/buildtool/execution_api/common/stream_dumper.hpp"
+#include "src/buildtool/execution_api/common/tree_reader.hpp"
+#include "src/buildtool/execution_api/execution_service/cas_utils.hpp"
+#include "src/buildtool/execution_api/local/local_action.hpp"
+#include "src/buildtool/execution_api/local/local_cas_reader.hpp"
+#include "src/buildtool/file_system/file_system_manager.hpp"
+#include "src/buildtool/file_system/object_type.hpp"
+#include "src/buildtool/logging/log_level.hpp"
+#include "src/buildtool/logging/logger.hpp"
+#include "src/buildtool/storage/config.hpp"
+#include "src/buildtool/storage/storage.hpp"
+#include "src/utils/cpp/expected.hpp"
+
+namespace {
+[[nodiscard]] auto CreateFallbackApi(
+ Storage const& storage,
+ RepositoryConfig const* repo_config) noexcept -> std::optional<GitApi> {
+ if (repo_config == nullptr or
+ not ProtocolTraits::IsNative(storage.GetHashFunction().GetType())) {
+ return std::nullopt;
+ }
+ return GitApi{repo_config};
+}
+} // namespace
+
+LocalApi::LocalApi(gsl::not_null<LocalContext const*> const& local_context,
+ RepositoryConfig const* repo_config) noexcept
+ : local_context_{*local_context},
+ git_api_{CreateFallbackApi(*local_context->storage, repo_config)} {}
+
+auto LocalApi::CreateAction(
+ ArtifactDigest const& root_digest,
+ std::vector<std::string> const& command,
+ std::string const& cwd,
+ std::vector<std::string> const& output_files,
+ std::vector<std::string> const& output_dirs,
+ std::map<std::string, std::string> const& env_vars,
+ std::map<std::string, std::string> const& properties) const noexcept
+ -> IExecutionAction::Ptr {
+ return IExecutionAction::Ptr{new (std::nothrow) LocalAction{&local_context_,
+ root_digest,
+ command,
+ cwd,
+ output_files,
+ output_dirs,
+ env_vars,
+ properties}};
+}
+
+// NOLINTNEXTLINE(google-default-arguments)
+auto LocalApi::RetrieveToPaths(
+ std::vector<Artifact::ObjectInfo> const& artifacts_info,
+ std::vector<std::filesystem::path> const& output_paths,
+ IExecutionApi const* /*alternative*/) const noexcept -> bool {
+ if (artifacts_info.size() != output_paths.size()) {
+ Logger::Log(LogLevel::Error,
+ "different number of digests and output paths.");
+ return false;
+ }
+
+ auto const reader =
+ TreeReader<LocalCasReader>{&local_context_.storage->CAS()};
+ for (std::size_t i = 0; i < artifacts_info.size(); ++i) {
+ auto const& info = artifacts_info[i];
+ if (not reader.StageTo({info}, {output_paths[i]})) {
+ if (not git_api_ or
+ not git_api_->RetrieveToPaths({info}, {output_paths[i]})) {
+ Logger::Log(LogLevel::Error,
+ "staging to output path {} failed.",
+ output_paths[i].string());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// NOLINTNEXTLINE(google-default-arguments)
+auto LocalApi::RetrieveToFds(
+ std::vector<Artifact::ObjectInfo> const& artifacts_info,
+ std::vector<int> const& fds,
+ bool raw_tree,
+ IExecutionApi const* /*alternative*/) const noexcept -> bool {
+ auto dumper = StreamDumper<LocalCasReader>{&local_context_.storage->CAS()};
+ return CommonRetrieveToFds(
+ artifacts_info,
+ fds,
+ [&dumper, &raw_tree](Artifact::ObjectInfo const& info,
+ gsl::not_null<FILE*> const& out) {
+ return dumper.DumpToStream(info, out, raw_tree);
+ },
+ [&git_api = git_api_, &raw_tree](Artifact::ObjectInfo const& info,
+ int fd) {
+ return git_api and git_api->IsAvailable(info.digest) and
+ git_api->RetrieveToFds({info}, {fd}, raw_tree);
+ });
+}
+
+auto LocalApi::RetrieveToCas(
+ std::vector<Artifact::ObjectInfo> const& artifacts_info,
+ IExecutionApi const& api) const noexcept -> bool {
+ // Return immediately if target CAS is this CAS
+ if (this == &api) {
+ return true;
+ }
+
+ // Determine missing artifacts in other CAS.
+ auto missing_artifacts_info = GetMissingArtifactsInfo<Artifact::ObjectInfo>(
+ api,
+ artifacts_info.begin(),
+ artifacts_info.end(),
+ [](Artifact::ObjectInfo const& info) { return info.digest; });
+ if (not missing_artifacts_info) {
+ Logger::Log(LogLevel::Error,
+ "LocalApi: Failed to retrieve the missing artifacts");
+ return false;
+ }
+
+ // Collect blobs of missing artifacts from local CAS. Trees are
+ // processed recursively before any blob is uploaded.
+ std::unordered_set<ArtifactBlob> container;
+ for (auto const& dgst : missing_artifacts_info->digests) {
+ auto const& info = missing_artifacts_info->back_map[dgst];
+ // Recursively process trees.
+ if (IsTreeObject(info.type)) {
+ auto reader =
+ TreeReader<LocalCasReader>{&local_context_.storage->CAS()};
+ auto const& result = reader.ReadDirectTreeEntries(
+ info.digest, std::filesystem::path{});
+ if (not result or not RetrieveToCas(result->infos, api)) {
+ return false;
+ }
+ }
+
+ // Determine artifact path.
+ auto const& path =
+ IsTreeObject(info.type)
+ ? local_context_.storage->CAS().TreePath(info.digest)
+ : local_context_.storage->CAS().BlobPath(
+ info.digest, IsExecutableObject(info.type));
+ if (not path) {
+ return false;
+ }
+
+ // Read artifact content (file or symlink).
+ auto const& content = FileSystemManager::ReadFile(*path);
+ if (not content) {
+ return false;
+ }
+
+ // Regenerate digest since object infos read by
+ // storage_.ReadTreeInfos() will contain 0 as size.
+ ArtifactDigest digest =
+ IsTreeObject(info.type)
+ ? ArtifactDigestFactory::HashDataAs<ObjectType::Tree>(
+ local_context_.storage_config->hash_function, *content)
+ : ArtifactDigestFactory::HashDataAs<ObjectType::File>(
+ local_context_.storage_config->hash_function, *content);
+
+ // Collect blob and upload to remote CAS if transfer size reached.
+ if (not UpdateContainerAndUpload(
+ &container,
+ ArtifactBlob{
+ std::move(digest), *content, IsExecutableObject(info.type)},
+ /*exception_is_fatal=*/true,
+ [&api](std::unordered_set<ArtifactBlob>&& blobs) {
+ return api.Upload(std::move(blobs),
+ /*skip_find_missing=*/true);
+ })) {
+ return false;
+ }
+ }
+
+ // Upload remaining blobs to remote CAS.
+ return api.Upload(std::move(container), /*skip_find_missing=*/true);
+}
+
+auto LocalApi::RetrieveToMemory(Artifact::ObjectInfo const& artifact_info)
+ const noexcept -> std::optional<std::string> {
+ std::optional<std::filesystem::path> location{};
+ if (IsTreeObject(artifact_info.type)) {
+ location = local_context_.storage->CAS().TreePath(artifact_info.digest);
+ }
+ else {
+ location = local_context_.storage->CAS().BlobPath(
+ artifact_info.digest, IsExecutableObject(artifact_info.type));
+ }
+ std::optional<std::string> content = std::nullopt;
+ if (location) {
+ content = FileSystemManager::ReadFile(*location);
+ }
+ if (not content and git_api_) {
+ content = git_api_->RetrieveToMemory(artifact_info);
+ }
+ return content;
+}
+
+auto LocalApi::Upload(std::unordered_set<ArtifactBlob>&& blobs,
+ bool /*skip_find_missing*/) const noexcept -> bool {
+ return std::all_of(
+ blobs.begin(),
+ blobs.end(),
+ [&cas = local_context_.storage->CAS()](ArtifactBlob const& blob) {
+ auto const cas_digest =
+ blob.digest.IsTree() ? cas.StoreTree(*blob.data)
+ : cas.StoreBlob(*blob.data, blob.is_exec);
+ return cas_digest and *cas_digest == blob.digest;
+ });
+}
+
+auto LocalApi::UploadTree(
+ std::vector<DependencyGraph::NamedArtifactNodePtr> const& artifacts)
+ const noexcept -> std::optional<ArtifactDigest> {
+ auto build_root = DirectoryTree::FromNamedArtifacts(artifacts);
+ if (not build_root) {
+ Logger::Log(LogLevel::Debug,
+ "failed to create build root from artifacts.");
+ return std::nullopt;
+ }
+
+ auto const& cas = local_context_.storage->CAS();
+ if (ProtocolTraits::IsNative(cas.GetHashFunction().GetType())) {
+ return CommonUploadTreeNative(*this, *build_root);
+ }
+ return CommonUploadTreeCompatible(
+ *this,
+ *build_root,
+ [&cas](std::vector<ArtifactDigest> const& digests,
+ gsl::not_null<std::vector<std::string>*> const& targets) {
+ targets->reserve(digests.size());
+ for (auto const& digest : digests) {
+ auto p = cas.BlobPath(digest,
+ /*is_executable=*/false);
+ auto content = FileSystemManager::ReadFile(*p);
+ targets->emplace_back(*content);
+ }
+ });
+}
+
+auto LocalApi::IsAvailable(ArtifactDigest const& digest) const noexcept
+ -> bool {
+ auto found = static_cast<bool>(
+ digest.IsTree()
+ ? local_context_.storage->CAS().TreePath(digest)
+ : local_context_.storage->CAS().BlobPath(digest, false));
+ if ((not found) and git_api_) {
+ if (git_api_->IsAvailable(digest)) {
+ auto plain_local = LocalApi(&local_context_);
+ auto obj_info = std::vector<Artifact::ObjectInfo>{};
+ obj_info.push_back(Artifact::ObjectInfo{
+ digest,
+ digest.IsTree() ? ObjectType::Tree : ObjectType::File,
+ false});
+ found = git_api_->RetrieveToCas(obj_info, plain_local);
+ }
+ }
+ return found;
+}
+
+auto LocalApi::GetMissingDigests(
+ std::unordered_set<ArtifactDigest> const& digests) const noexcept
+ -> std::unordered_set<ArtifactDigest> {
+ std::unordered_set<ArtifactDigest> result;
+ result.reserve(digests.size());
+ for (auto const& digest : digests) {
+ if (not IsAvailable(digest)) {
+ result.emplace(digest);
+ }
+ }
+ return result;
+}
+
+auto LocalApi::SplitBlob(ArtifactDigest const& blob_digest) const noexcept
+ -> std::optional<std::vector<ArtifactDigest>> {
+ Logger::Log(LogLevel::Debug, "SplitBlob({})", blob_digest.hash());
+ auto split_result =
+ CASUtils::SplitBlobFastCDC(blob_digest, *local_context_.storage);
+ if (not split_result) {
+ Logger::Log(LogLevel::Error, split_result.error().error_message());
+ return std::nullopt;
+ }
+ auto const& chunk_digests = *split_result;
+ Logger::Log(LogLevel::Debug, [&blob_digest, &chunk_digests]() {
+ std::stringstream ss{};
+ ss << "Split blob " << blob_digest.hash() << ":" << blob_digest.size()
+ << " into " << chunk_digests.size() << " chunks: [ ";
+ for (auto const& chunk_digest : chunk_digests) {
+ ss << chunk_digest.hash() << ":" << chunk_digest.size() << " ";
+ }
+ ss << "]";
+ return ss.str();
+ });
+ return *std::move(split_result);
+}
+
+auto LocalApi::SpliceBlob(ArtifactDigest const& blob_digest,
+ std::vector<ArtifactDigest> const& chunk_digests)
+ const noexcept -> std::optional<ArtifactDigest> {
+ Logger::Log(LogLevel::Debug,
+ "SpliceBlob({}, {} chunks)",
+ blob_digest.hash(),
+ chunk_digests.size());
+
+ auto splice_result = CASUtils::SpliceBlob(
+ blob_digest, chunk_digests, *local_context_.storage);
+ if (not splice_result) {
+ Logger::Log(LogLevel::Error, splice_result.error().error_message());
+ return std::nullopt;
+ }
+ return *std::move(splice_result);
+}
diff --git a/src/buildtool/execution_api/local/local_api.hpp b/src/buildtool/execution_api/local/local_api.hpp
index 9f58bc5a..853b0622 100644
--- a/src/buildtool/execution_api/local/local_api.hpp
+++ b/src/buildtool/execution_api/local/local_api.hpp
@@ -15,59 +15,28 @@
#ifndef INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_API_HPP
#define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_API_HPP
-#include <algorithm>
-#include <cstddef>
-#include <cstdio>
#include <filesystem>
-#include <functional>
#include <map>
-#include <memory>
-#include <new> // std::nothrow
#include <optional>
-#include <sstream>
#include <string>
-#include <unordered_map>
#include <unordered_set>
-#include <utility> // std::move
#include <vector>
-#include <grpcpp/support/status.h>
-
#include "gsl/gsl"
#include "src/buildtool/common/artifact.hpp"
#include "src/buildtool/common/artifact_digest.hpp"
-#include "src/buildtool/common/artifact_digest_factory.hpp"
-#include "src/buildtool/common/protocol_traits.hpp"
#include "src/buildtool/common/repository_config.hpp"
-#include "src/buildtool/crypto/hash_function.hpp"
-#include "src/buildtool/execution_api/bazel_msg/directory_tree.hpp"
#include "src/buildtool/execution_api/common/artifact_blob.hpp"
-#include "src/buildtool/execution_api/common/common_api.hpp"
#include "src/buildtool/execution_api/common/execution_action.hpp"
#include "src/buildtool/execution_api/common/execution_api.hpp"
-#include "src/buildtool/execution_api/common/stream_dumper.hpp"
-#include "src/buildtool/execution_api/common/tree_reader.hpp"
-#include "src/buildtool/execution_api/execution_service/cas_utils.hpp"
#include "src/buildtool/execution_api/git/git_api.hpp"
#include "src/buildtool/execution_api/local/context.hpp"
-#include "src/buildtool/execution_api/local/local_action.hpp"
-#include "src/buildtool/execution_api/local/local_cas_reader.hpp"
#include "src/buildtool/execution_engine/dag/dag.hpp"
-#include "src/buildtool/file_system/file_system_manager.hpp"
-#include "src/buildtool/file_system/object_type.hpp"
-#include "src/buildtool/logging/log_level.hpp"
-#include "src/buildtool/logging/logger.hpp"
-#include "src/buildtool/storage/config.hpp"
-#include "src/buildtool/storage/storage.hpp"
-#include "src/utils/cpp/expected.hpp"
-/// \brief API for local execution.
class LocalApi final : public IExecutionApi {
public:
explicit LocalApi(gsl::not_null<LocalContext const*> const& local_context,
- RepositoryConfig const* repo_config = nullptr) noexcept
- : local_context_{*local_context},
- git_api_{CreateFallbackApi(*local_context->storage, repo_config)} {}
+ RepositoryConfig const* repo_config = nullptr) noexcept;
[[nodiscard]] auto CreateAction(
ArtifactDigest const& root_digest,
@@ -77,46 +46,14 @@ class LocalApi final : public IExecutionApi {
std::vector<std::string> const& output_dirs,
std::map<std::string, std::string> const& env_vars,
std::map<std::string, std::string> const& properties) const noexcept
- -> IExecutionAction::Ptr final {
- return IExecutionAction::Ptr{new (std::nothrow)
- LocalAction{&local_context_,
- root_digest,
- command,
- cwd,
- output_files,
- output_dirs,
- env_vars,
- properties}};
- }
+ -> IExecutionAction::Ptr final;
// NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] auto RetrieveToPaths(
std::vector<Artifact::ObjectInfo> const& artifacts_info,
std::vector<std::filesystem::path> const& output_paths,
IExecutionApi const* /*alternative*/ = nullptr) const noexcept
- -> bool final {
- if (artifacts_info.size() != output_paths.size()) {
- Logger::Log(LogLevel::Error,
- "different number of digests and output paths.");
- return false;
- }
-
- auto const reader =
- TreeReader<LocalCasReader>{&local_context_.storage->CAS()};
- for (std::size_t i = 0; i < artifacts_info.size(); ++i) {
- auto const& info = artifacts_info[i];
- if (not reader.StageTo({info}, {output_paths[i]})) {
- if (not git_api_ or
- not git_api_->RetrieveToPaths({info}, {output_paths[i]})) {
- Logger::Log(LogLevel::Error,
- "staging to output path {} failed.",
- output_paths[i].string());
- return false;
- }
- }
- }
- return true;
- }
+ -> bool final;
// NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] auto RetrieveToFds(
@@ -124,228 +61,33 @@ class LocalApi final : public IExecutionApi {
std::vector<int> const& fds,
bool raw_tree,
IExecutionApi const* /*alternative*/ = nullptr) const noexcept
- -> bool final {
- auto dumper =
- StreamDumper<LocalCasReader>{&local_context_.storage->CAS()};
- return CommonRetrieveToFds(
- artifacts_info,
- fds,
- [&dumper, &raw_tree](Artifact::ObjectInfo const& info,
- gsl::not_null<FILE*> const& out) {
- return dumper.DumpToStream(info, out, raw_tree);
- },
- [&git_api = git_api_, &raw_tree](Artifact::ObjectInfo const& info,
- int fd) {
- return git_api and git_api->IsAvailable(info.digest) and
- git_api->RetrieveToFds({info}, {fd}, raw_tree);
- });
- }
+ -> bool final;
[[nodiscard]] auto RetrieveToCas(
std::vector<Artifact::ObjectInfo> const& artifacts_info,
- IExecutionApi const& api) const noexcept -> bool final {
- // Return immediately if target CAS is this CAS
- if (this == &api) {
- return true;
- }
-
- // Determine missing artifacts in other CAS.
- auto missing_artifacts_info =
- GetMissingArtifactsInfo<Artifact::ObjectInfo>(
- api,
- artifacts_info.begin(),
- artifacts_info.end(),
- [](Artifact::ObjectInfo const& info) { return info.digest; });
- if (not missing_artifacts_info) {
- Logger::Log(LogLevel::Error,
- "LocalApi: Failed to retrieve the missing artifacts");
- return false;
- }
-
- // Collect blobs of missing artifacts from local CAS. Trees are
- // processed recursively before any blob is uploaded.
- std::unordered_set<ArtifactBlob> container;
- for (auto const& dgst : missing_artifacts_info->digests) {
- auto const& info = missing_artifacts_info->back_map[dgst];
- // Recursively process trees.
- if (IsTreeObject(info.type)) {
- auto reader =
- TreeReader<LocalCasReader>{&local_context_.storage->CAS()};
- auto const& result = reader.ReadDirectTreeEntries(
- info.digest, std::filesystem::path{});
- if (not result or not RetrieveToCas(result->infos, api)) {
- return false;
- }
- }
-
- // Determine artifact path.
- auto const& path =
- IsTreeObject(info.type)
- ? local_context_.storage->CAS().TreePath(info.digest)
- : local_context_.storage->CAS().BlobPath(
- info.digest, IsExecutableObject(info.type));
- if (not path) {
- return false;
- }
-
- // Read artifact content (file or symlink).
- auto const& content = FileSystemManager::ReadFile(*path);
- if (not content) {
- return false;
- }
-
- // Regenerate digest since object infos read by
- // storage_.ReadTreeInfos() will contain 0 as size.
- ArtifactDigest digest =
- IsTreeObject(info.type)
- ? ArtifactDigestFactory::HashDataAs<ObjectType::Tree>(
- local_context_.storage_config->hash_function,
- *content)
- : ArtifactDigestFactory::HashDataAs<ObjectType::File>(
- local_context_.storage_config->hash_function,
- *content);
-
- // Collect blob and upload to remote CAS if transfer size reached.
- if (not UpdateContainerAndUpload(
- &container,
- ArtifactBlob{std::move(digest),
- *content,
- IsExecutableObject(info.type)},
- /*exception_is_fatal=*/true,
- [&api](std::unordered_set<ArtifactBlob>&& blobs) {
- return api.Upload(std::move(blobs),
- /*skip_find_missing=*/true);
- })) {
- return false;
- }
- }
-
- // Upload remaining blobs to remote CAS.
- return api.Upload(std::move(container), /*skip_find_missing=*/true);
- }
+ IExecutionApi const& api) const noexcept -> bool final;
[[nodiscard]] auto RetrieveToMemory(
Artifact::ObjectInfo const& artifact_info) const noexcept
- -> std::optional<std::string> override {
- std::optional<std::filesystem::path> location{};
- if (IsTreeObject(artifact_info.type)) {
- location =
- local_context_.storage->CAS().TreePath(artifact_info.digest);
- }
- else {
- location = local_context_.storage->CAS().BlobPath(
- artifact_info.digest, IsExecutableObject(artifact_info.type));
- }
- std::optional<std::string> content = std::nullopt;
- if (location) {
- content = FileSystemManager::ReadFile(*location);
- }
- if (not content and git_api_) {
- content = git_api_->RetrieveToMemory(artifact_info);
- }
- return content;
- }
+ -> std::optional<std::string> override;
[[nodiscard]] auto Upload(std::unordered_set<ArtifactBlob>&& blobs,
bool /*skip_find_missing*/) const noexcept
- -> bool final {
- return std::all_of(
- blobs.begin(),
- blobs.end(),
- [&cas = local_context_.storage->CAS()](ArtifactBlob const& blob) {
- auto const cas_digest =
- blob.digest.IsTree()
- ? cas.StoreTree(*blob.data)
- : cas.StoreBlob(*blob.data, blob.is_exec);
- return cas_digest and *cas_digest == blob.digest;
- });
- }
+ -> bool final;
[[nodiscard]] auto UploadTree(
std::vector<DependencyGraph::NamedArtifactNodePtr> const& artifacts)
- const noexcept -> std::optional<ArtifactDigest> final {
- auto build_root = DirectoryTree::FromNamedArtifacts(artifacts);
- if (not build_root) {
- Logger::Log(LogLevel::Debug,
- "failed to create build root from artifacts.");
- return std::nullopt;
- }
-
- auto const& cas = local_context_.storage->CAS();
- if (ProtocolTraits::IsNative(cas.GetHashFunction().GetType())) {
- return CommonUploadTreeNative(*this, *build_root);
- }
- return CommonUploadTreeCompatible(
- *this,
- *build_root,
- [&cas](std::vector<ArtifactDigest> const& digests,
- gsl::not_null<std::vector<std::string>*> const& targets) {
- targets->reserve(digests.size());
- for (auto const& digest : digests) {
- auto p = cas.BlobPath(digest,
- /*is_executable=*/false);
- auto content = FileSystemManager::ReadFile(*p);
- targets->emplace_back(*content);
- }
- });
- }
+ const noexcept -> std::optional<ArtifactDigest> final;
[[nodiscard]] auto IsAvailable(ArtifactDigest const& digest) const noexcept
- -> bool final {
- auto found = static_cast<bool>(
- digest.IsTree()
- ? local_context_.storage->CAS().TreePath(digest)
- : local_context_.storage->CAS().BlobPath(digest, false));
- if ((not found) and git_api_) {
- if (git_api_->IsAvailable(digest)) {
- auto plain_local = LocalApi(&local_context_);
- auto obj_info = std::vector<Artifact::ObjectInfo>{};
- obj_info.push_back(Artifact::ObjectInfo{
- digest,
- digest.IsTree() ? ObjectType::Tree : ObjectType::File,
- false});
- found = git_api_->RetrieveToCas(obj_info, plain_local);
- }
- }
- return found;
- }
+ -> bool final;
[[nodiscard]] auto GetMissingDigests(
std::unordered_set<ArtifactDigest> const& digests) const noexcept
- -> std::unordered_set<ArtifactDigest> final {
- std::unordered_set<ArtifactDigest> result;
- result.reserve(digests.size());
- for (auto const& digest : digests) {
- if (not IsAvailable(digest)) {
- result.emplace(digest);
- }
- }
- return result;
- }
+ -> std::unordered_set<ArtifactDigest> final;
[[nodiscard]] auto SplitBlob(ArtifactDigest const& blob_digest)
- const noexcept -> std::optional<std::vector<ArtifactDigest>> final {
- Logger::Log(LogLevel::Debug, "SplitBlob({})", blob_digest.hash());
- auto split_result =
- CASUtils::SplitBlobFastCDC(blob_digest, *local_context_.storage);
- if (not split_result) {
- Logger::Log(LogLevel::Error, split_result.error().error_message());
- return std::nullopt;
- }
- auto const& chunk_digests = *split_result;
- Logger::Log(LogLevel::Debug, [&blob_digest, &chunk_digests]() {
- std::stringstream ss{};
- ss << "Split blob " << blob_digest.hash() << ":"
- << blob_digest.size() << " into " << chunk_digests.size()
- << " chunks: [ ";
- for (auto const& chunk_digest : chunk_digests) {
- ss << chunk_digest.hash() << ":" << chunk_digest.size() << " ";
- }
- ss << "]";
- return ss.str();
- });
- return *std::move(split_result);
- }
+ const noexcept -> std::optional<std::vector<ArtifactDigest>> final;
[[nodiscard]] auto BlobSplitSupport() const noexcept -> bool final {
return true;
@@ -354,20 +96,7 @@ class LocalApi final : public IExecutionApi {
[[nodiscard]] auto SpliceBlob(
ArtifactDigest const& blob_digest,
std::vector<ArtifactDigest> const& chunk_digests) const noexcept
- -> std::optional<ArtifactDigest> final {
- Logger::Log(LogLevel::Debug,
- "SpliceBlob({}, {} chunks)",
- blob_digest.hash(),
- chunk_digests.size());
-
- auto splice_result = CASUtils::SpliceBlob(
- blob_digest, chunk_digests, *local_context_.storage);
- if (not splice_result) {
- Logger::Log(LogLevel::Error, splice_result.error().error_message());
- return std::nullopt;
- }
- return *std::move(splice_result);
- }
+ -> std::optional<ArtifactDigest> final;
[[nodiscard]] auto BlobSpliceSupport() const noexcept -> bool final {
return true;
@@ -376,16 +105,6 @@ class LocalApi final : public IExecutionApi {
private:
LocalContext const& local_context_;
std::optional<GitApi> const git_api_;
-
- [[nodiscard]] static auto CreateFallbackApi(
- Storage const& storage,
- RepositoryConfig const* repo_config) noexcept -> std::optional<GitApi> {
- if (repo_config == nullptr or
- not ProtocolTraits::IsNative(storage.GetHashFunction().GetType())) {
- return std::nullopt;
- }
- return GitApi{repo_config};
- }
};
#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_LOCAL_LOCAL_API_HPP