summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/main/TARGETS28
-rw-r--r--src/buildtool/main/analyse.cpp238
-rw-r--r--src/buildtool/main/analyse.hpp20
-rw-r--r--src/buildtool/main/main.cpp243
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);