// 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. #ifndef INCLUDED_SRC_OTHER_TOOLS_JUST_MR_CLI_HPP #define INCLUDED_SRC_OTHER_TOOLS_JUST_MR_CLI_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include "CLI/CLI.hpp" #include "fmt/core.h" #include "gsl/gsl" #include "nlohmann/json.hpp" #include "src/buildtool/common/clidefaults.hpp" #include "src/buildtool/common/retry_cli.hpp" #include "src/buildtool/common/user_structs.hpp" #include "src/buildtool/logging/log_level.hpp" #include "src/other_tools/just_mr/mirrors.hpp" #include "src/other_tools/just_mr/utils.hpp" /// \brief Arguments common to all just-mr subcommands struct MultiRepoCommonArguments { std::optional repository_config{std::nullopt}; std::optional absent_repository_file{std::nullopt}; std::optional checkout_locations_file{std::nullopt}; std::vector explicit_distdirs; LocalPathsPtr just_mr_paths = std::make_shared(); MirrorsPtr alternative_mirrors = std::make_shared(); std::optional> local_launcher{std::nullopt}; CAInfoPtr ca_info = std::make_shared(); std::optional just_path{std::nullopt}; std::optional main{std::nullopt}; std::optional rc_path{std::nullopt}; std::optional git_path{std::nullopt}; std::optional dump_rc{std::nullopt}; bool norc{false}; std::size_t jobs{std::max(1U, std::thread::hardware_concurrency())}; std::vector defines; std::optional remote_execution_address; bool compatible{false}; std::optional remote_serve_address; bool fetch_absent{false}; }; struct MultiRepoLogArguments { std::vector log_files; std::optional log_limit; std::optional restrict_stderr_log_limit; bool plain_log{false}; bool log_append{false}; }; struct MultiRepoSetupArguments { std::optional sub_main{std::nullopt}; bool sub_all{false}; }; struct MultiRepoFetchArguments { std::optional fetch_dir{std::nullopt}; bool backup_to_remote{false}; }; struct MultiRepoUpdateArguments { std::vector repos_to_update; }; struct MultiRepoGcArguments { bool drop_only{false}; }; // Arguments for invocation logging; set only via rc files struct InvocationLogArguments { std::optional directory{std::nullopt}; std::optional invocation_msg{std::nullopt}; std::vector context_vars{}; std::optional project_id{std::nullopt}; std::optional metadata{std::nullopt}; std::optional graph_file{std::nullopt}; std::optional graph_file_plain{std::nullopt}; std::optional dump_artifacts_to_build{std::nullopt}; std::optional dump_artifacts{std::nullopt}; std::optional profile{std::nullopt}; }; struct MultiRepoJustSubCmdsArguments { std::optional subcmd_name{std::nullopt}; std::vector additional_just_args; std::unordered_map> just_args; std::optional config; std::optional endpoint_configuration; }; // corresponding to the similarly-named arguments in 'just' struct MultiRepoRemoteAuthArguments { std::optional tls_ca_cert{std::nullopt}; std::optional tls_client_cert{std::nullopt}; std::optional tls_client_key{std::nullopt}; }; struct ForwardOnlyArguments { std::vector remote_execution_properties; }; enum class SubCommand : std::uint8_t { kUnknown, kMRVersion, kFetch, kUpdate, kSetup, kSetupEnv, kJustDo, kJustSubCmd, kGcRepo }; struct CommandLineArguments { SubCommand cmd{SubCommand::kUnknown}; MultiRepoCommonArguments common; RetryArguments retry; MultiRepoLogArguments log; MultiRepoSetupArguments setup; MultiRepoFetchArguments fetch; MultiRepoUpdateArguments update; MultiRepoGcArguments gc; MultiRepoJustSubCmdsArguments just_cmd; MultiRepoRemoteAuthArguments auth; ForwardOnlyArguments launch_fwd; InvocationLogArguments invocation_log; }; static inline void SetupMultiRepoCommonArguments( gsl::not_null const& app, gsl::not_null const& clargs) { // repository config is mandatory app->add_option_function( "-C, --repository-config", [clargs](auto const& repository_config_raw) { clargs->repository_config = std::filesystem::weakly_canonical(repository_config_raw); }, "Repository-description file to use.") ->type_name("FILE"); app->add_option_function( "--absent", [clargs](auto const& file_raw) { clargs->absent_repository_file = std::filesystem::weakly_canonical(file_raw); }, "File specifying the repositories to consider absent (overrides the " "pragma in the config file).") ->type_name("FILE"); app->add_option_function( "--local-build-root", [clargs](auto const& local_build_root_raw) { clargs->just_mr_paths->root = std::filesystem::weakly_canonical(local_build_root_raw); }, "Root for CAS, repository space, etc.") ->type_name("PATH"); app->add_option_function( "--checkout-locations", [clargs](auto const& checkout_locations_raw) { clargs->checkout_locations_file = std::filesystem::weakly_canonical(checkout_locations_raw); }, "Specification file for checkout locations.") ->type_name("CHECKOUT_LOCATIONS"); app->add_option_function( "-L, --local-launcher", [clargs](auto const& launcher_raw) { clargs->local_launcher = nlohmann::json::parse(launcher_raw) .template get>(); }, "JSON array with the list of strings representing the launcher to " "prepend actions' commands before being executed locally.") ->type_name("JSON"); app->add_option_function( "--distdir", [clargs](auto const& distdir_raw) { clargs->explicit_distdirs.emplace_back( std::filesystem::weakly_canonical(distdir_raw)); }, "Directory to look for distfiles before fetching.") ->type_name("PATH") ->trigger_on_parse(); // run callback on all instances while parsing, // not after all parsing is done app->add_flag("--no-fetch-ssl-verify", clargs->ca_info->no_ssl_verify, "Do not perform SSL verification when fetching archives from " "remote."); app->add_option_function( "--fetch-cacert", [clargs](auto const& cacert_raw) { clargs->ca_info->ca_bundle = std::filesystem::weakly_canonical(cacert_raw); }, "CA certificate bundle to use for SSL verification when fetching " "archives from remote.") ->type_name("CA_BUNDLE"); app->add_option("--just", clargs->just_path, fmt::format("The build tool to be launched (default: {}).", kDefaultJustPath)) ->type_name("PATH"); app->add_option("--main", clargs->main, "Main repository to consider from the configuration.") ->type_name("MAIN"); app->add_option_function( "--rc", [clargs](auto const& rc_path_raw) { clargs->rc_path = std::filesystem::weakly_canonical(rc_path_raw); }, "Use just-mrrc file from custom path.") ->type_name("RCFILE"); app->add_option("--git", clargs->git_path, fmt::format("Path to the git binary. (Default: {})", kDefaultGitPath)) ->type_name("PATH"); app->add_option( "--dump-rc", clargs->dump_rc, "Dump the effective rc value.") ->type_name("PATH"); app->add_flag("--norc", clargs->norc, "Do not use any just-mrrc file."); app->add_option("-j, --jobs", clargs->jobs, "Number of jobs to run (Default: Number of cores).") ->type_name("NUM"); app->add_option_function( "-D,--defines", [clargs](auto const& d) { clargs->defines.emplace_back(d); }, "Define overlay configuration to be forwarded to the invocation of" " just, in case the subcommand supports it; otherwise ignored.") ->type_name("JSON") ->trigger_on_parse(); // run callback on all instances while parsing, // not after all parsing is done app->add_option("-r,--remote-execution-address", clargs->remote_execution_address, "Address of a remote-execution service.") ->type_name("NAME:PORT"); app->add_flag( "--compatible", clargs->compatible, "At increased computational effort, be compatible with the original " "remote build execution protocol. As the change affects identifiers, " "the flag must be used consistently for all related invocations."); app->add_option("-R,--remote-serve-address", clargs->remote_serve_address, "Address of a remote 'serve' service.") ->type_name("NAME:PORT"); app->add_flag("--fetch-absent", clargs->fetch_absent, "Do not produce absent roots. For Git repositories, try to " "fetch served commit trees from the remote execution " "endpoint before reverting to the network."); } static inline auto SetupMultiRepoLogArguments( gsl::not_null const& app, gsl::not_null const& clargs) { app->add_option_function( "-f,--log-file", [clargs](auto const& log_file_) { clargs->log_files.emplace_back(log_file_); }, "Path to local log file.") ->type_name("PATH") ->trigger_on_parse(); // run callback on all instances while parsing, // not after all parsing is done app->add_option_function>( "--log-limit", [clargs](auto const& limit) { clargs->log_limit = ToLogLevel(limit); }, fmt::format("Log limit (higher is more verbose) in interval [{},{}] " "(Default: {}).", static_cast(kFirstLogLevel), static_cast(kLastLogLevel), static_cast(kDefaultLogLevel))) ->type_name("NUM"); app->add_option_function>( "--restrict-stderr-log-limit", [clargs](auto const& limit) { clargs->restrict_stderr_log_limit = ToLogLevel(limit); }, "Restrict logging on console to the minimum of the specified " "--log-limit and this value") ->type_name("NUM"); app->add_flag("--plain-log", clargs->plain_log, "Do not use ANSI escape sequences to highlight messages."); app->add_flag( "--log-append", clargs->log_append, "Append messages to log file instead of overwriting existing."); } static inline void SetupMultiRepoSetupArguments( gsl::not_null const& app, gsl::not_null const& clargs) { app->add_option("main-repo", clargs->sub_main, "Main repository to consider from the configuration.") ->type_name(""); app->add_flag("--all", clargs->sub_all, "Consider all repositories in the configuration."); } static inline void SetupMultiRepoFetchArguments( gsl::not_null const& app, gsl::not_null const& clargs) { app->add_option_function( "-o", [clargs](auto const& fetch_dir_raw) { clargs->fetch_dir = std::filesystem::weakly_canonical(fetch_dir_raw); }, "Directory to write distfiles when fetching.") ->type_name("PATH"); app->add_flag("--backup-to-remote", clargs->backup_to_remote, "Backup fetched archives to a remote CAS, if a " "remote-execution service is provided."); } static inline void SetupMultiRepoUpdateArguments( gsl::not_null const& app, gsl::not_null const& clargs) { // take all remaining args as positional app->add_option("repo", clargs->repos_to_update, "Repository to update.") ->type_name(""); } static inline void SetupMultiRepoGcArguments( gsl::not_null const& app, gsl::not_null const& clargs) { app->add_flag("--drop-only", clargs->drop_only, "Only drop old repository generations"); } static inline auto SetupMultiRepoRemoteAuthArguments( gsl::not_null const& app, gsl::not_null const& authargs) { app->add_option("--tls-ca-cert", authargs->tls_ca_cert, "Path to a TLS CA certificate that is trusted to sign the " "server certificate."); app->add_option("--tls-client-cert", authargs->tls_client_cert, "Path to the TLS client certificate."); app->add_option("--tls-client-key", authargs->tls_client_key, "Path to the TLS client key."); } #endif // INCLUDED_SRC_OTHER_TOOLS_JUST_MR_CLI_HPP