diff options
author | Klaus Aehlig <klaus.aehlig@huawei.com> | 2025-03-24 15:45:33 +0100 |
---|---|---|
committer | Klaus Aehlig <klaus.aehlig@huawei.com> | 2025-03-24 16:58:54 +0100 |
commit | a1e9efb1cea77bacf83fa7b576bc765d0155f2c6 (patch) | |
tree | 276ee2834ff7fe4af75ce4a2dfff15aa82491cd8 /src/buildtool/graph_traverser/graph_traverser.hpp | |
parent | d4ae1e4ca11876caf49d7fca2ae1ea880d194363 (diff) | |
download | justbuild-a1e9efb1cea77bacf83fa7b576bc765d0155f2c6.tar.gz |
GraphTraverser: move implementation to cpp file
The public interface of our graph traverser is typically called
once per program invocation, so there is no benefit in optimizing
across the boundaries of that call. Many calls do happen within the
internal functions of the graph traverser; but for that, the benefit
of being in the same compilation unit also happens if everything
is moved to the same cpp file. Therefore, make the graph traverser
a standard library and in this way, clean up our code basis.
Diffstat (limited to 'src/buildtool/graph_traverser/graph_traverser.hpp')
-rw-r--r-- | src/buildtool/graph_traverser/graph_traverser.hpp | 646 |
1 files changed, 17 insertions, 629 deletions
diff --git a/src/buildtool/graph_traverser/graph_traverser.hpp b/src/buildtool/graph_traverser/graph_traverser.hpp index b85902be..0958a507 100644 --- a/src/buildtool/graph_traverser/graph_traverser.hpp +++ b/src/buildtool/graph_traverser/graph_traverser.hpp @@ -14,67 +14,31 @@ #ifndef INCLUDED_SRC_BUILDTOOL_GRAPH_TRAVERSER_GRAPH_TRAVERSER_HPP #define INCLUDED_SRC_BUILDTOOL_GRAPH_TRAVERSER_GRAPH_TRAVERSER_HPP +#ifndef BOOTSTRAP_BUILD_TOOL -#ifdef __unix__ -#include <unistd.h> -#else -#error "Non-unix is not supported yet" -#endif - -#include <algorithm> -#include <atomic> -#include <condition_variable> #include <cstddef> -#include <cstdint> -#include <cstdio> #include <filesystem> -#include <fstream> #include <functional> -#include <iomanip> -#include <iostream> -#include <iterator> #include <map> -#include <memory> #include <optional> -#include <sstream> #include <string> -#include <thread> #include <tuple> #include <unordered_map> -#include <unordered_set> #include <utility> #include <vector> -#include "fmt/core.h" #include "gsl/gsl" #include "nlohmann/json.hpp" #include "src/buildtool/common/action_description.hpp" #include "src/buildtool/common/artifact.hpp" -#include "src/buildtool/common/artifact_blob.hpp" #include "src/buildtool/common/artifact_description.hpp" -#include "src/buildtool/common/artifact_digest.hpp" #include "src/buildtool/common/cli.hpp" #include "src/buildtool/common/identifier.hpp" -#include "src/buildtool/common/statistics.hpp" #include "src/buildtool/common/tree.hpp" -#include "src/buildtool/crypto/hash_function.hpp" -#include "src/buildtool/execution_api/common/api_bundle.hpp" -#include "src/buildtool/execution_api/common/common_api.hpp" -#include "src/buildtool/execution_api/common/execution_api.hpp" -#include "src/buildtool/execution_api/utils/subobject.hpp" #include "src/buildtool/execution_engine/dag/dag.hpp" #include "src/buildtool/execution_engine/executor/context.hpp" -#include "src/buildtool/execution_engine/executor/executor.hpp" -#include "src/buildtool/execution_engine/traverser/traverser.hpp" -#include "src/buildtool/file_system/file_system_manager.hpp" -#include "src/buildtool/file_system/jsonfs.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/progress_reporting/base_progress_reporter.hpp" -#include "src/utils/cpp/expected.hpp" -#include "src/utils/cpp/json.hpp" -#include "src/utils/cpp/path.hpp" class GraphTraverser { public: @@ -118,139 +82,13 @@ class GraphTraverser { std::vector<std::string>&& blobs, std::vector<Tree::Ptr>&& trees, std::vector<ArtifactDescription>&& extra_artifacts = {}) const - -> std::optional<BuildResult> { - DependencyGraph graph; // must outlive artifact_nodes - auto artifacts = BuildArtifacts(&graph, - artifact_descriptions, - runfile_descriptions, - std::move(action_descriptions), - std::move(trees), - std::move(blobs), - extra_artifacts); - if (not artifacts) { - return std::nullopt; - } - auto [rel_paths, artifact_nodes, extra_nodes] = *artifacts; - - auto const object_infos = CollectObjectInfos(artifact_nodes, logger_); - auto extra_infos = CollectObjectInfos(extra_nodes, logger_); - if (not object_infos or not extra_infos) { - return std::nullopt; - } - - Expects(extra_artifacts.size() == extra_infos->size()); - std::unordered_map<ArtifactDescription, Artifact::ObjectInfo> infos; - infos.reserve(extra_infos->size()); - std::transform( - std::make_move_iterator(extra_artifacts.begin()), - std::make_move_iterator(extra_artifacts.end()), - std::make_move_iterator(extra_infos->begin()), - std::inserter(infos, infos.end()), - std::make_pair<ArtifactDescription&&, Artifact::ObjectInfo&&>); - - bool failed_artifacts = std::any_of( - object_infos->begin(), object_infos->end(), [](auto const& info) { - return info.failed; - }); - - if (not clargs_.stage) { - PrintOutputs("Artifacts built, logical paths are:", - rel_paths, - artifact_nodes, - runfile_descriptions); - MaybePrintToStdout( - rel_paths, - artifact_nodes, - artifact_descriptions.size() == 1 - ? std::optional<std::string>{artifact_descriptions.begin() - ->first} - : std::nullopt); - return BuildResult{.output_paths = std::move(rel_paths), - .extra_infos = std::move(infos), - .failed_artifacts = failed_artifacts}; - } - - if (clargs_.stage->remember) { - if (not context_.apis->remote->ParallelRetrieveToCas( - *object_infos, *context_.apis->local, clargs_.jobs, true)) { - Logger::Log(logger_, - LogLevel::Warning, - "Failed to copy objects to CAS"); - } - } - - auto output_paths = RetrieveOutputs(rel_paths, *object_infos); - if (not output_paths) { - return std::nullopt; - } - PrintOutputs("Artifacts can be found in:", - *output_paths, - artifact_nodes, - runfile_descriptions); - - MaybePrintToStdout( - rel_paths, - artifact_nodes, - artifact_descriptions.size() == 1 - ? std::optional<std::string>{artifact_descriptions.begin() - ->first} - : std::nullopt); - - return BuildResult{.output_paths = *output_paths, - .extra_infos = std::move(infos), - .failed_artifacts = failed_artifacts}; - } + -> std::optional<BuildResult>; /// \brief Parses graph description into graph, traverses it and retrieves /// outputs specified by command line arguments [[nodiscard]] auto BuildAndStage( std::filesystem::path const& graph_description, - nlohmann::json const& artifacts) const -> std::optional<BuildResult> { - // Read blobs to upload and actions from graph description file - auto desc = ReadGraphDescription(graph_description, logger_); - if (not desc) { - return std::nullopt; - } - auto [blobs, tree_descs, actions] = *std::move(desc); - - HashFunction::Type const hash_type = - context_.apis->local->GetHashType(); - std::vector<ActionDescription::Ptr> action_descriptions{}; - action_descriptions.reserve(actions.size()); - for (auto const& [id, description] : actions.items()) { - auto action = - ActionDescription::FromJson(hash_type, id, description); - if (not action) { - return std::nullopt; // Error already logged - } - action_descriptions.emplace_back(std::move(*action)); - } - - std::vector<Tree::Ptr> trees{}; - for (auto const& [id, description] : tree_descs.items()) { - auto tree = Tree::FromJson(hash_type, id, description); - if (not tree) { - return std::nullopt; - } - trees.emplace_back(std::move(*tree)); - } - - std::map<std::string, ArtifactDescription> artifact_descriptions{}; - for (auto const& [rel_path, description] : artifacts.items()) { - auto artifact = - ArtifactDescription::FromJson(hash_type, description); - if (not artifact) { - return std::nullopt; // Error already logged - } - artifact_descriptions.emplace(rel_path, std::move(*artifact)); - } - - return BuildAndStage(artifact_descriptions, - {}, - std::move(action_descriptions), - std::move(blobs), - std::move(trees)); - } + nlohmann::json const& artifacts) const -> std::optional<BuildResult>; private: CommandLineArguments const clargs_; @@ -268,93 +106,13 @@ class GraphTraverser { std::filesystem::path const& graph_description, Logger const* logger) -> std::optional< - std::tuple<nlohmann::json, nlohmann::json, nlohmann::json>> { - auto const graph_description_opt = Json::ReadFile(graph_description); - if (not graph_description_opt.has_value()) { - Logger::Log(logger, - LogLevel::Error, - "parsing graph from {}", - graph_description.string()); - return std::nullopt; - } - auto blobs_opt = ExtractValueAs<std::vector<std::string>>( - *graph_description_opt, "blobs", [logger](std::string const& s) { - Logger::Log(logger, - LogLevel::Error, - "{}\ncan not retrieve value for \"blobs\" from " - "graph description.", - s); - }); - auto trees_opt = ExtractValueAs<nlohmann::json>( - *graph_description_opt, "trees", [logger](std::string const& s) { - Logger::Log(logger, - LogLevel::Error, - "{}\ncan not retrieve value for \"trees\" from " - "graph description.", - s); - }); - auto actions_opt = ExtractValueAs<nlohmann::json>( - *graph_description_opt, "actions", [logger](std::string const& s) { - Logger::Log(logger, - LogLevel::Error, - "{}\ncan not retrieve value for \"actions\" from " - "graph description.", - s); - }); - if (not blobs_opt or not trees_opt or not actions_opt) { - return std::nullopt; - } - return std::make_tuple(std::move(*blobs_opt), - std::move(*trees_opt), - std::move(*actions_opt)); - } + std::tuple<nlohmann::json, nlohmann::json, nlohmann::json>>; /// \brief Requires for the executor to upload blobs to CAS. In the case any /// of the uploads fails, execution is terminated /// \param[in] blobs blobs to be uploaded [[nodiscard]] auto UploadBlobs( - std::vector<std::string>&& blobs) const noexcept -> bool { - std::unordered_set<ArtifactBlob> container; - HashFunction const hash_function{context_.apis->remote->GetHashType()}; - for (auto& content : blobs) { - auto blob = ArtifactBlob::FromMemory( - hash_function, ObjectType::File, std::move(content)); - if (not blob.has_value()) { - logger_->Emit(LogLevel::Trace, "Failed to create ArtifactBlob"); - return false; - } - Logger::Log(logger_, LogLevel::Trace, [&]() { - return fmt::format( - "Will upload blob, its digest has id {} and size {}.", - blob->GetDigest().hash(), - blob->GetDigest().size()); - }); - // Store and/or upload blob, taking into account the maximum - // transfer size. - if (not UpdateContainerAndUpload( - &container, - *std::move(blob), - /*exception_is_fatal=*/true, - [&api = context_.apis->remote]( - std::unordered_set<ArtifactBlob>&& blobs) { - return api->Upload(std::move(blobs)); - }, - logger_)) { - return false; - } - } - // Upload remaining blobs. - auto result = context_.apis->remote->Upload(std::move(container)); - Logger::Log(logger_, LogLevel::Trace, [&]() { - std::stringstream msg{}; - msg << (result ? "Finished" : "Failed") << " upload of\n"; - for (auto const& blob : blobs) { - msg << " - " << nlohmann::json(blob).dump() << "\n"; - } - return msg.str(); - }); - return result; - } + std::vector<std::string>&& blobs) const noexcept -> bool; /// \brief Adds the artifacts to be retrieved to the graph /// \param[in] g dependency graph @@ -368,77 +126,18 @@ class GraphTraverser { std::map<std::string, ArtifactDescription> const& artifacts, std::map<std::string, ArtifactDescription> const& runfiles) -> std::optional<std::pair<std::vector<std::filesystem::path>, - std::vector<ArtifactIdentifier>>> { - std::vector<std::filesystem::path> rel_paths; - std::vector<ArtifactIdentifier> ids; - auto total_size = artifacts.size() + runfiles.size(); - rel_paths.reserve(total_size); - ids.reserve(total_size); - auto add_and_get_info = - [&g, &rel_paths, &ids]( - std::map<std::string, ArtifactDescription> const& descriptions) - -> bool { - for (auto const& [rel_path, artifact] : descriptions) { - rel_paths.emplace_back(rel_path); - ids.emplace_back(g->AddArtifact(artifact)); - } - return true; - }; - if (add_and_get_info(artifacts) and add_and_get_info(runfiles)) { - return std::make_pair(std::move(rel_paths), std::move(ids)); - } - return std::nullopt; - } + std::vector<ArtifactIdentifier>>>; /// \brief Traverses the graph. In case any of the artifact ids /// specified by the command line arguments is duplicated, execution is /// terminated. [[nodiscard]] auto Traverse( DependencyGraph const& g, - std::vector<ArtifactIdentifier> const& artifact_ids) const -> bool { - Executor executor{&context_, logger_, clargs_.build.timeout}; - bool traversing{}; - std::atomic<bool> done = false; - std::atomic<bool> failed = false; - std::condition_variable cv{}; - auto observer = - std::thread([this, &done, &cv]() { reporter_(&done, &cv); }); - { - Traverser t{executor, g, clargs_.jobs, &failed}; - traversing = - t.Traverse({std::begin(artifact_ids), std::end(artifact_ids)}); - } - done = true; - cv.notify_all(); - observer.join(); - return traversing and not failed; - } + std::vector<ArtifactIdentifier> const& artifact_ids) const -> bool; [[nodiscard]] auto TraverseRebuild( DependencyGraph const& g, - std::vector<ArtifactIdentifier> const& artifact_ids) const -> bool { - Rebuilder executor{&context_, clargs_.build.timeout}; - bool traversing{false}; - std::atomic<bool> done = false; - std::atomic<bool> failed = false; - std::condition_variable cv{}; - auto observer = - std::thread([this, &done, &cv]() { reporter_(&done, &cv); }); - { - Traverser t{executor, g, clargs_.jobs, &failed}; - traversing = - t.Traverse({std::begin(artifact_ids), std::end(artifact_ids)}); - } - done = true; - cv.notify_all(); - observer.join(); - - if (traversing and not failed and clargs_.rebuild->dump_flaky) { - std::ofstream file{*clargs_.rebuild->dump_flaky}; - file << executor.DumpFlakyActions().dump(2); - } - return traversing and not failed; - } + std::vector<ArtifactIdentifier> const& artifact_ids) const -> bool; /// \brief Retrieves nodes corresponding to artifacts with ids in artifacts. /// In case any of the identifiers doesn't correspond to a node inside the @@ -447,50 +146,9 @@ class GraphTraverser { DependencyGraph const& g, std::vector<ArtifactIdentifier> const& artifact_ids, Logger const* logger) noexcept - -> std::optional<std::vector<DependencyGraph::ArtifactNode const*>> { - std::vector<DependencyGraph::ArtifactNode const*> nodes{}; - - for (auto const& art_id : artifact_ids) { - auto const* node = g.ArtifactNodeWithId(art_id); - if (node == nullptr) { - Logger::Log(logger, - LogLevel::Error, - "Artifact {} not found in graph.", - art_id); - return std::nullopt; - } - nodes.push_back(node); - } - return nodes; - } + -> std::optional<std::vector<DependencyGraph::ArtifactNode const*>>; - void LogStatistics() const noexcept { - auto& stats = *context_.statistics; - if (clargs_.rebuild) { - std::stringstream ss{}; - ss << stats.RebuiltActionComparedCounter() - << " actions compared with cache"; - if (stats.ActionsFlakyCounter() > 0) { - ss << ", " << stats.ActionsFlakyCounter() - << " flaky actions found"; - ss << " (" << stats.ActionsFlakyTaintedCounter() - << " of which tainted)"; - } - if (stats.RebuiltActionMissingCounter() > 0) { - ss << ", no cache entry found for " - << stats.RebuiltActionMissingCounter() << " actions"; - } - ss << "."; - Logger::Log(logger_, LogLevel::Info, ss.str()); - } - else { - Logger::Log(logger_, - LogLevel::Info, - "Processed {} actions, {} cache hits.", - stats.ActionsQueuedCounter(), - stats.ActionsCachedCounter()); - } - } + void LogStatistics() const noexcept; [[nodiscard]] auto BuildArtifacts( gsl::not_null<DependencyGraph*> const& graph, @@ -503,104 +161,16 @@ class GraphTraverser { -> std::optional< std::tuple<std::vector<std::filesystem::path>, std::vector<DependencyGraph::ArtifactNode const*>, - std::vector<DependencyGraph::ArtifactNode const*>>> { - if (not UploadBlobs(std::move(blobs))) { - return std::nullopt; - } - - auto artifact_infos = - AddArtifactsToRetrieve(graph, artifacts, runfiles); - if (not artifact_infos) { - return std::nullopt; - } - auto& [output_paths, artifact_ids] = *artifact_infos; - - // Add extra artifacts to ids to build - artifact_ids.reserve(artifact_ids.size() + extra_artifacts.size()); - for (auto const& artifact : extra_artifacts) { - artifact_ids.emplace_back(graph->AddArtifact(artifact)); - } - - std::vector<ActionDescription> tree_actions{}; - tree_actions.reserve(trees.size()); - for (auto const& tree : trees) { - tree_actions.emplace_back(tree->Action()); - } - - if (not graph->Add(actions) or not graph->Add(tree_actions)) { - Logger::Log(logger_, LogLevel::Error, [&actions]() { - auto json = nlohmann::json::array(); - for (auto const& desc : actions) { - json.push_back(desc->ToJson()); - } - return fmt::format( - "could not build the dependency graph from the actions " - "described in {}.", - json.dump()); - }); - return std::nullopt; - } - - if (clargs_.rebuild ? not TraverseRebuild(*graph, artifact_ids) - : not Traverse(*graph, artifact_ids)) { - Logger::Log(logger_, LogLevel::Error, "Build failed."); - return std::nullopt; - } - - LogStatistics(); - - auto artifact_nodes = GetArtifactNodes(*graph, artifact_ids, logger_); - if (not artifact_nodes) { - return std::nullopt; - } - - // split extra artifacts' nodes from artifact nodes - auto const it_extra = - std::next(artifact_nodes->begin(), - static_cast<std::int64_t>(output_paths.size())); - auto extra_nodes = std::vector<DependencyGraph::ArtifactNode const*>{ - std::make_move_iterator(it_extra), - std::make_move_iterator(artifact_nodes->end())}; - artifact_nodes->erase(it_extra, artifact_nodes->end()); - - return std::make_tuple(std::move(output_paths), - std::move(*artifact_nodes), - std::move(extra_nodes)); - } + std::vector<DependencyGraph::ArtifactNode const*>>>; [[nodiscard]] auto PrepareOutputPaths( std::vector<std::filesystem::path> const& rel_paths) const - -> std::optional<std::vector<std::filesystem::path>> { - std::vector<std::filesystem::path> output_paths{}; - output_paths.reserve(rel_paths.size()); - for (auto const& rel_path : rel_paths) { - output_paths.emplace_back(clargs_.stage->output_dir / rel_path); - } - return output_paths; - } + -> std::optional<std::vector<std::filesystem::path>>; [[nodiscard]] static auto CollectObjectInfos( std::vector<DependencyGraph::ArtifactNode const*> const& artifact_nodes, Logger const* logger) - -> std::optional<std::vector<Artifact::ObjectInfo>> { - std::vector<Artifact::ObjectInfo> object_infos; - object_infos.reserve(artifact_nodes.size()); - for (auto const* art_ptr : artifact_nodes) { - auto const& info = art_ptr->Content().Info(); - if (info) { - object_infos.push_back(*info); - } - else { - Logger::Log(logger, - LogLevel::Error, - "artifact {} could not be retrieved, it can not be " - "found in CAS.", - art_ptr->Content().Id()); - return std::nullopt; - } - } - return object_infos; - } + -> std::optional<std::vector<Artifact::ObjectInfo>>; /// \brief Asks execution API to copy output artifacts to paths specified by /// command line arguments and writes location info. In case the executor @@ -608,201 +178,19 @@ class GraphTraverser { [[nodiscard]] auto RetrieveOutputs( std::vector<std::filesystem::path> const& rel_paths, std::vector<Artifact::ObjectInfo> const& object_infos) const - -> std::optional<std::vector<std::filesystem::path>> { - // Create output directory - if (not FileSystemManager::CreateDirectory(clargs_.stage->output_dir)) { - return std::nullopt; // Message logged in the file system manager - } - - auto output_paths = PrepareOutputPaths(rel_paths); - - if (not output_paths or - not context_.apis->remote->RetrieveToPaths( - object_infos, *output_paths, &*context_.apis->local)) { - Logger::Log( - logger_, LogLevel::Error, "Could not retrieve outputs."); - return std::nullopt; - } - - return output_paths; - } + -> std::optional<std::vector<std::filesystem::path>>; void PrintOutputs( std::string message, std::vector<std::filesystem::path> const& paths, std::vector<DependencyGraph::ArtifactNode const*> const& artifact_nodes, - std::map<std::string, ArtifactDescription> const& runfiles) const { - std::string msg_dbg{"Artifact ids:"}; - std::string msg_failed{"Failed artifacts:"}; - bool failed{false}; - nlohmann::json json{}; - for (std::size_t pos = 0; pos < paths.size(); ++pos) { - auto path = paths[pos].string(); - auto id = IdentifierToString(artifact_nodes[pos]->Content().Id()); - if (clargs_.build.show_runfiles or - not runfiles.contains(clargs_.stage - ? std::filesystem::proximate( - path, clargs_.stage->output_dir) - .string() - : path)) { - auto info = artifact_nodes[pos]->Content().Info(); - if (info) { - message += fmt::format("\n {} {}", path, info->ToString()); - if (info->failed) { - msg_failed += - fmt::format("\n {} {}", path, info->ToString()); - failed = true; - } - if (clargs_.build.dump_artifacts) { - json[path] = info->ToJson(); - } - } - else { - Logger::Log(logger_, - LogLevel::Error, - "Missing info for artifact {}.", - id); - } - } - msg_dbg += fmt::format("\n {}: {}", path, id); - } - - if (not clargs_.build.show_runfiles and not runfiles.empty()) { - message += fmt::format("\n({} runfiles omitted.)", runfiles.size()); - } - - Logger::Log(logger_, LogLevel::Info, "{}", message); - Logger::Log(logger_, LogLevel::Debug, "{}", msg_dbg); - if (failed) { - Logger::Log(logger_, LogLevel::Info, "{}", msg_failed); - } - - if (clargs_.build.dump_artifacts) { - if (*clargs_.build.dump_artifacts == "-") { - std::cout << std::setw(2) << json << std::endl; - } - else { - std::ofstream os(*clargs_.build.dump_artifacts); - os << std::setw(2) << json << std::endl; - } - } - } + std::map<std::string, ArtifactDescription> const& runfiles) const; void MaybePrintToStdout( std::vector<std::filesystem::path> const& paths, std::vector<DependencyGraph::ArtifactNode const*> const& artifacts, - std::optional<std::string> const& unique_artifact) const { - if (clargs_.build.print_to_stdout) { - auto const& remote = *context_.apis->remote; - for (std::size_t i = 0; i < paths.size(); i++) { - if (paths[i] == *(clargs_.build.print_to_stdout)) { - auto info = artifacts[i]->Content().Info(); - if (info) { - if (not remote.RetrieveToFds({*info}, - {dup(fileno(stdout))}, - /*raw_tree=*/false, - &*context_.apis->local)) { - Logger::Log(logger_, - LogLevel::Error, - "Failed to retrieve {}", - *(clargs_.build.print_to_stdout)); - } - } - else { - Logger::Log( - logger_, - LogLevel::Error, - "Failed to obtain object information for {}", - *(clargs_.build.print_to_stdout)); - } - return; - } - } - // Not directly an artifact, hence check if the path is contained in - // some artifact - auto target_path = ToNormalPath(std::filesystem::path{ - *clargs_.build.print_to_stdout}) - .relative_path(); - for (std::size_t i = 0; i < paths.size(); i++) { - auto const& path = paths[i]; - auto relpath = target_path.lexically_relative(path); - if ((not relpath.empty()) and *relpath.begin() != "..") { - Logger::Log( - logger_, - LogLevel::Info, - "'{}' not a direct logical path of the specified " - "target; will take subobject '{}' of '{}'", - *(clargs_.build.print_to_stdout), - relpath.string(), - path.string()); - auto info = artifacts[i]->Content().Info(); - if (info) { - auto new_info = - RetrieveSubPathId(*info, *context_.apis, relpath); - if (new_info) { - if (not remote.RetrieveToFds( - {*new_info}, - {dup(fileno(stdout))}, - /*raw_tree=*/false, - &*context_.apis->local)) { - Logger::Log(logger_, - LogLevel::Error, - "Failed to retrieve artifact {} at " - "path '{}' of '{}'", - new_info->ToString(), - relpath.string(), - path.string()); - } - } - } - else { - Logger::Log( - logger_, - LogLevel::Error, - "Failed to obtain object information for {}", - *(clargs_.build.print_to_stdout)); - } - return; - } - } - Logger::Log(logger_, - LogLevel::Warning, - "{} not a logical path of the specified target", - *(clargs_.build.print_to_stdout)); - } - else if (clargs_.build.print_unique) { - if (unique_artifact) { - auto const& remote = *context_.apis->remote; - std::optional<Artifact::ObjectInfo> info = std::nullopt; - for (std::size_t i = 0; i < paths.size(); i++) { - if (paths[i] == unique_artifact) { - info = artifacts[i]->Content().Info(); - } - } - if (info) { - if (not remote.RetrieveToFds({*info}, - {dup(fileno(stdout))}, - /*raw_tree=*/false, - &*context_.apis->local)) { - Logger::Log(logger_, - LogLevel::Error, - "Failed to retrieve {}", - *unique_artifact); - } - } - else { - Logger::Log(logger_, - LogLevel::Error, - "Failed to obtain object information for {}", - *unique_artifact); - } - return; - } - Logger::Log(logger_, - LogLevel::Info, - "Target does not have precisely one artifact."); - } - } + std::optional<std::string> const& unique_artifact) const; }; +#endif #endif // INCLUDED_SRC_BUILDTOOL_GRAPH_TRAVERSER_GRAPH_TRAVERSER_HPP |