diff options
Diffstat (limited to 'src/buildtool/execution_api')
-rw-r--r-- | src/buildtool/execution_api/local/local_action.cpp | 174 | ||||
-rw-r--r-- | src/buildtool/execution_api/local/local_action.hpp | 19 | ||||
-rw-r--r-- | src/buildtool/execution_api/local/local_api.hpp | 5 | ||||
-rw-r--r-- | src/buildtool/execution_api/local/local_response.hpp | 29 |
4 files changed, 168 insertions, 59 deletions
diff --git a/src/buildtool/execution_api/local/local_action.cpp b/src/buildtool/execution_api/local/local_action.cpp index e5457bcd..d42dcdd6 100644 --- a/src/buildtool/execution_api/local/local_action.cpp +++ b/src/buildtool/execution_api/local/local_action.cpp @@ -250,45 +250,79 @@ auto LocalAction::CreateDirectoryStructure( }); } -auto LocalAction::CollectOutputFile(std::filesystem::path const& exec_path, - std::string const& local_path) - const noexcept -> std::optional<bazel_re::OutputFile> { +/// \brief We expect either a regular file, or a symlink. +auto LocalAction::CollectOutputFileOrSymlink( + std::filesystem::path const& exec_path, + std::string const& local_path) const noexcept + -> std::optional<OutputFileOrSymlink> { auto file_path = exec_path / local_path; auto type = FileSystemManager::Type(file_path); - if ((not type) or (not IsFileObject(*type))) { - Logger::Log(LogLevel::Error, "expected file at {}", local_path); + if (not type) { + Logger::Log(LogLevel::Error, "expected known type at {}", local_path); return std::nullopt; } - bool is_executable = IsExecutableObject(*type); - auto digest = - storage_->CAS().StoreBlob</*kOwner=*/true>(file_path, is_executable); - if (digest) { - auto out_file = bazel_re::OutputFile{}; - out_file.set_path(local_path); - out_file.set_allocated_digest( - gsl::owner<bazel_re::Digest*>{new bazel_re::Digest{*digest}}); - out_file.set_is_executable(is_executable); - return out_file; + if (IsSymlinkObject(*type)) { + auto content = FileSystemManager::ReadSymlink(file_path); + if (content and PathIsNonUpwards(*content) and + 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 = storage_->CAS().StoreBlob</*kOwner=*/true>(file_path, + is_executable); + if (digest) { + auto out_file = bazel_re::OutputFile{}; + out_file.set_path(local_path); + out_file.set_allocated_digest( + gsl::owner<bazel_re::Digest*>{new bazel_re::Digest{*digest}}); + out_file.set_is_executable(is_executable); + return out_file; + } + } + else { + Logger::Log( + LogLevel::Error, "expected file or symlink at {}", local_path); } return std::nullopt; } -auto LocalAction::CollectOutputDir(std::filesystem::path const& exec_path, - std::string const& local_path) const noexcept - -> std::optional<bazel_re::OutputDirectory> { +auto LocalAction::CollectOutputDirOrSymlink( + std::filesystem::path const& exec_path, + std::string const& local_path) const noexcept + -> std::optional<OutputDirOrSymlink> { auto dir_path = exec_path / local_path; auto type = FileSystemManager::Type(dir_path); - if ((not type) or (not IsTreeObject(*type))) { - Logger::Log(LogLevel::Error, "expected directory at {}", local_path); + if (not type) { + Logger::Log(LogLevel::Error, "expected known type at {}", local_path); return std::nullopt; } - - if (auto digest = CreateDigestFromLocalOwnedTree(storage_, dir_path)) { - auto out_dir = bazel_re::OutputDirectory{}; - out_dir.set_path(local_path); - out_dir.set_allocated_tree_digest( - gsl::owner<bazel_re::Digest*>{new bazel_re::Digest{*digest}}); - return out_dir; + if (IsSymlinkObject(*type)) { + auto content = FileSystemManager::ReadSymlink(dir_path); + if (content and PathIsNonUpwards(*content) and + 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 (IsTreeObject(*type)) { + if (auto digest = CreateDigestFromLocalOwnedTree(storage_, dir_path)) { + auto out_dir = bazel_re::OutputDirectory{}; + out_dir.set_path(local_path); + out_dir.set_allocated_tree_digest( + gsl::owner<bazel_re::Digest*>{new bazel_re::Digest{*digest}}); + return out_dir; + } + } + else { + Logger::Log( + LogLevel::Error, "expected directory or symlink at {}", local_path); } return std::nullopt; } @@ -296,31 +330,73 @@ auto LocalAction::CollectOutputDir(std::filesystem::path const& exec_path, auto LocalAction::CollectAndStoreOutputs( bazel_re::ActionResult* result, std::filesystem::path const& exec_path) const noexcept -> bool { - logger_.Emit(LogLevel::Trace, "collecting outputs:"); - for (auto const& path : output_files_) { - auto out_file = CollectOutputFile(exec_path, path); - if (not out_file) { - logger_.Emit( - LogLevel::Error, "could not collect output file {}", path); - return false; + try { + logger_.Emit(LogLevel::Trace, "collecting outputs:"); + for (auto const& path : output_files_) { + auto out = CollectOutputFileOrSymlink(exec_path, path); + if (not out) { + logger_.Emit(LogLevel::Error, + "could not collect output file or symlink {}", + path); + return false; + } + if (std::holds_alternative<bazel_re::OutputSymlink>(*out)) { + auto out_symlink = std::get<bazel_re::OutputSymlink>(*out); + auto const& target = out_symlink.target(); + if (not PathIsNonUpwards(target)) { + logger_.Emit(LogLevel::Error, + "collected upwards output symlink {}", + path); + return false; + } + logger_.Emit( + LogLevel::Trace, " - symlink {}: {}", path, target); + result->mutable_output_file_symlinks()->Add( + std::move(out_symlink)); + } + else { + 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)); + } } - auto const& digest = out_file->digest().hash(); - logger_.Emit(LogLevel::Trace, " - file {}: {}", path, digest); - result->mutable_output_files()->Add(std::move(*out_file)); - } - for (auto const& path : output_dirs_) { - auto out_dir = CollectOutputDir(exec_path, path); - if (not out_dir) { - logger_.Emit( - LogLevel::Error, "could not collect output dir {}", path); - return false; + for (auto const& path : output_dirs_) { + auto out = CollectOutputDirOrSymlink(exec_path, path); + if (not out) { + logger_.Emit(LogLevel::Error, + "could not collect output dir or symlink {}", + path); + return false; + } + if (std::holds_alternative<bazel_re::OutputSymlink>(*out)) { + auto out_symlink = std::get<bazel_re::OutputSymlink>(*out); + auto const& target = out_symlink.target(); + if (not PathIsNonUpwards(target)) { + logger_.Emit(LogLevel::Error, + "collected upwards output symlink {}", + path); + return false; + } + logger_.Emit( + LogLevel::Trace, " - symlink {}: {}", path, target); + result->mutable_output_file_symlinks()->Add( + std::move(out_symlink)); + } + 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)); + } } - 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; + } catch (std::exception const& ex) { + // should never happen, but report nonetheless + logger_.Emit( + LogLevel::Error, "collecting outputs failed:\n{}", ex.what()); + return false; } - - return true; } auto LocalAction::DigestFromOwnedFile(std::filesystem::path const& file_path) diff --git a/src/buildtool/execution_api/local/local_action.hpp b/src/buildtool/execution_api/local/local_action.hpp index a49060e4..95f95ff4 100644 --- a/src/buildtool/execution_api/local/local_action.hpp +++ b/src/buildtool/execution_api/local/local_action.hpp @@ -38,6 +38,11 @@ class LocalAction final : public IExecutionAction { bool is_cached{}; }; + using OutputFileOrSymlink = + std::variant<bazel_re::OutputFile, bazel_re::OutputSymlink>; + using OutputDirOrSymlink = + std::variant<bazel_re::OutputDirectory, bazel_re::OutputSymlink>; + auto Execute(Logger const* logger) noexcept -> IExecutionResponse::Ptr final; @@ -112,13 +117,15 @@ class LocalAction final : public IExecutionAction { [[nodiscard]] auto CreateDirectoryStructure( std::filesystem::path const& exec_path) const noexcept -> bool; - [[nodiscard]] auto CollectOutputFile(std::filesystem::path const& exec_path, - std::string const& local_path) - const noexcept -> std::optional<bazel_re::OutputFile>; + [[nodiscard]] auto CollectOutputFileOrSymlink( + std::filesystem::path const& exec_path, + std::string const& local_path) const noexcept + -> std::optional<OutputFileOrSymlink>; - [[nodiscard]] auto CollectOutputDir(std::filesystem::path const& exec_path, - std::string const& local_path) - const noexcept -> std::optional<bazel_re::OutputDirectory>; + [[nodiscard]] auto CollectOutputDirOrSymlink( + std::filesystem::path const& exec_path, + std::string const& local_path) const noexcept + -> std::optional<OutputDirOrSymlink>; [[nodiscard]] auto CollectAndStoreOutputs( bazel_re::ActionResult* result, diff --git a/src/buildtool/execution_api/local/local_api.hpp b/src/buildtool/execution_api/local/local_api.hpp index 87296414..64e89203 100644 --- a/src/buildtool/execution_api/local/local_api.hpp +++ b/src/buildtool/execution_api/local/local_api.hpp @@ -206,8 +206,9 @@ class LocalApi final : public IExecutionApi { return false; } - // Read artifact content. - auto const& content = FileSystemManager::ReadFile(*path); + // Read artifact content (file or symlink). + auto const& content = + FileSystemManager::ReadContentAtPath(*path, info.type); if (not content) { return false; } diff --git a/src/buildtool/execution_api/local/local_response.hpp b/src/buildtool/execution_api/local/local_response.hpp index 135897d8..8e6cb04f 100644 --- a/src/buildtool/execution_api/local/local_response.hpp +++ b/src/buildtool/execution_api/local/local_response.hpp @@ -105,8 +105,7 @@ class LocalResponse final : public IExecutionResponse { action_result.output_file_symlinks_size()) + static_cast<std::size_t>( action_result.output_directory_symlinks_size()) + - static_cast<std::size_t>( - action_result.output_directories().size())); + static_cast<std::size_t>(action_result.output_directories_size())); DirSymlinks dir_symlinks{}; dir_symlinks_.reserve(static_cast<std::size_t>( @@ -153,6 +152,32 @@ class LocalResponse final : public IExecutionResponse { } } + // collect all symlinks and store them + for (auto const& link : action_result.output_file_symlinks()) { + try { + artifacts.emplace( + link.path(), + Artifact::ObjectInfo{ + .digest = ArtifactDigest::Create<ObjectType::File>( + link.target()), + .type = ObjectType::Symlink}); + } catch (...) { + return false; + } + } + for (auto const& link : action_result.output_directory_symlinks()) { + try { + artifacts.emplace( + link.path(), + Artifact::ObjectInfo{ + .digest = ArtifactDigest::Create<ObjectType::File>( + link.target()), + .type = ObjectType::Symlink}); + } catch (...) { + return false; + } + } + // collect directories and store them for (auto const& dir : action_result.output_directories()) { try { |