From af20b222322d943595cd580404eda7be7a0b5ba4 Mon Sep 17 00:00:00 2001 From: Paul Cristian Sarbu Date: Mon, 22 Aug 2022 13:28:41 +0200 Subject: Git CAS: Add fake repository wrapper for git odb --- src/buildtool/file_system/git_repo.cpp | 197 +++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 src/buildtool/file_system/git_repo.cpp (limited to 'src/buildtool/file_system/git_repo.cpp') diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp new file mode 100644 index 00000000..4b46bd9d --- /dev/null +++ b/src/buildtool/file_system/git_repo.cpp @@ -0,0 +1,197 @@ +// 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 + +#include "src/buildtool/logging/logger.hpp" +#include "src/utils/cpp/path.hpp" + +extern "C" { +#include +} + +namespace { + +constexpr std::size_t kWaitTime{2}; // time in ms between tries for git locks + +[[nodiscard]] auto GitLastError() noexcept -> std::string { + git_error const* err{nullptr}; + if ((err = git_error_last()) != nullptr and err->message != nullptr) { + return fmt::format("error code {}: {}", err->klass, err->message); + } + return ""; +} + +} // namespace + +auto GitRepo::Open(GitCASPtr git_cas) noexcept -> std::optional { +#ifdef BOOTSTRAP_BUILD_TOOL + return std::nullopt; +#else + auto repo = GitRepo(std::move(git_cas)); + if (repo.repo_ == nullptr) { + return std::nullopt; + } + return repo; +#endif // BOOTSTRAP_BUILD_TOOL +} + +auto GitRepo::Open(std::filesystem::path const& repo_path) noexcept + -> std::optional { +#ifdef BOOTSTRAP_BUILD_TOOL + return std::nullopt; +#else + auto repo = GitRepo(repo_path); + if (repo.repo_ == nullptr) { + return std::nullopt; + } + return repo; +#endif // BOOTSTRAP_BUILD_TOOL +} + +GitRepo::GitRepo(GitCASPtr git_cas) noexcept { +#ifndef BOOTSTRAP_BUILD_TOOL + if (git_cas != nullptr) { + if (git_repository_wrap_odb(&repo_, git_cas->odb_) != 0) { + Logger::Log(LogLevel::Error, + "could not create wrapper for git repository"); + git_repository_free(repo_); + repo_ = nullptr; + return; + } + is_repo_fake_ = true; + git_cas_ = std::move(git_cas); + } + else { + Logger::Log(LogLevel::Error, + "git repository creation attempted with null odb!"); + } +#endif // BOOTSTRAP_BUILD_TOOL +} + +GitRepo::GitRepo(std::filesystem::path const& repo_path) noexcept { +#ifndef BOOTSTRAP_BUILD_TOOL + try { + static std::mutex repo_mutex{}; + std::unique_lock lock{repo_mutex}; + auto cas = std::make_shared(); + // open repo, but retain it + if (git_repository_open(&repo_, repo_path.c_str()) != 0) { + Logger::Log(LogLevel::Error, + "opening git repository {} failed with:\n{}", + repo_path.string(), + GitLastError()); + git_repository_free(repo_); + repo_ = nullptr; + return; + } + // get odb + git_repository_odb(&cas->odb_, repo_); + if (cas->odb_ == nullptr) { + Logger::Log(LogLevel::Error, + "retrieving odb of git repository {} failed with:\n{}", + repo_path.string(), + GitLastError()); + git_repository_free(repo_); + repo_ = nullptr; + return; + } + is_repo_fake_ = false; + // save root path + cas->git_path_ = ToNormalPath(std::filesystem::absolute( + std::filesystem::path(git_repository_path(repo_)))); + // retain the pointer + git_cas_ = std::static_pointer_cast(cas); + } catch (std::exception const& ex) { + Logger::Log(LogLevel::Error, + "opening git object database failed with:\n{}", + ex.what()); + repo_ = nullptr; + } +#endif // BOOTSTRAP_BUILD_TOOL +} + +GitRepo::GitRepo(GitRepo&& other) noexcept + : git_cas_{std::move(other.git_cas_)}, + repo_{other.repo_}, + is_repo_fake_{other.is_repo_fake_} { + other.repo_ = nullptr; +} + +auto GitRepo::operator=(GitRepo&& other) noexcept -> GitRepo& { + git_cas_ = std::move(other.git_cas_); + repo_ = other.repo_; + is_repo_fake_ = other.is_repo_fake_; + other.git_cas_ = nullptr; + return *this; +} + +auto GitRepo::InitAndOpen(std::filesystem::path const& repo_path, + bool is_bare) noexcept -> std::optional { +#ifndef BOOTSTRAP_BUILD_TOOL + try { + static std::mutex repo_mutex{}; + std::unique_lock lock{repo_mutex}; + + auto git_state = GitContext(); // initialize libgit2 + + git_repository* tmp_repo{nullptr}; + size_t max_attempts = 3; // number of tries + int err = 0; + while (max_attempts > 0) { + --max_attempts; + err = git_repository_init( + &tmp_repo, repo_path.c_str(), static_cast(is_bare)); + if (err == 0) { + git_repository_free(tmp_repo); + return GitRepo(repo_path); // success + } + git_repository_free(tmp_repo); // cleanup before next attempt + // check if init hasn't already happened in another process + if (git_repository_open_ext(nullptr, + repo_path.c_str(), + GIT_REPOSITORY_OPEN_NO_SEARCH, + nullptr) == 0) { + return GitRepo(repo_path); // success + } + // repo still not created, so sleep and try again + std::this_thread::sleep_for(std::chrono::milliseconds(kWaitTime)); + } + Logger::Log( + LogLevel::Error, + "initializing git repository {} failed with error code:\n{}", + (repo_path / "").string(), + err); + } catch (std::exception const& ex) { + Logger::Log(LogLevel::Error, + "initializing git repository {} failed with:\n{}", + (repo_path / "").string(), + ex.what()); + } +#endif // BOOTSTRAP_BUILD_TOOL + return std::nullopt; +} + +auto GitRepo::GetGitCAS() const noexcept -> GitCASPtr { + return git_cas_; +} + +GitRepo::~GitRepo() noexcept { + // release resources + git_repository_free(repo_); +} + +auto GitRepo::IsRepoFake() const noexcept -> bool { + return is_repo_fake_; +} -- cgit v1.2.3