diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildtool/main/TARGETS | 28 | ||||
-rw-r--r-- | src/buildtool/main/analyse.cpp | 238 | ||||
-rw-r--r-- | src/buildtool/main/analyse.hpp | 20 | ||||
-rw-r--r-- | src/buildtool/main/main.cpp | 243 |
4 files changed, 286 insertions, 243 deletions
diff --git a/src/buildtool/main/TARGETS b/src/buildtool/main/TARGETS index 2ae81d86..cd26fbc3 100644 --- a/src/buildtool/main/TARGETS +++ b/src/buildtool/main/TARGETS @@ -10,16 +10,14 @@ , ["src/buildtool/graph_traverser", "graph_traverser"] , ["src/buildtool/logging", "logging"] , ["src/buildtool/progress_reporting", "base_progress_reporter"] - , ["src/buildtool/build_engine/base_maps", "directory_map"] - , ["src/buildtool/build_engine/base_maps", "rule_map"] - , ["src/buildtool/build_engine/base_maps", "source_map"] - , ["src/buildtool/build_engine/base_maps", "targets_file_map"] - , ["src/buildtool/build_engine/target_map", "target_map"] + , ["src/buildtool/build_engine/target_map", "result_map"] , ["src/buildtool/build_engine/target_map", "target_cache"] + , ["src/buildtool/multithreading", "task_system"] , ["src/utils/cpp", "concepts"] , ["src/utils/cpp", "json"] , "common" , "version" + , "analyse" , "install_cas" , "describe" ] @@ -70,6 +68,26 @@ ] , "stage": ["src", "buildtool", "main"] } +, "analyse": + { "type": ["@", "rules", "CC", "library"] + , "name": ["analyse"] + , "hdrs": ["analyse.hpp"] + , "srcs": ["analyse.cpp"] + , "deps": + [ ["src/buildtool/common", "cli"] + , ["src/buildtool/multithreading", "async_map_consumer"] + , ["src/buildtool/multithreading", "task_system"] + , ["src/buildtool/build_engine/target_map", "configured_target"] + , ["src/buildtool/build_engine/base_maps", "directory_map"] + , ["src/buildtool/build_engine/base_maps", "rule_map"] + , ["src/buildtool/build_engine/base_maps", "source_map"] + , ["src/buildtool/build_engine/base_maps", "targets_file_map"] + , ["src/buildtool/build_engine/target_map", "target_map"] + , ["src/buildtool/build_engine/target_map", "result_map"] + , ["src/buildtool/build_engine/analysed_target", "target"] + ] + , "stage": ["src", "buildtool", "main"] + } , "version": { "type": ["@", "rules", "CC", "library"] , "arguments_config": ["SOURCE_DATE_EPOCH", "VERSION_EXTRA_SUFFIX"] diff --git a/src/buildtool/main/analyse.cpp b/src/buildtool/main/analyse.cpp new file mode 100644 index 00000000..f4e941b4 --- /dev/null +++ b/src/buildtool/main/analyse.cpp @@ -0,0 +1,238 @@ +#include "src/buildtool/main/analyse.hpp" + +#include "src/buildtool/build_engine/base_maps/directory_map.hpp" +#include "src/buildtool/build_engine/base_maps/entity_name.hpp" +#include "src/buildtool/build_engine/base_maps/expression_map.hpp" +#include "src/buildtool/build_engine/base_maps/rule_map.hpp" +#include "src/buildtool/build_engine/base_maps/source_map.hpp" +#include "src/buildtool/build_engine/base_maps/targets_file_map.hpp" +#include "src/buildtool/build_engine/target_map/target_map.hpp" +#include "src/buildtool/multithreading/async_map_consumer.hpp" +#include "src/buildtool/multithreading/task_system.hpp" + +namespace { + +namespace Base = BuildMaps::Base; +namespace Target = BuildMaps::Target; + +template <HasToString K, typename V> +[[nodiscard]] auto DetectAndReportCycle(std::string const& name, + AsyncMapConsumer<K, V> const& map) + -> bool { + using namespace std::string_literals; + auto cycle = map.DetectCycle(); + if (cycle) { + bool found{false}; + std::ostringstream oss{}; + oss << fmt::format("Cycle detected in {}:", name) << std::endl; + for (auto const& k : *cycle) { + auto match = (k == cycle->back()); + auto prefix{match ? found ? "`-- "s : ".-> "s + : found ? "| "s + : " "s}; + oss << prefix << k.ToString() << std::endl; + found = found or match; + } + Logger::Log(LogLevel::Error, "{}", oss.str()); + return true; + } + return false; +} + +template <HasToString K, typename V> +void DetectAndReportPending(std::string const& name, + AsyncMapConsumer<K, V> const& map) { + using namespace std::string_literals; + auto keys = map.GetPendingKeys(); + if (not keys.empty()) { + std::ostringstream oss{}; + oss << fmt::format("Internal error, failed to evaluate pending {}:", + name) + << std::endl; + for (auto const& k : keys) { + oss << " " << k.ToString() << std::endl; + } + Logger::Log(LogLevel::Error, "{}", oss.str()); + } +} + +[[nodiscard]] auto GetActionNumber(const AnalysedTarget& target, int number) + -> std::optional<ActionDescription::Ptr> { + auto const& actions = target.Actions(); + if (number >= 0) { + if (actions.size() > static_cast<std::size_t>(number)) { + return actions[static_cast<std::size_t>(number)]; + } + } + else { + if (((static_cast<int64_t>(actions.size())) + + static_cast<int64_t>(number)) >= 0) { + return actions[static_cast<std::size_t>( + (static_cast<int64_t>(actions.size())) + + static_cast<int64_t>(number))]; + } + } + return std::nullopt; +} + +[[nodiscard]] auto SwitchToActionInput( + const std::shared_ptr<AnalysedTarget>& target, + const ActionDescription::Ptr& action) -> std::shared_ptr<AnalysedTarget> { + auto inputs = Expression::map_t::underlying_map_t{}; + for (auto const& [k, v] : action->Inputs()) { + inputs[k] = ExpressionPtr{Expression{v}}; + } + auto inputs_exp = ExpressionPtr{Expression::map_t{inputs}}; + auto provides = nlohmann::json::object(); + provides["cmd"] = action->GraphAction().Command(); + provides["env"] = action->GraphAction().Env(); + provides["output"] = action->OutputFiles(); + provides["output_dirs"] = action->OutputDirs(); + if (action->GraphAction().MayFail()) { + provides["may_fail"] = *(action->GraphAction().MayFail()); + } + + auto provides_exp = Expression::FromJson(provides); + return std::make_shared<AnalysedTarget>( + TargetResult{inputs_exp, provides_exp, Expression::kEmptyMap}, + std::vector<ActionDescription::Ptr>{action}, + target->Blobs(), + target->Trees(), + target->Vars(), + target->Tainted()); +} + +} // namespace + +[[nodiscard]] auto AnalyseTarget( + const Target::ConfiguredTarget& id, + gsl::not_null<Target::ResultTargetMap*> const& result_map, + std::size_t jobs, + AnalysisArguments const& clargs) -> std::optional<AnalysisResult> { + auto directory_entries = Base::CreateDirectoryEntriesMap(jobs); + auto expressions_file_map = Base::CreateExpressionFileMap(jobs); + auto rule_file_map = Base::CreateRuleFileMap(jobs); + auto targets_file_map = Base::CreateTargetsFileMap(jobs); + auto expr_map = Base::CreateExpressionMap(&expressions_file_map, jobs); + auto rule_map = Base::CreateRuleMap(&rule_file_map, &expr_map, jobs); + auto source_targets = Base::CreateSourceTargetMap(&directory_entries, jobs); + auto target_map = Target::CreateTargetMap(&source_targets, + &targets_file_map, + &rule_map, + &directory_entries, + result_map, + jobs); + Logger::Log(LogLevel::Info, "Requested target is {}", id.ToString()); + std::shared_ptr<AnalysedTarget> target{}; + + bool failed{false}; + { + TaskSystem ts{jobs}; + target_map.ConsumeAfterKeysReady( + &ts, + {id}, + [&target](auto values) { target = *values[0]; }, + [&failed](auto const& msg, bool fatal) { + Logger::Log(fatal ? LogLevel::Error : LogLevel::Warning, + "While processing targets:\n{}", + msg); + failed = failed or fatal; + }); + } + + if (failed) { + return std::nullopt; + } + + if (not target) { + Logger::Log( + LogLevel::Error, "Failed to analyse target: {}", id.ToString()); + if (not(DetectAndReportCycle("expression imports", expr_map) or + DetectAndReportCycle("target dependencies", target_map))) { + DetectAndReportPending("expressions", expr_map); + DetectAndReportPending("targets", expr_map); + } + return std::nullopt; + } + + // Clean up in parallel what is no longer needed + { + TaskSystem ts{jobs}; + target_map.Clear(&ts); + source_targets.Clear(&ts); + directory_entries.Clear(&ts); + expressions_file_map.Clear(&ts); + rule_file_map.Clear(&ts); + targets_file_map.Clear(&ts); + expr_map.Clear(&ts); + rule_map.Clear(&ts); + } + std::optional<std::string> modified{}; + + if (clargs.request_action_input) { + if (clargs.request_action_input->starts_with("%")) { + auto action_id = clargs.request_action_input->substr(1); + auto action = result_map->GetAction(action_id); + if (action) { + Logger::Log(LogLevel::Info, + "Request is input of action %{}", + action_id); + target = SwitchToActionInput(target, *action); + modified = fmt::format("%{}", action_id); + } + else { + Logger::Log(LogLevel::Error, + "Action {} not part of the action graph of the " + "requested target", + action_id); + return std::nullopt; + } + } + else if (clargs.request_action_input->starts_with("#")) { + auto number = + std::atoi(clargs.request_action_input->substr(1).c_str()); + auto action = GetActionNumber(*target, number); + if (action) { + Logger::Log( + LogLevel::Info, "Request is input of action #{}", number); + target = SwitchToActionInput(target, *action); + modified = fmt::format("#{}", number); + } + else { + Logger::Log(LogLevel::Error, + "Action #{} out of range for the requested target", + number); + return std::nullopt; + } + } + else { + auto action = result_map->GetAction(*clargs.request_action_input); + if (action) { + Logger::Log(LogLevel::Info, + "Request is input of action %{}", + *clargs.request_action_input); + target = SwitchToActionInput(target, *action); + modified = fmt::format("%{}", *clargs.request_action_input); + } + else { + auto number = std::atoi(clargs.request_action_input->c_str()); + auto action = GetActionNumber(*target, number); + if (action) { + Logger::Log(LogLevel::Info, + "Request is input of action #{}", + number); + target = SwitchToActionInput(target, *action); + modified = fmt::format("#{}", number); + } + else { + Logger::Log( + LogLevel::Error, + "Action #{} out of range for the requested target", + number); + return std::nullopt; + } + } + } + } + return AnalysisResult{id, target, modified}; +} diff --git a/src/buildtool/main/analyse.hpp b/src/buildtool/main/analyse.hpp new file mode 100644 index 00000000..5f6b5609 --- /dev/null +++ b/src/buildtool/main/analyse.hpp @@ -0,0 +1,20 @@ +#ifndef INCLUDED_SRC_BUILDOOL_MAIN_ANALYSE_HPP +#define INCLUDED_SRC_BUILDOOL_MAIN_ANALYSE_HPP + +#include "src/buildtool/build_engine/analysed_target/analysed_target.hpp" +#include "src/buildtool/build_engine/target_map/configured_target.hpp" +#include "src/buildtool/build_engine/target_map/result_map.hpp" +#include "src/buildtool/common/cli.hpp" + +struct AnalysisResult { + BuildMaps::Target::ConfiguredTarget id; + AnalysedTargetPtr target; + std::optional<std::string> modified; +}; + +[[nodiscard]] auto AnalyseTarget( + const BuildMaps::Target::ConfiguredTarget& id, + gsl::not_null<BuildMaps::Target::ResultTargetMap*> const& result_map, + std::size_t jobs, + AnalysisArguments const& clargs) -> std::optional<AnalysisResult>; +#endif diff --git a/src/buildtool/main/main.cpp b/src/buildtool/main/main.cpp index 7b432c16..c1a7c4ef 100644 --- a/src/buildtool/main/main.cpp +++ b/src/buildtool/main/main.cpp @@ -7,12 +7,7 @@ #include <unordered_map> #include <unordered_set> -#include "src/buildtool/build_engine/base_maps/directory_map.hpp" #include "src/buildtool/build_engine/base_maps/entity_name.hpp" -#include "src/buildtool/build_engine/base_maps/expression_map.hpp" -#include "src/buildtool/build_engine/base_maps/rule_map.hpp" -#include "src/buildtool/build_engine/base_maps/source_map.hpp" -#include "src/buildtool/build_engine/base_maps/targets_file_map.hpp" #include "src/buildtool/build_engine/expression/evaluator.hpp" #include "src/buildtool/build_engine/expression/expression.hpp" #include "src/buildtool/build_engine/target_map/target_cache.hpp" @@ -21,6 +16,7 @@ #include "src/buildtool/common/cli.hpp" #include "src/buildtool/common/repository_config.hpp" #include "src/buildtool/compatibility/compatibility.hpp" +#include "src/buildtool/main/analyse.hpp" #include "src/buildtool/main/describe.hpp" #include "src/buildtool/main/exit_codes.hpp" #include "src/buildtool/main/install_cas.hpp" @@ -428,47 +424,6 @@ void SetupHashFunction() { std::move(config)}; } -template <HasToString K, typename V> -[[nodiscard]] auto DetectAndReportCycle(std::string const& name, - AsyncMapConsumer<K, V> const& map) - -> bool { - using namespace std::string_literals; - auto cycle = map.DetectCycle(); - if (cycle) { - bool found{false}; - std::ostringstream oss{}; - oss << fmt::format("Cycle detected in {}:", name) << std::endl; - for (auto const& k : *cycle) { - auto match = (k == cycle->back()); - auto prefix{match ? found ? "`-- "s : ".-> "s - : found ? "| "s - : " "s}; - oss << prefix << k.ToString() << std::endl; - found = found or match; - } - Logger::Log(LogLevel::Error, "{}", oss.str()); - return true; - } - return false; -} - -template <HasToString K, typename V> -void DetectAndReportPending(std::string const& name, - AsyncMapConsumer<K, V> const& map) { - using namespace std::string_literals; - auto keys = map.GetPendingKeys(); - if (not keys.empty()) { - std::ostringstream oss{}; - oss << fmt::format("Internal error, failed to evaluate pending {}:", - name) - << std::endl; - for (auto const& k : keys) { - oss << " " << k.ToString() << std::endl; - } - Logger::Log(LogLevel::Error, "{}", oss.str()); - } -} - [[nodiscard]] auto DetermineWorkspaceRootByLookingForMarkers() -> std::filesystem::path { auto cwd = std::filesystem::current_path(); @@ -703,193 +658,6 @@ auto DetermineRoots(CommonArguments const& cargs, return {main_repo, main_ws_root}; } -struct AnalysisResult { - Target::ConfiguredTarget id; - AnalysedTargetPtr target; - std::optional<std::string> modified; -}; - -[[nodiscard]] auto GetActionNumber(const AnalysedTarget& target, int number) - -> std::optional<ActionDescription::Ptr> { - auto const& actions = target.Actions(); - if (number >= 0) { - if (actions.size() > static_cast<std::size_t>(number)) { - return actions[static_cast<std::size_t>(number)]; - } - } - else { - if (((static_cast<int64_t>(actions.size())) + - static_cast<int64_t>(number)) >= 0) { - return actions[static_cast<std::size_t>( - (static_cast<int64_t>(actions.size())) + - static_cast<int64_t>(number))]; - } - } - return std::nullopt; -} - -[[nodiscard]] auto SwitchToActionInput( - const std::shared_ptr<AnalysedTarget>& target, - const ActionDescription::Ptr& action) -> std::shared_ptr<AnalysedTarget> { - auto inputs = Expression::map_t::underlying_map_t{}; - for (auto const& [k, v] : action->Inputs()) { - inputs[k] = ExpressionPtr{Expression{v}}; - } - auto inputs_exp = ExpressionPtr{Expression::map_t{inputs}}; - auto provides = nlohmann::json::object(); - provides["cmd"] = action->GraphAction().Command(); - provides["env"] = action->GraphAction().Env(); - provides["output"] = action->OutputFiles(); - provides["output_dirs"] = action->OutputDirs(); - if (action->GraphAction().MayFail()) { - provides["may_fail"] = *(action->GraphAction().MayFail()); - } - - auto provides_exp = Expression::FromJson(provides); - return std::make_shared<AnalysedTarget>( - TargetResult{inputs_exp, provides_exp, Expression::kEmptyMap}, - std::vector<ActionDescription::Ptr>{action}, - target->Blobs(), - target->Trees(), - target->Vars(), - target->Tainted()); -} - -[[nodiscard]] auto AnalyseTarget( - gsl::not_null<Target::ResultTargetMap*> const& result_map, - std::string const& main_repo, - std::optional<std::filesystem::path> const& main_ws_root, - std::size_t jobs, - AnalysisArguments const& clargs) -> std::optional<AnalysisResult> { - auto directory_entries = Base::CreateDirectoryEntriesMap(jobs); - auto expressions_file_map = Base::CreateExpressionFileMap(jobs); - auto rule_file_map = Base::CreateRuleFileMap(jobs); - auto targets_file_map = Base::CreateTargetsFileMap(jobs); - auto expr_map = Base::CreateExpressionMap(&expressions_file_map, jobs); - auto rule_map = Base::CreateRuleMap(&rule_file_map, &expr_map, jobs); - auto source_targets = Base::CreateSourceTargetMap(&directory_entries, jobs); - auto target_map = Target::CreateTargetMap(&source_targets, - &targets_file_map, - &rule_map, - &directory_entries, - result_map, - jobs); - auto id = ReadConfiguredTarget(clargs, main_repo, main_ws_root); - Logger::Log(LogLevel::Info, "Requested target is {}", id.ToString()); - std::shared_ptr<AnalysedTarget> target{}; - - bool failed{false}; - { - TaskSystem ts{jobs}; - target_map.ConsumeAfterKeysReady( - &ts, - {id}, - [&target](auto values) { target = *values[0]; }, - [&failed](auto const& msg, bool fatal) { - Logger::Log(fatal ? LogLevel::Error : LogLevel::Warning, - "While processing targets:\n{}", - msg); - failed = failed or fatal; - }); - } - - if (failed) { - return std::nullopt; - } - - if (not target) { - Logger::Log( - LogLevel::Error, "Failed to analyse target: {}", id.ToString()); - if (not(DetectAndReportCycle("expression imports", expr_map) or - DetectAndReportCycle("target dependencies", target_map))) { - DetectAndReportPending("expressions", expr_map); - DetectAndReportPending("targets", expr_map); - } - return std::nullopt; - } - - // Clean up in parallel what is no longer needed - { - TaskSystem ts{jobs}; - target_map.Clear(&ts); - source_targets.Clear(&ts); - directory_entries.Clear(&ts); - expressions_file_map.Clear(&ts); - rule_file_map.Clear(&ts); - targets_file_map.Clear(&ts); - expr_map.Clear(&ts); - rule_map.Clear(&ts); - } - std::optional<std::string> modified{}; - - if (clargs.request_action_input) { - if (clargs.request_action_input->starts_with("%")) { - auto action_id = clargs.request_action_input->substr(1); - auto action = result_map->GetAction(action_id); - if (action) { - Logger::Log(LogLevel::Info, - "Request is input of action %{}", - action_id); - target = SwitchToActionInput(target, *action); - modified = fmt::format("%{}", action_id); - } - else { - Logger::Log(LogLevel::Error, - "Action {} not part of the action graph of the " - "requested target", - action_id); - return std::nullopt; - } - } - else if (clargs.request_action_input->starts_with("#")) { - auto number = - std::atoi(clargs.request_action_input->substr(1).c_str()); - auto action = GetActionNumber(*target, number); - if (action) { - Logger::Log( - LogLevel::Info, "Request is input of action #{}", number); - target = SwitchToActionInput(target, *action); - modified = fmt::format("#{}", number); - } - else { - Logger::Log(LogLevel::Error, - "Action #{} out of range for the requested target", - number); - return std::nullopt; - } - } - else { - auto action = result_map->GetAction(*clargs.request_action_input); - if (action) { - Logger::Log(LogLevel::Info, - "Request is input of action %{}", - *clargs.request_action_input); - target = SwitchToActionInput(target, *action); - modified = fmt::format("%{}", *clargs.request_action_input); - } - else { - auto number = std::atoi(clargs.request_action_input->c_str()); - auto action = GetActionNumber(*target, number); - if (action) { - Logger::Log(LogLevel::Info, - "Request is input of action #{}", - number); - target = SwitchToActionInput(target, *action); - modified = fmt::format("#{}", number); - } - else { - Logger::Log( - LogLevel::Error, - "Action #{} out of range for the requested target", - number); - return std::nullopt; - } - } - } - } - return AnalysisResult{id, target, modified}; -} - [[nodiscard]] auto ResultToJson(TargetResult const& result) -> nlohmann::json { return nlohmann::ordered_json{ {"artifacts", @@ -1336,11 +1104,10 @@ auto main(int argc, char* argv[]) -> int { #endif BuildMaps::Target::ResultTargetMap result_map{ arguments.common.jobs}; - auto result = AnalyseTarget(&result_map, - main_repo, - main_ws_root, - arguments.common.jobs, - arguments.analysis); + auto id = ReadConfiguredTarget( + arguments.analysis, main_repo, main_ws_root); + auto result = AnalyseTarget( + id, &result_map, arguments.common.jobs, arguments.analysis); if (result) { if (arguments.analysis.graph_file) { result_map.ToFile(*arguments.analysis.graph_file); |