summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOliver Reiche <oliver.reiche@huawei.com>2025-06-23 14:56:04 +0200
committerOliver Reiche <oliver.reiche@huawei.com>2025-06-24 14:56:58 +0200
commitbbcc5977f49646941ac35060bb74a27eda5fbd76 (patch)
tree4e11ded4722ea740fbeea12510efd34fe698c04b
parentc498bf564fa5d781c176f65c7a9a2d43376a81f1 (diff)
downloadjustbuild-bbcc5977f49646941ac35060bb74a27eda5fbd76.tar.gz
ExecutionAPI: Support output_paths in requests
... and prepare local execution for clients using only RBEv2.1 (setting only output_paths).
-rw-r--r--src/buildtool/execution_api/bazel_msg/bazel_msg_factory.cpp9
-rw-r--r--src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp3
-rw-r--r--src/buildtool/execution_api/common/execution_api.hpp11
-rw-r--r--src/buildtool/execution_api/local/local_action.cpp121
-rw-r--r--src/buildtool/execution_api/local/local_action.hpp63
-rw-r--r--src/buildtool/execution_api/local/local_api.cpp28
-rw-r--r--src/buildtool/execution_api/local/local_api.hpp24
-rw-r--r--src/buildtool/execution_api/remote/bazel/bazel_action.cpp44
-rw-r--r--src/buildtool/execution_api/remote/bazel/bazel_action.hpp26
-rw-r--r--src/buildtool/execution_api/remote/bazel/bazel_api.cpp31
-rw-r--r--src/buildtool/execution_api/remote/bazel/bazel_api.hpp4
-rw-r--r--src/buildtool/execution_api/serve/mr_local_api.hpp4
-rw-r--r--src/buildtool/execution_api/utils/outputscheck.hpp28
-rw-r--r--test/buildtool/execution_engine/executor/executor.test.cpp4
14 files changed, 351 insertions, 49 deletions
diff --git a/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.cpp b/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.cpp
index 68da1b87..5e6e6a45 100644
--- a/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.cpp
+++ b/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.cpp
@@ -189,12 +189,21 @@ struct DirectoryNodeBundle final {
std::copy(request.command_line->begin(),
request.command_line->end(),
pb::back_inserter(msg.mutable_arguments()));
+
+ // DEPRECATED as of v2.1: use output_paths instead, combining
+ // output_files and output_dirs.
std::copy(request.output_files->begin(),
request.output_files->end(),
pb::back_inserter(msg.mutable_output_files()));
std::copy(request.output_dirs->begin(),
request.output_dirs->end(),
pb::back_inserter(msg.mutable_output_directories()));
+
+ // NEW in v2.2
+ std::copy(request.output_paths->begin(),
+ request.output_paths->end(),
+ pb::back_inserter(msg.mutable_output_paths()));
+
std::copy(request.env_vars->begin(),
request.env_vars->end(),
pb::back_inserter(msg.mutable_environment_variables()));
diff --git a/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp b/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp
index 17c08f4d..d6557ae8 100644
--- a/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp
+++ b/src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp
@@ -219,6 +219,9 @@ struct BazelMsgFactory::ActionDigestRequest final {
/// \brief The paths of output directories.
VectorPtr<std::string> const output_dirs;
+ /// \brief Generic output paths (indicates protocol >=v2.1 should be used).
+ VectorPtr<std::string> const output_paths;
+
/// \brief The environment variables set.
VectorPtr<bazel_re::Command_EnvironmentVariable> const env_vars;
diff --git a/src/buildtool/execution_api/common/execution_api.hpp b/src/buildtool/execution_api/common/execution_api.hpp
index 001c9189..d61a664e 100644
--- a/src/buildtool/execution_api/common/execution_api.hpp
+++ b/src/buildtool/execution_api/common/execution_api.hpp
@@ -45,15 +45,18 @@ class IExecutionApi {
auto operator=(IExecutionApi&&) -> IExecutionApi& = default;
virtual ~IExecutionApi() = default;
- /// \brief Create a new action.
+ /// \brief Create a new action (>=RBEv2.0).
/// \param[in] root_digest Digest of the build root.
/// \param[in] command Command as argv vector
- /// \param[in] cwd Working direcotry, relative to execution root
+ /// \param[in] cwd Working directory, relative to execution root
/// \param[in] output_files List of paths to output files, relative to cwd
/// \param[in] output_dirs List of paths to output directories.
/// \param[in] env_vars The environment variables to set.
/// \param[in] properties Platform properties to set.
+ /// \param[in] force_legacy Force use of legacy API RBEv2.0
/// \returns The new action.
+ /// Note that types of output files and directories are not verified.
+ /// NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] virtual auto CreateAction(
ArtifactDigest const& root_digest,
std::vector<std::string> const& command,
@@ -61,8 +64,8 @@ class IExecutionApi {
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 = 0;
+ std::map<std::string, std::string> const& properties,
+ bool force_legacy = false) const noexcept -> IExecutionAction::Ptr = 0;
/// \brief Retrieve artifacts from CAS and store to specified paths.
/// Tree artifacts are resolved its containing file artifacts are
diff --git a/src/buildtool/execution_api/local/local_action.cpp b/src/buildtool/execution_api/local/local_action.cpp
index d9733f18..8050c3d7 100644
--- a/src/buildtool/execution_api/local/local_action.cpp
+++ b/src/buildtool/execution_api/local/local_action.cpp
@@ -134,8 +134,11 @@ auto LocalAction::Execute(Logger const* logger) noexcept
if (auto result =
local_context_.storage->ActionCache().CachedResult(*action)) {
if (result->exit_code() == 0 and
- ActionResultContainsExpectedOutputs(
- *result, output_files_, output_dirs_)) {
+ (mode_ == RequestMode::kV2_1
+ ? ActionResultContainsExpectedOutputs(*result,
+ output_paths_)
+ : ActionResultContainsExpectedOutputs(
+ *result, output_files_, output_dirs_))) {
return create_response(
logger,
action->hash(),
@@ -376,6 +379,16 @@ auto LocalAction::CreateDirectoryStructure(
}
return true;
};
+
+ if (mode_ == RequestMode::kV2_1) {
+ return std::all_of(
+ output_paths_.begin(),
+ output_paths_.end(),
+ [this, &exec_path, &create_dir](auto const& local_path) {
+ auto dir = (exec_path / cwd_ / local_path).parent_path();
+ return create_dir(dir);
+ });
+ }
return std::all_of(output_files_.begin(),
output_files_.end(),
[this, &exec_path, &create_dir](auto const& local_path) {
@@ -487,11 +500,109 @@ auto LocalAction::CollectOutputDirOrSymlink(
return std::nullopt;
}
+auto LocalAction::CollectOutputPath(
+ std::filesystem::path const& exec_path,
+ std::string const& local_path) const noexcept -> std::optional<OutputPath> {
+ auto out_path = exec_path / local_path;
+ auto type = FileSystemManager::Type(out_path, /*allow_upwards=*/true);
+ if (not type) {
+ logger_.Emit(LogLevel::Error, "expected known type at {}", local_path);
+ return std::nullopt;
+ }
+ if (IsSymlinkObject(*type)) {
+ if (auto content = FileSystemManager::ReadSymlink(out_path)) {
+ // in native mode: check validity of symlink
+ if (ProtocolTraits::IsNative(
+ local_context_.storage->GetHashFunction().GetType()) and
+ not PathIsNonUpwards(*content)) {
+ logger_.Emit(
+ LogLevel::Error, "found invalid symlink at {}", local_path);
+ return std::nullopt;
+ }
+ if (local_context_.storage->CAS().StoreBlob(*content)) {
+ auto out_symlink = bazel_re::OutputSymlink{};
+ out_symlink.set_path(local_path);
+ out_symlink.set_target(*content);
+ return out_symlink;
+ }
+ }
+ }
+ else if (IsFileObject(*type)) {
+ bool is_executable = IsExecutableObject(*type);
+ auto digest = local_context_.storage->CAS().StoreBlob</*kOwner=*/true>(
+ out_path, is_executable);
+ if (digest) {
+ auto out_file = bazel_re::OutputFile{};
+ out_file.set_path(local_path);
+ *out_file.mutable_digest() =
+ ArtifactDigestFactory::ToBazel(*digest);
+ out_file.set_is_executable(is_executable);
+ return out_file;
+ }
+ }
+ else if (IsTreeObject(*type)) {
+ if (auto digest = CreateDigestFromLocalOwnedTree(
+ *local_context_.storage, out_path)) {
+ auto out_dir = bazel_re::OutputDirectory{};
+ out_dir.set_path(local_path);
+ (*out_dir.mutable_tree_digest()) =
+ ArtifactDigestFactory::ToBazel(*digest);
+ return out_dir;
+ }
+ logger_.Emit(LogLevel::Error,
+ "found invalid entries in directory at {}",
+ local_path);
+ }
+ else {
+ logger_.Emit(LogLevel::Error,
+ "expected file, directory, or symlink at {}",
+ local_path);
+ }
+ return std::nullopt;
+}
+
auto LocalAction::CollectAndStoreOutputs(
bazel_re::ActionResult* result,
std::filesystem::path const& exec_path) const noexcept -> bool {
try {
logger_.Emit(LogLevel::Trace, "collecting outputs:");
+ if (mode_ == RequestMode::kV2_1) {
+ for (auto const& path : output_paths_) {
+ auto out = CollectOutputPath(exec_path, path);
+ if (not out) {
+ logger_.Emit(LogLevel::Error,
+ "could not collect output path {}",
+ path);
+ return false;
+ }
+ if (std::holds_alternative<bazel_re::OutputSymlink>(*out)) {
+ auto out_symlink = std::get<bazel_re::OutputSymlink>(*out);
+ logger_.Emit(LogLevel::Trace,
+ " - symlink {}: {}",
+ path,
+ out_symlink.target());
+ result->mutable_output_symlinks()->Add(
+ std::move(out_symlink));
+ }
+ else if (std::holds_alternative<bazel_re::OutputFile>(*out)) {
+ auto out_file = std::get<bazel_re::OutputFile>(*out);
+ auto const& digest = out_file.digest().hash();
+ logger_.Emit(
+ LogLevel::Trace, " - file {}: {}", path, digest);
+ result->mutable_output_files()->Add(std::move(out_file));
+ }
+ else {
+ auto out_dir = std::get<bazel_re::OutputDirectory>(*out);
+ auto const& digest = out_dir.tree_digest().hash();
+ logger_.Emit(
+ LogLevel::Trace, " - dir {}: {}", path, digest);
+ result->mutable_output_directories()->Add(
+ std::move(out_dir));
+ }
+ }
+ return true;
+ }
+ // if mode_ is RequestMode::kV2_0 or RequestMode::kBestEffort
for (auto const& path : output_files_) {
auto out = CollectOutputFileOrSymlink(exec_path, path);
if (not out) {
@@ -506,6 +617,9 @@ auto LocalAction::CollectAndStoreOutputs(
" - symlink {}: {}",
path,
out_symlink.target());
+ if (mode_ == RequestMode::kBestEffort) {
+ *result->mutable_output_symlinks()->Add() = out_symlink;
+ }
result->mutable_output_file_symlinks()->Add(
std::move(out_symlink));
}
@@ -530,6 +644,9 @@ auto LocalAction::CollectAndStoreOutputs(
" - symlink {}: {}",
path,
out_symlink.target());
+ if (mode_ == RequestMode::kBestEffort) {
+ *result->mutable_output_symlinks()->Add() = out_symlink;
+ }
result->mutable_output_directory_symlinks()->Add(
std::move(out_symlink));
}
diff --git a/src/buildtool/execution_api/local/local_action.hpp b/src/buildtool/execution_api/local/local_action.hpp
index 7610def0..47f912d8 100644
--- a/src/buildtool/execution_api/local/local_action.hpp
+++ b/src/buildtool/execution_api/local/local_action.hpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <chrono>
#include <compare>
+#include <cstdint>
#include <filesystem>
#include <functional> // IWYU pragma: keep
#include <map>
@@ -45,6 +46,12 @@
class LocalAction final : public IExecutionAction {
friend class LocalApi;
+ enum class RequestMode : std::uint8_t {
+ kV2_0, // RBEv2.0
+ kV2_1, // >=RBEv2.1
+ kBestEffort // construct both, let the server pick
+ };
+
public:
struct Output {
bazel_re::ActionResult action;
@@ -56,6 +63,9 @@ class LocalAction final : public IExecutionAction {
std::variant<bazel_re::OutputFile, bazel_re::OutputSymlink>;
using OutputDirOrSymlink =
std::variant<bazel_re::OutputDirectory, bazel_re::OutputSymlink>;
+ using OutputPath = std::variant<bazel_re::OutputFile,
+ bazel_re::OutputDirectory,
+ bazel_re::OutputSymlink>;
using FileCopies = std::unordered_map<Artifact::ObjectInfo, TmpDir::Ptr>;
@@ -76,31 +86,65 @@ class LocalAction final : public IExecutionAction {
std::string const cwd_;
std::vector<std::string> output_files_;
std::vector<std::string> output_dirs_;
+ std::vector<std::string> output_paths_;
std::map<std::string, std::string> const env_vars_;
std::vector<bazel_re::Platform_Property> const properties_;
std::chrono::milliseconds timeout_{kDefaultTimeout};
CacheFlag cache_flag_{CacheFlag::CacheOutput};
+ RequestMode mode_{};
+
+ explicit LocalAction(gsl::not_null<LocalContext const*> local_context,
+ ArtifactDigest root_digest,
+ std::vector<std::string> command,
+ std::string cwd,
+ std::vector<std::string> output_files,
+ std::vector<std::string> output_dirs,
+ std::map<std::string, std::string> env_vars,
+ std::map<std::string, std::string> const& properties,
+ bool best_effort) noexcept
+ : local_context_{*local_context},
+ root_digest_{std::move(root_digest)},
+ cmdline_{std::move(command)},
+ cwd_{std::move(cwd)},
+ output_files_{std::move(output_files)},
+ output_dirs_{std::move(output_dirs)},
+ env_vars_{std::move(env_vars)},
+ properties_{BazelMsgFactory::CreateMessageVectorFromMap<
+ bazel_re::Platform_Property>(properties)},
+ mode_{best_effort ? RequestMode::kBestEffort : RequestMode::kV2_0} {
+ std::sort(output_files_.begin(), output_files_.end());
+ std::sort(output_dirs_.begin(), output_dirs_.end());
+ if (best_effort) {
+ output_paths_.reserve(output_files_.size() + output_dirs_.size());
+ output_paths_.insert(output_paths_.end(),
+ output_files_.begin(),
+ output_files_.end());
+ output_paths_.insert(
+ output_paths_.end(), output_dirs_.begin(), output_dirs_.end());
+ std::sort(output_paths_.begin(), output_paths_.end());
+ }
+ }
+ // Alternative constructor with combined output_paths for files and dirs, as
+ // it is used by RBEv2.1 and above.
explicit LocalAction(
gsl::not_null<LocalContext const*> local_context,
ArtifactDigest root_digest,
std::vector<std::string> command,
std::string cwd,
- std::vector<std::string> output_files,
- std::vector<std::string> output_dirs,
+ std::vector<std::string> output_paths,
std::map<std::string, std::string> env_vars,
std::map<std::string, std::string> const& properties) noexcept
: local_context_{*local_context},
root_digest_{std::move(root_digest)},
cmdline_{std::move(command)},
cwd_{std::move(cwd)},
- output_files_{std::move(output_files)},
- output_dirs_{std::move(output_dirs)},
+ output_paths_{std::move(output_paths)},
env_vars_{std::move(env_vars)},
properties_{BazelMsgFactory::CreateMessageVectorFromMap<
- bazel_re::Platform_Property>(properties)} {
- std::sort(output_files_.begin(), output_files_.end());
- std::sort(output_dirs_.begin(), output_dirs_.end());
+ bazel_re::Platform_Property>(properties)},
+ mode_{RequestMode::kV2_1} {
+ std::sort(output_paths_.begin(), output_paths_.end());
}
[[nodiscard]] auto CreateActionDigest(ArtifactDigest const& exec_dir,
@@ -114,6 +158,7 @@ class LocalAction final : public IExecutionAction {
.cwd = &cwd_,
.output_files = &output_files_,
.output_dirs = &output_dirs_,
+ .output_paths = &output_paths_,
.env_vars = &env_vars,
.properties = &properties_,
.exec_dir = &exec_dir,
@@ -153,6 +198,10 @@ class LocalAction final : public IExecutionAction {
std::string const& local_path) const noexcept
-> std::optional<OutputDirOrSymlink>;
+ [[nodiscard]] auto CollectOutputPath(std::filesystem::path const& exec_path,
+ std::string const& local_path)
+ const noexcept -> std::optional<OutputPath>;
+
[[nodiscard]] auto CollectAndStoreOutputs(
bazel_re::ActionResult* result,
std::filesystem::path const& exec_path) const noexcept -> bool;
diff --git a/src/buildtool/execution_api/local/local_api.cpp b/src/buildtool/execution_api/local/local_api.cpp
index 3eede672..c0a3a30e 100644
--- a/src/buildtool/execution_api/local/local_api.cpp
+++ b/src/buildtool/execution_api/local/local_api.cpp
@@ -59,6 +59,7 @@ LocalApi::LocalApi(gsl::not_null<LocalContext const*> const& local_context,
: local_context_{*local_context},
git_api_{CreateFallbackApi(*local_context->storage, repo_config)} {}
+// NOLINTNEXTLINE(google-default-arguments)
auto LocalApi::CreateAction(
ArtifactDigest const& root_digest,
std::vector<std::string> const& command,
@@ -66,8 +67,13 @@ auto LocalApi::CreateAction(
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 {
+ std::map<std::string, std::string> const& properties,
+ bool force_legacy) const noexcept -> IExecutionAction::Ptr {
+ if (ProtocolTraits::IsNative(GetHashType())) {
+ // fall back to legacy for native
+ force_legacy = true;
+ }
+ bool best_effort = not force_legacy;
return IExecutionAction::Ptr{new (std::nothrow) LocalAction{&local_context_,
root_digest,
command,
@@ -75,6 +81,24 @@ auto LocalApi::CreateAction(
output_files,
output_dirs,
env_vars,
+ properties,
+ best_effort}};
+}
+
+auto LocalApi::CreateAction(
+ ArtifactDigest const& root_digest,
+ std::vector<std::string> const& command,
+ std::string const& cwd,
+ std::vector<std::string> const& output_paths,
+ 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_paths,
+ env_vars,
properties}};
}
diff --git a/src/buildtool/execution_api/local/local_api.hpp b/src/buildtool/execution_api/local/local_api.hpp
index e2374258..1b6ddeab 100644
--- a/src/buildtool/execution_api/local/local_api.hpp
+++ b/src/buildtool/execution_api/local/local_api.hpp
@@ -40,6 +40,7 @@ class LocalApi final : public IExecutionApi {
explicit LocalApi(gsl::not_null<LocalContext const*> const& local_context,
RepositoryConfig const* repo_config = nullptr) noexcept;
+ // NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] auto CreateAction(
ArtifactDigest const& root_digest,
std::vector<std::string> const& command,
@@ -47,9 +48,30 @@ class LocalApi final : public IExecutionApi {
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
+ std::map<std::string, std::string> const& properties,
+ bool force_legacy = false) const noexcept
-> IExecutionAction::Ptr final;
+ /// \brief Create a new action (>=RBEv2.1).
+ /// \param[in] root_digest Digest of the build root.
+ /// \param[in] command Command as argv vector
+ /// \param[in] cwd Working directory, relative to execution root
+ /// \param[in] output_paths List of output file/dir paths, relative to cwd
+ /// \param[in] env_vars The environment variables to set.
+ /// \param[in] properties Platform properties to set.
+ /// \returns The new action.
+ /// Note that, due to missing file/dir separation, the execution response
+ /// will not report output file symlinks and directory symlinks separately.
+ /// (see \ref LocalResponse::DirectorySymlinks)
+ [[nodiscard]] auto CreateAction(
+ ArtifactDigest const& root_digest,
+ std::vector<std::string> const& command,
+ std::string const& cwd,
+ std::vector<std::string> const& output_paths,
+ std::map<std::string, std::string> const& env_vars,
+ std::map<std::string, std::string> const& properties) const noexcept
+ -> IExecutionAction::Ptr;
+
// NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] auto RetrieveToPaths(
std::vector<Artifact::ObjectInfo> const& artifacts_info,
diff --git a/src/buildtool/execution_api/remote/bazel/bazel_action.cpp b/src/buildtool/execution_api/remote/bazel/bazel_action.cpp
index 6fa8eedc..604b680d 100644
--- a/src/buildtool/execution_api/remote/bazel/bazel_action.cpp
+++ b/src/buildtool/execution_api/remote/bazel/bazel_action.cpp
@@ -27,15 +27,15 @@
#include "src/buildtool/execution_api/utils/outputscheck.hpp"
#include "src/buildtool/logging/log_level.hpp"
-BazelAction::BazelAction(
- std::shared_ptr<BazelNetwork> network,
- ArtifactDigest root_digest,
- std::vector<std::string> command,
- std::string cwd,
- std::vector<std::string> output_files,
- std::vector<std::string> output_dirs,
- std::map<std::string, std::string> const& env_vars,
- std::map<std::string, std::string> const& properties) noexcept
+BazelAction::BazelAction(std::shared_ptr<BazelNetwork> network,
+ ArtifactDigest root_digest,
+ std::vector<std::string> command,
+ std::string cwd,
+ std::vector<std::string> output_files,
+ std::vector<std::string> output_dirs,
+ std::map<std::string, std::string> const& env_vars,
+ std::map<std::string, std::string> const& properties,
+ bool best_effort) noexcept
: network_{std::move(network)},
root_digest_{std::move(root_digest)},
cmdline_{std::move(command)},
@@ -45,9 +45,18 @@ BazelAction::BazelAction(
env_vars_{BazelMsgFactory::CreateMessageVectorFromMap<
bazel_re::Command_EnvironmentVariable>(env_vars)},
properties_{BazelMsgFactory::CreateMessageVectorFromMap<
- bazel_re::Platform_Property>(properties)} {
+ bazel_re::Platform_Property>(properties)},
+ mode_{best_effort ? RequestMode::kBestEffort : RequestMode::kV2_0} {
std::sort(output_files_.begin(), output_files_.end());
std::sort(output_dirs_.begin(), output_dirs_.end());
+ if (best_effort) {
+ output_paths_.reserve(output_files_.size() + output_dirs_.size());
+ output_paths_.insert(
+ output_paths_.end(), output_files_.begin(), output_files_.end());
+ output_paths_.insert(
+ output_paths_.end(), output_dirs_.begin(), output_dirs_.end());
+ std::sort(output_paths_.begin(), output_paths_.end());
+ }
}
auto BazelAction::Execute(Logger const* logger) noexcept
@@ -90,13 +99,15 @@ auto BazelAction::Execute(Logger const* logger) noexcept
};
if (do_cache) {
- if (auto result =
- network_->GetCachedActionResult(*action, output_files_)) {
+ if (auto result = network_->GetCachedActionResult(
+ *action,
+ mode_ == RequestMode::kV2_0 ? output_files_ : output_paths_)) {
if (result->exit_code() == 0 and
- ActionResultContainsExpectedOutputs(
- *result, output_files_, output_dirs_)
-
- ) {
+ (mode_ == RequestMode::kV2_0
+ ? ActionResultContainsExpectedOutputs(
+ *result, output_files_, output_dirs_)
+ : ActionResultContainsExpectedOutputs(*result,
+ output_paths_))) {
return create_response(
logger,
action->hash(),
@@ -154,6 +165,7 @@ auto BazelAction::CreateBundlesForAction(
.cwd = &cwd_,
.output_files = &output_files_,
.output_dirs = &output_dirs_,
+ .output_paths = &output_paths_,
.env_vars = &env_vars_,
.properties = &properties_,
.exec_dir = &exec_dir,
diff --git a/src/buildtool/execution_api/remote/bazel/bazel_action.hpp b/src/buildtool/execution_api/remote/bazel/bazel_action.hpp
index 08cb338c..ea3c4b53 100644
--- a/src/buildtool/execution_api/remote/bazel/bazel_action.hpp
+++ b/src/buildtool/execution_api/remote/bazel/bazel_action.hpp
@@ -16,6 +16,7 @@
#define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_REMOTE_BAZEL_BAZEL_ACTION_HPP
#include <chrono>
+#include <cstdint>
#include <map>
#include <memory>
#include <optional>
@@ -36,6 +37,11 @@
class BazelAction final : public IExecutionAction {
friend class BazelApi;
+ enum class RequestMode : std::uint8_t {
+ kV2_0, // RBEv2.0
+ kBestEffort // RBEv2.0 and >=RBEv2.1, let the server pick
+ };
+
public:
auto Execute(Logger const* logger) noexcept
-> IExecutionResponse::Ptr final;
@@ -51,20 +57,22 @@ class BazelAction final : public IExecutionAction {
std::string const cwd_;
std::vector<std::string> output_files_;
std::vector<std::string> output_dirs_;
+ std::vector<std::string> output_paths_;
std::vector<bazel_re::Command_EnvironmentVariable> const env_vars_;
std::vector<bazel_re::Platform_Property> const properties_;
CacheFlag cache_flag_{CacheFlag::CacheOutput};
std::chrono::milliseconds timeout_{kDefaultTimeout};
+ RequestMode mode_{};
- explicit BazelAction(
- std::shared_ptr<BazelNetwork> network,
- ArtifactDigest root_digest,
- std::vector<std::string> command,
- std::string cwd,
- std::vector<std::string> output_files,
- std::vector<std::string> output_dirs,
- std::map<std::string, std::string> const& env_vars,
- std::map<std::string, std::string> const& properties) noexcept;
+ explicit BazelAction(std::shared_ptr<BazelNetwork> network,
+ ArtifactDigest root_digest,
+ std::vector<std::string> command,
+ std::string cwd,
+ std::vector<std::string> output_files,
+ std::vector<std::string> output_dirs,
+ std::map<std::string, std::string> const& env_vars,
+ std::map<std::string, std::string> const& properties,
+ bool best_effort) noexcept;
[[nodiscard]] auto CreateBundlesForAction(
std::unordered_set<ArtifactBlob>* blobs,
diff --git a/src/buildtool/execution_api/remote/bazel/bazel_api.cpp b/src/buildtool/execution_api/remote/bazel/bazel_api.cpp
index c90b7024..00bb2e75 100644
--- a/src/buildtool/execution_api/remote/bazel/bazel_api.cpp
+++ b/src/buildtool/execution_api/remote/bazel/bazel_api.cpp
@@ -16,6 +16,7 @@
#include <algorithm>
#include <atomic>
+#include <compare>
#include <cstdio>
#include <exception>
#include <functional>
@@ -37,6 +38,7 @@
#include "src/buildtool/execution_api/common/stream_dumper.hpp"
#include "src/buildtool/execution_api/common/tree_reader.hpp"
#include "src/buildtool/execution_api/remote/bazel/bazel_action.hpp"
+#include "src/buildtool/execution_api/remote/bazel/bazel_capabilities_client.hpp"
#include "src/buildtool/execution_api/remote/bazel/bazel_network.hpp"
#include "src/buildtool/execution_api/remote/bazel/bazel_network_reader.hpp"
#include "src/buildtool/file_system/file_system_manager.hpp"
@@ -49,6 +51,9 @@
namespace {
+auto constexpr kVersion2dot1 =
+ Capabilities::Version{.major = 2, .minor = 1, .patch = 0};
+
[[nodiscard]] auto RetrieveToCas(
std::unordered_set<Artifact::ObjectInfo> const& infos,
IExecutionApi const& api,
@@ -146,6 +151,7 @@ BazelApi::BazelApi(BazelApi&& other) noexcept = default;
// implement destructor in cpp, where all members are complete types
BazelApi::~BazelApi() = default;
+// NOLINTNEXTLINE(google-default-arguments)
auto BazelApi::CreateAction(
ArtifactDigest const& root_digest,
std::vector<std::string> const& command,
@@ -153,8 +159,26 @@ auto BazelApi::CreateAction(
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 {
+ std::map<std::string, std::string> const& properties,
+ bool force_legacy) const noexcept -> IExecutionAction::Ptr {
+ if (ProtocolTraits::IsNative(GetHashType())) {
+ // fall back to legacy for native
+ force_legacy = true;
+ }
+
+ bool best_effort = not force_legacy;
+ if (not force_legacy and
+ network_->GetCapabilities()->high_api_version < kVersion2dot1) {
+ best_effort = false;
+ }
+ if (not best_effort and
+ network_->GetCapabilities()->low_api_version >= kVersion2dot1) {
+ Logger::Log(LogLevel::Warning,
+ "Server does not support RBEv2.0, falling back to newer "
+ "API version (best effort).");
+ best_effort = true;
+ }
+
return std::unique_ptr<BazelAction>{new (std::nothrow)
BazelAction{network_,
root_digest,
@@ -163,7 +187,8 @@ auto BazelApi::CreateAction(
output_files,
output_dirs,
env_vars,
- properties}};
+ properties,
+ best_effort}};
}
// NOLINTNEXTLINE(google-default-arguments)
diff --git a/src/buildtool/execution_api/remote/bazel/bazel_api.hpp b/src/buildtool/execution_api/remote/bazel/bazel_api.hpp
index df60204f..99e43578 100644
--- a/src/buildtool/execution_api/remote/bazel/bazel_api.hpp
+++ b/src/buildtool/execution_api/remote/bazel/bazel_api.hpp
@@ -58,6 +58,7 @@ class BazelApi final : public IExecutionApi {
auto operator=(BazelApi&&) -> BazelApi& = delete;
~BazelApi() final;
+ // NOLINTNEXTLINE(google-default-arguments)
[[nodiscard]] auto CreateAction(
ArtifactDigest const& root_digest,
std::vector<std::string> const& command,
@@ -65,7 +66,8 @@ class BazelApi final : public IExecutionApi {
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
+ std::map<std::string, std::string> const& properties,
+ bool force_legacy = false) const noexcept
-> IExecutionAction::Ptr final;
// NOLINTNEXTLINE(google-default-arguments)
diff --git a/src/buildtool/execution_api/serve/mr_local_api.hpp b/src/buildtool/execution_api/serve/mr_local_api.hpp
index d9f323cf..738f52fe 100644
--- a/src/buildtool/execution_api/serve/mr_local_api.hpp
+++ b/src/buildtool/execution_api/serve/mr_local_api.hpp
@@ -55,8 +55,8 @@ class MRLocalApi final : public IExecutionApi {
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 final {
+ std::map<std::string, std::string> const& /*properties*/,
+ bool /*force_legacy*/) const noexcept -> IExecutionAction::Ptr final {
// Execution not supported
return nullptr;
}
diff --git a/src/buildtool/execution_api/utils/outputscheck.hpp b/src/buildtool/execution_api/utils/outputscheck.hpp
index da20ee90..026fce35 100644
--- a/src/buildtool/execution_api/utils/outputscheck.hpp
+++ b/src/buildtool/execution_api/utils/outputscheck.hpp
@@ -62,6 +62,34 @@
}
}
+// overload for treating file and directory paths alike
+[[nodiscard]] static inline auto ActionResultContainsExpectedOutputs(
+ const bazel_re::ActionResult& result,
+ const std::vector<std::string>& expected_paths) noexcept -> bool {
+ try {
+ std::set<std::string> actual_output_paths{};
+ for (auto const& file : result.output_files()) {
+ actual_output_paths.emplace(file.path());
+ }
+ for (auto const& file : result.output_file_symlinks()) {
+ actual_output_paths.emplace(file.path());
+ }
+ for (auto const& file : result.output_directory_symlinks()) {
+ actual_output_paths.emplace(file.path());
+ }
+ for (auto const& dir : result.output_directories()) {
+ actual_output_paths.emplace(dir.path());
+ }
+ return std::all_of(expected_paths.begin(),
+ expected_paths.end(),
+ [&actual_output_paths](auto const& expected) {
+ return actual_output_paths.contains(expected);
+ });
+ } catch (...) {
+ return false;
+ }
+}
+
#endif
#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_UTILS_OUTPUTSCHECK_HPP
diff --git a/test/buildtool/execution_engine/executor/executor.test.cpp b/test/buildtool/execution_engine/executor/executor.test.cpp
index 0db5501a..8d37f9aa 100644
--- a/test/buildtool/execution_engine/executor/executor.test.cpp
+++ b/test/buildtool/execution_engine/executor/executor.test.cpp
@@ -205,8 +205,8 @@ class TestApi : public IExecutionApi {
std::vector<std::string> const& /*unused*/,
std::vector<std::string> const& /*unused*/,
std::map<std::string, std::string> const& /*unused*/,
- std::map<std::string, std::string> const& /*unused*/) const noexcept
- -> IExecutionAction::Ptr final {
+ std::map<std::string, std::string> const& /*unused*/,
+ bool /*unused*/) const noexcept -> IExecutionAction::Ptr final {
return std::make_unique<TestAction>(config_);
}
[[nodiscard]] auto RetrieveToPaths(