summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/task/task-register.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tiger-compiler/src/task/task-register.cc')
-rw-r--r--tiger-compiler/src/task/task-register.cc353
1 files changed, 353 insertions, 0 deletions
diff --git a/tiger-compiler/src/task/task-register.cc b/tiger-compiler/src/task/task-register.cc
new file mode 100644
index 0000000..d449eed
--- /dev/null
+++ b/tiger-compiler/src/task/task-register.cc
@@ -0,0 +1,353 @@
+/**
+ ** \file task/task-register.cc
+ ** \brief Implementation of task::TaskRegister.
+ **
+ */
+
+#include <algorithm>
+#include <exception>
+#include <filesystem>
+#include <fstream>
+#include <map>
+
+#include <common.hh>
+#include <misc/algorithm.hh>
+#include <task/argument-task.hh>
+#include <task/disjunctive-task.hh>
+#include <task/simple-task.hh>
+#include <task/task-register.hh>
+
+namespace task
+{
+ // Singleton stuff
+ TaskRegister& TaskRegister::instance()
+ {
+ static TaskRegister instance_;
+ return instance_;
+ }
+
+ // Register this task.
+ void TaskRegister::register_task(const SimpleTask& task)
+ {
+ auto it = register_task_(task);
+ // The task was already registered.
+ if (it == modules_.end())
+ return;
+
+ // Callback when the option is parsed.
+ auto cb = [this, &task](bool f) {
+ if (f)
+ enable_task(task.name_get());
+ };
+
+ namespace po = boost::program_options;
+ auto v = po::bool_switch()->notifier(cb);
+
+ it->second.add_options()(task.fullname_get(), v, task.desc_get());
+ }
+
+ void TaskRegister::register_task(const ArgumentTask& task)
+ {
+ auto it = register_task_(task);
+ // The taks was already registered.
+ if (it == modules_.end())
+ return;
+
+ // Callback when the option is parsed.
+ auto cb = [this, &task](const std::string& arg) {
+ task.arg_set(arg);
+ enable_task(task.name_get());
+ };
+
+ namespace po = boost::program_options;
+ auto v = po::value<std::string>()
+ ->value_name(task.argname_get())
+ ->required()
+ ->notifier(cb);
+
+ it->second.add_options()(task.fullname_get(), v, task.desc_get());
+ }
+
+ // Internal task registration.
+ // Add the task to the list of tasks and return an iterrator to the module
+ // the option belongs to. The module is created if needed.
+ // In case something went wrong, the end of `modules_' is returned.
+ TaskRegister::indexed_module_type::iterator
+ TaskRegister::register_task_(const Task& task)
+ {
+ if (task_list_.find(task.name_get()) != task_list_.end())
+ {
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::register_task(" << task.name_get()
+ << "): task name already registered.\n";
+ return modules_.end();
+ }
+ task_list_[task.name_get()] = &task;
+
+ // Short-hands.
+ namespace po = boost::program_options;
+ const std::string& module_name = task.module_name_get();
+ auto it = modules_.find(module_name);
+ if (it == modules_.end())
+ it = modules_.emplace(module_name, po::options_description(module_name))
+ .first;
+ // Return the iterator on the module the task belongs to.
+ return it;
+ }
+
+ // Request the execution of the task task_name.
+ void TaskRegister::enable_task(const std::string& task_name)
+ {
+ if (task_list_.find(task_name) == task_list_.end())
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::enable_task(" << task_name
+ << "): this task has not been registered.\n";
+ else
+ {
+ const Task* task = task_list_.find(task_name)->second;
+ resolve_dependencies(*task);
+ // FIXME: for efficiency, resolve_dependency should be called once.
+ // FIXME: detect cycle.
+ task_order_.emplace_back(task);
+ }
+ }
+
+ // Return the number of tasks to execute.
+ int TaskRegister::nb_of_task_to_execute_get() { return task_order_.size(); }
+
+ // Resolve dependencies between tasks.
+ void TaskRegister::resolve_dependencies(const Task& task)
+ {
+ tasks_list_type enabled_tasks;
+
+ // Retrieved already active tasks.
+ for (const std::string& s : task.dependencies_get())
+ if (task_list_.find(s) == task_list_.end())
+ {
+ task_error() << misc::error::error_type::failure << program_name
+ << ": TaskRegister::resolve_dependencies(\""
+ << task.name_get() << "\"): unknown task: \"" << s << '"'
+ << std::endl;
+ }
+ else
+ {
+ const Task* task_dep = task_list_.find(s)->second;
+ if (misc::has(task_order_, task_dep))
+ enabled_tasks.emplace_back(task_dep);
+ }
+
+ // Ask the task which dependent tasks should be activated.
+ const Task::deps_type dep_tasks = task.resolve_dependencies(enabled_tasks);
+
+ // Activate them.
+ for (const std::string& s : dep_tasks)
+ {
+ if (task_list_.find(s) != task_list_.end())
+ if (!misc::has(task_order_, task_list_.find(s)->second))
+ enable_task(s);
+ }
+ }
+
+ // Check whether one of the options in os has the string_key s.
+ template <typename T>
+ static bool
+ is_parsed(const std::string& s,
+ const std::vector<boost::program_options::basic_option<T>>& os)
+ {
+ using option = boost::program_options::basic_option<T>;
+
+ return std::ranges::find_if(
+ os, [&s](const option& o) { return s == o.string_key; })
+ != end(os);
+ }
+
+ char* TaskRegister::parse_arg(int argc, char* argv[])
+ {
+ // Short-hand.
+ namespace po = boost::program_options;
+ std::string input_file;
+
+ // Create the category containing `help', `version' and `usage'.
+ po::options_description generic;
+ generic.add_options()("help,?", "Give this help list")(
+ "usage", "Give a short usage message")(
+ "version", "Print program version")("license", "Print program license");
+
+ // Positional parameter.
+ po::options_description hidden;
+ po::positional_options_description positional;
+ hidden.add_options()("input-file",
+ po::value<std::string>(&input_file)->required(),
+ "Input file");
+ positional.add("input-file", 1);
+
+ // Create the top-level category, with visible options.
+ po::options_description visible_desc(program_doc);
+
+ // Add each category to the top-level one.
+ for (const auto& i : modules_)
+ visible_desc.add(i.second);
+ visible_desc.add(generic);
+
+ // Sum of visible options and positional argument.
+ po::options_description all_opts = visible_desc;
+ all_opts.add(hidden);
+
+ // Give control to boost.
+ try
+ {
+ // Sadly we can't use `boost::program_options::variables_map'. By doing
+ // so, we would lose the chronological order of the tasks, which is
+ // capital for TC to work.
+ auto parsed = po::command_line_parser(argc, argv)
+ .options(all_opts)
+ .positional(positional)
+ .run();
+
+ // We don't want to fail if these options are present. So we take care
+ // of them right now.
+ if (is_parsed("help", parsed.options))
+ std::cout << visible_desc;
+ else if (is_parsed("version", parsed.options))
+ std::cout << program_version;
+ else if (is_parsed("usage", parsed.options))
+ std::cout << "tc [OPTIONS...] INPUT-FILE\n";
+ else if (is_parsed("license", parsed.options))
+ {
+ std::filesystem::path licenses("licenses");
+
+ if (!std::filesystem::exists(licenses))
+ {
+ std::cerr << program_name << ": cannot open licenses directory"
+ << ": No such file or directory\n";
+ }
+ else if (!std::filesystem::is_directory(licenses))
+ {
+ std::cerr << program_name << ": cannot open licenses directory"
+ << ": Not a directory\n";
+ }
+ else
+ {
+ using directory_iterator = std::filesystem::directory_iterator;
+ for (const auto& entry : directory_iterator(licenses))
+ {
+ const auto& path = entry.path();
+
+ if (!std::filesystem::is_regular_file(path)
+ || !path.has_extension()
+ || path.extension() != ".license")
+ continue;
+
+ std::ifstream license(path);
+ if (!license.is_open())
+ continue;
+
+ std::cout << "\n === " << path.stem().string()
+ << " license notice ===\n\n"
+ << license.rdbuf()
+ << "\n === " << path.stem().string()
+ << " license notice ===\n"
+ << std::endl;
+ }
+ }
+ }
+ else
+ {
+ // Replace the traditional calls to `vm.store()' and `vm.notify()'.
+ for (const auto& i : parsed.options)
+ {
+ auto option = parsed.description->find(i.string_key, false);
+
+ po::variable_value v;
+ option.semantic()->parse(v.value(), i.value, true);
+ option.semantic()->notify(v.value());
+ }
+
+ // If no input file is given, throw.
+ if (input_file.size() == 0)
+ throw po::error("no file name");
+ }
+ }
+ catch (const po::error& e)
+ {
+ std::cerr << program_name << ": " << e.what()
+ << "\ntc [OPTIONS...] INPUT-FILE\n"
+ "Try `tc --help' or `tc --usage' for more information.\n";
+ throw std::invalid_argument("command line parsing error");
+ }
+ catch (const std::invalid_argument& e)
+ {
+ throw;
+ }
+
+ char* input_file_ = nullptr;
+ if (!input_file.empty())
+ {
+ input_file_ = new char[input_file.size() + 1];
+ strcpy(input_file_, input_file.c_str());
+ }
+ return input_file_;
+ }
+
+ // Display registered Tasks.
+ std::ostream& TaskRegister::print_task_list(std::ostream& ostr)
+ {
+ ostr << "List of registered tasks:\n";
+ for (const tasks_by_name_type::value_type& i : task_list_)
+ ostr << "\t* " << i.first << '\n';
+ return ostr << std::endl;
+ }
+
+ // Dump task graph.
+ std::ostream& TaskRegister::print_task_graph(std::ostream& ostr)
+ {
+ ostr << "/* Task graph */\n"
+ << "digraph Tasks {\n"
+ << " node [shape=box, fontsize=14]\n"
+ // Preserve the order of the children.
+ << " graph [ordering=out]\n";
+
+ for (const tasks_by_name_type::value_type& i : task_list_)
+ {
+ const Task& task = *i.second;
+ if (dynamic_cast<const DisjunctiveTask*>(&task))
+ ostr << " \"" << task.name_get() << "\" [shape=diamond]\n";
+ ostr << " \"" << task.name_get() << "\"";
+ if (task.dependencies_get().size())
+ {
+ ostr << " -> {";
+ for (const std::string& s : task.dependencies_get())
+ ostr << " \"" << s << "\"";
+ ostr << " } ";
+ }
+ ostr << '\n';
+ }
+
+ return ostr << "}\n";
+ }
+
+ // Display registered Tasks execution order.
+ std::ostream& TaskRegister::print_task_order(std::ostream& ostr)
+ {
+ ostr << "List of Task Order:\n";
+ for (const Task* t : task_order_)
+ ostr << "\t* " << t->name_get() << std::endl;
+ return ostr << std::endl;
+ }
+
+ // Execute tasks, checking dependencies.
+ void TaskRegister::execute()
+ {
+ // FIXME: should be the only one to call resolve_dependency.
+ for (const Task* t : task_order_)
+ {
+ std::string pref(t->module_name_get());
+ if (!pref.empty())
+ pref = pref[0] + std::string(": ");
+ timer_.push(pref + t->name_get());
+ t->execute();
+ timer_.pop(pref + t->name_get());
+ }
+ }
+
+} // namespace task