aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/config/utility.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/config/utility.hxx')
-rw-r--r--libbuild2/config/utility.hxx182
1 files changed, 164 insertions, 18 deletions
diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx
index 0429555..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
@@ -36,6 +38,9 @@ namespace build2
(*config_save_variable) (scope&, const variable&, optional<uint64_t>);
LIBBUILD2_SYMEXPORT extern void
+ (*config_save_environment) (scope&, const char*);
+
+ LIBBUILD2_SYMEXPORT extern void
(*config_save_module) (scope&, const char*, int);
LIBBUILD2_SYMEXPORT extern const string&
@@ -53,10 +58,22 @@ namespace build2
namespace config
{
- // Mark the variable to be saved during configuration.
+ // Mark a variable to be saved during configuration.
+ //
+ // Note: the save_*_omitted flags work best when undefined or (one of) the
+ // 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.
+ const uint64_t save_false_omitted = 0x08; // Treat false as undefined.
+ const uint64_t save_base = 0x10; // Custom save with base.
inline void
save_variable (scope& rs, const variable& var, uint64_t flags = 0)
@@ -65,7 +82,7 @@ namespace build2
config_save_variable (rs, var, flags);
}
- // Mark the variable as "unsaved" (always transient).
+ // Mark a variable as "unsaved" (always transient).
//
// Such variables are not very common and are usually used to control the
// process of configuration itself.
@@ -77,6 +94,93 @@ namespace build2
config_save_variable (rs, var, nullopt);
}
+ // Mark an environment variable to be saved during hermetic configuration.
+ //
+ // Some notes/suggestions on saving environment variables for tools (e.g.,
+ // compilers, etc):
+ //
+ // 1. We want to save variables that affect the result (e.g., build
+ // output) rather than byproducts (e.g., diagnostics).
+ //
+ // 2. Environment variables are often poorly documented (and not always in
+ // the ENVIRONMENT section; sometimes they are mentioned together with
+ // the corresponding option). A sensible approach in this case is to
+ // save documented (and perhaps well-known undocumented) variables --
+ // the user can always save additional variables if necessary. The way
+ // to discover undocumented environment variables is to grep the source
+ // code.
+ //
+ // 3. Sometime environment variables only affect certain modes of a tool.
+ // If such modes are not used, then there is no need to save the
+ // corresponding variables.
+ //
+ // 4. Finally, there could be environment variables that are incompatible
+ // with what we are doing (e.g., they change the mode of operation or
+ // some such; see GCC's DEPENDENCIES_OUTPUT for example). The two ways
+ // to deal with this is either clear them for each invocation or, if
+ // that's too burdensome and there is no good reason to have the build
+ // system invoked with such variables, detect their presence and fail.
+ // Note that unsetting them for the entire build system process is not
+ // an option since that would be racy.
+ //
+ // See also build2::hash_environment().
+ //
+ inline void
+ save_environment (scope& rs, const string& var)
+ {
+ if (config_save_environment != nullptr)
+ config_save_environment (rs, var.c_str ());
+ }
+
+ inline void
+ save_environment (scope& rs, const char* var)
+ {
+ if (config_save_environment != nullptr)
+ config_save_environment (rs, var);
+ }
+
+ inline void
+ save_environment (scope& rs, initializer_list<const char*> vars)
+ {
+ if (config_save_environment != nullptr)
+ {
+ for (const char* var: vars)
+ config_save_environment (rs, var);
+ }
+ }
+
+ inline void
+ save_environment (scope& rs, const cstrings& vars)
+ {
+ if (config_save_environment != nullptr)
+ {
+ for (const char* var: vars)
+ config_save_environment (rs, var);
+ }
+ }
+
+ inline void
+ save_environment (scope& rs, const strings& vars)
+ {
+ if (config_save_environment != nullptr)
+ {
+ for (const string& var: vars)
+ config_save_environment (rs, var.c_str ());
+ }
+ }
+
+ // A NULL-terminated list of variables (may itself be NULL).
+ //
+ inline void
+ save_environment (scope& rs, const char* const* vars)
+ {
+ if (vars != nullptr && config_save_environment != nullptr)
+ {
+ for (; *vars != nullptr; ++vars)
+ config_save_environment (rs, *vars);
+ }
+ }
+
// Establish module save order/priority with INT32_MIN being the highest.
// Modules with the same priority are saved in the order inserted.
//
@@ -136,9 +240,11 @@ namespace build2
//
// 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 in case
- // we want the mentioning of the variable to be omitted from persistent
- // storage (e.g., a config file) if the default value is used.
+ // value (in this case it also doesn't mark the variable for saving with
+ // the specified flags). This can be useful when we don't have a default
+ // value or in case we want the mentioning of the variable to be omitted
+ // from persistent storage (e.g., a config file) if the default value is
+ // used.
//
// Note also that we can first do the lookup without the default value and
// then, if there is no value, call the version with the default value and
@@ -147,27 +253,38 @@ namespace build2
// expensive. It is also ok to call both versions multiple times provided
// the flags are the same.
//
- // @@ Should we pass flags and interpret save_null_omitted to treat null
- // as undefined? Sounds logical.
- //
lookup
- lookup_config (scope& rs, const variable&);
+ lookup_config (scope& rs,
+ const variable&,
+ uint64_t save_flags = 0);
lookup
- lookup_config (bool& new_value, scope& rs, const variable&);
+ lookup_config (bool& new_value,
+ scope& rs,
+ const variable&,
+ uint64_t save_flags = 0);
// Note that the variable is expected to have already been entered.
//
inline lookup
- lookup_config (scope& rs, const string& var)
+ lookup_config (scope& rs,
+ const string& var,
+ uint64_t save_flags = 0)
{
- return lookup_config (rs, rs.ctx.var_pool[var]);
+ // Note: go straight for the public variable pool.
+ //
+ return lookup_config (rs, rs.ctx.var_pool[var], save_flags);
}
inline lookup
- lookup_config (bool& new_value, scope& rs, const string& var)
+ lookup_config (bool& new_value,
+ scope& rs,
+ const string& var,
+ uint64_t save_flags = 0)
{
- return lookup_config (new_value, rs, rs.ctx.var_pool[var]);
+ // Note: go straight for the public variable pool.
+ //
+ return lookup_config (new_value, rs, rs.ctx.var_pool[var], save_flags);
}
// Lookup a config.* variable value and, if the value is undefined, set it
@@ -195,8 +312,14 @@ namespace build2
// 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.
//
- // @@ Should save_null_omitted be interpreted to treat null as undefined?
- // Sounds logical.
+ // Note that while it may seem logical, these functions do not
+ // "reinterpret" defined values according to the save_*_omitted flags (for
+ // example, by returning the default value if the defined value is NULL
+ // and the save_null_omitted flag is specified). This is because such a
+ // reinterpretation may cause a diversion between the returned value and
+ // the re-queried config.* variable value if the defined value came from
+ // an override. To put another way, the save_*_omitted flags are purely to
+ // reduce the noise in config.build.
//
template <typename T>
lookup
@@ -248,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
@@ -264,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],
@@ -308,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)));
@@ -326,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)));
@@ -389,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);
}
}