From 619def44c1cca9f3cdf63544d5f24f2c7a7d9b77 Mon Sep 17 00:00:00 2001 From: Klaus Aehlig Date: Tue, 22 Feb 2022 17:03:21 +0100 Subject: Initial self-hosting commit This is the initial version of our tool that is able to build itself. In can be bootstrapped by ./bin/bootstrap.py Co-authored-by: Oliver Reiche Co-authored-by: Victor Moreno --- src/buildtool/file_system/file_root.hpp | 239 ++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/buildtool/file_system/file_root.hpp (limited to 'src/buildtool/file_system/file_root.hpp') diff --git a/src/buildtool/file_system/file_root.hpp b/src/buildtool/file_system/file_root.hpp new file mode 100644 index 00000000..1df40588 --- /dev/null +++ b/src/buildtool/file_system/file_root.hpp @@ -0,0 +1,239 @@ +#ifndef INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_FILE_ROOT_HPP +#define INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_FILE_ROOT_HPP + +#include +#include +#include +#include +#include + +#include "gsl-lite/gsl-lite.hpp" +#include "src/buildtool/common/artifact_description.hpp" +#include "src/buildtool/file_system/file_system_manager.hpp" +#include "src/buildtool/file_system/git_tree.hpp" + +class FileRoot { + using fs_root_t = std::filesystem::path; + struct git_root_t { + gsl::not_null cas; + gsl::not_null tree; + }; + using root_t = std::variant; + + public: + class DirectoryEntries { + using names_t = std::unordered_set; + using tree_t = gsl::not_null; + using entries_t = std::variant; + + public: + DirectoryEntries() noexcept = default; + explicit DirectoryEntries(names_t names) noexcept + : data_{std::move(names)} {} + explicit DirectoryEntries(tree_t git_tree) noexcept + : data_{std::move(git_tree)} {} + [[nodiscard]] auto Contains(std::string const& name) const noexcept + -> bool { + if (std::holds_alternative(data_)) { + return static_cast( + std::get(data_)->LookupEntryByName(name)); + } + if (std::holds_alternative(data_)) { + return std::get(data_).contains(name); + } + return false; + } + [[nodiscard]] auto Empty() const noexcept -> bool { + if (std::holds_alternative(data_)) { + try { + auto const& tree = std::get(data_); + return tree->begin() == tree->end(); + } catch (...) { + return false; + } + } + if (std::holds_alternative(data_)) { + return std::get(data_).empty(); + } + return true; + } + + private: + entries_t data_{}; + }; + + FileRoot() noexcept = default; + explicit FileRoot(std::filesystem::path root) noexcept + : root_{std::move(root)} {} + FileRoot(gsl::not_null cas, + gsl::not_null tree) noexcept + : root_{git_root_t{std::move(cas), std::move(tree)}} {} + + [[nodiscard]] static auto FromGit(std::filesystem::path const& repo_path, + std::string const& git_tree_id) noexcept + -> std::optional { + if (auto cas = GitCAS::Open(repo_path)) { + if (auto tree = GitTree::Read(cas, git_tree_id)) { + try { + return FileRoot{ + cas, std::make_shared(std::move(*tree))}; + } catch (...) { + } + } + } + return std::nullopt; + } + + // Indicates that subsequent calls to `Exists()`, `IsFile()`, + // `IsDirectory()`, and `FileType()` on contents of the same directory will + // be served without any additional file system lookups. + [[nodiscard]] auto HasFastDirectoryLookup() const noexcept -> bool { + return std::holds_alternative(root_); + } + + [[nodiscard]] auto Exists(std::filesystem::path const& path) const noexcept + -> bool { + if (std::holds_alternative(root_)) { + if (path == ".") { + return true; + } + return static_cast( + std::get(root_).tree->LookupEntryByPath(path)); + } + return FileSystemManager::Exists(std::get(root_) / path); + } + + [[nodiscard]] auto IsFile( + std::filesystem::path const& file_path) const noexcept -> bool { + if (std::holds_alternative(root_)) { + if (auto entry = + std::get(root_).tree->LookupEntryByPath( + file_path)) { + return entry->IsBlob(); + } + return false; + } + return FileSystemManager::IsFile(std::get(root_) / + file_path); + } + + [[nodiscard]] auto IsDirectory( + std::filesystem::path const& dir_path) const noexcept -> bool { + if (std::holds_alternative(root_)) { + if (dir_path == ".") { + return true; + } + if (auto entry = + std::get(root_).tree->LookupEntryByPath( + dir_path)) { + return entry->IsTree(); + } + return false; + } + return FileSystemManager::IsDirectory(std::get(root_) / + dir_path); + } + + [[nodiscard]] auto ReadFile(std::filesystem::path const& file_path) + const noexcept -> std::optional { + if (std::holds_alternative(root_)) { + if (auto entry = + std::get(root_).tree->LookupEntryByPath( + file_path)) { + return entry->Blob(); + } + return std::nullopt; + } + return FileSystemManager::ReadFile(std::get(root_) / + file_path); + } + + [[nodiscard]] auto ReadDirectory(std::filesystem::path const& dir_path) + const noexcept -> DirectoryEntries { + try { + if (std::holds_alternative(root_)) { + auto const& tree = std::get(root_).tree; + if (dir_path == ".") { + return DirectoryEntries{&(*tree)}; + } + if (auto entry = tree->LookupEntryByPath(dir_path)) { + if (auto const& found_tree = entry->Tree()) { + return DirectoryEntries{&(*found_tree)}; + } + } + } + else { + std::unordered_set names{}; + if (FileSystemManager::ReadDirectory( + std::get(root_) / dir_path, + [&names](auto name, auto /*type*/) { + names.emplace(name.string()); + return true; + })) { + return DirectoryEntries{std::move(names)}; + } + } + } catch (std::exception const& ex) { + Logger::Log(LogLevel::Error, + "reading directory {} failed with:\n{}", + dir_path.string(), + ex.what()); + } + return {}; + } + + [[nodiscard]] auto FileType(std::filesystem::path const& file_path) + const noexcept -> std::optional { + if (std::holds_alternative(root_)) { + if (auto entry = + std::get(root_).tree->LookupEntryByPath( + file_path)) { + if (entry->IsBlob()) { + return entry->Type(); + } + } + return std::nullopt; + } + auto type = + FileSystemManager::Type(std::get(root_) / file_path); + if (type and IsFileObject(*type)) { + return type; + } + return std::nullopt; + } + + [[nodiscard]] auto ReadBlob(std::string const& blob_id) const noexcept + -> std::optional { + if (std::holds_alternative(root_)) { + return std::get(root_).cas->ReadObject( + blob_id, /*is_hex_id=*/true); + } + return std::nullopt; + } + + // Create LOCAL or KNOWN artifact. Does not check existence for LOCAL. + [[nodiscard]] auto ToArtifactDescription( + std::filesystem::path const& file_path, + std::string const& repository) const noexcept + -> std::optional { + if (std::holds_alternative(root_)) { + if (auto entry = + std::get(root_).tree->LookupEntryByPath( + file_path)) { + if (entry->IsBlob()) { + return ArtifactDescription{ + ArtifactDigest{entry->Hash(), *entry->Size()}, + entry->Type(), + repository}; + } + } + return std::nullopt; + } + return ArtifactDescription{file_path, repository}; + } + + private: + root_t root_; +}; + +#endif // INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_FILE_ROOT_HPP -- cgit v1.2.3