From b39ce46b80ef5cccc592398e0a74ba8d02742ead Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 4 Nov 2019 13:34:52 +0200 Subject: Add $config.export() function This is similar to the config.export variable functionality except it can be called from within buildfiles. Note that this function can only be used during configure unless the config module creation was forced for other meta-operations with config.module=true in bootstrap.build. --- libbuild2/config/functions.cxx | 52 ++++++++++++++++++++++++++++ libbuild2/config/init.cxx | 31 +++++++++++++---- libbuild2/config/module.hxx | 10 +++--- libbuild2/config/operation.cxx | 78 ++++++++++++++++++++++++++---------------- libbuild2/config/operation.hxx | 16 +++++++++ libbuild2/config/utility.cxx | 48 ++++++++++---------------- libbuild2/config/utility.txx | 3 +- libbuild2/dist/init.cxx | 3 +- libbuild2/install/init.cxx | 7 ++-- libbuild2/scope.hxx | 8 ++--- libbuild2/test/init.cxx | 3 +- 11 files changed, 174 insertions(+), 85 deletions(-) create mode 100644 libbuild2/config/functions.cxx (limited to 'libbuild2') diff --git a/libbuild2/config/functions.cxx b/libbuild2/config/functions.cxx new file mode 100644 index 0000000..79447a4 --- /dev/null +++ b/libbuild2/config/functions.cxx @@ -0,0 +1,52 @@ +// file : libbuild2/config/functions.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include + +#include + +using namespace std; + +namespace build2 +{ + namespace config + { + void + functions (function_map& m) + { + function_family f (m, "config"); + + // Return the configuration file contents as a string, similar to the + // config.export variable functionality. + // + // Note that this function can only be used during configure unless the + // config module creation was forced for other meta-operations with + // config.module=true in bootstrap.build. + // + f[".export"] = [] (const scope* s) + { + if (s == nullptr) + fail << "config.export() called out of scope" << endf; + + s = s->root_scope (); + + if (s == nullptr) + fail << "config.export() called out of project" << endf; + + ostringstream os; + + // Empty project set should is ok as long as inherit is false. + // + project_set ps; + save_config (*s, os, "config.export()", false /* inherit */, ps); + + return os.str (); + }; + } + } +} diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index 4e1890a..cda34fa 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -9,6 +9,7 @@ #include #include #include +#include #include // exists() #include @@ -23,15 +24,19 @@ namespace build2 { namespace config { + void + functions (function_map&); // functions.cxx + bool boot (scope& rs, const location&, unique_ptr& mod) { tracer trace ("config::boot"); + context& ctx (rs.ctx); + l5 ([&]{trace << "for " << rs;}); - const string& mname (rs.ctx.current_mname); - const string& oname (rs.ctx.current_oname); + auto& vp (rs.ctx.var_pool.rw (rs)); // While config.import (see below) could theoretically be specified in a // buildfile, config.export is expected to always be specified as a @@ -46,8 +51,17 @@ namespace build2 // forced with config.module (useful if we need to call $config.export() // during other meta-operations). // - if (( mname == "configure" || mname == "create") || - (mname.empty () && (oname == "configure" || oname == "create"))) + // 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. + // + auto& c_m (vp.insert ("config.module", false /*ovr*/)); + + const string& mname (ctx.current_mname); + const string& oname (ctx.current_oname); + + if (( mname == "configure" || mname == "create") || + (mname.empty () && (oname == "configure" || oname == "create")) || + cast_false (rs.vars[c_m])) { unique_ptr m (new module); @@ -59,6 +73,12 @@ namespace build2 mod = move (m); } + // Register the config function family if this is the first instance of + // the config module. + // + if (!function_family::defined (ctx.functions, "config")) + functions (ctx.functions); + // Register meta-operations. Note that we don't register create_id // since it will be pre-processed into configure. // @@ -85,8 +105,7 @@ namespace build2 return true; } - const dir_path& out_root (rs.out_path ()); - l5 ([&]{trace << "for " << out_root;}); + l5 ([&]{trace << "for " << rs;}); assert (config_hints.empty ()); // We don't known any hints. diff --git a/libbuild2/config/module.hxx b/libbuild2/config/module.hxx index 6222319..4b9d4f2 100644 --- a/libbuild2/config/module.hxx +++ b/libbuild2/config/module.hxx @@ -19,11 +19,11 @@ namespace build2 { namespace config { - // An ordered list of modules each with an ordered list of list of - // config.* variables and their "save flags" (see save_variable()) that - // are used (as opposed to just being specified) in this configuration. - // Populated by the config utility functions (required(), optional()) - // and saved in the order populated. + // An ordered list of build system modules each with an ordered list of + // list of config.* variables and their "save flags" (see save_variable()) + // that are used (as opposed to just being specified) in this + // configuration. Populated by the config utility functions (required(), + // optional()) and saved in the order populated. // struct saved_variable { diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index 9050645..c9b5ef6 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -4,8 +4,6 @@ #include -#include - #include #include #include @@ -90,26 +88,23 @@ namespace build2 // If inherit is false, then don't rely on inheritance from outer scopes // (used for config.export). // - static void + void save_config (const scope& rs, - const path& f, + ostream& os, + const string& name, bool inherit, const project_set& projects) { context& ctx (rs.ctx); - const module& mod (*rs.lookup_module (module::name)); + const module* mod (rs.lookup_module (module::name)); - const string& df (f.string () != "-" ? f.string () : ""); - - if (verb) - text << (verb >= 2 ? "cat >" : "save ") << df; + if (mod == nullptr) + fail << "no configuration information available during this meta-" + << "operation"; try { - ofdstream ofs; - ostream& os (open_file_or_stdout (f, ofs)); - os << "# Created automatically by the config module, but feel " << "free to edit." << endl << "#" << endl; @@ -132,7 +127,7 @@ namespace build2 // names storage; - for (auto p: mod.saved_modules.order) + for (auto p: mod->saved_modules.order) { const string& sname (p.second->first); const saved_variables& svars (p.second->second); @@ -170,7 +165,8 @@ namespace build2 // still valid and we should probably continue using them but we // definitely want to move them to our config.build since they // will be dropped from the amalgamation's config.build on the - // next reconfigure. Let's also warn the user just in case. + // next reconfigure. Let's also warn the user just in case, + // unless there is no module and thus we couldn't really check. // // There is also another case that falls under this now that // overrides are by default amalgamation-wide rather than just @@ -178,13 +174,13 @@ namespace build2 // subproject but the override is now set on the outer project's // root. // - bool found (false); + bool found (false), checked (true); const scope* r (&rs); while ((r = r->parent_scope ()->root_scope ()) != nullptr) { if (l.belongs (*r)) { - // Find the config module. + // Find the config module (might not be there). // if (auto* m = r->lookup_module (module::name)) { @@ -215,6 +211,8 @@ namespace build2 found = false; } } + else + checked = false; break; } @@ -229,32 +227,30 @@ namespace build2 } else { - location loc (&f); - // If this value is not defined in a project's root scope, // then something is broken. // if (r == nullptr) - fail (loc) << "inherited variable " << var << " value " - << "is not from a root scope"; + fail << name << ": inherited variable " << var << " value " + << "is not from a root scope"; // If none of the outer project's configurations use this - // value, then we warn and save as our own. One special case - // where we don't want to warn the user is if the variable is - // overriden. + // value, then we warn (unless we couldn't check) and save as + // our own. One special case where we don't want to warn the + // user is if the variable is overriden. // - if (org.first == ovr.first) + if (checked && org.first == ovr.first) { diag_record dr; - dr << warn (loc) << "saving previously inherited variable " - << var; + dr << warn << name << ": saving previously inherited " + << "variable " << var; - dr << info (loc) << "because project " << *r - << " no longer uses it in its configuration"; + dr << info << "because project " << *r << " no longer uses " + << "it in its configuration"; if (verb >= 2) { - dr << info (loc) << "variable value: "; + dr << info << "variable value: "; if (*l) { @@ -322,12 +318,34 @@ namespace build2 os << n << " = [null]" << endl; } } + } + catch (const io_error& e) + { + fail << "unable to write " << name << ": " << e; + } + } + static void + save_config (const scope& rs, + const path& f, + bool inherit, + const project_set& projects) + { + const string& fs (f.string () != "-" ? f.string () : ""); + + if (verb) + text << (verb >= 2 ? "cat >" : "save ") << fs; + + try + { + ofdstream ofs; + ostream& os (); + save_config (rs, open_file_or_stdout (f, ofs), fs, inherit, projects); ofs.close (); } catch (const io_error& e) { - fail << "unable to write " << df << ": " << e; + fail << "unable to write " << fs << ": " << e; } } diff --git a/libbuild2/config/operation.hxx b/libbuild2/config/operation.hxx index 8b2a29d..9ec854a 100644 --- a/libbuild2/config/operation.hxx +++ b/libbuild2/config/operation.hxx @@ -5,6 +5,8 @@ #ifndef LIBBUILD2_CONFIG_OPERATION_HXX #define LIBBUILD2_CONFIG_OPERATION_HXX +#include + #include #include @@ -23,6 +25,20 @@ namespace build2 vector_view&, bool, const location&); + + // Configuration exporting. + // + using project_set = std::set; // Pointers for comparison. + + // If inherit is false, then don't rely on inheritance from outer scopes + // (used for config.export). In this case the already configured project + // set can be empty. + // + void + save_config (const scope& rs, + ostream&, const string& name, + bool inherit, + const project_set&); } } diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index 355e896..8a99a8b 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -18,12 +18,12 @@ namespace build2 namespace config { pair - omitted (scope& r, const variable& var) + omitted (scope& rs, const variable& var) { // This is a stripped-down version of the required() twisted // implementation. - pair org (r.find_original (var)); + pair org (rs.find_original (var)); bool n (false); // New flag. lookup l (org.first); @@ -35,7 +35,7 @@ namespace build2 if (var.overrides != nullptr) { - pair ovr (r.find_override (var, move (org))); + pair ovr (rs.find_override (var, move (org))); if (l != ovr.first) // Overriden? { @@ -46,26 +46,25 @@ namespace build2 } } - if (l.defined () && r.ctx.current_mif->id == configure_id) - save_variable (r, var); + if (l.defined ()) + save_variable (rs, var); return pair (l, n); } lookup - optional (scope& r, const variable& var) + optional (scope& rs, const variable& var) { - if (r.ctx.current_mif->id == configure_id) - save_variable (r, var); + save_variable (rs, var); - auto l (r[var]); + auto l (rs[var]); return l.defined () ? l - : lookup (r.assign (var), var, r); // NULL. + : lookup (rs.assign (var), var, rs); // NULL. } bool - specified (scope& r, const string& n) + specified (scope& rs, const string& n) { // Search all outer scopes for any value in this namespace. // @@ -75,8 +74,8 @@ namespace build2 // any original values, they will be "visible"; see find_override() for // details. // - const variable& vns (r.ctx.var_pool.rw (r).insert ("config." + n)); - for (scope* s (&r); s != nullptr; s = s->parent_scope ()) + const variable& vns (rs.ctx.var_pool.rw (rs).insert ("config." + n)); + for (scope* s (&rs); s != nullptr; s = s->parent_scope ()) { for (auto p (s->vars.find_namespace (vns)); p.first != p.second; @@ -103,8 +102,7 @@ namespace build2 const variable& var ( rs.ctx.var_pool.rw (rs).insert ("config." + n + ".configured")); - if (rs.ctx.current_mif->id == configure_id) - save_variable (rs, var); + save_variable (rs, var); auto l (rs[var]); // Include inherited values. return l && !cast (l); @@ -118,8 +116,7 @@ namespace build2 const variable& var ( rs.ctx.var_pool.rw (rs).insert ("config." + n + ".configured")); - if (rs.ctx.current_mif->id == configure_id) - save_variable (rs, var); + save_variable (rs, var); value& x (rs.assign (var)); @@ -133,25 +130,16 @@ namespace build2 } void - save_variable (scope& r, const variable& var, uint64_t flags) + save_variable (scope& rs, const variable& var, uint64_t flags) { - if (r.ctx.current_mif->id != configure_id) - return; - - // The project might not be using the config module. But then how - // could we be configuring it? Good question. - // - if (module* m = r.lookup_module (module::name)) + if (module* m = rs.lookup_module (module::name)) m->save_variable (var, flags); } void - save_module (scope& r, const char* name, int prio) + save_module (scope& rs, const char* name, int prio) { - if (r.ctx.current_mif->id != configure_id) - return; - - if (module* m = r.lookup_module (module::name)) + if (module* m = rs.lookup_module (module::name)) m->save_module (name, prio); } diff --git a/libbuild2/config/utility.txx b/libbuild2/config/utility.txx index 4c4e305..8c92f86 100644 --- a/libbuild2/config/utility.txx +++ b/libbuild2/config/utility.txx @@ -19,8 +19,7 @@ namespace build2 { // Note: see also omitted() if changing anything here. - if (root.ctx.current_mif->id == configure_id) - save_variable (root, var, save_flags); + save_variable (root, var, save_flags); pair org (root.find_original (var)); diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx index c6ffb67..bea8917 100644 --- a/libbuild2/dist/init.cxx +++ b/libbuild2/dist/init.cxx @@ -99,8 +99,7 @@ namespace build2 return true; } - const dir_path& out_root (rs.out_path ()); - l5 ([&]{trace << "for " << out_root;}); + l5 ([&]{trace << "for " << rs;}); assert (config_hints.empty ()); // We don't known any hints. diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 445f2b1..1054e83 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -137,8 +137,8 @@ namespace build2 context& ctx (rs.ctx); - // Register install function family if this is the first instance of the - // install modules. + // Register the install function family if this is the first instance of + // the install modules. // if (!function_family::defined (ctx.functions, "install")) functions (ctx.functions); @@ -188,8 +188,7 @@ namespace build2 return true; } - const dir_path& out_root (rs.out_path ()); - l5 ([&]{trace << "for " << out_root;}); + l5 ([&]{trace << "for " << rs;}); assert (config_hints.empty ()); // We don't known any hints. diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx index 2737958..cd8fcb2 100644 --- a/libbuild2/scope.hxx +++ b/libbuild2/scope.hxx @@ -51,10 +51,10 @@ namespace build2 scope* parent_scope () {return parent_;} const scope* parent_scope () const {return parent_;} - // Root scope of this scope or NULL if this scope is not (yet) - // in any (known) project. Note that if the scope itself is - // root, then this function return this. To get to the outer - // root, query the root scope of the parent. + // Root scope of this scope or NULL if this scope is not (yet) in any + // (known) project. Note that if the scope itself is root, then this + // function return this. To get to the outer root, query the root scope of + // the parent. // scope* root_scope () {return root_;} const scope* root_scope () const {return root_;} diff --git a/libbuild2/test/init.cxx b/libbuild2/test/init.cxx index 5681b37..af112fb 100644 --- a/libbuild2/test/init.cxx +++ b/libbuild2/test/init.cxx @@ -137,8 +137,7 @@ namespace build2 return true; } - const dir_path& out_root (rs.out_path ()); - l5 ([&]{trace << "for " << out_root;}); + l5 ([&]{trace << "for " << rs;}); assert (mod != nullptr); module& m (static_cast (*mod)); -- cgit v1.1