From 967be9e750221ab2ab783f95df79bb26d290a45e Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:07:58 +0200 Subject: add: added projects --- tiger-compiler/src/task/argument-task.cc | 29 ++ tiger-compiler/src/task/argument-task.hh | 58 ++++ tiger-compiler/src/task/boolean-task.cc | 22 ++ tiger-compiler/src/task/boolean-task.hh | 28 ++ tiger-compiler/src/task/disjunctive-task.cc | 32 +++ tiger-compiler/src/task/disjunctive-task.hh | 27 ++ tiger-compiler/src/task/function-task.cc | 22 ++ tiger-compiler/src/task/function-task.hh | 31 +++ tiger-compiler/src/task/fwd.hh | 22 ++ tiger-compiler/src/task/int-task.cc | 55 ++++ tiger-compiler/src/task/int-task.hh | 33 +++ tiger-compiler/src/task/libtask.hh | 107 +++++++ tiger-compiler/src/task/local.am | 15 + tiger-compiler/src/task/multiple-string-task.cc | 21 ++ tiger-compiler/src/task/multiple-string-task.hh | 34 +++ tiger-compiler/src/task/simple-task.cc | 21 ++ tiger-compiler/src/task/simple-task.hh | 34 +++ tiger-compiler/src/task/string-task.cc | 22 ++ tiger-compiler/src/task/string-task.hh | 28 ++ tiger-compiler/src/task/task-register.cc | 353 ++++++++++++++++++++++++ tiger-compiler/src/task/task-register.hh | 118 ++++++++ tiger-compiler/src/task/task-register.hxx | 15 + tiger-compiler/src/task/task.cc | 68 +++++ tiger-compiler/src/task/task.hh | 103 +++++++ tiger-compiler/src/task/task.hxx | 24 ++ tiger-compiler/src/task/tasks.cc | 28 ++ tiger-compiler/src/task/tasks.hh | 23 ++ 27 files changed, 1373 insertions(+) create mode 100644 tiger-compiler/src/task/argument-task.cc create mode 100644 tiger-compiler/src/task/argument-task.hh create mode 100644 tiger-compiler/src/task/boolean-task.cc create mode 100644 tiger-compiler/src/task/boolean-task.hh create mode 100644 tiger-compiler/src/task/disjunctive-task.cc create mode 100644 tiger-compiler/src/task/disjunctive-task.hh create mode 100644 tiger-compiler/src/task/function-task.cc create mode 100644 tiger-compiler/src/task/function-task.hh create mode 100644 tiger-compiler/src/task/fwd.hh create mode 100644 tiger-compiler/src/task/int-task.cc create mode 100644 tiger-compiler/src/task/int-task.hh create mode 100644 tiger-compiler/src/task/libtask.hh create mode 100644 tiger-compiler/src/task/local.am create mode 100644 tiger-compiler/src/task/multiple-string-task.cc create mode 100644 tiger-compiler/src/task/multiple-string-task.hh create mode 100644 tiger-compiler/src/task/simple-task.cc create mode 100644 tiger-compiler/src/task/simple-task.hh create mode 100644 tiger-compiler/src/task/string-task.cc create mode 100644 tiger-compiler/src/task/string-task.hh create mode 100644 tiger-compiler/src/task/task-register.cc create mode 100644 tiger-compiler/src/task/task-register.hh create mode 100644 tiger-compiler/src/task/task-register.hxx create mode 100644 tiger-compiler/src/task/task.cc create mode 100644 tiger-compiler/src/task/task.hh create mode 100644 tiger-compiler/src/task/task.hxx create mode 100644 tiger-compiler/src/task/tasks.cc create mode 100644 tiger-compiler/src/task/tasks.hh (limited to 'tiger-compiler/src/task') diff --git a/tiger-compiler/src/task/argument-task.cc b/tiger-compiler/src/task/argument-task.cc new file mode 100644 index 0000000..82194f3 --- /dev/null +++ b/tiger-compiler/src/task/argument-task.cc @@ -0,0 +1,29 @@ +/** + ** \file task/argument-task.cc + ** \brief ArgumentTask class implementation. + */ + +#include +#include + +namespace task +{ + ArgumentTask::ArgumentTask(const char* name, + const char* module_name, + const char* desc, + const char* argname, + std::string deps) + : Task(name, module_name, desc, deps) + , argname_(argname) + { + // Register this task. + TaskRegister::instance().register_task(*this); + } + + const std::string& ArgumentTask::arg_get() const { return arg_; } + + void ArgumentTask::arg_set(const std::string& arg) const { arg_ = arg; } + + const char* ArgumentTask::argname_get() const { return argname_; } + +} // namespace task diff --git a/tiger-compiler/src/task/argument-task.hh b/tiger-compiler/src/task/argument-task.hh new file mode 100644 index 0000000..775b084 --- /dev/null +++ b/tiger-compiler/src/task/argument-task.hh @@ -0,0 +1,58 @@ +/** + ** \file task/argument-task.hh + ** \brief Declare the task::ArgumentTask class. + */ + +#pragma once + +#include + +#include + +namespace task +{ + /** \brief A `Task' expecting arguments prior to be executed. + + This class embodies tasks which need an additional parameter for their + `execute()' method. Therefore a call to `arg_set()' must be made prior to + the `execute()' method, to set the value of this expected argument. + This class also automates the registering of its derived classes, as does its + sibling class `SimpleTask' for tasks without additional argument. + */ + class ArgumentTask : public Task + { + /** \name Ctor & dtor. + ** \{ */ + public: + /// Construct and register an ArgumentTask. + ArgumentTask(const char* name, + const char* module_name, + const char* desc, + const char* argname, + std::string deps = ""); + + /** \} */ + + /** \name Accessors. + ** \{ */ + public: + /// Access to 'arg'. + const std::string& arg_get() const; + + /// Access to `arg'. + virtual void arg_set(const std::string& arg) const; + + /// Access to `argname'. + const char* argname_get() const; + + /** \} */ + + protected: + /// ArgumentTask argument value. + mutable std::string arg_; + + /// Argument name to be displayed when printing. + const char* argname_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/boolean-task.cc b/tiger-compiler/src/task/boolean-task.cc new file mode 100644 index 0000000..9923082 --- /dev/null +++ b/tiger-compiler/src/task/boolean-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/boolean-task.cc + ** \brief Implementation of task::BooleanTask. + ** + */ + +#include + +namespace task +{ + BooleanTask::BooleanTask(bool& flag, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + , flag_(flag) + {} + + void BooleanTask::execute() const { flag_ = true; } + +} //namespace task diff --git a/tiger-compiler/src/task/boolean-task.hh b/tiger-compiler/src/task/boolean-task.hh new file mode 100644 index 0000000..88298b3 --- /dev/null +++ b/tiger-compiler/src/task/boolean-task.hh @@ -0,0 +1,28 @@ +/** + ** \file task/boolean-task.hh + ** \brief Declare the task::BooleanTask class. + ** + */ +#pragma once + +#include + +namespace task +{ + /// A simple Task that sets a Boolean variable to true. + class BooleanTask : public SimpleTask + { + public: + BooleanTask(bool& flag, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + + private: + bool& flag_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/disjunctive-task.cc b/tiger-compiler/src/task/disjunctive-task.cc new file mode 100644 index 0000000..07661bf --- /dev/null +++ b/tiger-compiler/src/task/disjunctive-task.cc @@ -0,0 +1,32 @@ +/** + ** \file task/disjunctive-task.cc + ** \brief Implementation of task::DisjunctiveTask. + ** + */ + +#include + +namespace task +{ + DisjunctiveTask::DisjunctiveTask(const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + {} + + void DisjunctiveTask::execute() const + { + // Nothing to do. + } + + Task::deps_type + DisjunctiveTask::resolve_dependencies(tasks_list_type& active_tasks) const + { + if (!active_tasks.empty() || dependencies_.empty()) + return Task::deps_type(); + else + return Task::deps_type(1, *dependencies_.begin()); + } + +} // namespace task diff --git a/tiger-compiler/src/task/disjunctive-task.hh b/tiger-compiler/src/task/disjunctive-task.hh new file mode 100644 index 0000000..6cb52fc --- /dev/null +++ b/tiger-compiler/src/task/disjunctive-task.hh @@ -0,0 +1,27 @@ +/** + ** \file task/disjunctive-task.hh + ** \brief Declare the DisjunctiveTask class. + ** + */ +#pragma once + +#include + +namespace task +{ + /// A Task that makes sure that AT LEAST one of its dependencies is + /// scheduled. + class DisjunctiveTask : public SimpleTask + { + public: + DisjunctiveTask(const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + deps_type + resolve_dependencies(tasks_list_type& active_tasks) const override; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/function-task.cc b/tiger-compiler/src/task/function-task.cc new file mode 100644 index 0000000..309a276 --- /dev/null +++ b/tiger-compiler/src/task/function-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/function-task.cc + ** \brief Implementation of task::FunctionTask. + ** + */ + +#include + +namespace task +{ + FunctionTask::FunctionTask(callback_type& callback, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : SimpleTask(name, module_name, desc, deps) + , execute_(callback) + {} + + void FunctionTask::execute() const { execute_(); } + +} //namespace task diff --git a/tiger-compiler/src/task/function-task.hh b/tiger-compiler/src/task/function-task.hh new file mode 100644 index 0000000..a08b285 --- /dev/null +++ b/tiger-compiler/src/task/function-task.hh @@ -0,0 +1,31 @@ +/** + ** \file task/function-task.hh + ** \brief Declare the task::FunctionTask class. + ** + */ +#pragma once + +#include + +namespace task +{ + /// A simple Task that invokes a callback function. + class FunctionTask : public SimpleTask + { + public: + using callback_type = auto() -> void; + + FunctionTask(callback_type& callback, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + public: + void execute() const override; + + private: + callback_type& execute_; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/fwd.hh b/tiger-compiler/src/task/fwd.hh new file mode 100644 index 0000000..620016f --- /dev/null +++ b/tiger-compiler/src/task/fwd.hh @@ -0,0 +1,22 @@ +/** + ** \file task/fwd.hh + ** \brief Interface to the Task module. + ** + */ +#pragma once + +namespace task +{ + // task.hh + class Task; + + // argument-task.hh + class ArgumentTask; + + // simple-task.hh + class SimpleTask; + + // task-register.hh + class TaskRegister; + +} // namespace task diff --git a/tiger-compiler/src/task/int-task.cc b/tiger-compiler/src/task/int-task.cc new file mode 100644 index 0000000..bf26271 --- /dev/null +++ b/tiger-compiler/src/task/int-task.cc @@ -0,0 +1,55 @@ +/** + ** \file task/int-task.cc + ** \brief IntTask class implementation. + ** + */ + +#include +#include + +#include +#include + +namespace task +{ + IntTask::IntTask(int& var, + int min, + int max, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "NUM", deps) + , var_(var) + , min_(min) + , max_(max) + {} + + void IntTask::arg_set(const std::string& arg) const + { + arg_ = arg; + + try + { + var_ = stol(arg); + } + catch (const std::invalid_argument& e) + { + std::cerr << program_name << ": expected an integer: " << arg << '\n'; + throw; + } + + if (!(min_ <= var_ && var_ <= max_)) + { + std::cerr << program_name << ": invalid integer: " << var_ << '\n'; + + throw std::invalid_argument(std::string("invalid integer: ") + arg_); + } + } + + void IntTask::execute() const + { + // Assignment done in arg_set. + } + +} //namespace task diff --git a/tiger-compiler/src/task/int-task.hh b/tiger-compiler/src/task/int-task.hh new file mode 100644 index 0000000..a45518d --- /dev/null +++ b/tiger-compiler/src/task/int-task.hh @@ -0,0 +1,33 @@ +/** + ** \file task/int-task.hh + ** \brief Declare the IntTask class. + ** + */ +#pragma once + +#include + +namespace task +{ + /// A simple Task that sets an Int variable. + class IntTask : public ArgumentTask + { + public: + IntTask(int& var, + int min, + int max, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + void arg_set(const std::string& arg) const override; + + private: + int& var_; + int min_; + int max_; + }; + +} //namespace task diff --git a/tiger-compiler/src/task/libtask.hh b/tiger-compiler/src/task/libtask.hh new file mode 100644 index 0000000..518d377 --- /dev/null +++ b/tiger-compiler/src/task/libtask.hh @@ -0,0 +1,107 @@ +/** + ** \file task/libtask.hh + ** \brief Interface to the Task module. + ** + */ + +/* This file must not be protected against multiple inclusion, + because it might be read to define or declare the tasks. */ + +#include +#include +#include +#include +#include +#include + +/// Handling of Task. +namespace task +{} // namespace task + +/** A means to concatenate tokens with delayed evaluation. */ +#define CONCAT_(A, B) A##B +#define CONCAT(A, B) CONCAT_(A, B) + +/** Make a "unique" token using the line number of invocation of this + macro. + + Conflicts are possible, but unlikely: tasks declared at the same + line number, but in different files, are not a problem, since + anyway the tasks are static, and in different namespaces. + + Just don't declare two tasks on the same line! */ +#define TASK_UNIQUE() CONCAT(task_, __LINE__) + +/*------------------------------. +| Easy instantiation of Tasks. | +`------------------------------*/ + +#undef TASK_GROUP +#undef TASK_DECLARE +#undef BOOLEAN_TASK_DECLARE +#undef INT_TASK_DECLARE +#undef STRING_TASK_DECLARE +#undef MULTIPLE_STRING_TASK_DECLARE +#undef DISJUNCTIVE_TASK_DECLARE + +// Should we define the objects, or just declare them? +#if DEFINE_TASKS + +/// Define the current Task group name. +# define TASK_GROUP(Name) const char group_name[] = Name + +/// Instantiate a FunctionTask. +# define TASK_DECLARE(Name, Help, Routine, Deps) \ + extern void(Routine)(); \ + static task::FunctionTask task_##Routine(Routine, group_name, Help, Name, \ + Deps) + +/// Instantiate a BooleanTask. +# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) \ + bool Flag; \ + static task::BooleanTask task_##Flag(Flag, group_name, Help, Name, Deps) + +# define TASK_UNIQUE_NAME_(Line) task_##Line + +# define TASK_UNIQUE_NAME TASK_UNIQUE_NAME_(__LINE__) + +/// Instantiate an IntTask. +# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps) \ + static task::IntTask TASK_UNIQUE()(Flag, Min, Max, group_name, Help, Name, \ + Deps) + +/// Instantiate a StringTask. +# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \ + std::string Flag = Default; \ + static task::StringTask task_##Flag(Flag, group_name, Help, Name, Deps) + +/// Instanciate a MultipleStringTask +# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \ + extern task::MultipleStringTask::callback_type Routine; \ + static task::MultipleStringTask task_##Routine(Routine, group_name, Help, \ + Name, Deps) + +/// Instantiate a DisjunctiveTask. +# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps) \ + static task::DisjunctiveTask task_##Routine(group_name, Help, Name, Deps) + +#else // !DEFINE_TASKS + +/// Define the current Task group name. +# define TASK_GROUP(Name) extern const char* group_name +/// Instantiate a FunctionTask. +# define TASK_DECLARE(Name, Help, Routine, Deps) extern void(Routine)() +/// Instantiate a BooleanTask. +# define BOOLEAN_TASK_DECLARE(Name, Help, Flag, Deps) extern bool Flag; +/// Instantiate an IntTask. +# define INT_TASK_DECLARE(Name, Min, Max, Help, Flag, Deps) +/// Instantiate a StringTask. +# define STRING_TASK_DECLARE(Name, Default, Help, Flag, Deps) \ + extern std::string Flag; +/// Instantiate a MultipleStringTask. +# define MULTIPLE_STRING_TASK_DECLARE(Name, Help, Routine, Deps) \ + extern void(Routine)(std::string); +/// Instantiate a DisjunctiveTask. +# define DISJUNCTIVE_TASK_DECLARE(Name, Help, Deps) + +#endif // !DEFINE_TASKS diff --git a/tiger-compiler/src/task/local.am b/tiger-compiler/src/task/local.am new file mode 100644 index 0000000..a9bd6aa --- /dev/null +++ b/tiger-compiler/src/task/local.am @@ -0,0 +1,15 @@ +## Task module. +src_libtc_la_SOURCES += \ + %D%/fwd.hh %D%/libtask.hh \ + %D%/task.hh %D%/task.hxx %D%/task.cc \ + %D%/boolean-task.hh %D%/boolean-task.cc \ + %D%/function-task.hh %D%/function-task.cc \ + %D%/int-task.hh %D%/int-task.cc \ + %D%/string-task.hh %D%/string-task.cc \ + %D%/multiple-string-task.hh %D%/multiple-string-task.cc \ + %D%/disjunctive-task.hh %D%/disjunctive-task.cc \ + %D%/task-register.hh %D%/task-register.cc %D%/task-register.hxx \ + %D%/simple-task.hh %D%/simple-task.cc \ + %D%/argument-task.hh %D%/argument-task.cc + +TASKS += %D%/tasks.hh %D%/tasks.cc diff --git a/tiger-compiler/src/task/multiple-string-task.cc b/tiger-compiler/src/task/multiple-string-task.cc new file mode 100644 index 0000000..a789663 --- /dev/null +++ b/tiger-compiler/src/task/multiple-string-task.cc @@ -0,0 +1,21 @@ +/** + ** \file task/multiple-string-task.cc + ** \brief Implementation of task::MultipleStringTask. + */ + +#include + +namespace task +{ + MultipleStringTask::MultipleStringTask(callback_type& cb, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "DIR", deps) + , execute_(cb) + {} + + void MultipleStringTask::execute() const { execute_(arg_get()); } + +} //namespace task diff --git a/tiger-compiler/src/task/multiple-string-task.hh b/tiger-compiler/src/task/multiple-string-task.hh new file mode 100644 index 0000000..a770b3b --- /dev/null +++ b/tiger-compiler/src/task/multiple-string-task.hh @@ -0,0 +1,34 @@ +/** + ** \file task/multiple-string-task.hh + ** \brief Declaration of task::MultipleStringTask. + ** + */ + +#pragma once + +#include + +#include + +namespace task +{ + /// A simple task that invokes callback function with string argument. + class MultipleStringTask : public ArgumentTask + { + public: + using callback_type = auto(const std::string&) -> void; + + MultipleStringTask(callback_type& cb, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + public: + void execute() const override; + + public: + callback_type& execute_; + }; + +} // namespace task diff --git a/tiger-compiler/src/task/simple-task.cc b/tiger-compiler/src/task/simple-task.cc new file mode 100644 index 0000000..031a182 --- /dev/null +++ b/tiger-compiler/src/task/simple-task.cc @@ -0,0 +1,21 @@ +/** + ** \file task/simple-task.cc + ** \brief SimpleTask class implementation. + */ + +#include +#include + +namespace task +{ + SimpleTask::SimpleTask(const char* name, + const char* module_name, + const char* desc, + std::string deps) + : Task(name, module_name, desc, deps) + { + // Register this task. + TaskRegister::instance().register_task(*this); + } + +} // namespace task diff --git a/tiger-compiler/src/task/simple-task.hh b/tiger-compiler/src/task/simple-task.hh new file mode 100644 index 0000000..b44b8ea --- /dev/null +++ b/tiger-compiler/src/task/simple-task.hh @@ -0,0 +1,34 @@ +/** + ** \file task/simple-task.hh + ** \brief Declare the task::SimpleTask class. + */ + +#pragma once + +#include + +#include + +namespace task +{ + /** \brief A code factoring class for tasks without arguments. + + The purpose of this class is to avoid duplicating the code which register a + task without arguments. The sibling class task::ArgumentTask does pretty much + the same (with extended functionnalities) for tasks with arguments. + */ + class SimpleTask : public Task + { + /** \name Ctor. */ + /** \{ */ + public: + /// Construct and register a SimpleTask. + SimpleTask(const char* name, + const char* module_name, + const char* desc, + std::string deps = ""); + + /** \} */ + }; + +} // namespace task diff --git a/tiger-compiler/src/task/string-task.cc b/tiger-compiler/src/task/string-task.cc new file mode 100644 index 0000000..db7776c --- /dev/null +++ b/tiger-compiler/src/task/string-task.cc @@ -0,0 +1,22 @@ +/** + ** \file task/string-task.cc + ** \brief Implementation of task::StringTask. + ** + */ + +#include + +namespace task +{ + StringTask::StringTask(std::string& var, + const char* module_name, + const char* desc, + const char* name, + std::string deps) + : ArgumentTask(name, module_name, desc, "STRING", deps) + , var_(var) + {} + + void StringTask::execute() const { var_ = arg_get(); } + +} //namespace task diff --git a/tiger-compiler/src/task/string-task.hh b/tiger-compiler/src/task/string-task.hh new file mode 100644 index 0000000..9e06293 --- /dev/null +++ b/tiger-compiler/src/task/string-task.hh @@ -0,0 +1,28 @@ +/** + ** \file task/string-task.hh + ** \brief Declare the task::StringTask class. + ** + */ +#pragma once + +#include + +namespace task +{ + /// A simple Task that sets a String variable. + class StringTask : public ArgumentTask + { + public: + StringTask(std::string& var, + const char* module_name, + const char* desc, + const char* name, + std::string deps); + + void execute() const override; + + private: + std::string& var_; + }; + +} //namespace task 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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() + ->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 + static bool + is_parsed(const std::string& s, + const std::vector>& os) + { + using option = boost::program_options::basic_option; + + 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(&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(&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 diff --git a/tiger-compiler/src/task/task-register.hh b/tiger-compiler/src/task/task-register.hh new file mode 100644 index 0000000..6608e02 --- /dev/null +++ b/tiger-compiler/src/task/task-register.hh @@ -0,0 +1,118 @@ +/** + ** \file task/task-register.hh + ** \brief Declare the task::TaskRegister class. + ** + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace task +{ + /** \brief Register Tasks. + + The purpose of the TaskRegister class is to collect tasks + and organize their execution using their dependencies (like 'make'). + For modeling details, see Design Patterns: singleton. + */ + class TaskRegister + { + // Make it non-copyable. + TaskRegister(const TaskRegister&) = delete; + TaskRegister& operator=(const TaskRegister&) = delete; + + /// \name Ctor & dtor. + private: + /// Construct a TaskRegister. + TaskRegister() = default; + + public: + /// Access to the unique TaskRegister. + static TaskRegister& instance(); + + /** \name Tasks registering. + ** \{ */ + /// Register task \a task. + void register_task(const SimpleTask& task); + void register_task(const ArgumentTask& task); + /// Register the task \a task_name for execution. + void enable_task(const std::string& task_name); + + /// Return the number of tasks to execute. + int nb_of_task_to_execute_get(); + /** \} */ + + /// \name Task reordering. + private: + /** \brief Resolve dependencies between tasks. + ** + ** Resolve dependencies on tasks registered for execution. + ** Make a depth first search of implicit tasks graph, + ** check cycles and build an ordered list of tasks. */ + void resolve_dependencies(const Task& task); + + public: + /** \brief Parse \a argv and determine which tasks to execute. + ** + ** Use boost::program_options. */ + char* parse_arg(int argc, char* argv[]); + + /** \name Display TaskRegister content. + ** \{ */ + /// Display registered Tasks. + std::ostream& print_task_list(std::ostream& ostr); + /// Display task graph. + std::ostream& print_task_graph(std::ostream& ostr); + /// Display registered Tasks execution order. + std::ostream& print_task_order(std::ostream& ostr); + + /** \name Using registered Tasks. + ** \{ */ + /// Execute tasks, checking dependencies. + void execute(); + /** \} */ + + /** \name Time management. + ** \{ */ + /// Access to the tasks timer. + const misc::timer& timer_get() const; + /** \} */ + + /// Ordered vector of tasks. + using tasks_list_type = std::vector; + + private: + /// Associate a task name to a task. + using tasks_by_name_type = std::map; + /// Associate a module name to a task module. + using indexed_module_type = + std::map; + + // Common code between the two overload of `register_task'. + indexed_module_type::iterator register_task_(const Task& task); + + /// 'string to task' map. + tasks_by_name_type task_list_; + + /// 'ordered for execution' tasks list. + tasks_list_type task_order_; + + /// Tasks timer. + misc::timer timer_; + + /// Task modules. + indexed_module_type modules_; + }; + +} // namespace task + +#include diff --git a/tiger-compiler/src/task/task-register.hxx b/tiger-compiler/src/task/task-register.hxx new file mode 100644 index 0000000..a72969a --- /dev/null +++ b/tiger-compiler/src/task/task-register.hxx @@ -0,0 +1,15 @@ +/** + ** \file task/task-register.hxx + ** \brief Inline methods of task::TaskRegister. + ** + */ + +#pragma once + +#include + +namespace task +{ + inline const misc::timer& TaskRegister::timer_get() const { return timer_; } + +} // namespace task. diff --git a/tiger-compiler/src/task/task.cc b/tiger-compiler/src/task/task.cc new file mode 100644 index 0000000..c675ffa --- /dev/null +++ b/tiger-compiler/src/task/task.cc @@ -0,0 +1,68 @@ +/** + ** \file task/task.cc + ** \brief Implementation of task::Task. + ** + */ + +#include +#include + +#include + +namespace task +{ + Task::Task(const char* name, + const char* module_name, + const char* desc, + const std::string& deps) + : name_(normalize(name)) + , fullname_(name_) + , module_name_(module_name) + , desc_(desc) + { + // Compute its dependencies. + std::string::size_type start = 0; + while (start < deps.size()) + { + std::string::size_type end = deps.find(' ', start); + if (end > deps.size()) + end = deps.size(); + dependencies_.emplace_back(normalize(deps.substr(start, end - start))); + start = end + 1; + } + + // See if it has a short option, such as "h|help". + if (name_.size() >= 2 && name_[1] == '|') + { + fullname_ += ','; + fullname_ += name_[0]; + name_.erase(0, 2); + fullname_.erase(0, 2); + } + } + + Task::deps_type Task::resolve_dependencies(tasks_list_type&) const + { + // By default, consider that all dependencies are required. + return dependencies_; + } + + /// Display dependencies of this task. + void Task::print_dependencies() const + { + std::cout << "Dependencies for task " << name_ << ":\n"; + for (const std::string& s : dependencies_) + std::cout << "\t" << s << '\n'; + std::cout << std::flush; + } + + std::string Task::normalize(const std::string& task_name) + { + std::string normalized_name; + std::ranges::replace_copy( + task_name, std::inserter(normalized_name, normalized_name.begin()), '_', + '-'); + return normalized_name; + } + +} // namespace task diff --git a/tiger-compiler/src/task/task.hh b/tiger-compiler/src/task/task.hh new file mode 100644 index 0000000..6081f1f --- /dev/null +++ b/tiger-compiler/src/task/task.hh @@ -0,0 +1,103 @@ +/** + ** \file task/task.hh + ** \brief Declare the task::Task class. + */ + +#pragma once + +#include +#include + +#include + +namespace task +{ + /** \brief A function bound to a command line option. + + The purpose of the Task class is to abstract the execution of a module + and the tasks on which its execution depends. This is an implementation + of the Command Design Pattern. + */ + class Task + { + /** \name Ctor & dtor. */ + /** \{ */ + public: + /** \brief Construct a Task. + + \param name name of this task (used for long option) + \param module_name name of the module to which the task belongs. + \param desc description of this task + \param deps optional space separated list of task names + */ + Task(const char* name, + const char* module_name, + const char* desc, + const std::string& deps = ""); + + /// Destroy this Task. + virtual ~Task() = default; + /** \}*/ + + /** \name Abstract methods. + ** \{ */ + public: + /// Execute this task. + virtual void execute() const = 0; + /** \} */ + + using tasks_list_type = TaskRegister::tasks_list_type; + + using deps_type = std::vector; + + virtual deps_type resolve_dependencies(tasks_list_type& active_tasks) const; + + /** \name Accessors. + ** \{ */ + public: + /** Access to 'name'. + + 'const char*' is preferred to 'std::string' because TaskRegister + calls function requiring 'const char*'. + The use of 'std::string::c_str()' is so forbidden and a call to + 'strdup(std::string::c_str())' would imply dummy memory leaks. */ + const char* name_get() const; + + /// Access to 'module_name'. + const char* module_name_get() const; + + /// Access to 'fullname'. + const char* fullname_get() const; + + /// Access to 'desc'. + const char* desc_get() const; + + /// Access to tasks dependencies. + const deps_type& dependencies_get() const; + + /** \} */ + + public: + /// Display dependencies of this task . + void print_dependencies() const; + + public: + /// Normalize the name of a task. + static std::string normalize(const std::string& task_name); + + protected: + /// Task name. + std::string name_; + /// Task name plus short name. + std::string fullname_; + /// Module name to which the task belongs. + const char* module_name_; + /// A short description of this task (displayed in program usage). + const char* desc_; + /// Contains the name of the tasks on which this one depends. + deps_type dependencies_; + }; + +} // namespace task + +#include diff --git a/tiger-compiler/src/task/task.hxx b/tiger-compiler/src/task/task.hxx new file mode 100644 index 0000000..02fad0e --- /dev/null +++ b/tiger-compiler/src/task/task.hxx @@ -0,0 +1,24 @@ +/** + ** \file task/task.hxx + ** \brief Inline methods for task/task.hh. + */ +#pragma once + +#include + +namespace task +{ + inline const char* Task::name_get() const { return name_.c_str(); } + + inline const char* Task::module_name_get() const { return module_name_; } + + inline const char* Task::fullname_get() const { return fullname_.c_str(); } + + inline const char* Task::desc_get() const { return desc_; } + + inline const Task::deps_type& Task::dependencies_get() const + { + return dependencies_; + } + +} // namespace task diff --git a/tiger-compiler/src/task/tasks.cc b/tiger-compiler/src/task/tasks.cc new file mode 100644 index 0000000..33eb35f --- /dev/null +++ b/tiger-compiler/src/task/tasks.cc @@ -0,0 +1,28 @@ +/** + ** \file task/tasks.cc + ** \brief Task module related tasks. + */ + +#include + +#include +#include +#define DEFINE_TASKS 1 +#include +#undef DEFINE_TASKS + +// Task module related tasks' implementation. +namespace task::tasks +{ + void tasks_list() { TaskRegister::instance().print_task_list(std::cout); } + + void tasks_graph() { TaskRegister::instance().print_task_graph(std::cout); } + + void tasks_selection() + { + TaskRegister::instance().print_task_order(std::cout); + } + + void time_report() { task_timer.dump_on_destruction(std::cerr); } + +} // namespace task::tasks diff --git a/tiger-compiler/src/task/tasks.hh b/tiger-compiler/src/task/tasks.hh new file mode 100644 index 0000000..b18943d --- /dev/null +++ b/tiger-compiler/src/task/tasks.hh @@ -0,0 +1,23 @@ +/** + ** \file task/tasks.hh + ** \brief Task module related tasks. + */ + +#pragma once + +#include + +namespace task::tasks +{ + TASK_GROUP("0. Tasks"); + + /// List all the existing tasks. + TASK_DECLARE("task-list", "list registered tasks", tasks_list, ""); + /// Dump task graph. + TASK_DECLARE("task-graph", "show task graph", tasks_graph, ""); + /// List the selected tasks in order. + TASK_DECLARE("task-selection", "list tasks to be run", tasks_selection, ""); + /// Ask for a time report at the end of the execution. + TASK_DECLARE("time-report", "report execution times", time_report, ""); + +} // namespace task::tasks -- cgit v1.2.3