diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildtool/main/TARGETS | 27 | ||||
-rw-r--r-- | src/buildtool/main/cli.cpp | 209 | ||||
-rw-r--r-- | src/buildtool/main/cli.hpp | 56 | ||||
-rw-r--r-- | src/buildtool/main/diagnose.cpp | 314 | ||||
-rw-r--r-- | src/buildtool/main/diagnose.hpp | 25 | ||||
-rw-r--r-- | src/buildtool/main/main.cpp | 513 |
6 files changed, 631 insertions, 513 deletions
diff --git a/src/buildtool/main/TARGETS b/src/buildtool/main/TARGETS index b74a5fe4..974c0e61 100644 --- a/src/buildtool/main/TARGETS +++ b/src/buildtool/main/TARGETS @@ -4,8 +4,7 @@ , "name": ["just"] , "srcs": ["main.cpp"] , "private-deps": - [ ["src/buildtool/common", "cli"] - , ["src/buildtool/common", "config"] + [ ["src/buildtool/common", "config"] , ["src/buildtool/storage", "storage"] , ["src/buildtool/compatibility", "compatibility"] , ["src/buildtool/graph_traverser", "graph_traverser"] @@ -24,10 +23,12 @@ , ["src/buildtool/execution_api/local", "config"] , ["src/buildtool/execution_api/remote", "config"] , "common" + , "cli" , "version" , "analyse" , "install_cas" , "describe" + , "diagnose" , "constants" ] , "stage": ["src", "buildtool", "main"] @@ -68,6 +69,15 @@ , "hdrs": ["exit_codes.hpp"] , "stage": ["src", "buildtool", "main"] } +, "cli": + { "type": ["@", "rules", "CC", "library"] + , "name": ["cli"] + , "hdrs": ["cli.hpp"] + , "srcs": ["cli.cpp"] + , "stage": ["src", "buildtool", "main"] + , "deps": [["src/buildtool/common", "cli"]] + , "private-deps": [["@", "gsl", "", "gsl"], "common"] + } , "install_cas": { "type": ["@", "rules", "CC", "library"] , "name": ["install_cas"] @@ -110,6 +120,19 @@ , ["src/buildtool/build_engine/target_map", "target_map"] ] } +, "diagnose": + { "type": ["@", "rules", "CC", "library"] + , "name": ["diagnose"] + , "hdrs": ["diagnose.hpp"] + , "srcs": ["diagnose.cpp"] + , "stage": ["src", "buildtool", "main"] + , "deps": + [ "analyse" + , ["src/buildtool/common", "cli"] + , ["src/buildtool/build_engine/target_map", "result_map"] + ] + , "private-deps": [["src/utils/cpp", "json"], ["@", "json", "", "json"]] + } , "version": { "type": ["@", "rules", "CC", "library"] , "arguments_config": ["SOURCE_DATE_EPOCH", "VERSION_EXTRA_SUFFIX"] diff --git a/src/buildtool/main/cli.cpp b/src/buildtool/main/cli.cpp new file mode 100644 index 00000000..cb53acde --- /dev/null +++ b/src/buildtool/main/cli.cpp @@ -0,0 +1,209 @@ +// 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/buildtool/main/cli.hpp" + +#include "src/buildtool/main/exit_codes.hpp" + +#include <gsl/gsl> + +namespace { + +/// \brief Setup arguments for sub command "just describe". +auto SetupDescribeCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCommonArguments(app, &clargs->common); + SetupAnalysisArguments(app, &clargs->analysis, false); + SetupLogArguments(app, &clargs->log); + SetupDescribeArguments(app, &clargs->describe); +} + +/// \brief Setup arguments for sub command "just analyse". +auto SetupAnalyseCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCommonArguments(app, &clargs->common); + SetupLogArguments(app, &clargs->log); + SetupAnalysisArguments(app, &clargs->analysis); + SetupCacheArguments(app, &clargs->endpoint); + SetupEndpointArguments(app, &clargs->endpoint); + SetupDiagnosticArguments(app, &clargs->diagnose); + SetupCompatibilityArguments(app); +} + +/// \brief Setup arguments for sub command "just build". +auto SetupBuildCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCommonArguments(app, &clargs->common); + SetupLogArguments(app, &clargs->log); + SetupAnalysisArguments(app, &clargs->analysis); + SetupCacheArguments(app, &clargs->endpoint); + SetupEndpointArguments(app, &clargs->endpoint); + SetupCommonAuthArguments(app, &clargs->auth); + SetupClientAuthArguments(app, &clargs->cauth); + SetupCommonBuildArguments(app, &clargs->build); + SetupBuildArguments(app, &clargs->build); + SetupCompatibilityArguments(app); +} + +/// \brief Setup arguments for sub command "just install". +auto SetupInstallCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupBuildCommandArguments(app, clargs); // same as build + SetupStageArguments(app, &clargs->stage); // plus stage +} + +/// \brief Setup arguments for sub command "just rebuild". +auto SetupRebuildCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupBuildCommandArguments(app, clargs); // same as build + SetupRebuildArguments(app, &clargs->rebuild); // plus rebuild +} + +/// \brief Setup arguments for sub command "just install-cas". +auto SetupInstallCasCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCompatibilityArguments(app); + SetupCacheArguments(app, &clargs->endpoint); + SetupEndpointArguments(app, &clargs->endpoint); + SetupCommonAuthArguments(app, &clargs->auth); + SetupClientAuthArguments(app, &clargs->cauth); + SetupFetchArguments(app, &clargs->fetch); + SetupLogArguments(app, &clargs->log); +} + +/// \brief Setup arguments for sub command "just traverse". +auto SetupTraverseCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCommonArguments(app, &clargs->common); + SetupLogArguments(app, &clargs->log); + SetupCacheArguments(app, &clargs->endpoint); + SetupEndpointArguments(app, &clargs->endpoint); + SetupCommonAuthArguments(app, &clargs->auth); + SetupClientAuthArguments(app, &clargs->cauth); + SetupGraphArguments(app, &clargs->graph); // instead of analysis + SetupCommonBuildArguments(app, &clargs->build); + SetupBuildArguments(app, &clargs->build); + SetupStageArguments(app, &clargs->stage); + SetupCompatibilityArguments(app); +} + +/// \brief Setup arguments for sub command "just gc". +auto SetupGcCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupLogArguments(app, &clargs->log); + SetupCacheArguments(app, &clargs->endpoint); +} + +/// \brief Setup arguments for sub command "just execute". +auto SetupExecutionServiceCommandArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<CommandLineArguments*> const& clargs) { + SetupCompatibilityArguments(app); + SetupCommonBuildArguments(app, &clargs->build); + SetupCacheArguments(app, &clargs->endpoint); + SetupExecutionServiceArguments(app, &clargs->es); + SetupLogArguments(app, &clargs->log); + SetupCommonAuthArguments(app, &clargs->auth); + SetupServerAuthArguments(app, &clargs->sauth); +} +} // namespace + +auto ParseCommandLineArguments(int argc, char const* const* argv) + -> CommandLineArguments { + CLI::App app("just, a generic build tool"); + app.option_defaults()->take_last(); + + auto* cmd_version = app.add_subcommand( + "version", "Print version information in JSON format."); + auto* cmd_describe = app.add_subcommand( + "describe", "Describe the rule generating a target."); + auto* cmd_analyse = + app.add_subcommand("analyse", "Analyse specified targets."); + auto* cmd_build = app.add_subcommand("build", "Build specified targets."); + auto* cmd_install = + app.add_subcommand("install", "Build and stage specified targets."); + auto* cmd_rebuild = app.add_subcommand( + "rebuild", "Rebuild and compare artifacts to cached build."); + auto* cmd_install_cas = + app.add_subcommand("install-cas", "Fetch and stage artifact from CAS."); + auto* cmd_gc = + app.add_subcommand("gc", "Trigger garbage collection of local cache."); + auto* cmd_execution = app.add_subcommand( + "execute", "Start single node execution service on this machine."); + auto* cmd_traverse = + app.group("") // group for creating hidden options + ->add_subcommand("traverse", + "Build and stage artifacts from graph file."); + app.require_subcommand(1); + + CommandLineArguments clargs; + SetupDescribeCommandArguments(cmd_describe, &clargs); + SetupAnalyseCommandArguments(cmd_analyse, &clargs); + SetupBuildCommandArguments(cmd_build, &clargs); + SetupInstallCommandArguments(cmd_install, &clargs); + SetupRebuildCommandArguments(cmd_rebuild, &clargs); + SetupInstallCasCommandArguments(cmd_install_cas, &clargs); + SetupTraverseCommandArguments(cmd_traverse, &clargs); + SetupGcCommandArguments(cmd_gc, &clargs); + SetupExecutionServiceCommandArguments(cmd_execution, &clargs); + try { + app.parse(argc, argv); + } catch (CLI::Error& e) { + std::exit(app.exit(e)); + } catch (std::exception const& ex) { + Logger::Log(LogLevel::Error, "Command line parse error: {}", ex.what()); + std::exit(kExitFailure); + } + + if (*cmd_version) { + clargs.cmd = SubCommand::kVersion; + } + else if (*cmd_describe) { + clargs.cmd = SubCommand::kDescribe; + } + else if (*cmd_analyse) { + clargs.cmd = SubCommand::kAnalyse; + } + else if (*cmd_build) { + clargs.cmd = SubCommand::kBuild; + } + else if (*cmd_install) { + clargs.cmd = SubCommand::kInstall; + } + else if (*cmd_rebuild) { + clargs.cmd = SubCommand::kRebuild; + } + else if (*cmd_install_cas) { + clargs.cmd = SubCommand::kInstallCas; + } + else if (*cmd_traverse) { + clargs.cmd = SubCommand::kTraverse; + } + else if (*cmd_gc) { + clargs.cmd = SubCommand::kGc; + } + else if (*cmd_execution) { + clargs.cmd = SubCommand::kExecute; + } + + return clargs; +} diff --git a/src/buildtool/main/cli.hpp b/src/buildtool/main/cli.hpp new file mode 100644 index 00000000..7a580ff6 --- /dev/null +++ b/src/buildtool/main/cli.hpp @@ -0,0 +1,56 @@ +// 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. + +#ifndef INCLUDED_SRC_BUILDTOOL_MAIN_CLI +#define INCLUDED_SRC_BUILDTOOL_MAIN_CLI + +#include "src/buildtool/common/cli.hpp" + +enum class SubCommand { + kUnknown, + kVersion, + kDescribe, + kAnalyse, + kBuild, + kInstall, + kRebuild, + kInstallCas, + kTraverse, + kGc, + kExecute +}; + +struct CommandLineArguments { + SubCommand cmd{SubCommand::kUnknown}; + CommonArguments common; + LogArguments log; + AnalysisArguments analysis; + DescribeArguments describe; + DiagnosticArguments diagnose; + EndpointArguments endpoint; + BuildArguments build; + StageArguments stage; + RebuildArguments rebuild; + FetchArguments fetch; + GraphArguments graph; + CommonAuthArguments auth; + ClientAuthArguments cauth; + ServerAuthArguments sauth; + ExecutionServiceArguments es; +}; + +auto ParseCommandLineArguments(int argc, char const* const* argv) + -> CommandLineArguments; + +#endif diff --git a/src/buildtool/main/diagnose.cpp b/src/buildtool/main/diagnose.cpp new file mode 100644 index 00000000..1c3552cd --- /dev/null +++ b/src/buildtool/main/diagnose.cpp @@ -0,0 +1,314 @@ +// 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/buildtool/main/diagnose.hpp" + +#include "nlohmann/json.hpp" +#include "src/utils/cpp/json.hpp" + +namespace { + +namespace Base = BuildMaps::Base; +namespace Target = BuildMaps::Target; + +[[nodiscard]] auto ResultToJson(TargetResult const& result) -> nlohmann::json { + return nlohmann::ordered_json{ + {"artifacts", + result.artifact_stage->ToJson( + Expression::JsonMode::SerializeAllButNodes)}, + {"runfiles", + result.runfiles->ToJson(Expression::JsonMode::SerializeAllButNodes)}, + {"provides", + result.provides->ToJson(Expression::JsonMode::SerializeAllButNodes)}}; +} + +[[nodiscard]] auto TargetActionsToJson(AnalysedTargetPtr const& target) + -> nlohmann::json { + auto actions = nlohmann::json::array(); + std::for_each(target->Actions().begin(), + target->Actions().end(), + [&actions](auto const& action) { + actions.push_back(action->ToJson()); + }); + return actions; +} + +[[nodiscard]] auto TreesToJson(AnalysedTargetPtr const& target) + -> nlohmann::json { + auto trees = nlohmann::json::object(); + std::for_each( + target->Trees().begin(), + target->Trees().end(), + [&trees](auto const& tree) { trees[tree->Id()] = tree->ToJson(); }); + + return trees; +} + +void DumpActions(std::string const& file_path, AnalysisResult const& result) { + auto const dump_string = + IndentListsOnlyUntilDepth(TargetActionsToJson(result.target), 2, 1); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "Actions for target {}:", result.id.ToString()); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping actions for target {} to file '{}'.", + result.id.ToString(), + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +void DumpBlobs(std::string const& file_path, AnalysisResult const& result) { + auto blobs = nlohmann::json::array(); + for (auto const& s : result.target->Blobs()) { + blobs.push_back(s); + } + auto const dump_string = blobs.dump(2); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "Blobs for target {}:", result.id.ToString()); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping blobs for target {} to file '{}'.", + result.id.ToString(), + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +void DumpVars(std::string const& file_path, AnalysisResult const& result) { + auto vars = std::vector<std::string>{}; + vars.reserve(result.target->Vars().size()); + for (auto const& x : result.target->Vars()) { + vars.push_back(x); + } + std::sort(vars.begin(), vars.end()); + auto const dump_string = nlohmann::json(vars).dump(); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "Variables for target {}:", result.id.ToString()); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping varables for target {} to file '{}'.", + result.id.ToString(), + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +void DumpTrees(std::string const& file_path, AnalysisResult const& result) { + auto const dump_string = TreesToJson(result.target).dump(2); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "Trees for target {}:", result.id.ToString()); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping trees for target {} to file '{}'.", + result.id.ToString(), + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +void DumpTargets(std::string const& file_path, + std::vector<Target::ConfiguredTarget> const& target_ids, + std::string const& target_qualifier = "") { + auto repo_map = nlohmann::json::object(); + auto conf_list = + [&repo_map](Base::EntityName const& ref) -> nlohmann::json& { + if (ref.IsAnonymousTarget()) { + auto const& anon = ref.GetAnonymousTarget(); + auto& anon_map = repo_map[Base::EntityName::kAnonymousMarker]; + auto& rule_map = anon_map[anon.rule_map.ToIdentifier()]; + return rule_map[anon.target_node.ToIdentifier()]; + } + auto const& named = ref.GetNamedTarget(); + auto& location_map = repo_map[Base::EntityName::kLocationMarker]; + auto& module_map = location_map[named.repository]; + auto& target_map = module_map[named.module]; + return target_map[named.name]; + }; + std::for_each( + target_ids.begin(), target_ids.end(), [&conf_list](auto const& id) { + if ((not id.target.IsNamedTarget()) or + id.target.GetNamedTarget().reference_t == + BuildMaps::Base::ReferenceType::kTarget) { + conf_list(id.target).push_back(id.config.ToJson()); + } + }); + auto const dump_string = IndentListsOnlyUntilDepth(repo_map, 2); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "List of analysed {}targets:", target_qualifier); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping list of analysed {}targets to file '{}'.", + target_qualifier, + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +auto DumpExpressionToMap(gsl::not_null<nlohmann::json*> const& map, + ExpressionPtr const& expr) -> bool { + auto const& id = expr->ToIdentifier(); + if (not map->contains(id)) { + (*map)[id] = expr->ToJson(); + return true; + } + return false; +} + +// NOLINTNEXTLINE(misc-no-recursion) +void DumpNodesInExpressionToMap(gsl::not_null<nlohmann::json*> const& map, + ExpressionPtr const& expr) { + if (expr->IsNode()) { + if (DumpExpressionToMap(map, expr)) { + auto const& node = expr->Node(); + if (node.IsAbstract()) { + DumpNodesInExpressionToMap(map, + node.GetAbstract().target_fields); + } + else if (node.IsValue()) { + DumpNodesInExpressionToMap(map, node.GetValue()); + } + } + } + else if (expr->IsList()) { + for (auto const& entry : expr->List()) { + DumpNodesInExpressionToMap(map, entry); + } + } + else if (expr->IsMap()) { + for (auto const& [_, value] : expr->Map()) { + DumpNodesInExpressionToMap(map, value); + } + } + else if (expr->IsResult()) { + DumpNodesInExpressionToMap(map, expr->Result().provides); + } +} + +void DumpAnonymous(std::string const& file_path, + std::vector<Target::ConfiguredTarget> const& target_ids) { + auto anon_map = nlohmann::json{{"nodes", nlohmann::json::object()}, + {"rule_maps", nlohmann::json::object()}}; + std::for_each( + target_ids.begin(), target_ids.end(), [&anon_map](auto const& id) { + if (id.target.IsAnonymousTarget()) { + auto const& anon_t = id.target.GetAnonymousTarget(); + DumpExpressionToMap(&anon_map["rule_maps"], anon_t.rule_map); + DumpNodesInExpressionToMap(&anon_map["nodes"], + anon_t.target_node); + } + }); + auto const dump_string = IndentListsOnlyUntilDepth(anon_map, 2); + if (file_path == "-") { + Logger::Log(LogLevel::Info, "List of anonymous target data:"); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping list of anonymous target data to file '{}'.", + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} + +void DumpNodes(std::string const& file_path, AnalysisResult const& result) { + auto node_map = nlohmann::json::object(); + DumpNodesInExpressionToMap(&node_map, result.target->Provides()); + auto const dump_string = IndentListsOnlyUntilDepth(node_map, 2); + if (file_path == "-") { + Logger::Log( + LogLevel::Info, "Target nodes of target {}:", result.id.ToString()); + std::cout << dump_string << std::endl; + } + else { + Logger::Log(LogLevel::Info, + "Dumping target nodes of target {} to file '{}'.", + result.id.ToString(), + file_path); + std::ofstream os(file_path); + os << dump_string << std::endl; + } +} +} // namespace + +void DiagnoseResults(AnalysisResult const& result, + BuildMaps::Target::ResultTargetMap const& result_map, + DiagnosticArguments const& clargs) { + Logger::Log( + LogLevel::Info, + "Result of{} target {}: {}", + result.modified + ? fmt::format(" input of action {} of", *result.modified) + : "", + result.id.ToString(), + IndentOnlyUntilDepth( + ResultToJson(result.target->Result()), + 2, + 2, + std::unordered_map<std::string, std::size_t>{{"/provides", 3}})); + if (clargs.dump_actions) { + DumpActions(*clargs.dump_actions, result); + } + if (clargs.dump_blobs) { + DumpBlobs(*clargs.dump_blobs, result); + } + if (clargs.dump_trees) { + DumpTrees(*clargs.dump_trees, result); + } + if (clargs.dump_vars) { + DumpVars(*clargs.dump_vars, result); + } + if (clargs.dump_targets) { + DumpTargets(*clargs.dump_targets, result_map.ConfiguredTargets()); + } + if (clargs.dump_export_targets) { + DumpTargets( + *clargs.dump_export_targets, result_map.ExportTargets(), "export "); + } + if (clargs.dump_targets_graph) { + auto graph = result_map.ConfiguredTargetsGraph().dump(2); + Logger::Log(LogLevel::Info, + "Dumping graph of configured-targets to file {}.", + *clargs.dump_targets_graph); + std::ofstream os(*clargs.dump_targets_graph); + os << graph << std::endl; + } + if (clargs.dump_anonymous) { + DumpAnonymous(*clargs.dump_anonymous, result_map.ConfiguredTargets()); + } + if (clargs.dump_nodes) { + DumpNodes(*clargs.dump_nodes, result); + } +} diff --git a/src/buildtool/main/diagnose.hpp b/src/buildtool/main/diagnose.hpp new file mode 100644 index 00000000..79a150b8 --- /dev/null +++ b/src/buildtool/main/diagnose.hpp @@ -0,0 +1,25 @@ +// 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. + +#ifndef INCLUDED_SRC_BUILDTOOL_MAIN_DIAGNOSE_HPP +#define INCLUDED_SRC_BUILDTOOL_MAIN_DIAGNOSE_HPP + +#include "src/buildtool/build_engine/target_map/result_map.hpp" +#include "src/buildtool/common/cli.hpp" +#include "src/buildtool/main/analyse.hpp" + +void DiagnoseResults(AnalysisResult const& result, + BuildMaps::Target::ResultTargetMap const& result_map, + DiagnosticArguments const& clargs); +#endif diff --git a/src/buildtool/main/main.cpp b/src/buildtool/main/main.cpp index 8d2d4d2e..e362b7ef 100644 --- a/src/buildtool/main/main.cpp +++ b/src/buildtool/main/main.cpp @@ -26,13 +26,14 @@ #include "src/buildtool/build_engine/expression/expression.hpp" #include "src/buildtool/build_engine/target_map/target_map.hpp" #include "src/buildtool/common/artifact_description.hpp" -#include "src/buildtool/common/cli.hpp" #include "src/buildtool/common/repository_config.hpp" #include "src/buildtool/compatibility/compatibility.hpp" #include "src/buildtool/execution_api/local/config.hpp" #include "src/buildtool/main/analyse.hpp" +#include "src/buildtool/main/cli.hpp" #include "src/buildtool/main/constants.hpp" #include "src/buildtool/main/describe.hpp" +#include "src/buildtool/main/diagnose.hpp" #include "src/buildtool/main/exit_codes.hpp" #include "src/buildtool/main/install_cas.hpp" #include "src/buildtool/storage/config.hpp" @@ -63,226 +64,6 @@ namespace { namespace Base = BuildMaps::Base; namespace Target = BuildMaps::Target; -enum class SubCommand { - kUnknown, - kVersion, - kDescribe, - kAnalyse, - kBuild, - kInstall, - kRebuild, - kInstallCas, - kTraverse, - kGc, - kExecute -}; - -struct CommandLineArguments { - SubCommand cmd{SubCommand::kUnknown}; - CommonArguments common; - LogArguments log; - AnalysisArguments analysis; - DescribeArguments describe; - DiagnosticArguments diagnose; - EndpointArguments endpoint; - BuildArguments build; - StageArguments stage; - RebuildArguments rebuild; - FetchArguments fetch; - GraphArguments graph; - CommonAuthArguments auth; - ClientAuthArguments cauth; - ServerAuthArguments sauth; - ExecutionServiceArguments es; -}; - -/// \brief Setup arguments for sub command "just describe". -auto SetupDescribeCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCommonArguments(app, &clargs->common); - SetupAnalysisArguments(app, &clargs->analysis, false); - SetupLogArguments(app, &clargs->log); - SetupDescribeArguments(app, &clargs->describe); -} - -/// \brief Setup arguments for sub command "just analyse". -auto SetupAnalyseCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCommonArguments(app, &clargs->common); - SetupLogArguments(app, &clargs->log); - SetupAnalysisArguments(app, &clargs->analysis); - SetupCacheArguments(app, &clargs->endpoint); - SetupEndpointArguments(app, &clargs->endpoint); - SetupDiagnosticArguments(app, &clargs->diagnose); - SetupCompatibilityArguments(app); -} - -/// \brief Setup arguments for sub command "just build". -auto SetupBuildCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCommonArguments(app, &clargs->common); - SetupLogArguments(app, &clargs->log); - SetupAnalysisArguments(app, &clargs->analysis); - SetupCacheArguments(app, &clargs->endpoint); - SetupEndpointArguments(app, &clargs->endpoint); - SetupCommonAuthArguments(app, &clargs->auth); - SetupClientAuthArguments(app, &clargs->cauth); - SetupCommonBuildArguments(app, &clargs->build); - SetupBuildArguments(app, &clargs->build); - SetupCompatibilityArguments(app); -} - -/// \brief Setup arguments for sub command "just install". -auto SetupInstallCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupBuildCommandArguments(app, clargs); // same as build - SetupStageArguments(app, &clargs->stage); // plus stage -} - -/// \brief Setup arguments for sub command "just rebuild". -auto SetupRebuildCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupBuildCommandArguments(app, clargs); // same as build - SetupRebuildArguments(app, &clargs->rebuild); // plus rebuild -} - -/// \brief Setup arguments for sub command "just install-cas". -auto SetupInstallCasCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCompatibilityArguments(app); - SetupCacheArguments(app, &clargs->endpoint); - SetupEndpointArguments(app, &clargs->endpoint); - SetupCommonAuthArguments(app, &clargs->auth); - SetupClientAuthArguments(app, &clargs->cauth); - SetupFetchArguments(app, &clargs->fetch); - SetupLogArguments(app, &clargs->log); -} - -/// \brief Setup arguments for sub command "just traverse". -auto SetupTraverseCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCommonArguments(app, &clargs->common); - SetupLogArguments(app, &clargs->log); - SetupCacheArguments(app, &clargs->endpoint); - SetupEndpointArguments(app, &clargs->endpoint); - SetupCommonAuthArguments(app, &clargs->auth); - SetupClientAuthArguments(app, &clargs->cauth); - SetupGraphArguments(app, &clargs->graph); // instead of analysis - SetupCommonBuildArguments(app, &clargs->build); - SetupBuildArguments(app, &clargs->build); - SetupStageArguments(app, &clargs->stage); - SetupCompatibilityArguments(app); -} - -/// \brief Setup arguments for sub command "just gc". -auto SetupGcCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupLogArguments(app, &clargs->log); - SetupCacheArguments(app, &clargs->endpoint); -} - -/// \brief Setup arguments for sub command "just execute". -auto SetupExecutionServiceCommandArguments( - gsl::not_null<CLI::App*> const& app, - gsl::not_null<CommandLineArguments*> const& clargs) { - SetupCompatibilityArguments(app); - SetupCommonBuildArguments(app, &clargs->build); - SetupCacheArguments(app, &clargs->endpoint); - SetupExecutionServiceArguments(app, &clargs->es); - SetupLogArguments(app, &clargs->log); - SetupCommonAuthArguments(app, &clargs->auth); - SetupServerAuthArguments(app, &clargs->sauth); -} - -auto ParseCommandLineArguments(int argc, char const* const* argv) - -> CommandLineArguments { - CLI::App app("just, a generic build tool"); - app.option_defaults()->take_last(); - - auto* cmd_version = app.add_subcommand( - "version", "Print version information in JSON format."); - auto* cmd_describe = app.add_subcommand( - "describe", "Describe the rule generating a target."); - auto* cmd_analyse = - app.add_subcommand("analyse", "Analyse specified targets."); - auto* cmd_build = app.add_subcommand("build", "Build specified targets."); - auto* cmd_install = - app.add_subcommand("install", "Build and stage specified targets."); - auto* cmd_rebuild = app.add_subcommand( - "rebuild", "Rebuild and compare artifacts to cached build."); - auto* cmd_install_cas = - app.add_subcommand("install-cas", "Fetch and stage artifact from CAS."); - auto* cmd_gc = - app.add_subcommand("gc", "Trigger garbage collection of local cache."); - auto* cmd_execution = app.add_subcommand( - "execute", "Start single node execution service on this machine."); - auto* cmd_traverse = - app.group("") // group for creating hidden options - ->add_subcommand("traverse", - "Build and stage artifacts from graph file."); - app.require_subcommand(1); - - CommandLineArguments clargs; - SetupDescribeCommandArguments(cmd_describe, &clargs); - SetupAnalyseCommandArguments(cmd_analyse, &clargs); - SetupBuildCommandArguments(cmd_build, &clargs); - SetupInstallCommandArguments(cmd_install, &clargs); - SetupRebuildCommandArguments(cmd_rebuild, &clargs); - SetupInstallCasCommandArguments(cmd_install_cas, &clargs); - SetupTraverseCommandArguments(cmd_traverse, &clargs); - SetupGcCommandArguments(cmd_gc, &clargs); - SetupExecutionServiceCommandArguments(cmd_execution, &clargs); - try { - app.parse(argc, argv); - } catch (CLI::Error& e) { - std::exit(app.exit(e)); - } catch (std::exception const& ex) { - Logger::Log(LogLevel::Error, "Command line parse error: {}", ex.what()); - std::exit(kExitFailure); - } - - if (*cmd_version) { - clargs.cmd = SubCommand::kVersion; - } - else if (*cmd_describe) { - clargs.cmd = SubCommand::kDescribe; - } - else if (*cmd_analyse) { - clargs.cmd = SubCommand::kAnalyse; - } - else if (*cmd_build) { - clargs.cmd = SubCommand::kBuild; - } - else if (*cmd_install) { - clargs.cmd = SubCommand::kInstall; - } - else if (*cmd_rebuild) { - clargs.cmd = SubCommand::kRebuild; - } - else if (*cmd_install_cas) { - clargs.cmd = SubCommand::kInstallCas; - } - else if (*cmd_traverse) { - clargs.cmd = SubCommand::kTraverse; - } - else if (*cmd_gc) { - clargs.cmd = SubCommand::kGc; - } - else if (*cmd_execution) { - clargs.cmd = SubCommand::kExecute; - } - - return clargs; -} - void SetupDefaultLogging() { LogConfig::SetLogLimit(kDefaultLogLevel); LogConfig::SetSinks({LogSinkCmdLine::CreateFactory()}); @@ -911,296 +692,6 @@ auto DetermineRoots(CommonArguments const& cargs, return {main_repo, main_ws_root}; } -[[nodiscard]] auto ResultToJson(TargetResult const& result) -> nlohmann::json { - return nlohmann::ordered_json{ - {"artifacts", - result.artifact_stage->ToJson( - Expression::JsonMode::SerializeAllButNodes)}, - {"runfiles", - result.runfiles->ToJson(Expression::JsonMode::SerializeAllButNodes)}, - {"provides", - result.provides->ToJson(Expression::JsonMode::SerializeAllButNodes)}}; -} - -[[nodiscard]] auto TargetActionsToJson(AnalysedTargetPtr const& target) - -> nlohmann::json { - auto actions = nlohmann::json::array(); - std::for_each(target->Actions().begin(), - target->Actions().end(), - [&actions](auto const& action) { - actions.push_back(action->ToJson()); - }); - return actions; -} - -[[nodiscard]] auto TreesToJson(AnalysedTargetPtr const& target) - -> nlohmann::json { - auto trees = nlohmann::json::object(); - std::for_each( - target->Trees().begin(), - target->Trees().end(), - [&trees](auto const& tree) { trees[tree->Id()] = tree->ToJson(); }); - - return trees; -} - -void DumpActions(std::string const& file_path, AnalysisResult const& result) { - auto const dump_string = - IndentListsOnlyUntilDepth(TargetActionsToJson(result.target), 2, 1); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "Actions for target {}:", result.id.ToString()); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping actions for target {} to file '{}'.", - result.id.ToString(), - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -void DumpBlobs(std::string const& file_path, AnalysisResult const& result) { - auto blobs = nlohmann::json::array(); - for (auto const& s : result.target->Blobs()) { - blobs.push_back(s); - } - auto const dump_string = blobs.dump(2); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "Blobs for target {}:", result.id.ToString()); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping blobs for target {} to file '{}'.", - result.id.ToString(), - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -void DumpVars(std::string const& file_path, AnalysisResult const& result) { - auto vars = std::vector<std::string>{}; - vars.reserve(result.target->Vars().size()); - for (auto const& x : result.target->Vars()) { - vars.push_back(x); - } - std::sort(vars.begin(), vars.end()); - auto const dump_string = nlohmann::json(vars).dump(); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "Variables for target {}:", result.id.ToString()); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping varables for target {} to file '{}'.", - result.id.ToString(), - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -void DumpTrees(std::string const& file_path, AnalysisResult const& result) { - auto const dump_string = TreesToJson(result.target).dump(2); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "Trees for target {}:", result.id.ToString()); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping trees for target {} to file '{}'.", - result.id.ToString(), - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -void DumpTargets(std::string const& file_path, - std::vector<Target::ConfiguredTarget> const& target_ids, - std::string const& target_qualifier = "") { - auto repo_map = nlohmann::json::object(); - auto conf_list = - [&repo_map](Base::EntityName const& ref) -> nlohmann::json& { - if (ref.IsAnonymousTarget()) { - auto const& anon = ref.GetAnonymousTarget(); - auto& anon_map = repo_map[Base::EntityName::kAnonymousMarker]; - auto& rule_map = anon_map[anon.rule_map.ToIdentifier()]; - return rule_map[anon.target_node.ToIdentifier()]; - } - auto const& named = ref.GetNamedTarget(); - auto& location_map = repo_map[Base::EntityName::kLocationMarker]; - auto& module_map = location_map[named.repository]; - auto& target_map = module_map[named.module]; - return target_map[named.name]; - }; - std::for_each( - target_ids.begin(), target_ids.end(), [&conf_list](auto const& id) { - if ((not id.target.IsNamedTarget()) or - id.target.GetNamedTarget().reference_t == - BuildMaps::Base::ReferenceType::kTarget) { - conf_list(id.target).push_back(id.config.ToJson()); - } - }); - auto const dump_string = IndentListsOnlyUntilDepth(repo_map, 2); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "List of analysed {}targets:", target_qualifier); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping list of analysed {}targets to file '{}'.", - target_qualifier, - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -auto DumpExpressionToMap(gsl::not_null<nlohmann::json*> const& map, - ExpressionPtr const& expr) -> bool { - auto const& id = expr->ToIdentifier(); - if (not map->contains(id)) { - (*map)[id] = expr->ToJson(); - return true; - } - return false; -} - -// NOLINTNEXTLINE(misc-no-recursion) -void DumpNodesInExpressionToMap(gsl::not_null<nlohmann::json*> const& map, - ExpressionPtr const& expr) { - if (expr->IsNode()) { - if (DumpExpressionToMap(map, expr)) { - auto const& node = expr->Node(); - if (node.IsAbstract()) { - DumpNodesInExpressionToMap(map, - node.GetAbstract().target_fields); - } - else if (node.IsValue()) { - DumpNodesInExpressionToMap(map, node.GetValue()); - } - } - } - else if (expr->IsList()) { - for (auto const& entry : expr->List()) { - DumpNodesInExpressionToMap(map, entry); - } - } - else if (expr->IsMap()) { - for (auto const& [_, value] : expr->Map()) { - DumpNodesInExpressionToMap(map, value); - } - } - else if (expr->IsResult()) { - DumpNodesInExpressionToMap(map, expr->Result().provides); - } -} - -void DumpAnonymous(std::string const& file_path, - std::vector<Target::ConfiguredTarget> const& target_ids) { - auto anon_map = nlohmann::json{{"nodes", nlohmann::json::object()}, - {"rule_maps", nlohmann::json::object()}}; - std::for_each( - target_ids.begin(), target_ids.end(), [&anon_map](auto const& id) { - if (id.target.IsAnonymousTarget()) { - auto const& anon_t = id.target.GetAnonymousTarget(); - DumpExpressionToMap(&anon_map["rule_maps"], anon_t.rule_map); - DumpNodesInExpressionToMap(&anon_map["nodes"], - anon_t.target_node); - } - }); - auto const dump_string = IndentListsOnlyUntilDepth(anon_map, 2); - if (file_path == "-") { - Logger::Log(LogLevel::Info, "List of anonymous target data:"); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping list of anonymous target data to file '{}'.", - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -void DumpNodes(std::string const& file_path, AnalysisResult const& result) { - auto node_map = nlohmann::json::object(); - DumpNodesInExpressionToMap(&node_map, result.target->Provides()); - auto const dump_string = IndentListsOnlyUntilDepth(node_map, 2); - if (file_path == "-") { - Logger::Log( - LogLevel::Info, "Target nodes of target {}:", result.id.ToString()); - std::cout << dump_string << std::endl; - } - else { - Logger::Log(LogLevel::Info, - "Dumping target nodes of target {} to file '{}'.", - result.id.ToString(), - file_path); - std::ofstream os(file_path); - os << dump_string << std::endl; - } -} - -[[nodiscard]] auto DiagnoseResults(AnalysisResult const& result, - Target::ResultTargetMap const& result_map, - DiagnosticArguments const& clargs) { - Logger::Log( - LogLevel::Info, - "Result of{} target {}: {}", - result.modified - ? fmt::format(" input of action {} of", *result.modified) - : "", - result.id.ToString(), - IndentOnlyUntilDepth( - ResultToJson(result.target->Result()), - 2, - 2, - std::unordered_map<std::string, std::size_t>{{"/provides", 3}})); - if (clargs.dump_actions) { - DumpActions(*clargs.dump_actions, result); - } - if (clargs.dump_blobs) { - DumpBlobs(*clargs.dump_blobs, result); - } - if (clargs.dump_trees) { - DumpTrees(*clargs.dump_trees, result); - } - if (clargs.dump_vars) { - DumpVars(*clargs.dump_vars, result); - } - if (clargs.dump_targets) { - DumpTargets(*clargs.dump_targets, result_map.ConfiguredTargets()); - } - if (clargs.dump_export_targets) { - DumpTargets( - *clargs.dump_export_targets, result_map.ExportTargets(), "export "); - } - if (clargs.dump_targets_graph) { - auto graph = result_map.ConfiguredTargetsGraph().dump(2); - Logger::Log(LogLevel::Info, - "Dumping graph of configured-targets to file {}.", - *clargs.dump_targets_graph); - std::ofstream os(*clargs.dump_targets_graph); - os << graph << std::endl; - } - if (clargs.dump_anonymous) { - DumpAnonymous(*clargs.dump_anonymous, result_map.ConfiguredTargets()); - } - if (clargs.dump_nodes) { - DumpNodes(*clargs.dump_nodes, result); - } -} - // Return disjoint maps for artifacts and runfiles [[nodiscard]] auto ReadOutputArtifacts(AnalysedTargetPtr const& target) -> std::pair<std::map<std::string, ArtifactDescription>, |