summaryrefslogtreecommitdiff
path: root/src/other_tools/just_mr/fetch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/other_tools/just_mr/fetch.cpp')
-rw-r--r--src/other_tools/just_mr/fetch.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/other_tools/just_mr/fetch.cpp b/src/other_tools/just_mr/fetch.cpp
new file mode 100644
index 00000000..c6a4bc17
--- /dev/null
+++ b/src/other_tools/just_mr/fetch.cpp
@@ -0,0 +1,300 @@
+// Copyright 2023 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 "src/other_tools/just_mr/fetch.hpp"
+
+#include <filesystem>
+
+#include "nlohmann/json.hpp"
+#include "src/buildtool/logging/log_level.hpp"
+#include "src/buildtool/logging/logger.hpp"
+#include "src/buildtool/multithreading/task_system.hpp"
+#include "src/other_tools/just_mr/exit_codes.hpp"
+#include "src/other_tools/just_mr/progress_reporting/progress.hpp"
+#include "src/other_tools/just_mr/progress_reporting/progress_reporter.hpp"
+#include "src/other_tools/just_mr/utils.hpp"
+#include "src/other_tools/ops_maps/content_cas_map.hpp"
+#include "src/other_tools/ops_maps/repo_fetch_map.hpp"
+
+auto MultiRepoFetch(std::shared_ptr<Configuration> const& config,
+ MultiRepoCommonArguments const& common_args,
+ MultiRepoSetupArguments const& setup_args,
+ MultiRepoFetchArguments const& fetch_args) -> int {
+ // provide report
+ Logger::Log(LogLevel::Info, "Performing repositories fetch");
+
+ // find fetch dir
+ auto fetch_dir = fetch_args.fetch_dir;
+ if (not fetch_dir) {
+ for (auto const& d : common_args.just_mr_paths->distdirs) {
+ if (FileSystemManager::IsDirectory(d)) {
+ fetch_dir = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(d));
+ break;
+ }
+ }
+ }
+ if (not fetch_dir) {
+ auto considered = nlohmann::json(common_args.just_mr_paths->distdirs);
+ Logger::Log(LogLevel::Error,
+ "No directory found to fetch to, considered {}",
+ considered.dump());
+ return kExitFetchError;
+ }
+
+ auto repos = (*config)["repositories"];
+ if (not repos.IsNotNull()) {
+ Logger::Log(LogLevel::Error,
+ "Config: Mandatory key \"repositories\" missing");
+ return kExitFetchError;
+ }
+ auto fetch_repos =
+ std::make_shared<JustMR::SetupRepos>(); // repos to setup and include
+ JustMR::Utils::DefaultReachableRepositories(repos, fetch_repos);
+
+ if (not setup_args.sub_all) {
+ auto main = common_args.main;
+ if (not main and not fetch_repos->to_include.empty()) {
+ main = *std::min_element(fetch_repos->to_include.begin(),
+ fetch_repos->to_include.end());
+ }
+ if (main) {
+ JustMR::Utils::ReachableRepositories(repos, *main, fetch_repos);
+ }
+
+ std::function<bool(std::filesystem::path const&,
+ std::filesystem::path const&)>
+ is_subpath = [](std::filesystem::path const& path,
+ std::filesystem::path const& base) {
+ return (std::filesystem::proximate(path, base) == base);
+ };
+
+ // warn if fetch_dir is in invocation workspace
+ if (common_args.just_mr_paths->workspace_root and
+ is_subpath(*fetch_dir,
+ *common_args.just_mr_paths->workspace_root)) {
+ auto repo_desc = repos->Get(*main, Expression::none_t{});
+ auto repo = repo_desc->Get("repository", Expression::none_t{});
+ auto repo_path = repo->Get("path", Expression::none_t{});
+ auto repo_type = repo->Get("type", Expression::none_t{});
+ if (repo_path->IsString() and repo_type->IsString() and
+ (repo_type->String() == "file")) {
+ auto repo_path_as_path =
+ std::filesystem::path(repo_path->String());
+ if (not repo_path_as_path.is_absolute()) {
+ repo_path_as_path = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(
+ common_args.just_mr_paths->setup_root /
+ repo_path_as_path));
+ }
+ // only warn if repo workspace differs to invocation workspace
+ if (not is_subpath(
+ repo_path_as_path,
+ *common_args.just_mr_paths->workspace_root)) {
+ Logger::Log(
+ LogLevel::Warning,
+ "Writing distribution files to workspace location {}, "
+ "which is different to the workspace of the requested "
+ "main repository {}.",
+ nlohmann::json(fetch_dir->string()).dump(),
+ nlohmann::json(repo_path_as_path.string()).dump());
+ }
+ }
+ }
+ }
+
+ Logger::Log(LogLevel::Info, "Fetching to {}", fetch_dir->string());
+
+ // gather all repos to be fetched
+ std::vector<ArchiveRepoInfo> repos_to_fetch{};
+ repos_to_fetch.reserve(
+ fetch_repos->to_include.size()); // pre-reserve a maximum size
+ for (auto const& repo_name : fetch_repos->to_include) {
+ auto repo_desc = repos->At(repo_name);
+ if (not repo_desc) {
+ Logger::Log(LogLevel::Error,
+ "Config: Missing config entry for repository {}",
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ auto repo = repo_desc->get()->At("repository");
+ if (repo) {
+ auto resolved_repo_desc =
+ JustMR::Utils::ResolveRepo(repo->get(), repos);
+ if (not resolved_repo_desc) {
+ Logger::Log(LogLevel::Error,
+ "Config: Found cyclic dependency for repository {}",
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ // get repo_type
+ auto repo_type = (*resolved_repo_desc)->At("type");
+ if (not repo_type) {
+ Logger::Log(
+ LogLevel::Error,
+ "Config: Mandatory key \"type\" missing for repository {}",
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ if (not repo_type->get()->IsString()) {
+ Logger::Log(LogLevel::Error,
+ "Config: Unsupported value {} for key \"type\" for "
+ "repository {}",
+ repo_type->get()->ToString(),
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ auto repo_type_str = repo_type->get()->String();
+ if (not kCheckoutTypeMap.contains(repo_type_str)) {
+ Logger::Log(LogLevel::Error,
+ "Unknown repository type {} for {}",
+ nlohmann::json(repo_type_str).dump(),
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ // only do work if repo is archive type
+ if (kCheckoutTypeMap.at(repo_type_str) == CheckoutType::Archive) {
+ // check mandatory fields
+ auto repo_desc_content = (*resolved_repo_desc)->At("content");
+ if (not repo_desc_content) {
+ Logger::Log(LogLevel::Error,
+ "Mandatory field \"content\" is missing");
+ return kExitFetchError;
+ }
+ if (not repo_desc_content->get()->IsString()) {
+ Logger::Log(
+ LogLevel::Error,
+ "Unsupported value {} for mandatory field \"content\"",
+ repo_desc_content->get()->ToString());
+ return kExitFetchError;
+ }
+ auto repo_desc_fetch = (*resolved_repo_desc)->At("fetch");
+ if (not repo_desc_fetch) {
+ Logger::Log(LogLevel::Error,
+ "Mandatory field \"fetch\" is missing");
+ return kExitFetchError;
+ }
+ if (not repo_desc_fetch->get()->IsString()) {
+ Logger::Log(LogLevel::Error,
+ "ArchiveCheckout: Unsupported value {} for "
+ "mandatory field \"fetch\"",
+ repo_desc_fetch->get()->ToString());
+ return kExitFetchError;
+ }
+ auto repo_desc_subdir =
+ (*resolved_repo_desc)->Get("subdir", Expression::none_t{});
+ auto subdir =
+ std::filesystem::path(repo_desc_subdir->IsString()
+ ? repo_desc_subdir->String()
+ : "")
+ .lexically_normal();
+ auto repo_desc_distfile =
+ (*resolved_repo_desc)
+ ->Get("distfile", Expression::none_t{});
+ auto repo_desc_sha256 =
+ (*resolved_repo_desc)->Get("sha256", Expression::none_t{});
+ auto repo_desc_sha512 =
+ (*resolved_repo_desc)->Get("sha512", Expression::none_t{});
+
+ ArchiveRepoInfo archive_info = {
+ .archive = {.content = repo_desc_content->get()->String(),
+ .distfile =
+ repo_desc_distfile->IsString()
+ ? std::make_optional(
+ repo_desc_distfile->String())
+ : std::nullopt,
+ .fetch_url = repo_desc_fetch->get()->String(),
+ .sha256 = repo_desc_sha256->IsString()
+ ? std::make_optional(
+ repo_desc_sha256->String())
+ : std::nullopt,
+ .sha512 = repo_desc_sha512->IsString()
+ ? std::make_optional(
+ repo_desc_sha512->String())
+ : std::nullopt,
+ .origin = repo_name,
+ .origin_from_distdir = false},
+ .repo_type = repo_type_str,
+ .subdir = subdir.empty() ? "." : subdir.string(),
+ .pragma_special = std::nullopt // not used
+ };
+ // add to list
+ repos_to_fetch.emplace_back(std::move(archive_info));
+ }
+ }
+ else {
+ Logger::Log(LogLevel::Error,
+ "Config: Missing repository description for {}",
+ nlohmann::json(repo_name).dump());
+ return kExitFetchError;
+ }
+ }
+
+ // report progress
+ auto nr = repos_to_fetch.size();
+ Logger::Log(LogLevel::Info,
+ "Found {} {} to fetch",
+ nr,
+ nr == 1 ? "archive" : "archives");
+
+ // create async maps
+ auto content_cas_map = CreateContentCASMap(
+ common_args.just_mr_paths, common_args.ca_info, common_args.jobs);
+ auto repo_fetch_map =
+ CreateRepoFetchMap(&content_cas_map, *fetch_dir, common_args.jobs);
+
+ // set up progress observer
+ JustMRProgress::Instance().SetTotal(repos_to_fetch.size());
+ std::atomic<bool> done{false};
+ std::condition_variable cv{};
+ auto reporter = JustMRProgressReporter::Reporter();
+ auto observer =
+ std::thread([reporter, &done, &cv]() { reporter(&done, &cv); });
+
+ // do the fetch
+ bool failed{false};
+ {
+ TaskSystem ts{common_args.jobs};
+ repo_fetch_map.ConsumeAfterKeysReady(
+ &ts,
+ repos_to_fetch,
+ [&failed](auto const& values) {
+ // report any fetch fails
+ for (auto const& val : values) {
+ if (not *val) {
+ failed = true;
+ break;
+ }
+ }
+ },
+ [&failed](auto const& msg, bool fatal) {
+ Logger::Log(fatal ? LogLevel::Error : LogLevel::Warning,
+ "While performing just-mr fetch:\n{}",
+ msg);
+ failed = failed or fatal;
+ });
+ }
+
+ // close progress observer
+ done = true;
+ cv.notify_all();
+ observer.join();
+
+ if (failed) {
+ return kExitFetchError;
+ }
+ // report success
+ Logger::Log(LogLevel::Info, "Fetch completed");
+ return kExitSuccess;
+}