From 9f71deeeb0f8e6fe2c29f209fc96f466fc2831b6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 16 Mar 2020 08:06:15 +0200 Subject: Rework config::{omitted,required,optional}() into unified config_lookup() --- libbuild2/bin/init.cxx | 148 ++++++++++++++------------- libbuild2/cc/compile-rule.cxx | 2 +- libbuild2/cc/init.cxx | 15 +-- libbuild2/cc/module.cxx | 53 +++++----- libbuild2/cc/module.hxx | 5 +- libbuild2/config/operation.cxx | 10 +- libbuild2/config/utility.cxx | 17 +-- libbuild2/config/utility.hxx | 227 +++++++++++++++++++++++++++-------------- libbuild2/config/utility.ixx | 62 +++++++++++ libbuild2/config/utility.txx | 39 +++++-- libbuild2/dist/init.cxx | 19 ++-- libbuild2/file.hxx | 21 ++-- libbuild2/install/init.cxx | 14 ++- libbuild2/test/init.cxx | 7 +- 14 files changed, 402 insertions(+), 237 deletions(-) create mode 100644 libbuild2/config/utility.ixx (limited to 'libbuild2') diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx index bb935de..25d5c39 100644 --- a/libbuild2/bin/init.cxx +++ b/libbuild2/bin/init.cxx @@ -160,11 +160,9 @@ namespace build2 // load_module (rs, rs, "bin.vars", loc); - // Configure. + // Configuration. // - using config::required; - using config::optional; - using config::omitted; + using config::lookup_config; // Adjust module priority (binutils). // @@ -185,7 +183,7 @@ namespace build2 { value& v (rs.assign ("bin.lib")); if (!v) - v = *required (rs, "config.bin.lib", "both").first; + v = *lookup_config (rs, "config.bin.lib", "both"); } // config.bin.exe.lib @@ -193,7 +191,7 @@ namespace build2 { value& v (rs.assign ("bin.exe.lib")); if (!v) - v = *required (rs, "config.bin.exe.lib", exe_lib).first; + v = *lookup_config (rs, "config.bin.exe.lib", exe_lib); } // config.bin.liba.lib @@ -201,7 +199,7 @@ namespace build2 { value& v (rs.assign ("bin.liba.lib")); if (!v) - v = *required (rs, "config.bin.liba.lib", liba_lib).first; + v = *lookup_config (rs, "config.bin.liba.lib", liba_lib); } // config.bin.libs.lib @@ -209,7 +207,7 @@ namespace build2 { value& v (rs.assign ("bin.libs.lib")); if (!v) - v = *required (rs, "config.bin.libs.lib", libs_lib).first; + v = *lookup_config (rs, "config.bin.libs.lib", libs_lib); } // config.bin.rpath[_link] @@ -218,10 +216,10 @@ namespace build2 // any. // rs.assign ("bin.rpath") += cast_null ( - optional (rs, "config.bin.rpath")); + lookup_config (rs, "config.bin.rpath", nullptr)); rs.assign ("bin.rpath_link") += cast_null ( - optional (rs, "config.bin.rpath_link")); + lookup_config (rs, "config.bin.rpath_link", nullptr)); // config.bin.rpath[_link].auto // @@ -229,12 +227,12 @@ namespace build2 lookup l; rs.assign ("bin.rpath.auto") = - (l = omitted (rs, "config.bin.rpath.auto").first) + (l = lookup_config (rs, "config.bin.rpath.auto")) ? cast (l) : true; rs.assign ("bin.rpath_link.auto") = - (l = omitted (rs, "config.bin.rpath_link.auto").first) + (l = lookup_config (rs, "config.bin.rpath_link.auto")) ? cast (l) : true; } @@ -246,12 +244,12 @@ namespace build2 // that might have been specified before loading the module. // { - lookup p (omitted (rs, "config.bin.prefix").first); - lookup s (omitted (rs, "config.bin.suffix").first); + lookup p (lookup_config (rs, "config.bin.prefix")); + lookup s (lookup_config (rs, "config.bin.suffix")); auto set = [&rs] (const char* bv, const char* cv, lookup l) { - if (lookup o = omitted (rs, cv).first) + if (lookup o = lookup_config (rs, cv)) l = o; if (l) @@ -267,7 +265,7 @@ namespace build2 if (first) { - bool new_val (false); // Set any new values? + bool new_cfg (false); // Any new configuration values? // config.bin.target // @@ -277,14 +275,15 @@ namespace build2 // We first see if the value was specified via the configuration // mechanism. // - auto p (omitted (rs, var)); - lookup l (p.first); + lookup l (lookup_config (new_cfg, rs, var)); // Then see if there is a config hint (e.g., from the cc module). // bool hint (false); if (!l) { + // Note: new_cfg is false for a hinted value. + // if (auto hl = extra.hints[var]) { l = hl; @@ -342,8 +341,6 @@ namespace build2 fail << "unable to parse binutils target '" << s << "': " << e << info << "consider using the --config-sub option"; } - - new_val = new_val || p.second; // False for a hinted value. } // config.bin.pattern @@ -354,13 +351,14 @@ namespace build2 // We first see if the value was specified via the configuration // mechanism. // - auto p (omitted (rs, var)); - lookup l (p.first); + lookup l (lookup_config (new_cfg, rs, var)); // Then see if there is a config hint (e.g., from the C++ module). // if (!l) { + // Note: new_cfg is false for a hinted value. + // if (auto hl = extra.hints[var]) l = hl; } @@ -382,14 +380,13 @@ namespace build2 } rs.assign ("bin.pattern") = s; - new_val = new_val || p.second; // False for a hinted value. } } - // If we set any new values (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). // - if (verb >= (new_val ? 2 : 3)) + if (verb >= (new_cfg ? 2 : 3)) { diag_record dr (text); @@ -587,10 +584,14 @@ namespace build2 vp.insert ("config.bin.ranlib", true); } - // Configure. + // Configuration. // if (first) { + using config::lookup_config; + + bool new_cfg (false); // Any new configuration values? + // config.bin.ar // config.bin.ranlib // @@ -619,31 +620,28 @@ namespace build2 // changes, say, the C++ compiler (which hinted the pattern), then // ar will automatically change as well. // - auto ap ( - config::required ( - rs, - "config.bin.ar", - path (apply_pattern (ar_d, pat.pattern)), - false, - config::save_default_commented)); - - auto rp ( - config::required ( - rs, - "config.bin.ranlib", - nullptr, - false, - config::save_default_commented)); - - const path& ar (cast (ap.first)); - const path* ranlib (cast_null (rp.first)); + const path& ar ( + cast ( + lookup_config (new_cfg, + rs, + "config.bin.ar", + path (apply_pattern (ar_d, pat.pattern)), + config::save_default_commented))); + + const path* ranlib ( + cast_null ( + lookup_config (new_cfg, + rs, + "config.bin.ranlib", + nullptr, + config::save_default_commented))); ar_info ari (guess_ar (ar, ranlib, pat.paths)); - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). // - if (verb >= (ap.second || rp.second ? 2 : 3)) + if (verb >= (new_cfg ? 2 : 3)) { diag_record dr (text); @@ -750,10 +748,14 @@ namespace build2 vp.insert ("config.bin.ld", true); } - // Configure. + // Configuration. // if (first) { + using config::lookup_config; + + bool new_cfg (false); // Any new configuration values? + // config.bin.ld // // Use the target to decide on the default ld name. @@ -765,21 +767,20 @@ namespace build2 // pattern_paths pat (lookup_pattern (rs)); - auto p ( - config::required ( - rs, - "config.bin.ld", - path (apply_pattern (ld_d, pat.pattern)), - false, - config::save_default_commented)); + const path& ld ( + cast ( + lookup_config (new_cfg, + rs, + "config.bin.ld", + path (apply_pattern (ld_d, pat.pattern)), + config::save_default_commented))); - const path& ld (cast (p.first)); ld_info ldi (guess_ld (ld, pat.paths)); - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). // - if (verb >= (p.second ? 2 : 3)) + if (verb >= (new_cfg ? 2 : 3)) { diag_record dr (text); @@ -883,10 +884,14 @@ namespace build2 vp.insert ("config.bin.rc", true); } - // Configure. + // Configuration. // if (first) { + using config::lookup_config; + + bool new_cfg (false); // Any new configuration values? + // config.bin.rc // // Use the target to decide on the default rc name. @@ -898,21 +903,20 @@ namespace build2 // pattern_paths pat (lookup_pattern (rs)); - auto p ( - config::required ( - rs, - "config.bin.rc", - path (apply_pattern (rc_d, pat.pattern)), - false, - config::save_default_commented)); + const path& rc ( + cast ( + lookup_config (new_cfg, + rs, + "config.bin.rc", + path (apply_pattern (rc_d, pat.pattern)), + config::save_default_commented))); - const path& rc (cast (p.first)); rc_info rci (guess_rc (rc, pat.paths)); - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). // - if (verb >= (p.second ? 2 : 3)) + if (verb >= (new_cfg ? 2 : 3)) { text << "bin.rc " << project (rs) << '@' << rs << '\n' << " rc " << rci.path << '\n' diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 1cefcc5..a8916cf 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -5321,7 +5321,7 @@ namespace build2 extra, /* root_pre */ {string (x) + '.'}, /* root_modules */ "", /* root_post */ - nullopt, /* config */ + nullopt, /* config_module */ false, /* buildfile */ "the cc module", 2); /* verbosity */ diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx index 202c612..723b678 100644 --- a/libbuild2/cc/init.cxx +++ b/libbuild2/cc/init.cxx @@ -244,8 +244,9 @@ namespace build2 // load_module (rs, rs, "cc.core.guess", loc); - // Configure. + // Configuration. // + using config::lookup_config; // Adjust module priority (compiler). // @@ -261,21 +262,21 @@ namespace build2 // // rs.assign ("cc.poptions") += cast_null ( - config::optional (rs, "config.cc.poptions")); + lookup_config (rs, "config.cc.poptions", nullptr)); rs.assign ("cc.coptions") += cast_null ( - config::optional (rs, "config.cc.coptions")); + lookup_config (rs, "config.cc.coptions", nullptr)); rs.assign ("cc.loptions") += cast_null ( - config::optional (rs, "config.cc.loptions")); + lookup_config (rs, "config.cc.loptions", nullptr)); rs.assign ("cc.aoptions") += cast_null ( - config::optional (rs, "config.cc.aoptions")); + lookup_config (rs, "config.cc.aoptions", nullptr)); rs.assign ("cc.libs") += cast_null ( - config::optional (rs, "config.cc.libs")); + lookup_config (rs, "config.cc.libs", nullptr)); - if (lookup l = config::omitted (rs, "config.cc.reprocess").first) + if (lookup l = lookup_config (rs, "config.cc.reprocess")) rs.assign ("cc.reprocess") = *l; // Load the bin.config module. diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 138a11b..70cbc47 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -48,6 +48,10 @@ namespace build2 const variable& config_c_coptions (vp["config.cc.coptions"]); const variable& config_c_loptions (vp["config.cc.loptions"]); + // Configuration. + // + using config::lookup_config; + // config.x // strings mode; @@ -56,9 +60,9 @@ namespace build2 // default value every time will be a waste. So try without a default // first. // - auto p (config::omitted (rs, config_x)); + lookup l (lookup_config (new_config, rs, config_x)); - if (!p.first) + if (!l) { // If there is a config.x value for one of the modules that can hint // us the toolchain, load it's .guess module. This makes sure that @@ -113,17 +117,16 @@ namespace build2 // user changes the source of the pattern/mode, this one will get // updated as well. // - p = config::required ( - rs, - config_x, - move (d), - false, - cc_loaded ? config::save_default_commented : 0); + l = lookup_config (new_config, + rs, + config_x, + move (d), + cc_loaded ? config::save_default_commented : 0); } // Split the value into the compiler path and mode. // - const strings& v (cast (*p.first)); + const strings& v (cast (l)); path xc; { @@ -145,9 +148,9 @@ namespace build2 // x_info = &build2::cc::guess ( x, x_lang, move (xc), - cast_null (config::omitted (rs, config_x_id).first), - cast_null (config::omitted (rs, config_x_version).first), - cast_null (config::omitted (rs, config_x_target).first), + cast_null (lookup_config (rs, config_x_id)), + cast_null (lookup_config (rs, config_x_version)), + cast_null (lookup_config (rs, config_x_target)), mode, cast_null (rs[config_c_poptions]), cast_null (rs[config_x_poptions]), @@ -155,8 +158,6 @@ namespace build2 cast_null (rs[config_x_coptions]), cast_null (rs[config_c_loptions]), cast_null (rs[config_x_loptions])); - - new_ = p.second; } const compiler_info& xi (*x_info); @@ -338,6 +339,10 @@ namespace build2 const compiler_info& xi (*x_info); const target_triplet& tt (cast (rs[x_target])); + // Configuration. + // + using config::lookup_config; + // config.x.{p,c,l}options // config.x.libs // @@ -365,24 +370,24 @@ namespace build2 // x.coptions += # Note: '+='. // rs.assign (x_poptions) += cast_null ( - config::optional (rs, config_x_poptions)); + lookup_config (rs, config_x_poptions, nullptr)); rs.assign (x_coptions) += cast_null ( - config::optional (rs, config_x_coptions)); + lookup_config (rs, config_x_coptions, nullptr)); rs.assign (x_loptions) += cast_null ( - config::optional (rs, config_x_loptions)); + lookup_config (rs, config_x_loptions, nullptr)); rs.assign (x_aoptions) += cast_null ( - config::optional (rs, config_x_aoptions)); + lookup_config (rs, config_x_aoptions, nullptr)); rs.assign (x_libs) += cast_null ( - config::optional (rs, config_x_libs)); + lookup_config (rs, config_x_libs, nullptr)); // config.x.std overrides x.std // { - lookup l (config::omitted (rs, config_x_std).first); + lookup l (lookup_config (rs, config_x_std)); const string* v; if (l.defined ()) @@ -407,7 +412,7 @@ namespace build2 // if (x_translatable_headers != nullptr) { - lookup l (config::omitted (rs, *config_x_translatable_headers).first); + lookup l (lookup_config (rs, *config_x_translatable_headers)); // @@ MODHDR: if(modules) ? // @@ -533,10 +538,10 @@ namespace build2 } #endif - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). // - if (verb >= (new_ ? 2 : 3)) + if (verb >= (new_config ? 2 : 3)) { const strings& mode (cast (rs[x_mode])); diff --git a/libbuild2/cc/module.hxx b/libbuild2/cc/module.hxx index 4eca976..28e8d51 100644 --- a/libbuild2/cc/module.hxx +++ b/libbuild2/cc/module.hxx @@ -66,6 +66,8 @@ namespace build2 size_t sys_lib_dirs_extra; size_t sys_inc_dirs_extra; + bool new_config = false; // See guess() and init() for details. + private: // Defined in gcc.cxx. // @@ -82,9 +84,6 @@ namespace build2 pair msvc_library_search_dirs (const process_path&, scope&) const; - - private: - bool new_; // See guess() and init() for details. }; class LIBBUILD2_CC_SYMEXPORT module: public build2::module, diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index 07110e0..58f6aae 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -1219,13 +1219,13 @@ namespace build2 // configurations). // create_project (d, - dir_path (), /* amalgamation */ + dir_path (), /* amalgamation */ bmod, - "", /* root_pre */ + "", /* root_pre */ rmod, - "", /* root_post */ - "config", /* config */ - true, /* buildfile */ + "", /* root_post */ + string ("config"), /* config_module */ + true, /* buildfile */ "the create meta-operation"); save_config (ctx, d); diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index b9fc513..8ad150e 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -17,9 +17,9 @@ namespace build2 namespace config { pair - omitted (scope& rs, const variable& var) + lookup_config_impl (scope& rs, const variable& var) { - // This is a stripped-down version of the required()'s twisted logic. + // This is a stripped-down version of the default value case. pair org (rs.find_original (var)); @@ -50,19 +50,8 @@ namespace build2 return pair (l, n); } - lookup - optional (scope& rs, const variable& var) - { - save_variable (rs, var); - - auto l (rs[var]); - return l.defined () - ? l - : lookup (rs.assign (var), var, rs); // NULL. - } - bool - specified (scope& rs, const string& n) + specified_config (scope& rs, const string& n) { // Search all outer scopes for any value in this namespace. // diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx index 493d296..bf4728f 100644 --- a/libbuild2/config/utility.hxx +++ b/libbuild2/config/utility.hxx @@ -77,116 +77,184 @@ namespace build2 config_save_module (rs, module, prio); } - // Set, if necessary, a required config.* variable. + // Lookup a config.* variable value and, if the value is defined, mark it + // as saved. + // + // The second version in addition sets the new_value argument to true if + // the value is "new" (but not to false; so it can be used to accumulate + // the result from multiple calls). A value is considered new if it was + // set to the default value (inherited or not, including overrides). We + // also treat command line overrides (inherited or not) as new. In this + // case new means either the default value was inherited or it was + // overridden. This flag is usually used to test that the new value is + // valid, print the configuration report, etc. + // + // Unlike the rest of the lookup_config() versions, this one leaves the + // unspecified value as undefined rather than setting it to a default + // value. This can be useful when we don't have a default value or if we + // want the mentioning of the variable to be omitted from persistent + // storage (e.g., a config file) if the default value is used. + // + // @@ Should we pass flags and interpret save_null_omitted to treat null + // as undefined? Sounds logical. + // + lookup + lookup_config (scope& rs, const variable&); + + lookup + lookup_config (bool& new_value, scope& rs, const variable&); + + // Note that the variable is expected to have already been entered. + // + inline lookup + lookup_config (scope& rs, const string& var) + { + return lookup_config (rs, rs.ctx.var_pool[var]); + } + + inline lookup + lookup_config (bool& new_value, scope& rs, const string& var) + { + return lookup_config (new_value, rs, rs.ctx.var_pool[var]); + } + + // Lookup a config.* variable value and, if the value is undefined, set it + // to the default. Always mark it as saved. + // + // If the default value is nullptr, then the unspecified value is set to + // NULL which can be used to distinguish between the "not yet configured", + // "configured as unspecified", and "configures as empty" cases which can + // have different semantics if the value is merged into a non-config.* + // variable. This default value is traditionally used for "optional" + // values such as command line options. + // + // The value is returned as lookup (even though it is always defined + // though potentially as NULL) in order to pass along its location (could + // be used to detect inheritance, etc). + // + // The second version in addition sets the new_value argument as described + // above. Note, however, that if the save_default_commented flag is + // specified, then the default value is never considered "new" since for + // such variables absence of a value means it is the default value. // // 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 amalgamation), // then its value is "overridden" to the default value on this root scope. - // See save_variable() for more information on save_flags. - // - // Return the reference to the value as well as the indication of whether - // the value is "new", that is, it was set to the default value (inherited - // or not, including overrides). We also treat command line overrides - // (inherited or not) as new. This flag is usually used to test that the - // new value is valid, print report, etc. We return the value as lookup - // (always defined) to pass along its location (could be used to detect - // inheritance, etc). - // - // Note also that if save_flags has save_default_commented, then a default - // value is never considered "new" since for such variables absence of a - // value means the default value. // // @@ Should save_null_omitted be interpreted to treat null as undefined? // Sounds logical. // template - pair - required (scope& rs, - const variable&, - T&& default_value, - bool override = false, - uint64_t save_flags = 0); + lookup + lookup_config (scope& rs, + const variable&, + T&& default_value, + uint64_t save_flags = 0, + bool override = false); - // Note that the variable is expected to have already been entered. - // template - inline pair - required (scope& rs, - const string& var, - T&& default_value, - bool override = false, - uint64_t save_flags = 0) + lookup + lookup_config (bool& new_value, + scope& rs, + const variable&, + T&& default_value, + uint64_t save_flags = 0, + bool override = false); + + inline lookup + lookup_config (scope& rs, + const variable& var, + const char* default_value, + uint64_t save_flags = 0, + bool override = false) { - return required (rs, - rs.ctx.var_pool[var], - std::forward (default_value), // VC14 - override, - save_flags); + return lookup_config ( + rs, var, string (default_value), save_flags, override); } - inline pair - required (scope& rs, - const string& var, - const char* default_value, - bool override = false, - uint64_t save_flags = 0) + inline lookup + lookup_config (bool& new_value, + scope& rs, + const variable& var, + const char* default_value, + uint64_t save_flags = 0, + bool override = false) { - return required (rs, var, string (default_value), override, save_flags); + return lookup_config ( + new_value, rs, var, string (default_value), save_flags, override); } - // As above, but leave the unspecified value as undefined rather than - // setting it to the default value. - // - // This can be useful when we don't have a default value but may figure - // out some fallback. See config.bin.target for an example. - // - LIBBUILD2_SYMEXPORT pair - omitted (scope& rs, const variable&); - // Note that the variable is expected to have already been entered. // - inline pair - omitted (scope& rs, const string& var) + template + inline lookup + lookup_config (scope& rs, + const string& var, + T&& default_value, + uint64_t save_flags = 0, + bool override = false) { - return omitted (rs, rs.ctx.var_pool[var]); + return lookup_config (rs, + rs.ctx.var_pool[var], + std::forward (default_value), // VC14 + save_flags, + override); } - // Set, if necessary, an optional config.* variable. In particular, an - // unspecified variable is set to NULL which is used to distinguish - // between the "configured as unspecified" and "not yet configured" cases. - // - // Return the value (as always defined lookup), which can be NULL. - // - // @@ Rename since clashes with the optional class template. - // - // @@ Does it make sense to return the new indicator here as well, - // for consistency/generality. - // - LIBBUILD2_SYMEXPORT lookup - optional (scope& rs, const variable&); + template + inline lookup + lookup_config (bool& new_value, + scope& rs, + const string& var, + T&& default_value, + uint64_t save_flags = 0, + bool override = false) + { + return lookup_config (new_value, + rs, + rs.ctx.var_pool[var], + std::forward (default_value), // VC14 + save_flags, + override); + } + + inline lookup + lookup_config (scope& rs, + const string& var, + const char* default_value, + uint64_t save_flags = 0, + bool override = false) + { + return lookup_config ( + rs, var, string (default_value), save_flags, override); + } - // Note that the variable is expected to have already been registered. - // inline lookup - optional (scope& rs, const string& var) + lookup_config (bool& new_value, + scope& rs, + const string& var, + const char* default_value, + uint64_t save_flags = 0, + bool override = false) { - return optional (rs, rs.ctx.var_pool[var]); + return lookup_config ( + new_value, rs, var, string (default_value), save_flags, override); } - // Check whether there are any variables specified from the config + // 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 - // this functionality is not (yet) used and omit writing a whole - // bunch of NULL config.install.* values to the config.build file. - // We call it omitted/delayed configuration. + // config.install.* values. If there are none, then we can assume this + // functionality is not (yet) used and omit writing a whole bunch of NULL + // config.install.* values to the config.build file. We call this + // omitted/delayed configuration. // - // 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 (e.g., in order to avoid re- - // running the tests, etc). + // Note that this function detects and ignores special config.* variables + // (such as config.*.configured) which may be used by a module to remember + // that it is unconfigured (e.g., in order to avoid re-running the tests, + // etc; see below). // LIBBUILD2_SYMEXPORT bool - specified (scope& rs, const string& var); + specified_config (scope& rs, const string& var); // Check if there is a false config.*.configured value. This mechanism can // be used to "remember" that the module is left unconfigured in order to @@ -204,6 +272,7 @@ namespace build2 } } +#include #include #endif // LIBBUILD2_CONFIG_UTILITY_HXX diff --git a/libbuild2/config/utility.ixx b/libbuild2/config/utility.ixx new file mode 100644 index 0000000..79d5470 --- /dev/null +++ b/libbuild2/config/utility.ixx @@ -0,0 +1,62 @@ +// file : libbuild2/config/utility.ixx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + namespace config + { + LIBBUILD2_SYMEXPORT pair + lookup_config_impl (scope&, const variable&); + + template + pair + lookup_config_impl (scope&, const variable&, T&&, uint64_t, bool); + + inline lookup + lookup_config (scope& rs, const variable& var) + { + return lookup_config_impl (rs, var).first; + } + + inline lookup + lookup_config (bool& new_value, scope& rs, const variable& var) + { + auto r (lookup_config_impl (rs, var)); + new_value = new_value || r.second; + return r.first; + } + + template + inline lookup + lookup_config (scope& rs, + const variable& var, + T&& def_val, + uint64_t sflags, + bool def_ovr) + { + return lookup_config_impl (rs, + var, + std::forward (def_val), // VC14 + sflags, + def_ovr).first; + } + + template + inline lookup + lookup_config (bool& new_value, + scope& rs, + const variable& var, + T&& def_val, + uint64_t sflags, + bool def_ovr) + { + auto r (lookup_config_impl (rs, + var, + std::forward (def_val), // VC14 + sflags, + def_ovr)); + new_value = new_value || r.second; + return r.first; + } + } +} diff --git a/libbuild2/config/utility.txx b/libbuild2/config/utility.txx index f52df8d..ae40ba7 100644 --- a/libbuild2/config/utility.txx +++ b/libbuild2/config/utility.txx @@ -7,13 +7,14 @@ namespace build2 { template pair - required (scope& rs, - const variable& var, - T&& def_val, - bool def_ovr, - uint64_t sflags) + lookup_config_impl (scope& rs, + const variable& var, + T&& def_val, + uint64_t sflags, + bool def_ovr) { - // Note: see also omitted() if changing anything here. + // Note: see also the other lookup_config() implementation if changing + // anything here. save_variable (rs, var, sflags); @@ -28,6 +29,32 @@ namespace build2 // are going to do is first ignore overrides and perform the normal // logic on the original. Then we apply the overrides on the result. // + // Note that this is not exactly the "lookup and set to default if + // undefined" semantics in case there is no original but there is an + // override. In this case we will set original to default and then apply + // the override, which could be append or non-recursive (as mentioned + // above). It does, however, feel like taking into account the default + // in such cases is the correct semantics since append is meant as an + // addition to something existing and non-recursive override is only + // meant to override at the level it was specified. Though it won't be + // surprising at all if we end up with some counter-intuitive behavior + // here. + // + // Actually, the above analysis is not the full picture: if we have one + // of those overrides (append, non-recursive) in the outer project, then + // the lookup_config() call at that level will set the corresponding + // variable on that scope and we will see it as "original-defined" from + // our scope. Of course if there is no call to lookup_config() for this + // variable in the outer scope, then we won't see anything but then our + // behavior in this case seems correct: since that value is not part of + // the configuration (and won't be saved), then we should stick to our + // default. In other words, we should only inherit the value if it is + // actually recognized as a configuration value by the outer project. + // + // So, to summarize the current understanding, while our semantics is + // not exactly "lookup and set to default if undefined" in some obscure + // corner cases, it seem to be the correct/preferred one. + // if (!l.defined () || (def_ovr && !l.belongs (rs))) { value& v (rs.assign (var) = std::forward (def_val)); // VC14 diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx index ff2d757..8eaafa2 100644 --- a/libbuild2/dist/init.cxx +++ b/libbuild2/dist/init.cxx @@ -111,7 +111,10 @@ namespace build2 // must be explicitly specified or we will complain if and when // we try to dist. // - bool s (config::specified (rs, "dist")); + using config::lookup_config; + using config::specified_config; + + bool s (specified_config (rs, "dist")); // Adjust module priority so that the config.dist.* values are saved at // the end of config.build. @@ -126,7 +129,7 @@ namespace build2 if (s) { - if (lookup l = config::optional (rs, "config.dist.root")) + if (lookup l = lookup_config (rs, "config.dist.root", nullptr)) v = cast (l); // Strip abs_dir_path. } } @@ -138,9 +141,9 @@ namespace build2 if (s) { - if (lookup l = config::required (rs, - "config.dist.cmd", - path ("install")).first) + if (lookup l = lookup_config (rs, + "config.dist.cmd", + path ("install"))) v = run_search (cast (l), true); } } @@ -154,10 +157,10 @@ namespace build2 if (s) { - if (lookup l = config::optional (rs, "config.dist.archives")) + if (lookup l = lookup_config (rs, "config.dist.archives", nullptr)) a = *l; - if (lookup l = config::optional (rs, "config.dist.checksums")) + if (lookup l = lookup_config (rs, "config.dist.checksums", nullptr)) { c = *l; @@ -173,7 +176,7 @@ namespace build2 // // Omit it from the configuration unless specified. // - config::omitted (rs, "config.dist.uncommitted"); + lookup_config (rs, "config.dist.uncommitted"); return true; } diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index d3c6787..2ca72b1 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -265,16 +265,17 @@ namespace build2 // Create a build system project in the specified directory. // LIBBUILD2_SYMEXPORT void - create_project (const dir_path&, - const optional& amalgamation, - const strings& boot_modules, // Bootstrap modules. - const string& root_pre, // Extra root.build text. - const strings& root_modules, // Root modules. - const string& root_post, // Extra root.build text. - const optional& config, // Config module to load. - bool buildfile, // Create root buildfile. - const char* who, // Who is creating it. - uint16_t verbosity = 1); // Diagnostic verbosity. + create_project ( + const dir_path&, + const optional& amalgamation, + const strings& boot_modules, // Bootstrap modules. + const string& root_pre, // Extra root.build text. + const strings& root_modules, // Root modules. + const string& root_post, // Extra root.build text. + const optional& config_module, // Config module to load. + bool buildfile, // Create root buildfile. + const char* who, // Who is creating it. + uint16_t verbosity = 1); // Diagnostic verbosity. } #include diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 34a5625..06e5d3a 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -63,11 +63,13 @@ namespace build2 vn += var; const variable& vr (rs.var_pool ().insert (move (vn), true)); + using config::lookup_config; + l = dv != nullptr - ? config::required (rs, vr, *dv, override).first + ? lookup_config (rs, vr, *dv, 0 /* save_flags */, override) : (global - ? config::optional (rs, vr) - : config::omitted (rs, vr).first); + ? lookup_config (rs, vr, nullptr) + : lookup_config (rs, vr)); } if (global) @@ -237,8 +239,10 @@ namespace build2 // { using build2::path; + using config::lookup_config; + using config::specified_config; - bool s (config::specified (rs, "install")); + bool s (specified_config (rs, "install")); // Adjust module priority so that the (numerous) config.install.* // values are saved at the end of config.build. @@ -280,7 +284,7 @@ namespace build2 if (s) { - if (lookup l = config::optional (rs, cvar)) + if (lookup l = lookup_config (rs, cvar, nullptr)) v = cast (l); // Strip abs_dir_path. } } diff --git a/libbuild2/test/init.cxx b/libbuild2/test/init.cxx index c0d0331..a21e8f2 100644 --- a/libbuild2/test/init.cxx +++ b/libbuild2/test/init.cxx @@ -140,8 +140,9 @@ namespace build2 auto& m (extra.module_as ()); - // Configure. + // Configuration. // + using config::lookup_config; // Adjust module priority so that the config.test.* values are saved at // the end of config.build. @@ -150,7 +151,7 @@ namespace build2 // config.test // - if (lookup l = config::omitted (rs, m.config_test).first) + if (lookup l = lookup_config (rs, m.config_test)) { // Figure out which root scope it came from. // @@ -166,7 +167,7 @@ namespace build2 // config.test.output // - if (lookup l = config::omitted (rs, m.config_test_output).first) + if (lookup l = lookup_config (rs, m.config_test_output)) { const name_pair& p (cast (l)); -- cgit v1.1