From 534ca7619a62a74bce8e4b30931aaf99f9c3beb6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 16 Aug 2020 16:21:35 +0200 Subject: Add support for post-configure and pre-disfigure hooks --- libbuild2/config/init.cxx | 45 ++++++++++++++++++++++------------------ libbuild2/config/module.cxx | 24 +++++++++++++++++++++ libbuild2/config/module.hxx | 13 ++++++++++++ libbuild2/config/operation.cxx | 16 ++++++++++++++ libbuild2/config/utility.cxx | 3 +++ libbuild2/config/utility.hxx | 47 +++++++++++++++++++++++++++++++++++------- libbuild2/context.hxx | 10 +++++++++ 7 files changed, 131 insertions(+), 27 deletions(-) diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index df71fae..1513a47 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -96,35 +96,38 @@ namespace build2 auto& c_p (vp.insert>> ( "config.config.persist", true /* ovr */, v_p)); - // Only create the module if we are configuring or creating or if it was - // requested with config.config.module (useful if we need to call - // $config.save() during other meta-operations). + // Only create the module if we are configuring, creating, or + // disfiguring or if it was requested with config.config.module (useful + // if we need to call $config.save() during other meta-operations). // - // Detecting the former (configuring/creating) is a bit tricky since the - // build2 core may not yet know if this is the case. But we know. + // Detecting the former (configure/disfigure/creating) is a bit tricky + // since the build2 core may not yet know if this is the case. But we + // know. // auto& c_m (vp.insert ("config.config.module", false /*ovr*/, v_p)); - const string& mname (ctx.current_mname); - const string& oname (ctx.current_oname); - - if (( mname == "configure" || mname == "create") || - (mname.empty () && (oname == "configure" || oname == "create")) || + bool d; + if ((d = ctx.bootstrap_meta_operation ("disfigure")) || + ctx.bootstrap_meta_operation ("configure") || + ctx.bootstrap_meta_operation ("create") || cast_false (rs.vars[c_m])) { - // Used as a variable prefix by configure_execute(). - // - vp.insert ("config"); - auto& m (extra.set_module (new module)); - // Adjust priority for the config module and import pseudo-module so - // that their variables come first in config.build. - // - m.save_module ("config", INT32_MIN); - m.save_module ("import", INT32_MIN); + if (!d) + { + // Used as a variable prefix by configure_execute(). + // + vp.insert ("config"); + + // Adjust priority for the config module and import pseudo-module so + // that their variables come first in config.build. + // + m.save_module ("config", INT32_MIN); + m.save_module ("import", INT32_MIN); - m.save_variable (c_p, save_null_omitted); + m.save_variable (c_p, save_null_omitted); + } } // Register the config function family if this is the first instance of @@ -319,6 +322,8 @@ namespace build2 config_save_variable = &module::save_variable; config_save_module = &module::save_module; config_preprocess_create = &preprocess_create; + config_configure_post = &module::configure_post; + config_disfigure_pre = &module::disfigure_pre; return mod_functions; } diff --git a/libbuild2/config/module.cxx b/libbuild2/config/module.cxx index 96107cc..6b0c82a 100644 --- a/libbuild2/config/module.cxx +++ b/libbuild2/config/module.cxx @@ -67,6 +67,30 @@ namespace build2 m->save_module (name, prio); } + bool module:: + configure_post (scope& rs, configure_post_hook* h) + { + if (module* m = rs.find_module (module::name)) + { + m->configure_post_.push_back (h); + return true; + } + + return false; + } + + bool module:: + disfigure_pre (scope& rs, disfigure_pre_hook* h) + { + if (module* m = rs.find_module (module::name)) + { + m->disfigure_pre_.push_back (h); + return true; + } + + return false; + } + const string module::name ("config"); const uint64_t module::version (1); } diff --git a/libbuild2/config/module.hxx b/libbuild2/config/module.hxx index 5cb4faa..96220ac 100644 --- a/libbuild2/config/module.hxx +++ b/libbuild2/config/module.hxx @@ -15,6 +15,8 @@ #include #include +#include + namespace build2 { namespace config @@ -94,6 +96,17 @@ namespace build2 i->second.find (var) != i->second.end (); } + // Configure/disfigure hooks. + // + static bool + configure_post (scope&, configure_post_hook*); + + static bool + disfigure_pre (scope&, disfigure_pre_hook*); + + small_vector configure_post_; + small_vector disfigure_pre_; + // Cached (during init) config.config.persist value, if defined. // const vector>* persist = nullptr; diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index c606d09..b07df42 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -590,6 +590,15 @@ namespace build2 fail << "operation-specific configuration not yet supported"; } + if (c_s == nullptr) + { + if (module* m = rs.find_module (module::name)) + { + for (auto hook: m->configure_post_) + hook (a, rs); + } + } + // Configure subprojects that have been loaded. // if (const subprojects* ps = *rs.root_extra->subprojects) @@ -892,6 +901,12 @@ namespace build2 } } + if (module* m = rs.find_module (module::name)) + { + for (auto hook: m->disfigure_pre_) + r = hook (a, rs) || r; + } + // We distinguish between a complete disfigure and operation- // specific. // @@ -937,6 +952,7 @@ namespace build2 } else { + fail << "operation-specific disfiguration not yet supported"; } return r; diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index b3f94be..b10c980 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -14,6 +14,9 @@ namespace build2 vector_view&, bool, const location&); + bool (*config_configure_post) (scope&, bool (*)(action, const scope&)); + bool (*config_disfigure_pre) (scope&, bool (*)(action, const scope&)); + namespace config { pair diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx index fb3023b..209ef5c 100644 --- a/libbuild2/config/utility.hxx +++ b/libbuild2/config/utility.hxx @@ -22,13 +22,14 @@ namespace build2 // // The only persistence-specific aspects of this functionality are marking // of the variables as to be persisted (saved, potentially with flags), - // establishing the module saving order (priority), and configuration - // creation (the create meta-operation implementation) These are accessed - // through the config module entry points (which are NULL for transient - // configurations). Note also that the exact interpretation of the save - // flags and module order depends on the config module implementation (which - // may ignore them as not applicable). An implementation may also define - // custom save flags (for example, accessible through the config.save + // establishing the module saving order (priority), configuration creation + // (the create meta-operation implementation), as well as configure and + // disfigure hooks (for example, for second-level configuration). These are + // accessed through the config module entry points (which are NULL for + // transient configurations). Note also that the exact interpretation of the + // save flags and module order depends on the config module implementation + // (which may ignore them as not applicable). An implementation may also + // define custom save flags (for example, accessible through the config.save // attribute). Such flags should start from 0x100000000. // LIBBUILD2_SYMEXPORT extern void @@ -44,6 +45,12 @@ namespace build2 bool, const location&); + LIBBUILD2_SYMEXPORT extern bool + (*config_configure_post) (scope&, bool (*)(action, const scope&)); + + LIBBUILD2_SYMEXPORT extern bool + (*config_disfigure_pre) (scope&, bool (*)(action, const scope&)); + namespace config { // Mark the variable to be saved during configuration. @@ -77,6 +84,32 @@ namespace build2 config_save_module (rs, module, prio); } + // Post-configure and pre-disfigure hooks. Normally used to save/remove + // persistent state. Return true if anything has been done (used for + // diagnostics). + // + // The registration functions return true if the hook has been registered. + // + // Note that the hooks are called for the top-level project and all its + // subprojects (if registered in the subproject root scope), from outer to + // inner for configure and from inner to outer for disfigure. It's the + // responsibility of the hook implementation to handle any aggregation. + // + using configure_post_hook = bool (action, const scope&); + using disfigure_pre_hook = bool (action, const scope&); + + inline bool + configure_post (scope& rs, configure_post_hook* h) + { + return config_configure_post != nullptr && config_configure_post (rs, h); + } + + inline bool + disfigure_pre (scope& rs, disfigure_pre_hook* h) + { + return config_disfigure_pre != nullptr && config_disfigure_pre (rs, h); + } + // Lookup a config.* variable value and, if the value is defined, mark it // as saved. // diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index a8b6b01..c4e1259 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -265,6 +265,16 @@ namespace build2 const operation_info* current_inner_oif; const operation_info* current_outer_oif; + // Check whether this is the specified meta-operation during bootstrap + // (when current_mif may not be yet known). + // + bool + bootstrap_meta_operation (const char* mo) + { + return ((current_mname == mo ) || + (current_mname.empty () && current_oname == mo)); + }; + // Current operation number (1-based) in the meta-operation batch. // size_t current_on; -- cgit v1.1