summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/other_tools/just_mr/TARGETS42
-rw-r--r--src/other_tools/just_mr/cli.hpp161
-rw-r--r--src/other_tools/just_mr/exit_codes.hpp30
-rw-r--r--src/other_tools/just_mr/main.cpp526
4 files changed, 758 insertions, 1 deletions
diff --git a/src/other_tools/just_mr/TARGETS b/src/other_tools/just_mr/TARGETS
index 24a6b289..3f1efd1b 100644
--- a/src/other_tools/just_mr/TARGETS
+++ b/src/other_tools/just_mr/TARGETS
@@ -1,4 +1,22 @@
-{ "utils":
+{ "just-mr":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "arguments_config": ["BUILD_STATIC_BINARY"]
+ , "name": ["just-mr"]
+ , "srcs": ["main.cpp"]
+ , "private-deps":
+ [ ["src/buildtool/build_engine/expression", "expression"]
+ , ["src/buildtool/logging", "logging"]
+ , "cli"
+ , "exit_codes"
+ ]
+ , "stage": ["src", "other_tools", "just_mr"]
+ , "private-ldflags":
+ { "type": "if"
+ , "cond": {"type": "var", "name": "BUILD_STATIC_BINARY"}
+ , "then": ["-static"]
+ }
+ }
+, "utils":
{ "type": ["@", "rules", "CC", "library"]
, "name": ["utils"]
, "hdrs": ["utils.hpp"]
@@ -13,4 +31,26 @@
, "private-deps":
[["src/utils/cpp", "path"], ["src/buildtool/execution_api/local", "local"]]
}
+, "exit_codes":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["exit_codes"]
+ , "hdrs": ["exit_codes.hpp"]
+ , "stage": ["src", "other_tools", "just_mr"]
+ }
+, "cli":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["cli"]
+ , "hdrs": ["cli.hpp"]
+ , "deps":
+ [ ["@", "cli11", "", "cli11"]
+ , ["@", "fmt", "", "fmt"]
+ , ["@", "gsl-lite", "", "gsl-lite"]
+ , ["@", "json", "", "json"]
+ , ["src/buildtool/execution_api/local", "config"]
+ , ["src/other_tools/just_mr", "utils"]
+ , ["src/buildtool/logging", "log_level"]
+ ]
+ , "stage": ["src", "other_tools", "just_mr"]
+ , "private-deps": [["src/buildtool/logging", "logging"]]
+ }
}
diff --git a/src/other_tools/just_mr/cli.hpp b/src/other_tools/just_mr/cli.hpp
new file mode 100644
index 00000000..148d2c85
--- /dev/null
+++ b/src/other_tools/just_mr/cli.hpp
@@ -0,0 +1,161 @@
+// 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 <filesystem>
+#include <optional>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "CLI/CLI.hpp"
+#include "fmt/core.h"
+#include "gsl-lite/gsl-lite.hpp"
+#include "nlohmann/json.hpp"
+#include "src/buildtool/execution_api/local/config.hpp"
+#include "src/buildtool/logging/log_level.hpp"
+#include "src/other_tools/just_mr/utils.hpp"
+
+constexpr auto kDefaultLogLevel = LogLevel::Progress;
+constexpr auto kDefaultTimeout = std::chrono::milliseconds{300000};
+
+/// \brief Arguments common to all just-mr subcommands
+struct MultiRepoCommonArguments {
+ std::optional<std::filesystem::path> repository_config{std::nullopt};
+ std::optional<std::filesystem::path> checkout_locations_file{std::nullopt};
+ std::vector<std::string> explicit_distdirs{};
+ JustMR::PathsPtr just_mr_paths = std::make_shared<JustMR::Paths>();
+ std::optional<std::filesystem::path> just_path{std::nullopt};
+ std::optional<std::string> main{std::nullopt};
+ std::optional<std::filesystem::path> rc_path{std::nullopt};
+ bool norc{false};
+ std::size_t jobs{std::max(1U, std::thread::hardware_concurrency())};
+};
+
+struct MultiRepoSetupArguments {
+ std::optional<std::string> sub_main{std::nullopt};
+ bool sub_all{false};
+};
+
+struct MultiRepoFetchArguments {
+ std::optional<std::filesystem::path> fetch_dir{std::nullopt};
+};
+
+struct MultiRepoUpdateArguments {
+ std::vector<std::string> repos_to_update{};
+};
+
+struct MultiRepoJustSubCmdsArguments {
+ std::optional<std::string> subcmd_name{std::nullopt};
+ std::vector<std::string> additional_just_args{};
+ std::unordered_map<std::string, std::vector<std::string>> just_args{};
+};
+
+static inline void SetupMultiRepoCommonArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<MultiRepoCommonArguments*> const& clargs) {
+ // repository config is mandatory
+ app->add_option_function<std::string>(
+ "-C, --repository-config",
+ [clargs](auto const& repository_config_raw) {
+ clargs->repository_config = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(repository_config_raw));
+ },
+ "Repository-description file to use.")
+ ->type_name("FILE");
+ app->add_option_function<std::string>(
+ "--local-build-root",
+ [clargs](auto const& local_build_root_raw) {
+ clargs->just_mr_paths->root = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(local_build_root_raw));
+ },
+ "Root for CAS, repository space, etc.")
+ ->type_name("PATH");
+ app->add_option_function<std::string>(
+ "-L",
+ [clargs](auto const& checkout_locations_raw) {
+ clargs->checkout_locations_file =
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(checkout_locations_raw));
+ },
+ "Specification file for checkout locations.")
+ ->type_name("CHECKOUT_LOCATION");
+ app->add_option_function<std::string>(
+ "--distdir",
+ [clargs](auto const& distdir_raw) {
+ clargs->explicit_distdirs.emplace_back(
+ std::filesystem::weakly_canonical(std::filesystem::absolute(
+ std::filesystem::path(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_option("--just", clargs->just_path, "Path to the just binary.")
+ ->type_name("PATH");
+ app->add_option("--main",
+ clargs->main,
+ "Main repository to consider from the configuration.")
+ ->type_name("MAIN");
+ app->add_option_function<std::string>(
+ "--rc",
+ [clargs](auto const& rc_path_raw) {
+ clargs->rc_path = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(rc_path_raw));
+ },
+ "Use just-mrrc file from custom path.")
+ ->type_name("RCFILE");
+ 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");
+}
+
+static inline void SetupMultiRepoSetupArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<MultiRepoSetupArguments*> 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<CLI::App*> const& app,
+ gsl::not_null<MultiRepoFetchArguments*> const& clargs) {
+ app->add_option_function<std::string>(
+ "-o",
+ [clargs](auto const& fetch_dir_raw) {
+ clargs->fetch_dir = std::filesystem::weakly_canonical(
+ std::filesystem::absolute(fetch_dir_raw));
+ },
+ "Directory to write distfiles when fetching.")
+ ->type_name("PATH");
+}
+
+static inline void SetupMultiRepoUpdateArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<MultiRepoUpdateArguments*> const& clargs) {
+ // take all remaining args as positional
+ app->add_option("repo", clargs->repos_to_update, "Repository to update.")
+ ->type_name("");
+}
+
+#endif // INCLUDED_SRC_OTHER_TOOLS_JUST_MR_CLI_HPP \ No newline at end of file
diff --git a/src/other_tools/just_mr/exit_codes.hpp b/src/other_tools/just_mr/exit_codes.hpp
new file mode 100644
index 00000000..8e5bf7cc
--- /dev/null
+++ b/src/other_tools/just_mr/exit_codes.hpp
@@ -0,0 +1,30 @@
+// 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_EXIT_CODES_HPP
+#define INCLUDED_SRC_OTHER_TOOLS_JUST_MR_EXIT_CODES_HPP
+
+enum JustMRExitCodes {
+ kExitSuccess = 0,
+ kExitExecError = 64, // error in execvp
+ kExitGenericFailure = 65, // none of the known errors
+ kExitUnknownCommand = 66, // unknown subcommand error
+ kExitClargsError = 67, // error in parsing clargs
+ kExitConfigError = 68, // error in parsing config
+ kExitFetchError = 69, // error in just-mr fetch
+ kExitUpdateError = 70, // error in just-mr update
+ kExitSetupError = 71 // error in just-mr setup(-env)
+};
+
+#endif // INCLUDED_SRC_OTHER_TOOLS_JUST_MR_EXIT_CODES_HPP
diff --git a/src/other_tools/just_mr/main.cpp b/src/other_tools/just_mr/main.cpp
new file mode 100644
index 00000000..45390c94
--- /dev/null
+++ b/src/other_tools/just_mr/main.cpp
@@ -0,0 +1,526 @@
+// 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 "src/buildtool/build_engine/expression/configuration.hpp"
+#include "src/buildtool/logging/log_config.hpp"
+#include "src/buildtool/logging/log_sink_cmdline.hpp"
+#include "src/other_tools/just_mr/cli.hpp"
+#include "src/other_tools/just_mr/exit_codes.hpp"
+
+namespace {
+
+enum class SubCommand {
+ kUnknown,
+ kFetch,
+ kUpdate,
+ kSetup,
+ kSetupEnv,
+ kJustDo,
+ kJustSubCmd
+};
+
+struct CommandLineArguments {
+ SubCommand cmd{SubCommand::kUnknown};
+ MultiRepoCommonArguments common;
+ MultiRepoSetupArguments setup;
+ MultiRepoFetchArguments fetch;
+ MultiRepoUpdateArguments update;
+ MultiRepoJustSubCmdsArguments just_cmd;
+};
+
+struct SetupRepos {
+ std::vector<std::string> to_setup;
+ std::vector<std::string> to_include;
+};
+
+/// \brief Setup arguments for just-mr itself, common to all subcommands.
+void SetupCommonCommandArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<CommandLineArguments*> const& clargs) {
+ SetupMultiRepoCommonArguments(app, &clargs->common);
+}
+
+/// \brief Setup arguments for subcommand "just-mr fetch".
+void SetupFetchCommandArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<CommandLineArguments*> const& clargs) {
+ SetupMultiRepoSetupArguments(app, &clargs->setup);
+ SetupMultiRepoFetchArguments(app, &clargs->fetch);
+}
+
+/// \brief Setup arguments for subcommand "just-mr update".
+void SetupUpdateCommandArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<CommandLineArguments*> const& clargs) {
+ SetupMultiRepoUpdateArguments(app, &clargs->update);
+}
+
+/// \brief Setup arguments for subcommand "just-mr setup" and
+/// "just-mr setup-env".
+void SetupSetupCommandArguments(
+ gsl::not_null<CLI::App*> const& app,
+ gsl::not_null<CommandLineArguments*> const& clargs) {
+ SetupMultiRepoSetupArguments(app, &clargs->setup);
+}
+
+void SetupDefaultLogging() {
+ LogConfig::SetLogLimit(kDefaultLogLevel);
+ LogConfig::SetSinks({LogSinkCmdLine::CreateFactory()});
+}
+
+[[nodiscard]] auto ParseCommandLineArguments(int argc, char const* const* argv)
+ -> CommandLineArguments {
+ CLI::App app("just-mr");
+ app.option_defaults()->take_last();
+ auto* cmd_setup =
+ app.add_subcommand("setup", "Setup and generate just configuration");
+ auto* cmd_setup_env = app.add_subcommand(
+ "setup-env", "Setup without workspace root for the main repository.");
+ auto* cmd_fetch =
+ app.add_subcommand("fetch", "Fetch and store distribution files.");
+ auto* cmd_update = app.add_subcommand(
+ "update",
+ "Advance Git commit IDs and print updated just-mr configuration.");
+ auto* cmd_do = app.add_subcommand(
+ "do", "Canonical way of specifying just subcommands. ");
+ cmd_do->set_help_flag(); // disable help flag
+ // define just subcommands
+ std::vector<CLI::App*> cmd_just_subcmds{};
+ cmd_just_subcmds.reserve(kKnownJustSubcommands.size());
+ for (auto const& known_subcmd : kKnownJustSubcommands) {
+ auto* subcmd = app.add_subcommand(
+ known_subcmd.first,
+ "Run setup and call \'just " + known_subcmd.first + "\'.");
+ subcmd->set_help_flag(); // disable help flag
+ cmd_just_subcmds.emplace_back(subcmd);
+ }
+ app.require_subcommand(1);
+
+ CommandLineArguments clargs;
+ // first, set the common arguments for just-mr itself
+ SetupCommonCommandArguments(&app, &clargs);
+ // then, set the arguments for each subcommand
+ SetupSetupCommandArguments(cmd_setup, &clargs);
+ SetupSetupCommandArguments(cmd_setup_env, &clargs);
+ SetupFetchCommandArguments(cmd_fetch, &clargs);
+ SetupUpdateCommandArguments(cmd_update, &clargs);
+
+ // for 'just' calls, allow extra arguments
+ cmd_do->allow_extras();
+ for (auto const& sub_cmd : cmd_just_subcmds) {
+ sub_cmd->allow_extras();
+ }
+
+ try {
+ app.parse(argc, argv);
+ } catch (CLI::Error& e) {
+ [[maybe_unused]] auto err = app.exit(e);
+ std::exit(kExitClargsError);
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error, "Command line parse error: {}", ex.what());
+ std::exit(kExitClargsError);
+ }
+
+ if (*cmd_setup) {
+ clargs.cmd = SubCommand::kSetup;
+ }
+ else if (*cmd_setup_env) {
+ clargs.cmd = SubCommand::kSetupEnv;
+ }
+ else if (*cmd_fetch) {
+ clargs.cmd = SubCommand::kFetch;
+ }
+ else if (*cmd_update) {
+ clargs.cmd = SubCommand::kUpdate;
+ }
+ else if (*cmd_do) {
+ clargs.cmd = SubCommand::kJustDo;
+ // get remaining args
+ clargs.just_cmd.additional_just_args = cmd_do->remaining();
+ }
+ else {
+ for (auto const& sub_cmd : cmd_just_subcmds) {
+ if (*sub_cmd) {
+ clargs.cmd = SubCommand::kJustSubCmd;
+ clargs.just_cmd.subcmd_name =
+ sub_cmd->get_name(); // get name of subcommand
+ // get remaining args
+ clargs.just_cmd.additional_just_args = sub_cmd->remaining();
+ break; // no need to go further
+ }
+ }
+ }
+
+ return clargs;
+}
+
+[[nodiscard]] auto ReadLocation(
+ nlohmann::json const& location,
+ std::optional<std::filesystem::path> const& ws_root)
+ -> std::optional<std::pair<std::filesystem::path, std::filesystem::path>> {
+ if (not location.contains("path") or not location.contains("root")) {
+ Logger::Log(LogLevel::Error,
+ "Malformed location object: {}",
+ location.dump(-1));
+ std::exit(kExitConfigError);
+ }
+ auto root = location["root"].get<std::string>();
+ auto path = location["path"].get<std::string>();
+ auto base = location.contains("base") ? location["base"].get<std::string>()
+ : std::string(".");
+
+ std::filesystem::path root_path{};
+ if (root == "workspace") {
+ if (not ws_root) {
+ Logger::Log(LogLevel::Warning,
+ "Not in workspace root, ignoring location {}.",
+ location.dump(-1));
+ return std::nullopt;
+ }
+ root_path = *ws_root;
+ }
+ if (root == "home") {
+ root_path = LocalExecutionConfig::GetUserHome();
+ }
+ if (root == "system") {
+ root_path = FileSystemManager::GetCurrentDirectory().root_path();
+ }
+ return std::make_pair(std::filesystem::weakly_canonical(
+ std::filesystem::absolute(root_path / path)),
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(root_path / base)));
+}
+
+[[nodiscard]] auto ReadLocation(
+ ExpressionPtr const& location,
+ std::optional<std::filesystem::path> const& ws_root)
+ -> std::optional<std::pair<std::filesystem::path, std::filesystem::path>> {
+ if (location.IsNotNull()) {
+ auto root = location->Get("root", Expression::none_t{});
+ auto path = location->Get("path", Expression::none_t{});
+ auto base = location->Get("base", std::string("."));
+
+ if (not path.IsNotNull() or not root.IsNotNull() or
+ not kLocationTypes.contains(root->String())) {
+ Logger::Log(LogLevel::Error,
+ "Malformed location object: {}",
+ location.ToJson().dump(-1));
+ std::exit(kExitConfigError);
+ }
+ auto root_str = root->String();
+ std::filesystem::path root_path{};
+ if (root_str == "workspace") {
+ if (not ws_root) {
+ Logger::Log(LogLevel::Warning,
+ "Not in workspace root, ignoring location {}.",
+ location.ToJson().dump(-1));
+ return std::nullopt;
+ }
+ root_path = *ws_root;
+ }
+ if (root_str == "home") {
+ root_path = LocalExecutionConfig::GetUserHome();
+ }
+ if (root_str == "system") {
+ root_path = FileSystemManager::GetCurrentDirectory().root_path();
+ }
+ return std::make_pair(
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(root_path / path->String())),
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(root_path / base->String())));
+ }
+ return std::nullopt;
+}
+
+/// \brief Read just-mrrc file and set up various configs. Return the path to
+/// the repository config file, if any is provided.
+[[nodiscard]] auto ReadJustMRRC(
+ gsl::not_null<CommandLineArguments*> const& clargs)
+ -> std::optional<std::filesystem::path> {
+ Configuration rc_config{};
+ auto rc_path = clargs->common.rc_path;
+ // set default if rcpath not given
+ if (not clargs->common.norc) {
+ if (not rc_path) {
+ if (not FileSystemManager::IsFile(kDefaultRCPath)) {
+ return std::nullopt;
+ }
+ rc_path = kDefaultRCPath;
+ }
+ else {
+
+ if (not FileSystemManager::IsFile(*rc_path)) {
+ Logger::Log(LogLevel::Error,
+ "Cannot read RC file {}.",
+ rc_path->string());
+ std::exit(kExitConfigError);
+ }
+ }
+ try {
+ std::ifstream fs(*rc_path);
+ auto map = Expression::FromJson(nlohmann::json::parse(fs));
+ if (not map->IsMap()) {
+ Logger::Log(LogLevel::Error,
+ "RC file {} does not contain a JSON object.",
+ rc_path->string());
+ std::exit(kExitConfigError);
+ }
+ rc_config = Configuration{map};
+ } catch (std::exception const& e) {
+ Logger::Log(LogLevel::Error,
+ "Parsing RC file {} failed with error:\n{}",
+ rc_path->string(),
+ e.what());
+ std::exit(kExitConfigError);
+ }
+ }
+ // read local build root; overwritten if user provided it already
+ if (not clargs->common.just_mr_paths->root) {
+ auto build_root =
+ ReadLocation(rc_config["local build root"],
+ clargs->common.just_mr_paths->workspace_root);
+ if (build_root) {
+ clargs->common.just_mr_paths->root = build_root->first;
+ }
+ }
+ // read checkout locations file; overwritten if user provided it already
+ if (not clargs->common.checkout_locations_file) {
+ auto checkout =
+ ReadLocation(rc_config["checkout locations"],
+ clargs->common.just_mr_paths->workspace_root);
+ if (checkout) {
+ if (not FileSystemManager::IsFile(checkout->first)) {
+ Logger::Log(LogLevel::Error,
+ "Cannot find checkout locations file {}.",
+ checkout->first.string());
+ std::exit(kExitConfigError);
+ }
+ clargs->common.checkout_locations_file = checkout->first;
+ }
+ }
+ // read distdirs; user can append, but does not overwrite
+ auto distdirs = rc_config["distdirs"];
+ if (distdirs.IsNotNull()) {
+ auto const& distdirs_list = distdirs->List();
+ for (auto const& l : distdirs_list) {
+ auto paths =
+ ReadLocation(l, clargs->common.just_mr_paths->workspace_root);
+ if (paths) {
+ if (FileSystemManager::IsDirectory(paths->first)) {
+ clargs->common.just_mr_paths->distdirs.emplace_back(
+ paths->first);
+ }
+ else {
+ Logger::Log(LogLevel::Warning,
+ "Ignoring non-existing distdir {}.",
+ paths->first.string());
+ }
+ }
+ }
+ }
+ // read just path; overwritten if user provided it already
+ if (not clargs->common.just_path) {
+ auto just = ReadLocation(rc_config["just"],
+ clargs->common.just_mr_paths->workspace_root);
+ if (just) {
+ clargs->common.just_path = just->first;
+ }
+ }
+ // read additional just args; user can append, but does not overwrite
+ auto just_args = rc_config["just args"];
+ if (just_args.IsNotNull()) {
+ for (auto const& [cmd_name, cmd_args] : just_args->Map()) {
+ // get list of string args for current command
+ clargs->just_cmd.just_args[cmd_name] =
+ [&cmd_args = cmd_args]() -> std::vector<std::string> {
+ std::vector<std::string> args{};
+ auto const& args_list = cmd_args->List();
+ args.resize(args_list.size());
+ for (auto const& arg : args_list) {
+ args.emplace_back(arg->String());
+ }
+ return args;
+ }();
+ }
+ }
+ // read config lookup order
+ auto config_lookup_order = rc_config["config lookup order"];
+ if (config_lookup_order.IsNotNull()) {
+ for (auto const& entry : config_lookup_order->List()) {
+ auto paths = ReadLocation(
+ entry, clargs->common.just_mr_paths->workspace_root);
+ if (paths and FileSystemManager::IsFile(paths->first)) {
+ clargs->common.just_mr_paths->setup_root = paths->second;
+ return paths->first;
+ }
+ }
+ }
+ else {
+ for (auto const& entry : kDefaultConfigLocations) {
+ auto paths = ReadLocation(
+ entry, clargs->common.just_mr_paths->workspace_root);
+ if (paths and FileSystemManager::IsFile(paths->first)) {
+ clargs->common.just_mr_paths->setup_root = paths->second;
+ return paths->first;
+ }
+ }
+ }
+ return std::nullopt; // default return value
+}
+
+[[nodiscard]] auto ReadConfiguration(
+ std::filesystem::path const& config_file) noexcept
+ -> std::shared_ptr<Configuration> {
+ std::shared_ptr<Configuration> config{nullptr};
+ if (not FileSystemManager::IsFile(config_file)) {
+ Logger::Log(LogLevel::Error,
+ "Cannot read config file {}.",
+ config_file.string());
+ std::exit(kExitConfigError);
+ }
+ try {
+ std::ifstream fs(config_file);
+ auto map = Expression::FromJson(nlohmann::json::parse(fs));
+ if (not map->IsMap()) {
+ Logger::Log(LogLevel::Error,
+ "Config file {} does not contain a JSON object.",
+ config_file.string());
+ std::exit(kExitConfigError);
+ }
+ config = std::make_shared<Configuration>(map);
+ } catch (std::exception const& e) {
+ Logger::Log(LogLevel::Error,
+ "Parsing config file {} failed with error:\n{}",
+ config_file.string(),
+ e.what());
+ std::exit(kExitConfigError);
+ }
+ return config;
+}
+
+} // namespace
+
+auto main(int argc, char* argv[]) -> int {
+ SetupDefaultLogging();
+ try {
+ // get the user-defined arguments
+ auto arguments = ParseCommandLineArguments(argc, argv);
+
+ auto config_file = ReadJustMRRC(&arguments);
+ if (arguments.common.repository_config) {
+ config_file = arguments.common.repository_config;
+ }
+ if (not config_file) {
+ Logger::Log(LogLevel::Error,
+ "Cannot find repository configuration.");
+ std::exit(kExitConfigError);
+ }
+
+ auto config = ReadConfiguration(*config_file);
+
+ // if optional args were not read from just-mrrc or given by user, use
+ // the defaults
+ if (not arguments.common.just_path) {
+ arguments.common.just_path = kDefaultJustPath;
+ }
+ if (not arguments.common.just_mr_paths->root) {
+ arguments.common.just_mr_paths->root =
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(kDefaultBuildRoot));
+ }
+ if (not arguments.common.checkout_locations_file and
+ FileSystemManager::IsFile(std::filesystem::weakly_canonical(
+ std::filesystem::absolute(kDefaultCheckoutLocationsFile)))) {
+ arguments.common.checkout_locations_file =
+ std::filesystem::weakly_canonical(
+ std::filesystem::absolute(kDefaultCheckoutLocationsFile));
+ }
+ if (arguments.common.just_mr_paths->distdirs.empty()) {
+ arguments.common.just_mr_paths->distdirs.emplace_back(
+ kDefaultDistdirs);
+ }
+
+ // read checkout locations
+ if (arguments.common.checkout_locations_file) {
+ try {
+ std::ifstream ifs(*arguments.common.checkout_locations_file);
+ auto checkout_locations_json = nlohmann::json::parse(ifs);
+ arguments.common.just_mr_paths->git_checkout_locations =
+ checkout_locations_json
+ .value("checkouts", nlohmann::json::object())
+ .value("git", nlohmann::json::object());
+ } catch (std::exception const& e) {
+ Logger::Log(
+ LogLevel::Error,
+ "Parsing checkout locations file {} failed with error:\n{}",
+ arguments.common.checkout_locations_file->string(),
+ e.what());
+ std::exit(kExitConfigError);
+ }
+ }
+
+ // append explicitly-given distdirs
+ arguments.common.just_mr_paths->distdirs.insert(
+ arguments.common.just_mr_paths->distdirs.end(),
+ arguments.common.explicit_distdirs.begin(),
+ arguments.common.explicit_distdirs.end());
+
+ // Setup LocalExecutionConfig to store the local_build_root properly
+ // and make the cas and git cache roots available
+ if (not LocalExecutionConfig::SetBuildRoot(
+ *arguments.common.just_mr_paths->root)) {
+ Logger::Log(LogLevel::Error,
+ "Failed to configure local build root.");
+ return kExitGenericFailure;
+ }
+
+ // check for conflicts in main repo name
+ if ((not arguments.setup.sub_all) and arguments.common.main and
+ arguments.setup.sub_main and
+ (arguments.common.main != arguments.setup.sub_main)) {
+ Logger::Log(LogLevel::Warning,
+ "Conflicting options for main repository, selecting {}",
+ *arguments.setup.sub_main);
+ }
+ if (arguments.setup.sub_main) {
+ arguments.common.main = arguments.setup.sub_main;
+ }
+
+ if (arguments.cmd == SubCommand::kJustDo or
+ arguments.cmd == SubCommand::kJustSubCmd) {
+ return kExitSuccess;
+ }
+
+ if (arguments.cmd == SubCommand::kSetup or
+ arguments.cmd == SubCommand::kSetupEnv) {
+ return kExitSuccess;
+ }
+
+ if (arguments.cmd == SubCommand::kUpdate) {
+ return kExitSuccess;
+ }
+
+ if (arguments.cmd == SubCommand::kFetch) {
+ return kExitSuccess;
+ }
+
+ // default exit code for unknown subcommand
+ return kExitUnknownCommand;
+ } catch (std::exception const& ex) {
+ Logger::Log(
+ LogLevel::Error, "Caught exception with message: {}", ex.what());
+ }
+ return kExitGenericFailure;
+}