// file : libbuild2/module.hxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_MODULE_HXX #define LIBBUILD2_MODULE_HXX #include <map> #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> #include <libbuild2/variable.hxx> #include <libbuild2/diagnostics.hxx> #include <libbuild2/export.hxx> namespace build2 { // A few high-level notes on the terminology: From the user's perspective, // the module is "loaded" (with the `using` directive). From the // implementation's perspectives, the module library is "loaded" and the // module is "bootstrapped" (or "booted" for short) and then "initialized" // (or "inited"). class scope; class location; class module_base { public: virtual ~module_base () = default; }; // Return true if the module should be initialized first (the order of // initialization within each group is unspecified). // using module_boot_function = bool (scope& root, const location&, unique_ptr<module_base>&); // Return false if the module configuration (normally based on the default // values) was unsuccessful but this is not (yet) an error. One example // would be the optional use of a module. Or a module might remain // unconfigured for as long as it is actually not used (e.g., install, // dist). The return value is used to set the <module>.configured variable. // using module_init_function = bool (scope& root, scope& base, const location&, unique_ptr<module_base>&, bool first, // First time for this project. bool optional, // Loaded with using? (optional module). const variable_map& hints); // Configuration hints (see below). // If the boot function is not NULL, then such a module is said to require // bootstrapping and must be loaded in bootstrap.build. // struct module_functions { const char* name; // Module/submodule name. module_boot_function* boot; module_init_function* init; }; // The build2_<name>_load() function will be written in C++ and will be // called from C++ but we need to suppress name mangling to be able to use // dlsym() or equivalent, thus extern "C". // // The <name> part in the function name is the main module name without // submodule components (for example, `c` in `c.config`) and the load // function is expected to return boot/init functions for all its submodules // (if any) as well as for the module itself as an array of module_functions // terminated with an all-NULL entry. // // Note that the load function is guaranteed to be called during serial // execution (either from main() or during the load phase). // extern "C" using module_load_function = const module_functions* (); // Module state. // struct module_state { bool boot; // True if the module boot'ed but not yet init'ed. bool first; // True if the boot'ed module must be init'ed first. module_init_function* init; unique_ptr<module_base> module; const location loc; // Boot location. }; struct module_map: std::map<string, module_state> { template <typename T> T* lookup (const string& name) const { auto i (find (name)); return i != end () ? static_cast<T*> (i->second.module.get ()) : nullptr; } }; // Boot the specified module loading its library if necessary. // LIBBUILD2_SYMEXPORT void boot_module (scope& root, const string& name, const location&); // Init the specified module loading its library if necessary. Used by the // parser but also by some modules to init prerequisite modules. Return true // if the module was both successfully loaded and configured (false can only // be returned if optional is true). // // The config_hints variable map can be used to pass configuration hints // from one module to another. For example, the cxx modude may pass the // target platform (which was extracted from the C++ compiler) to the bin // module (which may not always be able to extract the same information from // its tools). // LIBBUILD2_SYMEXPORT bool init_module (scope& root, scope& base, const string& name, const location&, bool optional = false, const variable_map& config_hints = empty_variable_map); // An alias to use from other modules (we could also distinguish between // boot and init). // // @@ TODO: maybe incorporate the .loaded variable check we have all over // (it's not clear if init_module() already has this semantics)? // inline bool load_module (scope& root, scope& base, const string& name, const location& loc, bool optional = false, const variable_map& config_hints = empty_variable_map) { return init_module (root, base, name, loc, optional, config_hints); } // Loaded modules (as in libraries). // // A NULL entry for the main module indicates that a module library was not // found. // using loaded_module_map = std::map<string, const module_functions*>; LIBBUILD2_SYMEXPORT extern loaded_module_map loaded_modules; } #endif // LIBBUILD2_MODULE_HXX