// Copyright 2022 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/remote/bazel/bazel_action.hpp" #include #include #include #include // std::move #include "gsl/gsl" #include "src/buildtool/common/artifact_digest_factory.hpp" #include "src/buildtool/execution_api/bazel_msg/bazel_msg_factory.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_response.hpp" #include "src/buildtool/execution_api/utils/outputscheck.hpp" #include "src/buildtool/logging/log_level.hpp" BazelAction::BazelAction(std::shared_ptr network, ArtifactDigest root_digest, std::vector command, std::string cwd, std::vector output_files, std::vector output_dirs, std::map const& env_vars, std::map const& properties, bool best_effort) noexcept : network_{std::move(network)}, 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_{BazelMsgFactory::CreateMessageVectorFromMap< bazel_re::Command_EnvironmentVariable>(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()); } } auto BazelAction::Execute(Logger const* logger) noexcept -> IExecutionResponse::Ptr { std::unordered_set blobs{}; auto do_cache = CacheEnabled(cache_flag_); auto action = CreateBundlesForAction(&blobs, root_digest_, not do_cache); if (not action) { if (logger != nullptr) { logger->Emit(LogLevel::Error, "failed to create an action digest for {}", root_digest_.hash()); } return nullptr; } if (logger != nullptr) { logger->Emit(LogLevel::Trace, "start execution\n" " - exec_dir digest: {}\n" " - action digest: {}", root_digest_.hash(), action->hash()); } auto create_response = [](Logger const* logger, std::string const& action_hash, auto&&... args) -> IExecutionResponse::Ptr { try { return IExecutionResponse::Ptr{new BazelResponse{ action_hash, std::forward(args)...}}; } catch (...) { if (logger != nullptr) { logger->Emit(LogLevel::Error, "failed to create a response for {}", action_hash); } } return nullptr; }; if (do_cache) { if (auto result = network_->GetCachedActionResult( *action, mode_ == RequestMode::kV2_0 ? output_files_ : output_paths_)) { if (result->exit_code() == 0 and (mode_ == RequestMode::kV2_0 ? ActionResultContainsExpectedOutputs( *result, output_files_, output_dirs_) : ActionResultContainsExpectedOutputs(*result, output_paths_))) { return create_response( logger, action->hash(), network_, BazelExecutionClient::ExecutionOutput{ .action_result = *result, .cached_result = true}); } } } if (ExecutionEnabled(cache_flag_) and network_->UploadBlobs(std::move(blobs))) { if (auto output = network_->ExecuteBazelActionSync(*action)) { if (cache_flag_ == CacheFlag::PretendCached) { // ensure the same id is created as if caching were enabled auto action_cached = CreateBundlesForAction(nullptr, root_digest_, false); if (not action_cached) { if (logger != nullptr) { logger->Emit( LogLevel::Error, "failed to create a cached action digest for {}", root_digest_.hash()); } return nullptr; } output->cached_result = true; return create_response(logger, action_cached->hash(), network_, *std::move(output)); } return create_response( logger, action->hash(), network_, *std::move(output)); } } return nullptr; } auto BazelAction::CreateBundlesForAction( std::unordered_set* blobs, ArtifactDigest const& exec_dir, bool do_not_cache) const noexcept -> std::optional { using StoreFunc = BazelMsgFactory::ActionDigestRequest::BlobStoreFunc; std::optional store_blob = std::nullopt; if (blobs != nullptr) { store_blob = [&blobs](ArtifactBlob&& blob) { blobs->emplace(std::move(blob)); }; } BazelMsgFactory::ActionDigestRequest request{ .command_line = &cmdline_, .cwd = &cwd_, .output_files = &output_files_, .output_dirs = &output_dirs_, .output_paths = &output_paths_, .env_vars = &env_vars_, .properties = &properties_, .exec_dir = &exec_dir, .hash_function = network_->GetHashFunction(), .timeout = timeout_, .skip_action_cache = do_not_cache, .store_blob = std::move(store_blob)}; auto const action_digest = BazelMsgFactory::CreateActionDigestFromCommandLine(request); if (not action_digest) { return std::nullopt; } return ArtifactDigestFactory::ToBazel(*action_digest); }