| // Copyright 2025 Google Inc. All Rights Reserved. |
| // |
| // 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 "delayed_commands.h" |
| |
| #include "build_config.h" |
| #include "delayed_worker.h" |
| #include "delayed_worker_fuchsia_bazel.h" |
| #include "graph.h" |
| |
| // Set to 1 to enable debug messages (which will break regression tests). |
| #define DEBUG 0 |
| |
| #if DEBUG |
| #include <stdio.h> |
| #define LOG(...) fprintf(stderr, __VA_ARGS__) |
| #else |
| #define LOG(...) (void)0 |
| #endif |
| |
| bool DelayedCommands::empty() const { |
| return delayed_edges_.empty(); |
| } |
| |
| void DelayedCommands::SetWorker(std::unique_ptr<DelayedWorker> worker) { |
| worker_ = std::move(worker); |
| } |
| |
| void DelayedCommands::SetSubprocessSet(SubprocessSet* subprocess_set) { |
| LOG("DELAYED COMMANDS: SET SUBPROCESS_SET"); |
| if (subprocess_set) { |
| tool_runner_ = DelayedWorkerRunner::CreateFrom(*subprocess_set); |
| } else { |
| tool_runner_.reset(); |
| } |
| } |
| |
| void DelayedCommands::SetRunnerForTests( |
| std::unique_ptr<DelayedWorkerRunner> tool_runner) { |
| LOG("DELAYED COMMANDS: SET RUNNER FOR TESTS"); |
| tool_runner_ = std::move(tool_runner); |
| } |
| |
| bool DelayedCommands::CheckEdge(const Edge* edge) { |
| if (!worker_) { |
| // No worker means no delayed commands. |
| LOG("NO DELAYED_COMMANDS WORKER\n"); |
| return false; |
| } |
| |
| std::string description = edge->GetDescription(); |
| if (worker_->CheckEdge(edge, description)) { |
| LOG("FOUND DELAYED EDGE: %s\n", edge->GetDescription().c_str()); |
| delayed_edges_.push_back(edge); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| DelayedCommands::Result DelayedCommands::BuildEdges() { |
| if (delayed_edges_.empty()) { |
| return { ExitSuccess, "", {} }; |
| } |
| |
| if (!tool_runner_) { |
| // Dry-run: fake success. |
| return { ExitSuccess, "", std::move(delayed_edges_) }; |
| } |
| |
| DelayedWorker::RequestInfo req; |
| req.request_id = ++next_request_id_; |
| |
| uint32_t action_id = 0; |
| for (const Edge* edge : delayed_edges_) { |
| /// Compute output paths list. |
| std::vector<std::string> ninja_outputs; |
| ninja_outputs.reserve(edge->outputs_.size()); |
| for (const auto& output : edge->outputs_) { |
| ninja_outputs.push_back(output->path()); |
| } |
| |
| /// Append one action to the request. |
| req.actions.push_back({ ++action_id, edge->GetBinding("command"), |
| edge->GetDescription(), std::move(ninja_outputs), |
| edge->GetUnescapedDepfile() }); |
| } |
| |
| for (const auto& pair : build_metadata_) { |
| req.build_metadata.try_emplace(pair.first, pair.second); |
| } |
| |
| DelayedWorker::ResponseInfo response = |
| worker_->BuildEdges(req, *tool_runner_); |
| |
| if (response.request_id != req.request_id) { |
| return { ExitFailure, |
| StringFormat( |
| "Invalid worker tool response request_id=%u, expected %u", |
| static_cast<unsigned>(response.request_id), |
| static_cast<unsigned>(req.request_id)), |
| std::move(delayed_edges_) }; |
| } |
| |
| return { static_cast<ExitStatus>(response.status), std::move(response.output), |
| std::move(delayed_edges_) }; |
| }; |
| |
| DelayedCommands::DelayedCommands(const BuildConfig& config) { |
| for (const auto& pair : config.build_metadata) { |
| build_metadata_.try_emplace(pair.first, pair.second); |
| } |
| } |
| |
| DelayedCommands::~DelayedCommands() = default; |