diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-04-11 07:57:19 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-04-11 07:57:19 +0200 |
commit | 0342dc2fcdd78ef28a4e59d84193a3807068d726 (patch) | |
tree | e750c3062d6ff54f0d409fe1a25984b7e78592c8 | |
parent | 5f7c3f923de106f9d204a8f3500274731ae84fd9 (diff) |
New configuration logic, iteration 1
31 files changed, 338 insertions, 257 deletions
diff --git a/build2/bin/module b/build2/bin/module index 765eb2c..44dfbc7 100644 --- a/build2/bin/module +++ b/build2/bin/module @@ -16,7 +16,7 @@ namespace build2 { extern "C" bool bin_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index 5b1d8ca..866bab2 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -34,7 +34,7 @@ namespace build2 bin_init (scope& r, scope& b, const location&, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool) { @@ -47,17 +47,16 @@ namespace build2 { auto& v (var_pool); - // @@ OVR + // Note: some overridable, some not. // + v.insert<path> ("config.bin.ar", true); + v.insert<path> ("config.bin.ranlib", true); - v.insert<path> ("config.bin.ar"); - v.insert<path> ("config.bin.ranlib"); - - v.insert<string> ("config.bin.lib"); - v.insert<strings> ("config.bin.exe.lib"); - v.insert<strings> ("config.bin.liba.lib"); - v.insert<strings> ("config.bin.libso.lib"); - v.insert<dir_paths> ("config.bin.rpath"); + v.insert<string> ("config.bin.lib", true); + v.insert<strings> ("config.bin.exe.lib", true); + v.insert<strings> ("config.bin.liba.lib", true); + v.insert<strings> ("config.bin.libso.lib", true); + v.insert<dir_paths> ("config.bin.rpath", true); v.insert<string> ("bin.lib"); v.insert<strings> ("bin.exe.lib"); @@ -65,7 +64,7 @@ namespace build2 v.insert<strings> ("bin.libso.lib"); v.insert<dir_paths> ("bin.rpath"); - v.insert<string> ("bin.libprefix"); + v.insert<string> ("bin.libprefix", true); } // Register target types. @@ -158,8 +157,8 @@ namespace build2 // This one is optional and we merge it into bin.rpath, if any. // See the cxx module for details on merging. // - if (const value& v = config::optional (r, "config.bin.rpath")) - b.assign ("bin.rpath") += cast<dir_paths> (v); + b.assign ("bin.rpath") += cast_null<dir_paths> ( + config::optional (r, "config.bin.rpath")); // config.bin.ar // config.bin.ranlib diff --git a/build2/cli/module b/build2/cli/module index ab629f1..0469b2c 100644 --- a/build2/cli/module +++ b/build2/cli/module @@ -16,7 +16,7 @@ namespace build2 { extern "C" bool cli_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx index bf975e7..7b98e93 100644 --- a/build2/cli/module.cxx +++ b/build2/cli/module.cxx @@ -29,7 +29,7 @@ namespace build2 cli_init (scope& root, scope& base, const location& loc, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool optional) { @@ -55,13 +55,11 @@ namespace build2 { auto& v (var_pool); - // @@ OVR - - v.insert<bool> ("config.cli.configured"); - - v.insert<path> ("config.cli"); + // Note: some overridable, some not. + // + v.insert<path> ("config.cli", true); + v.insert<strings> ("config.cli.options", true); - v.insert<strings> ("config.cli.options"); v.insert<strings> ("cli.options"); } @@ -88,13 +86,8 @@ namespace build2 // Don't re-run tests if the configuration says we are unconfigured. // - if (optional) - { - auto l (root["config.cli.configured"]); - - if (l && !cast<bool> (l)) + if (optional && config::unconfigured (root, "config.cli")) return false; - } // config.cli // @@ -167,7 +160,7 @@ namespace build2 // Note that we are unconfigured so that we don't keep re-testing // this on each run. // - root.assign ("config.cli.configured") = false; + config::unconfigured (root, "config.cli", true); if (verb >= 2) text << cli << " not found, leaving cli module unconfigured"; @@ -198,7 +191,9 @@ namespace build2 // Clear the unconfigured flag, if any. // - root.assign ("config.cli.configured") = true; + // @@ Get rid of needing to do this. + // + config::unconfigured (root, "config.cli", false); if (!ver.empty () && verb >= 2) text << cli << " " << ver; @@ -210,8 +205,8 @@ namespace build2 // cli.* variables. See the cxx module for more information on // this merging semantics and some of its tricky aspects. // - if (const value& v = config::optional (root, "config.cli.options")) - base.assign ("cli.options") += cast<strings> (v); + base.assign ("cli.options") += cast_null<strings> ( + config::optional (root, "config.cli.options")); // Register our rules. // diff --git a/build2/config/module b/build2/config/module index 5f3c826..6012442 100644 --- a/build2/config/module +++ b/build2/config/module @@ -5,21 +5,35 @@ #ifndef BUILD2_CONFIG_MODULE #define BUILD2_CONFIG_MODULE +#include <butl/prefix-map> + #include <build2/types> #include <build2/utility> #include <build2/module> +#include <build2/variable> namespace build2 { namespace config { + struct module: module_base + { + // A sorted list of config.* variables and flags (currently unused) that + // are used (as opposed to just specified) in this configuration. + // Populated by the config utility functions (required(), optional()) + // + butl::prefix_map<variable_cref, uint64_t, '.'> vars; + + static const string name; + }; + extern "C" void - config_boot (scope&, const location&, unique_ptr<module>&); + config_boot (scope&, const location&, unique_ptr<module_base>&); extern "C" bool config_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/config/module.cxx b/build2/config/module.cxx index 6d2bd70..16eabe9 100644 --- a/build2/config/module.cxx +++ b/build2/config/module.cxx @@ -9,6 +9,7 @@ #include <build2/file> #include <build2/rule> #include <build2/scope> +#include <build2/context> #include <build2/diagnostics> #include <build2/config/operation> @@ -20,12 +21,14 @@ namespace build2 { namespace config { + const string module::name ("config"); + //@@ Same as in operation.cxx // static const path config_file ("build/config.build"); extern "C" void - config_boot (scope& root, const location&, unique_ptr<module>&) + config_boot (scope& root, const location&, unique_ptr<module_base>&) { tracer trace ("config::boot"); @@ -54,7 +57,7 @@ namespace build2 config_init (scope& root, scope&, const location& l, - unique_ptr<module>&, + unique_ptr<module_base>& mod, bool first, bool) { @@ -68,6 +71,11 @@ namespace build2 l5 ([&]{trace << "for " << root.out_path ();}); + // Only create the module if we are configuring. + // + if (current_mif->id == configure_id) + mod.reset (new module); + // Register alias and fallback rule for the configure meta-operation. // { diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index e75f8d0..d1a01ea 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -15,6 +15,8 @@ #include <build2/algorithm> #include <build2/diagnostics> +#include <build2/config/module> + using namespace std; using namespace butl; @@ -51,8 +53,6 @@ namespace build2 ofs.exceptions (ofstream::failbit | ofstream::badbit); - //@@ TODO: quote path - // ofs << "# Created automatically by the config module." << endl << "#" << endl << "src_root = " << src_root << endl; @@ -72,6 +72,8 @@ namespace build2 if (verb) text << (verb >= 2 ? "config::save " : "save ") << f; + const module& mod (*root.modules.lookup<const module> (module::name)); + try { ofstream ofs (f.string ()); @@ -80,8 +82,8 @@ namespace build2 ofs.exceptions (ofstream::failbit | ofstream::badbit); - ofs << "# Created automatically by the config module, but" << endl - << "# feel free to edit." << endl + ofs << "# Created automatically by the config module, but feel " << + "free to edit." << endl << "#" << endl; if (auto l = root.vars["amalgamation"]) @@ -92,28 +94,33 @@ namespace build2 << "#" << endl; } - // Save all the variables in the config namespace that are set - // on the project's root scope. + // Save config variables. // names storage; - for (auto p (root.vars.find_namespace ("config")); - p.first != p.second; - ++p.first) + for (const auto& p: mod.vars) { - const variable& var (p.first->first); - const value& val (p.first->second); - const string& n (var.name); + const variable& var (p.first); - // Skip special variables. + lookup l (root[var]); + + // We only write values that are set on our root scope or are global + // overrides. Anything in-between is inherited. We might also not + // have any value at all (see unconfigured()). // - if (n == "config.loaded" || - n == "config.configured") + if (!l.defined () || + !(l.belongs (root) || l.belongs (*global_scope))) continue; - // We will only write config.*.configured if it is false - // (true is implied by its absence). + const value& val (*l); + + // We will only write config.*.configured if it is false (true is + // implied by its absence). + // + // @@ Do we still need this? // + const string& n (var.name); + if (n.size () > 11 && n.compare (n.size () - 11, 11, ".configured") == 0) { @@ -121,24 +128,13 @@ namespace build2 continue; } - // Warn the user if the value that we are saving differs - // from the one they specified on the command line. - // - auto l ((*global_scope)[var]); - if (l.defined () && *l != val) - { - warn << "variable " << var.name << " configured value " - << "differs from command line value" << - info << "reconfigure the project to use command line value"; - } - if (val) { storage.clear (); ofs << var.name << " = " << reverse (val, storage) << endl; } else - ofs << var.name << " = #[null]" << endl; // @@ TODO: [null] + ofs << var.name << " = [null]" << endl; } } catch (const ofstream::failure&) @@ -148,13 +144,19 @@ namespace build2 } static void - configure_project (action a, scope& root) + configure_project (action a, scope& root, set<scope*>& projects) { tracer trace ("configure_project"); const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); + if (!projects.insert (&root).second) + { + l5 ([&]{trace << "skipping already configured " << out_root;}); + return; + } + // Make sure the directories exist. // if (out_root != src_root) @@ -200,7 +202,7 @@ namespace build2 if (nroot.out_path () != out_nroot) // This subproject not loaded. continue; - configure_project (a, nroot); + configure_project (a, nroot, projects); } } } @@ -219,6 +221,8 @@ namespace build2 // callbacks here since the meta operation is configure and we // know what we are doing. // + set<scope*> projects; + for (void* v: ts) { target& t (*static_cast<target*> (v)); @@ -243,11 +247,12 @@ namespace build2 match (action (configure_id, id), t); } - configure_project (a, *rs); + configure_project (a, *rs, projects); } } meta_operation_info configure { + configure_id, "configure", "configure", "configuring", @@ -299,15 +304,21 @@ namespace build2 disfigure_match (action, action_targets&) {} static bool - disfigure_project (action a, scope& root) + disfigure_project (action a, scope& root, set<scope*>& projects) { tracer trace ("disfigure_project"); - bool m (false); // Keep track of whether we actually did anything. - const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); + if (!projects.insert (&root).second) + { + l5 ([&]{trace << "skipping already disfigured " << out_root;}); + return true; + } + + bool m (false); // Keep track of whether we actually did anything. + // Disfigure subprojects. Since we don't load buildfiles during // disfigure, we do it for all known subprojects. // @@ -338,7 +349,7 @@ namespace build2 bootstrap_src (nroot); } - m = disfigure_project (a, nroot) || m; + m = disfigure_project (a, nroot, projects) || m; // We use mkdir_p() to create the out_root of a subproject // which means there could be empty parent directories left @@ -408,11 +419,13 @@ namespace build2 { tracer trace ("disfigure_execute"); + set<scope*> projects; + for (void* v: ts) { scope& root (*static_cast<scope*> (v)); - if (!disfigure_project (a, root)) + if (!disfigure_project (a, root, projects)) { // Create a dir{$out_root/} target to signify the project's // root in diagnostics. Not very clean but seems harmless. @@ -428,6 +441,7 @@ namespace build2 } meta_operation_info disfigure { + disfigure_id, "disfigure", "disfigure", "disfiguring", diff --git a/build2/config/utility b/build2/config/utility index 9218106..451df30 100644 --- a/build2/config/utility +++ b/build2/config/utility @@ -19,12 +19,14 @@ namespace build2 { // Set, if necessary, a required config.* variable. // - // If override is true and the variable doesn't come from this root - // scope or from the command line, then its value is "overridden" - // for this root scope. + // If override is true and the variable doesn't come from this root scope + // or from the command line (i.e., it is inherited from the amalgamtion), + // then its value is "overridden" for this root scope. // - // Return the reference to the value as well as the indication of - // whether the variable has actually been set. + // Return the reference to the value as well as the indication of whether + // the value is "new", that is, it was either set (including override) or + // it came from the command line and was not inherited. This is usually + // used to test the new value. // template <typename T> pair<reference_wrapper<const value>, bool> @@ -68,19 +70,6 @@ namespace build2 return optional (root, var_pool.find (var)); } - // As above but assumes the value is dir_path and makes it - // absolute if the value specified on the command line is - // relative. - // - const value& - optional_absolute (scope& root, const variable&); - - inline const value& - optional_absolute (scope& root, const string& var) - { - return optional_absolute (root, var_pool.find (var)); - } - // Check whether there are any variables specified from the config // namespace. The idea is that we can check if there are any, say, // config.install.* values. If there are none, then we can assume @@ -90,10 +79,25 @@ namespace build2 // // Note that this function detects and ignores the special // config.*.configured variable which may be used by a module to - // "remember" that it is unconfigured. + // "remember" that it is unconfigured (e.g., in order to avoid re- + // running the tests, etc). // bool specified (scope& root, const string& ns); + + // + // + bool + unconfigured (scope& root, const string& ns); + + void + unconfigured (scope& root, const string& ns, bool); + + // Enter the variable so that it is saved during configuration. See + // config::module. + // + void + save_variable (scope& root, const variable&, uint64_t flags = 0); } } diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx index e96a896..1dbf3d3 100644 --- a/build2/config/utility.cxx +++ b/build2/config/utility.cxx @@ -6,53 +6,37 @@ #include <build2/context> +#include <build2/config/module> + using namespace std; namespace build2 { namespace config { - const value& - optional (scope& root, const variable& var) + void + save_variable (scope& root, const variable& var, uint64_t flags) { - auto l (root[var]); - - return l.defined () - ? l.belongs (*global_scope) ? (root.assign (var) = *l) : *l - : root.assign (var); // NULL + if (current_mif->id == configure_id) + { + // The project might not be using the config module. But then how + // could we be configuring it? Good question. + // + if (module* mod = root.modules.lookup<module> (module::name)) + mod->vars.emplace (var, flags); + } } const value& - optional_absolute (scope& root, const variable& var) + optional (scope& root, const variable& var) { - auto l (root[var]); + if (current_mif->id == configure_id) + save_variable (root, var); - if (!l.defined ()) - return root.assign (var); // NULL - - if (!l.belongs (*global_scope)) // Value from (some) root scope. - return *l; - - // Make the command-line value absolute. This is necessary to avoid - // a warning issued by the config module about global/root scope - // value mismatch. - // - // @@ CMDVAR - // - value& v (const_cast<value&> (*l)); - - if (v && !v.empty ()) - { - dir_path& d (cast<dir_path> (v)); - - if (d.relative ()) - { - d = work / d; - d.normalize (); - } - } - - return root.assign (var) = v; + auto l (root[var]); + return l.defined () + ? *l + : root.assign (var); // NULL. } bool @@ -60,6 +44,12 @@ namespace build2 { // Search all outer scopes for any value in this namespace. // + // What about "pure" overrides, i.e., those without any original values? + // Well, they will also be found since their names have the original + // variable as a prefix. But do they apply? Yes, since we haven't found + // any original values, they will be "visible"; see find_override() for + // details. + // for (scope* s (&r); s != nullptr; s = s->parent_scope ()) { for (auto p (s->vars.find_namespace (ns)); @@ -78,5 +68,32 @@ namespace build2 return false; } + + bool + unconfigured (scope& root, const string& ns) + { + // Note: not overridable. + // + const variable& var (var_pool.insert<bool> (ns + ".configured")); + + if (current_mif->id == configure_id) + save_variable (root, var); + + auto l (root[var]); // Include inherited values. + return l && !cast<bool> (l); + } + + void + unconfigured (scope& root, const string& ns, bool v) + { + // Note: not overridable. + // + const variable& var (var_pool.insert<bool> (ns + ".configured")); + + if (current_mif->id == configure_id) + save_variable (root, var); + + root.assign (var) = !v; + } } } diff --git a/build2/config/utility.txx b/build2/config/utility.txx index d3c57a5..f49be75 100644 --- a/build2/config/utility.txx +++ b/build2/config/utility.txx @@ -3,6 +3,7 @@ // license : MIT; see accompanying LICENSE file #include <build2/scope> +#include <build2/context> namespace build2 { @@ -10,20 +11,43 @@ namespace build2 { template <typename T> pair<reference_wrapper<const value>, bool> - required (scope& root, const variable& var, const T& def_value, bool ovr) + required (scope& root, const variable& var, const T& def_val, bool def_ovr) { - using result = pair<reference_wrapper<const value>, bool>; + if (current_mif->id == configure_id) + save_variable (root, var); - if (auto l = root[var]) + pair<lookup, size_t> org (root.find_original (var)); + lookup l (org.first); + bool n (false); + + // The interaction with command line overrides can get tricky. For + // example, the override to defaul value could make (non-recursive) + // command line override in the outer scope no longer apply. So what we + // are going to do is first ignore overrides and perform the normal + // logic on the original. Then we apply the overrides on the result. + // + if (!l.defined () || (def_ovr && !l.belongs (root))) { - if (l.belongs (*global_scope)) - return result (root.assign (var) = *l, true); + l = lookup ((root.assign (var) = def_val), root); + org = make_pair (l, 1); // Lookup depth is 1 since in root.vars. + n = true; + } + + if (var.override != nullptr) + { + pair<lookup, size_t> ovr (root.find_override (var, move (org))); + + if (l != ovr.first) // Overriden? + { + l = move (ovr.first); - if (!ovr || l.belongs (root)) - return result (*l, false); + // Overriden and not inherited (same logic as in save_config()). + // + n = l.belongs (root) || l.belongs (*global_scope); + } } - return result (root.assign (var) = def_value, true); + return pair<reference_wrapper<const value>, bool> (*l, n); } } } diff --git a/build2/cxx/module b/build2/cxx/module index b9c50f4..39d1218 100644 --- a/build2/cxx/module +++ b/build2/cxx/module @@ -16,7 +16,7 @@ namespace build2 { extern "C" bool cxx_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index aac2af0..fba52b8 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -33,7 +33,7 @@ namespace build2 cxx_init (scope& r, scope& b, const location& loc, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool) { @@ -62,14 +62,13 @@ namespace build2 { auto& v (var_pool); - // @@ OVR - - v.insert<path> ("config.cxx", true); - - v.insert<strings> ("config.cxx.poptions"); - v.insert<strings> ("config.cxx.coptions"); - v.insert<strings> ("config.cxx.loptions"); - v.insert<strings> ("config.cxx.libs"); + // Note: some overridable, some not. + // + v.insert<path> ("config.cxx", true); + v.insert<strings> ("config.cxx.poptions", true); + v.insert<strings> ("config.cxx.coptions", true); + v.insert<strings> ("config.cxx.loptions", true); + v.insert<strings> ("config.cxx.libs", true); v.insert<strings> ("cxx.poptions"); v.insert<strings> ("cxx.coptions"); @@ -81,7 +80,7 @@ namespace build2 v.insert<strings> ("cxx.export.loptions"); v.insert<strings> ("cxx.export.libs"); - v.insert<string> ("cxx.std"); + v.insert<string> ("cxx.std", true); } // Register target types. @@ -158,17 +157,17 @@ namespace build2 // using cxx // cxx.coptions += <overriding options> # Note: '+='. // - if (const value& v = config::optional (r, "config.cxx.poptions")) - b.assign ("cxx.poptions") += cast<strings> (v); + b.assign ("cxx.poptions") += cast_null<strings> ( + config::optional (r, "config.cxx.poptions")); - if (const value& v = config::optional (r, "config.cxx.coptions")) - b.assign ("cxx.coptions") += cast<strings> (v); + b.assign ("cxx.coptions") += cast_null<strings> ( + config::optional (r, "config.cxx.coptions")); - if (const value& v = config::optional (r, "config.cxx.loptions")) - b.assign ("cxx.loptions") += cast<strings> (v); + b.assign ("cxx.loptions") += cast_null<strings> ( + config::optional (r, "config.cxx.loptions")); - if (const value& v = config::optional (r, "config.cxx.libs")) - b.assign ("cxx.libs") += cast<strings> (v); + b.assign ("cxx.libs") += cast_null<strings> ( + config::optional (r, "config.cxx.libs")); // config.cxx // diff --git a/build2/dist/module b/build2/dist/module index 829f3f7..61e44c3 100644 --- a/build2/dist/module +++ b/build2/dist/module @@ -15,11 +15,11 @@ namespace build2 namespace dist { extern "C" void - dist_boot (scope&, const location&, unique_ptr<module>&); + dist_boot (scope&, const location&, unique_ptr<module_base>&); extern "C" bool dist_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/dist/module.cxx b/build2/dist/module.cxx index 4b68bb8..2a7ba92 100644 --- a/build2/dist/module.cxx +++ b/build2/dist/module.cxx @@ -23,7 +23,7 @@ namespace build2 static rule rule_; extern "C" void - dist_boot (scope& r, const location&, unique_ptr<module>&) + dist_boot (scope& r, const location&, unique_ptr<module_base>&) { tracer trace ("dist::boot"); @@ -39,20 +39,18 @@ namespace build2 { auto& v (var_pool); - // @@ OVR + // Note: some overridable, some not. + // + v.insert<abs_dir_path> ("config.dist.root", true); + v.insert<strings> ("config.dist.archives", true); + v.insert<path> ("config.dist.cmd", true); - v.insert<bool> ("dist"); + v.insert<dir_path> ("dist.root"); + v.insert<path> ("dist.cmd"); + v.insert<strings> ("dist.archives"); - v.insert<string> ("dist.package"); - - v.insert<dir_path> ("dist.root"); - v.insert<dir_path> ("config.dist.root"); - - v.insert<path> ("dist.cmd"); - v.insert<path> ("config.dist.cmd"); - - v.insert<strings> ("dist.archives"); - v.insert<strings> ("config.dist.archives"); + v.insert<bool> ("dist"); // Flag. + v.insert<string> ("dist.package"); // Project's package name. } } @@ -60,7 +58,7 @@ namespace build2 dist_init (scope& r, scope&, const location& l, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool) { @@ -97,10 +95,10 @@ namespace build2 if (s) { - const value& cv (config::optional_absolute (r, "config.dist.root")); + const value& cv (config::optional (r, "config.dist.root")); if (cv && !cv.empty ()) - v = cv; + v = cast<dir_path> (cv); // Strip abs_dir_path. } } diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx index 8a890fa..9ae5773 100644 --- a/build2/dist/operation.cxx +++ b/build2/dist/operation.cxx @@ -72,7 +72,7 @@ namespace build2 // Make sure we have the necessary configuration before // we get down to business. // - auto l (rs->vars["dist.root"]); //@@ OVR + auto l (rs->vars["dist.root"]); if (!l || l->empty ()) fail << "unknown root distribution directory" << @@ -84,14 +84,14 @@ namespace build2 fail << "root distribution directory " << dist_root << " does not exist"; - l = rs->vars["dist.package"]; //@@ OVR + l = rs->vars["dist.package"]; if (!l || l->empty ()) fail << "unknown distribution package name" << info << "did you forget to set dist.package?"; const string& dist_package (cast<string> (l)); - const path& dist_cmd (cast<path> (rs->vars["dist.cmd"])); // @@ OVR + const path& dist_cmd (cast<path> (rs->vars["dist.cmd"])); // Get the list of operations supported by this project. Skip // default_id. @@ -281,7 +281,7 @@ namespace build2 // Archive if requested. // - if (auto l = rs->vars["dist.archives"]) // @@ OVR + if (auto l = rs->vars["dist.archives"]) { for (const string& e: cast<strings> (l)) archive (dist_root, dist_package, e); @@ -425,6 +425,7 @@ namespace build2 } meta_operation_info dist { + dist_id, "dist", "distribute", "distributing", diff --git a/build2/file.cxx b/build2/file.cxx index e494f5a..c3d2273 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -18,6 +18,8 @@ #include <build2/lexer> #include <build2/parser> +#include <build2/config/utility> + using namespace std; using namespace butl; @@ -859,42 +861,19 @@ namespace build2 break; } - // Then try the config.import.* mechanism (overridable variable). + // Then try the config.import.* mechanism. // if (out_root.empty ()) { - // @@ OVR + // Note: overridable variable with path auto-completion. // const variable& var ( - var_pool.insert<dir_path> ("config.import." + project, true)); + var_pool.insert<abs_dir_path> ("config.import." + project, true)); if (auto l = iroot[var]) { - out_root = cast<dir_path> (l); - - if (l.belongs (*global_scope)) // A value from command line. - { - // Process the path by making it absolute and normalized. - // - if (out_root.relative ()) - out_root = work / out_root; - - out_root.normalize (); - - // Set on our root scope (part of our configuration). - // - iroot.assign (var) = out_root; - - // Also update the command-line value. This is necessary to avoid - // a warning issued by the config module about global/root scope - // value mismatch. Not very clean. - // - // @@ CMDVAR - // - dir_path& d (cast<dir_path> (const_cast<value&> (*l))); - if (d != out_root) - d = out_root; - } + out_root = cast<abs_dir_path> (l); + config::save_variable (iroot, var); // Mark as part of configuration. } else { diff --git a/build2/install/module b/build2/install/module index a02d110..f81b6ac 100644 --- a/build2/install/module +++ b/build2/install/module @@ -15,11 +15,11 @@ namespace build2 namespace install { extern "C" void - install_boot (scope&, const location&, unique_ptr<module>&); + install_boot (scope&, const location&, unique_ptr<module_base>&); extern "C" bool install_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/install/module.cxx b/build2/install/module.cxx index 7838a65..e7840ca 100644 --- a/build2/install/module.cxx +++ b/build2/install/module.cxx @@ -50,7 +50,7 @@ namespace build2 vn = "config.install."; vn += name; vn += var; - const variable& vr (var_pool.insert<T> (move (vn))); // @@ OVR + const variable& vr (var_pool.insert<T> (move (vn), true)); cv = dv != nullptr ? &config::required (r, vr, *dv, override).first.get () @@ -60,7 +60,7 @@ namespace build2 vn = "install."; vn += name; vn += var; - const variable& vr (var_pool.insert<T> (move (vn))); // @@ OVR + const variable& vr (var_pool.insert<T> (move (vn))); // Not overridable. value& v (r.assign (vr)); @@ -76,11 +76,12 @@ namespace build2 } } + template <typename T> static void set_dir (bool s, // specified scope& r, // root scope const char* n, // var name - const string& ps, // path (as string) + const T& p, // path const string& fm = string (), // file mode const string& dm = string (), // dir mode const build2::path& c = build2::path (), // command @@ -88,7 +89,6 @@ namespace build2 { using build2::path; - dir_path p (ps); set_var (s, r, n, "", p.empty () ? nullptr : &p, o); set_var (s, r, n, ".mode", fm.empty () ? nullptr : &fm); set_var (s, r, n, ".dir_mode", dm.empty () ? nullptr : &dm); @@ -101,7 +101,7 @@ namespace build2 static file_rule file_; extern "C" void - install_boot (scope& r, const location&, unique_ptr<module>&) + install_boot (scope& r, const location&, unique_ptr<module_base>&) { tracer trace ("install::boot"); @@ -116,7 +116,7 @@ namespace build2 install_init (scope& r, scope& b, const location& l, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool) { @@ -139,9 +139,9 @@ namespace build2 { auto& v (var_pool); - // @@ OVR + // Note: not overridable. // - v.insert<dir_path> ("install"); + v.insert<dir_path> ("install"); // Flag. } // Register our alias and file installer rule. @@ -162,22 +162,21 @@ namespace build2 bool s (config::specified (r, "config.install")); const string& n (cast<string> (r["project"])); - set_dir (s, r, "root", "", "", "755", path ("install")); - set_dir (s, r, "data_root", "root", "644"); - set_dir (s, r, "exec_root", "root", "755"); - - set_dir (s, r, "sbin", "exec_root/sbin"); - set_dir (s, r, "bin", "exec_root/bin"); - set_dir (s, r, "lib", "exec_root/lib"); - set_dir (s, r, "libexec", "exec_root/libexec/" + n, "", "", path (), true); + set_dir (s, r, "root", abs_dir_path (), "", "755", path ("install")); + set_dir (s, r, "data_root", dir_path ("root"), "644"); + set_dir (s, r, "exec_root", dir_path ("root"), "755"); - set_dir (s, r, "data", "data_root/share/" + n, "", "", path (), true); - set_dir (s, r, "include", "data_root/include"); + set_dir (s, r, "sbin", dir_path ("exec_root/sbin")); + set_dir (s, r, "bin", dir_path ("exec_root/bin")); + set_dir (s, r, "lib", dir_path ("exec_root/lib")); + set_dir (s, r, "libexec", dir_path ("exec_root/libexec/" + n), "", "", path (), true); - set_dir (s, r, "doc", "data_root/share/doc/" + n, "", "", path (), true); - set_dir (s, r, "man", "data_root/share/man"); + set_dir (s, r, "data", dir_path ("data_root/share/" + n), "", "", path (), true); + set_dir (s, r, "include", dir_path ("data_root/include")); - set_dir (s, r, "man1", "man/man1"); + set_dir (s, r, "doc", dir_path ("data_root/share/doc/" + n), "", "", path (), true); + set_dir (s, r, "man", dir_path ("data_root/share/man")); + set_dir (s, r, "man1", dir_path ("man/man1")); } // Configure "installability" for built-in target types. diff --git a/build2/install/operation.cxx b/build2/install/operation.cxx index 7778c70..77deb2c 100644 --- a/build2/install/operation.cxx +++ b/build2/install/operation.cxx @@ -20,6 +20,7 @@ namespace build2 } operation_info install { + install_id, "install", "install", "installing", diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx index e756fc2..24c1df0 100644 --- a/build2/install/rule.cxx +++ b/build2/install/rule.cxx @@ -371,10 +371,10 @@ namespace build2 if (r.mode.empty ()) r.mode = "644"; if (r.dir_mode.empty ()) r.dir_mode = "755"; - // If the directory still doesn't exist, then this means it was specified - // as absolute (it will normally be install.root with everything else - // defined in term of it). We used to fail in this case but that proved - // to be just too anal. So now we just create it. + // If the directory still doesn't exist, then this means it was + // specified as absolute (it will normally be install.root with + // everything else defined in term of it). We used to fail in this + // case but that proved to be just too anal. So now we just create it. // if (!dir_exists (r.dir)) // May throw (e.g., EACCES). // fail << "installation directory " << d << " does not exist"; diff --git a/build2/module b/build2/module index 7c8719e..25574d1 100644 --- a/build2/module +++ b/build2/module @@ -17,16 +17,16 @@ namespace build2 class scope; class location; - class module + class module_base { public: virtual - ~module () = default; + ~module_base () = default; }; extern "C" using module_boot_function = - void (scope& root, const location&, unique_ptr<module>&); + void (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 @@ -39,7 +39,7 @@ namespace build2 bool (scope& root, scope& base, const location&, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, // First time for this project. bool optional); // Loaded with 'using?' (optional module). @@ -48,11 +48,22 @@ namespace build2 { bool boot; // True if the module boot'ed but not yet init'ed. module_init_function* init; - unique_ptr<build2::module> module; + unique_ptr<module_base> module; const location loc; // Boot location. }; - using loaded_module_map = std::map<string, module_state>; + struct loaded_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; + } + }; // Load and boot the specified module. // diff --git a/build2/operation b/build2/operation index 688663b..152353b 100644 --- a/build2/operation +++ b/build2/operation @@ -174,6 +174,7 @@ namespace build2 struct meta_operation_info { + const meta_operation_id id; const string name; // Name derivatives for diagnostics. If empty, then the meta- @@ -253,6 +254,7 @@ namespace build2 // struct operation_info { + const operation_id id; const string name; // Name derivatives for diagnostics. Note that unlike meta-operations, diff --git a/build2/operation.cxx b/build2/operation.cxx index ef620ba..c5ba2fb 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -172,6 +172,7 @@ namespace build2 } meta_operation_info perform { + perform_id, "perform", "", "", @@ -189,6 +190,7 @@ namespace build2 // operations // operation_info default_ { + default_id, "<default>", "", "", @@ -199,6 +201,7 @@ namespace build2 }; operation_info update { + update_id, "update", "update", "updating", @@ -209,6 +212,7 @@ namespace build2 }; operation_info clean { + clean_id, "clean", "clean", "cleaning", diff --git a/build2/parser.cxx b/build2/parser.cxx index 6926ce3..2c21de3 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -2660,16 +2660,15 @@ namespace build2 scope* nrs (&create_bootstrap_inner (*rs, out_base)); if (rs != nrs) - { - load_root_pre (*nrs); // Load outer roots recursively. rs = nrs; - } } // Switch to the new root scope. // if (rs != root_) { + load_root_pre (*rs); // Load new root(s) recursively. + l5 ([&]{trace << "switching to root scope " << rs->out_path ();}); root_ = rs; } diff --git a/build2/scope.cxx b/build2/scope.cxx index 75652a2..19f3b56 100644 --- a/build2/scope.cxx +++ b/build2/scope.cxx @@ -27,18 +27,26 @@ namespace build2 ++d; if (f) - if (auto l = s->target_vars.find (*tt, *tn, var)) + { + lookup l (s->target_vars.find (*tt, *tn, var)); + + if (l.defined ()) return make_pair (move (l), d); + } ++d; if (f && gt != nullptr) - if (auto l = s->target_vars.find (*gt, *gn, var)) + { + lookup l (s->target_vars.find (*gt, *gn, var)); + + if (l.defined ()) return make_pair (move (l), d); + } } ++d; - if (auto r = s->vars.find (var)) - return make_pair (lookup (r, &s->vars), d); + if (const value* v = s->vars.find (var)) + return make_pair (lookup (v, &s->vars), d); switch (var.visibility) { diff --git a/build2/test/module b/build2/test/module index 8736851..756b4f6 100644 --- a/build2/test/module +++ b/build2/test/module @@ -15,11 +15,11 @@ namespace build2 namespace test { extern "C" void - test_boot (scope&, const location&, unique_ptr<module>&); + test_boot (scope&, const location&, unique_ptr<module_base>&); extern "C" bool test_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); + scope&, scope&, const location&, unique_ptr<module_base>&, bool, bool); } } diff --git a/build2/test/module.cxx b/build2/test/module.cxx index d5f6430..3ae9996 100644 --- a/build2/test/module.cxx +++ b/build2/test/module.cxx @@ -22,7 +22,7 @@ namespace build2 static rule rule_; extern "C" void - test_boot (scope& root, const location&, unique_ptr<module>&) + test_boot (scope& root, const location&, unique_ptr<module_base>&) { tracer trace ("test::boot"); @@ -38,12 +38,12 @@ namespace build2 { auto& v (var_pool); - // @@ OVR - - v.insert<bool> ("test"); - v.insert<name> ("test.input"); - v.insert<name> ("test.output"); - v.insert<name> ("test.roundtrip"); + // Note: none are overridable. + // + v.insert<bool> ("test"); + v.insert<name> ("test.input"); + v.insert<name> ("test.output"); + v.insert<name> ("test.roundtrip"); v.insert<strings> ("test.options"); v.insert<strings> ("test.arguments"); } @@ -53,7 +53,7 @@ namespace build2 test_init (scope& root, scope&, const location& l, - unique_ptr<module>&, + unique_ptr<module_base>&, bool first, bool) { diff --git a/build2/test/operation.cxx b/build2/test/operation.cxx index dc213cf..a63c409 100644 --- a/build2/test/operation.cxx +++ b/build2/test/operation.cxx @@ -20,6 +20,7 @@ namespace build2 } operation_info test { + test_id, "test", "test", "testing", diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index 75bae4a..ccf52e0 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -96,7 +96,6 @@ namespace build2 // We should have either arguments or input/roundtrip. Again, use // lookup depth to figure out who takes precedence. // - //@@ OVR auto ip (t.find ("test.input")); auto op (t.find ("test.output")); auto rp (t.find ("test.roundtrip")); @@ -307,7 +306,7 @@ namespace build2 // Do we have options? // - if (auto l = t["test.options"]) //@@ OVR + if (auto l = t["test.options"]) append_options (args, cast<strings> (l)); // Do we have input? @@ -323,7 +322,7 @@ namespace build2 // else { - if (auto l = t["test.arguments"]) //@@ OVR + if (auto l = t["test.arguments"]) append_options (args, cast<strings> (l)); } diff --git a/build2/variable b/build2/variable index 77e9db2..14bf7af 100644 --- a/build2/variable +++ b/build2/variable @@ -161,6 +161,9 @@ namespace build2 template <typename T> value& operator= (T); template <typename T> value& operator+= (T); + template <typename T> value& operator+= (T* v) { + return v != nullptr ? *this += *v : *this;} + value& operator= (const char* v) {return *this = string (v);} value& operator+= (const char* v) {return *this += string (v);} diff --git a/build2/variable.cxx b/build2/variable.cxx index 4a2ffa9..db65e5b 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -536,6 +536,8 @@ namespace build2 if (d.relative ()) d.complete (); + d.normalize (); + return abs_dir_path (move (d)); } |