diff options
Diffstat (limited to 'libbuild2/config')
-rw-r--r-- | libbuild2/config/functions.cxx | 40 | ||||
-rw-r--r-- | libbuild2/config/host-config.cxx.in | 3 | ||||
-rw-r--r-- | libbuild2/config/init.cxx | 222 | ||||
-rw-r--r-- | libbuild2/config/module.hxx | 2 | ||||
-rw-r--r-- | libbuild2/config/operation.cxx | 112 | ||||
-rw-r--r-- | libbuild2/config/operation.hxx | 6 | ||||
-rw-r--r-- | libbuild2/config/types.hxx | 25 | ||||
-rw-r--r-- | libbuild2/config/utility.cxx | 61 | ||||
-rw-r--r-- | libbuild2/config/utility.hxx | 38 | ||||
-rw-r--r-- | libbuild2/config/utility.txx | 4 |
10 files changed, 419 insertions, 94 deletions
diff --git a/libbuild2/config/functions.cxx b/libbuild2/config/functions.cxx index 3e1b8a3..b1a61a2 100644 --- a/libbuild2/config/functions.cxx +++ b/libbuild2/config/functions.cxx @@ -23,8 +23,8 @@ namespace build2 // $config.origin() // - // Return the origin of the specified configuration variable value. - // Possible result values and their semantics are as follows: + // Return the origin of the value of the specified configuration + // variable. Possible result values and their semantics are as follows: // // undefined // The variable is undefined. @@ -60,33 +60,15 @@ namespace build2 if (s == nullptr) fail << "config.origin() called out of project" << endf; - string n (convert<string> (move (name))); + switch (origin (*s, convert<string> (move (name))).first) + { + case variable_origin::undefined: return "undefined"; + case variable_origin::default_: return "default"; + case variable_origin::buildfile: return "buildfile"; + case variable_origin::override_: return "override"; + } - // Make sure this is a config.* variable. This could matter since we - // reply on the semantics of value::extra. We could also detect - // special variables like config.booted, some config.config.*, etc., - // (see config_save() for details) but that seems harmless. - // - if (n.compare (0, 7, "config.") != 0) - fail << "non-config.* variable passed to config.origin()" << endf; - - const variable* var (s->ctx.var_pool.find (n)); - - if (var == nullptr) - return "undefined"; - - pair<lookup, size_t> org (s->lookup_original (*var)); - pair<lookup, size_t> ovr (var->overrides == nullptr - ? org - : s->lookup_override (*var, org)); - - if (!ovr.first.defined ()) - return "undefined"; - - if (org.first != ovr.first) - return "override"; - - return org.first->extra ? "default" : "buildfile"; + return ""; // Should not reach. }; // $config.save() @@ -113,7 +95,7 @@ namespace build2 // See save_config() for details. // assert (s->ctx.phase == run_phase::load); - module* mod (s->rw ().find_module<module> (module::name)); + const module* mod (s->find_module<module> (module::name)); if (mod == nullptr) fail << "config.save() called without config module"; diff --git a/libbuild2/config/host-config.cxx.in b/libbuild2/config/host-config.cxx.in index 9e3e0c2..6b1ce77 100644 --- a/libbuild2/config/host-config.cxx.in +++ b/libbuild2/config/host-config.cxx.in @@ -9,5 +9,8 @@ namespace build2 // extern const char host_config[] = R"###($host_config$)###"; extern const char build2_config[] = R"###($build2_config$)###"; + + extern const char host_config_no_warnings[] = R"###($host_config_no_warnings$)###"; + extern const char build2_config_no_warnings[] = R"###($build2_config_no_warnings$)###"; } } diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index 774ffb8..2f134c4 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -26,6 +26,8 @@ namespace build2 { namespace config { + static const file_rule file_rule_ (true /* check_type */); + void functions (function_map&); // functions.cxx @@ -39,7 +41,7 @@ namespace build2 save_environment (const value& d, const value* b, names& storage) { if (b == nullptr) - return make_pair (reverse (d, storage), "="); + return make_pair (reverse (d, storage, true /* reduce */), "="); // The plan is to iterator over environment variables adding those that // are not in base to storage. There is, however, a complication: we may @@ -100,7 +102,10 @@ namespace build2 // reserved to not be valid module names (`build`). We also currently // treat `import` as special. // - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); // NOTE: all config.** variables are by default made (via a pattern) to // be overridable with global visibility. So we must override this if a @@ -205,6 +210,9 @@ namespace build2 #ifndef BUILD2_BOOTSTRAP extern const char host_config[]; extern const char build2_config[]; + + extern const char host_config_no_warnings[]; + extern const char build2_config_no_warnings[]; #endif bool @@ -234,7 +242,7 @@ namespace build2 ? &extra.module_as<module> () : nullptr); - auto& vp (rs.var_pool ()); + auto& vp (rs.var_pool (true /* public */)); // Note: config.* is pattern-typed to global visibility. // @@ -243,12 +251,38 @@ namespace build2 auto& c_v (vp.insert<uint64_t> ("config.version", false /*ovr*/, v_p)); auto& c_l (vp.insert<paths> ("config.config.load", true /* ovr */)); + // Omit loading the configuration from the config.build file (it is + // still loaded from config.config.load if specified). Similar to + // config.config.load, only values specified on this project's root + // scope and global scope are considered. + // + // Note that this variable is not saved in config.build and is expected + // to always be specified as a command line override. + // + auto& c_u (vp.insert<bool> ("config.config.unload", true /*ovr*/)); + // Configuration variables to disfigure. // // The exact semantics is to ignore these variables when loading // config.build (and any files specified in config.config.load), letting // them to take on the default values (more precisely, the current - // implementation undefined them after loading config.build). + // implementation undefined them after loading config.build). See also + // config.config.unload. + // + // Besides names, variables can also be specified as patterns in the + // config.<prefix>.(*|**)[<suffix>] form where `*` matches single + // component names (i.e., `foo` but not `foo.bar`), and `**` matches + // single and multi-component names. Currently only single wildcard (`*` + // or `**`) is supported. Additionally, a pattern in the + // config.<prefix>(*|**) form (i.e., without `.` after <prefix>) matches + // config.<prefix>.(*|**) plus config.<prefix> itself (but not + // config.<prefix>foo). + // + // For example, to disfigure all the project configuration variables + // (while preserving all the module configuration variables; note + // quoting to prevent pattern expansion): + // + // b config.config.disfigure="'config.hello**'" // // Note that this variable is not saved in config.build and is expected // to always be specified as a command line override. @@ -352,9 +386,10 @@ namespace build2 save_null_omitted | save_empty_omitted | save_base, &save_environment); - // Load config.build if one exists followed by extra files specified in - // config.config.load (we don't need to worry about disfigure since we - // will never be init'ed). + // Load config.build if one exists (and unless config.config.unload is + // specified) followed by extra files specified in config.config.load + // (we don't need to worry about disfigure since we will never be + // init'ed). // auto load_config = [&rs, &c_v] (istream& is, const path_name& in, @@ -399,15 +434,37 @@ namespace build2 auto load_config_file = [&load_config] (const path& f, const location& l) { path_name fn (f); - ifdstream ifs; - load_config (open_file_or_stdin (fn, ifs), fn, l); + try + { + ifdstream ifs; + load_config (open_file_or_stdin (fn, ifs), fn, l); + } + catch (const io_error& e) + { + fail << "unable to read buildfile " << fn << ": " << e; + } }; + // Load config.build unless requested not to. + // { - path f (config_file (rs)); + // The same semantics as in config.config.load below. + // + bool u; + { + lookup l (rs[c_u]); + u = (l && + (l.belongs (rs) || l.belongs (ctx.global_scope)) && + cast_false<bool> (l)); + } + + if (!u) + { + path f (config_file (rs)); - if (exists (f)) - load_config_file (f, l); + if (exists (f)) + load_config_file (f, l); + } } if (lookup l = rs[c_l]) @@ -430,14 +487,23 @@ namespace build2 const string& s (f.string ()); - if (s[0] != '~') + if (s.empty ()) + fail << "empty path in config.config.load"; + else if (s[0] != '~') load_config_file (f, l); - else if (s == "~host" || s == "~build2") + else if (s == "~host" || s == "~host-no-warnings" || + s == "~build2" || s == "~build2-no-warnings") { #ifdef BUILD2_BOOTSTRAP assert (false); #else - istringstream is (s[1] == 'h' ? host_config : build2_config); + istringstream is (s[1] == 'h' + ? (s.size () == 5 + ? host_config + : host_config_no_warnings) + : (s.size () == 7 + ? build2_config + : build2_config_no_warnings)); load_config (is, path_name (s), l); #endif } @@ -450,16 +516,112 @@ namespace build2 // Undefine variables specified with config.config.disfigure. // - if (const strings* vs = cast_null<strings> (rs[c_d])) + if (const strings* ns = cast_null<strings> (rs[c_d])) { - for (const string& v: *vs) + auto p (rs.vars.lookup_namespace ("config")); + + for (auto i (p.first); i != p.second; ) { - // An unknown variable can't possibly be defined. + const variable& var (i->first); + + // This can be one of the overrides (__override, __prefix, etc), + // which we skip. // - if (const variable* var = vp.find (v)) + if (!var.override ()) { - rs.vars.erase (*var); // Undefine. + bool m (false); + + for (const string& n: *ns) + { + if (n.compare (0, 7, "config.") != 0) + fail << "config.* variable expected in " + << "config.config.disfigure instead of '" << n << "'"; + + size_t p (n.find ('*')); + + if (p == string::npos) + { + if ((m = var.name == n)) + break; + } + else + { + // Pattern in one of these forms: + // + // config.<prefix>.(*|**)[<suffix>] + // config.<prefix>(*|**) + // + // BTW, an alternative way to handle this would be to + // translate it to a path and use our path_match() machinery, + // similar to how we do it for build config include/exclude. + // Perhaps one day when/if we decide to support multiple + // wildcards. + // + if (p == 7) + fail << "config.<prefix>* pattern expected in " + << "config.config.disfigure instead of '" << n << "'"; + + bool r (n[p + 1] == '*'); // Recursive. + + size_t pe; // Prefix end/size. + if (n[p - 1] != '.') + { + // Second form should have no suffix. + // + if (p + (r ? 2 : 1) != n.size ()) + fail << "config.<prefix>(*|**) pattern expected in " + << "config.config.disfigure instead of '" << n << "'"; + + // Match just <prefix>. + // + if ((m = n.compare (0, p, var.name) == 0)) + break; + + pe = p; + } + else + pe = p - 1; + + // Match <prefix> followed by `.`. + // + if (n.compare (0, pe, var.name, 0, pe) != 0 || + var.name[pe] != '.') + continue; + + // Match suffix. + // + size_t sb (p + (r ? 2 : 1)); // Suffix begin. + size_t sn (n.size () - sb); // Suffix size. + + size_t te; // Stem end. + if (sn == 0) // No suffix. + te = var.name.size (); + else + { + if (var.name.size () < pe + 1 + sn) // Too short. + continue; + + te = var.name.size () - sn; + + if (n.compare (sb, sn, var.name, te, sn) != 0) + continue; + } + + // Match stem. + // + if ((m = r || var.name.find ('.', pe + 1) >= te)) + break; + } + } + + if (m) + { + i = rs.vars.erase (i); // Undefine. + continue; + } } + + ++i; } } @@ -564,19 +726,23 @@ namespace build2 // Register alias and fallback rule for the configure meta-operation. // - // We need this rule for out-of-any-project dependencies (e.g., - // libraries imported from /usr/lib). We are registring it on the - // global scope similar to builtin rules. - // - rs.global_scope ().insert_rule<mtime_target> ( - configure_id, 0, "config.file", file_rule::instance); - rs.insert_rule<alias> (configure_id, 0, "config.alias", alias_rule::instance); // This allows a custom configure rule while doing nothing by default. // rs.insert_rule<target> (configure_id, 0, "config.noop", noop_rule::instance); - rs.insert_rule<file> (configure_id, 0, "config.noop", noop_rule::instance); + + // We need this rule for out-of-any-project dependencies (for example, + // libraries imported from /usr/lib). We are registering it on the + // global scope similar to builtin rules. + // + // Note: use target instead of anything more specific (such as + // mtime_target) in order not to take precedence over the rules above. + // + // See a similar rule in the dist module. + // + rs.global_scope ().insert_rule<target> ( + configure_id, 0, "config.file", file_rule_); return true; } diff --git a/libbuild2/config/module.hxx b/libbuild2/config/module.hxx index 82b79be..8d3ff67 100644 --- a/libbuild2/config/module.hxx +++ b/libbuild2/config/module.hxx @@ -160,7 +160,7 @@ namespace build2 save_module (scope&, const char*, int); const saved_variable* - find_variable (const variable& var) + find_variable (const variable& var) const { auto i (saved_modules.find_sup (var.name)); if (i != saved_modules.end ()) diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index ed98f90..150bf1a 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -61,8 +61,10 @@ namespace build2 path f (src_root / rs.root_extra->out_root_file); - if (verb) - text << (verb >= 2 ? "cat >" : "save ") << f; + if (verb >= 2) + text << "cat >" << f; + else if (verb) + print_diag ("save", f); try { @@ -132,7 +134,8 @@ namespace build2 bool r; if (c.compare (p, 4 , "save") == 0) r = true; else if (c.compare (p, 4 , "drop") == 0) r = false; - else fail << "invalid config.config.persist action '" << c << "'"; + else fail << "invalid config.config.persist action '" << c << "'" + << endf; bool w (false); if ((p += 4) != c.size ()) @@ -161,11 +164,18 @@ namespace build2 // and this function can be called from a buildfile (probably only // during serial execution but still). // + // We could also be configuring multiple projects (including from + // pkg_configure() in bpkg) but feels like we should be ok since we + // only modify this project's root scope data which should not affect + // any other project. + // + // See also save_environment() for a similar issue. + // void save_config (const scope& rs, ostream& os, const path_name& on, bool inherit, - module& mod, + const module& mod, const project_set& projects) { context& ctx (rs.ctx); @@ -179,7 +189,7 @@ namespace build2 if (v) { storage.clear (); - dr << "'" << reverse (v, storage) << "'"; + dr << "'" << reverse (v, storage, true /* reduce */) << "'"; } else dr << "[null]"; @@ -207,6 +217,8 @@ namespace build2 // saved according to config.config.persist potentially warning if the // variable would otherwise be dropped. // + // Note: go straight for the public variable pool. + // auto& vp (ctx.var_pool); for (auto p (rs.vars.lookup_namespace ("config")); @@ -247,6 +259,24 @@ namespace build2 continue; } + // A common reason behind an unused config.import.* value is an + // unused dependency. That is, there is depends in manifest but no + // import in buildfile (or import could be conditional in which case + // depends should also be conditional). So let's suggest this + // possibility. Note that the project name may have been sanitized + // to a variable name. Oh, well, better than nothing. + // + auto info_import = [] (diag_record& dr, const string& var) + { + if (var.compare (0, 14, "config.import.") == 0) + { + size_t p (var.find ('.', 14)); + + dr << info << "potentially unused dependency on " + << string (var, 14, p == string::npos ? p : p - 14); + } + }; + const value& v (p.first->second); pair<bool, bool> r (save_config_variable (*var, @@ -255,7 +285,7 @@ namespace build2 true /* unused */)); if (r.first) // save { - mod.save_variable (*var, 0); + const_cast<module&> (mod).save_variable (*var, 0); if (r.second) // warn { @@ -274,6 +304,7 @@ namespace build2 diag_record dr; dr << warn (on) << "saving no longer used variable " << *var; + info_import (dr, var->name); if (verb >= 2) info_value (dr, v); } @@ -284,6 +315,7 @@ namespace build2 { diag_record dr; dr << warn (on) << "dropping no longer used variable " << *var; + info_import (dr, var->name); info_value (dr, v); } } @@ -509,8 +541,8 @@ namespace build2 // Handle the save_default_commented flag. // - if ((org.first.defined () && org.first->extra) && // Default value. - org.first == ovr.first && // Not overriden. + if (org.first.defined () && org.first->extra == 1 && // Default. + org.first == ovr.first && // No override. (flags & save_default_commented) != 0) { os << first () << '#' << n << " =" << endl; @@ -527,7 +559,7 @@ namespace build2 pair<names_view, const char*> p ( sv.save != nullptr ? sv.save (v, base, storage) - : make_pair (reverse (v, storage), "=")); + : make_pair (reverse (v, storage, true /* reduce */), "=")); // Might becomes empty after a custom save function had at it. // @@ -556,7 +588,7 @@ namespace build2 save_config (const scope& rs, const path& f, bool inherit, - module& mod, + const module& mod, const project_set& projects) { path_name fn (f); @@ -564,8 +596,10 @@ namespace build2 if (f.string () == "-") fn.name = "<stdout>"; - if (verb) - text << (verb >= 2 ? "cat >" : "save ") << fn; + if (verb >= 2) + text << "cat >" << fn; + else if (verb) + print_diag ("save", fn); try { @@ -582,6 +616,9 @@ namespace build2 // Update config.config.environment value for a hermetic configuration. // + // @@ We are modifying the module. See also save_config() for a similar + // issue. + // static void save_environment (scope& rs, module& mod) { @@ -636,6 +673,8 @@ namespace build2 } } + // Note: go straight for the public variable pool. + // value& v (rs.assign (*rs.ctx.var_pool.find ("config.config.environment"))); // Note that setting new config.config.environment value invalidates the @@ -652,9 +691,9 @@ namespace build2 static void configure_project (action a, - scope& rs, + const scope& rs, const variable* c_s, // config.config.save - module& mod, + const module& mod, project_set& projects) { tracer trace ("configure_project"); @@ -674,7 +713,7 @@ namespace build2 // if (out_root != src_root) { - mkdir_p (out_root / rs.root_extra->build_dir); + mkdir_p (out_root / rs.root_extra->build_dir, 1); mkdir (out_root / rs.root_extra->bootstrap_dir, 2); } @@ -688,7 +727,7 @@ namespace build2 // for the other half of this logic). // if (cast_false<bool> (rs["config.config.hermetic"])) - save_environment (rs, mod); + save_environment (const_cast<scope&> (rs), const_cast<module&> (mod)); // Save src-root.build unless out_root is the same as src. // @@ -720,6 +759,11 @@ namespace build2 lookup l (rs[*c_s]); if (l && (l.belongs (rs) || l.belongs (ctx.global_scope))) { + const path& f (cast<path> (l)); + + if (f.empty ()) + fail << "empty path in " << *c_s; + // While writing the complete configuration seems like a natural // default, there might be a desire to take inheritance into // account (if, say, we are exporting at multiple levels). One can @@ -727,8 +771,7 @@ namespace build2 // still want to support this mode somehow in the future (it seems // like an override of config.config.persist should do the trick). // - save_config ( - rs, cast<path> (l), false /* inherit */, mod, projects); + save_config (rs, f, false /* inherit */, mod, projects); } } } @@ -751,14 +794,14 @@ namespace build2 { const dir_path& pd (p.second); dir_path out_nroot (out_root / pd); - scope& nrs (ctx.scopes.find_out (out_nroot).rw ()); + const scope& nrs (ctx.scopes.find_out (out_nroot)); // Skip this subproject if it is not loaded or doesn't use the // config module. // if (nrs.out_path () == out_nroot) { - if (module* m = nrs.find_module<module> (module::name)) + if (const module* m = nrs.find_module<module> (module::name)) { configure_project (a, nrs, c_s, *m, projects); } @@ -811,6 +854,8 @@ namespace build2 // Don't translate default to update. In our case unspecified // means configure everything. // + // Note: see pkg_configure() in bpkg if changing anything here. + // return o; } @@ -847,6 +892,8 @@ namespace build2 static void configure_pre (context&, const values& params, const location& l) { + // Note: see pkg_configure() in bpkg if changing anything here. + // forward (params, "configure", l); // Validate. } @@ -870,7 +917,9 @@ namespace build2 fail (l) << "forwarding to source directory " << rs.src_path (); } else - load (params, rs, buildfile, out_base, src_base, l); // Normal load. + // Normal load. + // + perform_load (params, rs, buildfile, out_base, src_base, l); } static void @@ -890,7 +939,7 @@ namespace build2 ts.push_back (&rs); } else - search (params, rs, bs, bf, tk, l, ts); // Normal search. + perform_search (params, rs, bs, bf, tk, l, ts); // Normal search. } static void @@ -910,6 +959,8 @@ namespace build2 context& ctx (fwd ? ts[0].as<scope> ().ctx : ts[0].as<target> ().ctx); + // Note: go straight for the public variable pool. + // const variable* c_s (ctx.var_pool.find ("config.config.save")); if (c_s->overrides == nullptr) @@ -964,20 +1015,28 @@ namespace build2 ctx.current_operation (*oif); + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, {}, true /* inner */, location ()); + phase_lock pl (ctx, run_phase::match); match_sync (action (configure_id, id), t); + + if (oif->operation_post != nullptr) + oif->operation_post (ctx, {}, true /* inner */); } } configure_project (a, - rs->rw (), + *rs, c_s, - *rs->rw ().find_module<module> (module::name), + *rs->find_module<module> (module::name), projects); } } } + // NOTE: see pkg_configure() in bpkg if changing anything here. + // const meta_operation_info mo_configure { configure_id, "configure", @@ -1276,6 +1335,8 @@ namespace build2 // Add the default config.config.persist value unless there is a custom // one (specified as a command line override). // + // Note: go straight for the public variable pool. + // const variable& var (*ctx.var_pool.find ("config.config.persist")); if (!rs[var].defined ()) @@ -1392,7 +1453,8 @@ namespace build2 string ("config"), /* config_module */ nullopt, /* config_file */ true, /* buildfile */ - "the create meta-operation"); + "the create meta-operation", + 1 /* verbosity */); save_config (ctx, d); } diff --git a/libbuild2/config/operation.hxx b/libbuild2/config/operation.hxx index 9e2a91e..1662941 100644 --- a/libbuild2/config/operation.hxx +++ b/libbuild2/config/operation.hxx @@ -15,8 +15,8 @@ namespace build2 { class module; - extern const meta_operation_info mo_configure; - extern const meta_operation_info mo_disfigure; + LIBBUILD2_SYMEXPORT extern const meta_operation_info mo_configure; + LIBBUILD2_SYMEXPORT extern const meta_operation_info mo_disfigure; const string& preprocess_create (context&, @@ -37,7 +37,7 @@ namespace build2 save_config (const scope& rs, ostream&, const path_name&, bool inherit, - module&, + const module&, const project_set&); // See config.config.hermetic.environment. diff --git a/libbuild2/config/types.hxx b/libbuild2/config/types.hxx new file mode 100644 index 0000000..3cdc5e3 --- /dev/null +++ b/libbuild2/config/types.hxx @@ -0,0 +1,25 @@ +// file : libbuild2/config/types.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_CONFIG_TYPES_HXX +#define LIBBUILD2_CONFIG_TYPES_HXX + +#include <libbuild2/types.hxx> + +namespace build2 +{ + namespace config + { + // The origin of the value of a configuration variable. + // + enum class variable_origin + { + undefined, // Undefined. + default_, // Default value from the config directive. + buildfile, // Value from a buildfile, normally config.build. + override_ // Value from a command line override. + }; + } +} + +#endif // LIBBUILD2_CONFIG_TYPES_HXX diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index a78b263..6574367 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -32,7 +32,7 @@ namespace build2 // Treat an inherited value that was set to default as new. // - if (l.defined () && l->extra) + if (l.defined () && l->extra == 1) n = true; if (var.overrides != nullptr) @@ -81,7 +81,9 @@ namespace build2 const string& n, initializer_list<const char*> ig) { - auto& vp (rs.var_pool ()); + // Note: go straight for the public variable pool. + // + auto& vp (rs.ctx.var_pool); // Search all outer scopes for any value in this namespace. // @@ -128,7 +130,7 @@ namespace build2 // Pattern-typed as bool. // const variable& var ( - rs.var_pool ().insert ("config." + n + ".configured")); + rs.var_pool (true).insert ("config." + n + ".configured")); save_variable (rs, var); @@ -142,7 +144,7 @@ namespace build2 // Pattern-typed as bool. // const variable& var ( - rs.var_pool ().insert ("config." + n + ".configured")); + rs.var_pool (true).insert ("config." + n + ".configured")); save_variable (rs, var); @@ -156,5 +158,56 @@ namespace build2 else return false; } + + pair<variable_origin, lookup> + origin (const scope& rs, const string& n) + { + // Note: go straight for the public variable pool. + // + const variable* var (rs.ctx.var_pool.find (n)); + + if (var == nullptr) + { + if (n.compare (0, 7, "config.") != 0) + throw invalid_argument ("config.* variable expected"); + + return make_pair (variable_origin::undefined, lookup ()); + } + + return origin (rs, *var); + } + + pair<variable_origin, lookup> + origin (const scope& rs, const variable& var) + { + // Make sure this is a config.* variable. This could matter since we + // rely on the semantics of value::extra. We could also detect + // special variables like config.booted, some config.config.*, etc., + // (see config_save() for details) but that seems harmless. + // + if (var.name.compare (0, 7, "config.") != 0) + throw invalid_argument ("config.* variable expected"); + + return origin (rs, var, rs.lookup_original (var)); + } + + pair<variable_origin, lookup> + origin (const scope& rs, const variable& var, pair<lookup, size_t> org) + { + pair<lookup, size_t> ovr (var.overrides == nullptr + ? org + : rs.lookup_override (var, org)); + + if (!ovr.first.defined ()) + return make_pair (variable_origin::undefined, lookup ()); + + if (org.first != ovr.first) + return make_pair (variable_origin::override_, ovr.first); + + return make_pair (org.first->extra == 1 + ? variable_origin::default_ + : variable_origin::buildfile, + org.first); + } } } diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx index cb82ea6..1e2ff53 100644 --- a/libbuild2/config/utility.hxx +++ b/libbuild2/config/utility.hxx @@ -11,6 +11,8 @@ #include <libbuild2/scope.hxx> #include <libbuild2/variable.hxx> +#include <libbuild2/config/types.hxx> + #include <libbuild2/export.hxx> namespace build2 @@ -62,6 +64,11 @@ namespace build2 // omitted value(s) is the default (see a note in lookup_config() // documentation for details). // + // The below lookup_*() functions mark the default value by setting + // value::extra to 1. Note that it's exactly 1 and not "not 0" since other + // values could have other meaning (see, for example, package skeleton + // in bpkg). + // const uint64_t save_default_commented = 0x01; // Based on value::extra. const uint64_t save_null_omitted = 0x02; // Treat NULL as undefined. const uint64_t save_empty_omitted = 0x04; // Treat empty as undefined. @@ -264,6 +271,8 @@ namespace build2 const string& var, uint64_t save_flags = 0) { + // Note: go straight for the public variable pool. + // return lookup_config (rs, rs.ctx.var_pool[var], save_flags); } @@ -273,6 +282,8 @@ namespace build2 const string& var, uint64_t save_flags = 0) { + // Note: go straight for the public variable pool. + // return lookup_config (new_value, rs, rs.ctx.var_pool[var], save_flags); } @@ -360,6 +371,8 @@ namespace build2 uint64_t save_flags = 0, bool override = false) { + // Note: go straight for the public variable pool. + // return lookup_config (rs, rs.ctx.var_pool[var], std::forward<T> (default_value), // VC14 @@ -376,6 +389,8 @@ namespace build2 uint64_t save_flags = 0, bool override = false) { + // Note: go straight for the public variable pool. + // return lookup_config (new_value, rs, rs.ctx.var_pool[var], @@ -420,7 +435,7 @@ namespace build2 const V* cv ( cast_null<V> ( lookup_config (rs, - rs.var_pool ().insert<V> ("config." + var), + rs.var_pool (true).insert<V> ("config." + var), std::forward<T> (default_value)))); // VC14 value& v (bs.assign<V> (move (var))); @@ -438,7 +453,7 @@ namespace build2 const V* cv ( cast_null<V> ( lookup_config (rs, - rs.var_pool ().insert<V> ("config." + var), + rs.var_pool (true).insert<V> ("config." + var), std::forward<T> (default_value)))); // VC14 value& v (bs.append<V> (move (var))); @@ -501,6 +516,25 @@ namespace build2 // LIBBUILD2_SYMEXPORT bool unconfigured (scope& rs, const string& var, bool value); + + // Return the origin of the value of the specified configuration variable + // plus the value itself. See $config.origin() for details. + // + // Throws invalid_argument if the passed variable is not config.*. + // + LIBBUILD2_SYMEXPORT pair<variable_origin, lookup> + origin (const scope& rs, const string& name); + + LIBBUILD2_SYMEXPORT pair<variable_origin, lookup> + origin (const scope& rs, const variable&); + + // As above but using the result of scope::lookup_original() or + // semantically equivalent (e.g., lookup_namespace()). + // + // Note that this version does not check that the variable is config.*. + // + LIBBUILD2_SYMEXPORT pair<variable_origin, lookup> + origin (const scope& rs, const variable&, pair<lookup, size_t> original); } } diff --git a/libbuild2/config/utility.txx b/libbuild2/config/utility.txx index b88f76c..71e41fd 100644 --- a/libbuild2/config/utility.txx +++ b/libbuild2/config/utility.txx @@ -58,7 +58,7 @@ namespace build2 if (!l.defined () || (def_ovr && !l.belongs (rs))) { value& v (rs.assign (var) = std::forward<T> (def_val)); // VC14 - v.extra = true; // Default value flag. + v.extra = 1; // Default value flag. n = (sflags & save_default_commented) == 0; // Absence means default. l = lookup (v, var, rs); @@ -66,7 +66,7 @@ namespace build2 } // Treat an inherited value that was set to default as new. // - else if (l->extra) + else if (l->extra == 1) n = (sflags & save_default_commented) == 0; // Absence means default. if (var.overrides != nullptr) |