aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-09-15 19:28:08 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-09-17 15:35:13 +0300
commit9298908b00c1977fcce5a3ac8e29e982cc28c97f (patch)
treed84e999bbda0cca6566df7462255261c9c88840c
parent131c8cb4424c475bbdaf41912ba1ca66869322de (diff)
Add support for BUILD2_VAR_OVR and BUILD2_DEF_OPT environment variables
-rw-r--r--build2/b-options.cxx10
-rw-r--r--build2/b.cli9
-rw-r--r--build2/b.cxx186
3 files changed, 172 insertions, 33 deletions
diff --git a/build2/b-options.cxx b/build2/b-options.cxx
index 373190a..95772bb 100644
--- a/build2/b-options.cxx
+++ b/build2/b-options.cxx
@@ -1559,7 +1559,15 @@ namespace build2
<< "The \033[1mHOME\033[0m environment variable is used to determine the user's home directory." << ::std::endl
<< "If it is not set, then \033[1mgetpwuid(3)\033[0m is used instead. This value is used to" << ::std::endl
<< "shorten paths printed in diagnostics by replacing the home directory with \033[1m~/\033[0m." << ::std::endl
- << "It is also made available to \033[1mbuildfile\033[0m's as the \033[1mbuild.home\033[0m variable." << ::std::endl;
+ << "It is also made available to \033[1mbuildfile\033[0m's as the \033[1mbuild.home\033[0m variable." << ::std::endl
+ << ::std::endl
+ << "The \033[1mBUILD2_VAR_OVR\033[0m environment variable is used to propagate global variable" << ::std::endl
+ << "overrides to nested build system driver invocations. Its value is a list of" << ::std::endl
+ << "global variable assignments separated with newlines." << ::std::endl
+ << ::std::endl
+ << "The \033[1mBUILD2_DEF_OPT\033[0m environment variable is used to suppress loading of default" << ::std::endl
+ << "options files in nested build system driver invocations. Its values are \033[1mfalse\033[0m" << ::std::endl
+ << "or \033[1m0\033[0m to suppress and \033[1mtrue\033[0m or \033[1m1\033[0m to load." << ::std::endl;
p = ::build2::cl::usage_para::text;
diff --git a/build2/b.cli b/build2/b.cli
index 31247f9..57640b6 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -692,5 +692,12 @@ namespace build2
value is used to shorten paths printed in diagnostics by replacing the home
directory with \cb{~/}. It is also made available to \cb{buildfile}'s as the
\cb{build.home} variable.
- "
+
+ The \cb{BUILD2_VAR_OVR} environment variable is used to propagate global
+ variable overrides to nested build system driver invocations. Its value is a
+ list of global variable assignments separated with newlines.
+
+ The \cb{BUILD2_DEF_OPT} environment variable is used to suppress loading of
+ default options files in nested build system driver invocations. Its values
+ are \cb{false} or \cb{0} to suppress and \cb{true} or \cb{1} to load."
}
diff --git a/build2/b.cxx b/build2/b.cxx
index 66b217d..449f1e1 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -230,9 +230,8 @@ main (int argc, char* argv[])
try
{
// Note that the diagnostics verbosity level can only be calculated after
- // default options are loaded and merged (see below). Thus, to trace the
- // default options files search, we refer to the verbosity level specified
- // on the command line.
+ // default options are loaded and merged (see below). Thus, until then we
+ // refer to the verbosity level specified on the command line.
//
auto verbosity = [] ()
{
@@ -372,9 +371,109 @@ main (int argc, char* argv[])
args += ')';
}
- // Handle default options files.
+ // Get/set an environment variable tracing the operation.
//
- if (!ops.no_default_options ()) // Command line option.
+ auto get_env = [&verbosity, &trace] (const char* nm)
+ {
+ optional<string> r (getenv (nm));
+
+ if (verbosity () >= 5)
+ {
+ if (r)
+ trace << nm << ": '" << *r << "'";
+ else
+ trace << nm << ": <NULL>";
+ }
+
+ return r;
+ };
+
+ auto set_env = [&verbosity, &trace] (const char* nm, const string& vl)
+ {
+ try
+ {
+ if (verbosity () >= 5)
+ trace << "setting " << nm << "='" << vl << "'";
+
+ setenv (nm, vl);
+ }
+ catch (const system_error& e)
+ {
+ // The variable value can potentially be long/multi-line, so let's
+ // print it last.
+ //
+ fail << "unable to set environment variable " << nm << ": " << e <<
+ info << "value: '" << vl << "'";
+ }
+ };
+
+ // If the BUILD2_VAR_OVR environment variable is present, then parse its
+ // value as a newline-separated global variable overrides and prepend
+ // them to the overrides specified on the command line.
+ //
+ // Note that this means global overrides may not contain a newline.
+
+ // Verify that the string is a valid global override. Uses the file name
+ // and the options flag for diagnostics only.
+ //
+ auto verify_glb_ovr = [] (const string& v, const path_name& fn, bool opt)
+ {
+ size_t p (v.find ('=', 1));
+ if (p == string::npos || v[0] != '!')
+ {
+ diag_record dr (fail (fn));
+ dr << "expected " << (opt ? "option or " : "") << "global "
+ << "variable override instead of '" << v << "'";
+
+ if (p != string::npos)
+ dr << info << "prefix variable assignment with '!'";
+ }
+
+ if (p == 1 || (p == 2 && v[1] == '+')) // '!=' or '!+=' ?
+ fail (fn) << "missing variable name in '" << v << "'";
+ };
+
+ optional<string> env_ovr (get_env ("BUILD2_VAR_OVR"));
+ if (env_ovr)
+ {
+ path_name fn ("<BUILD2_VAR_OVR>");
+
+ auto i (cmd_vars.begin ());
+ for (size_t b (0), e (0); next_word (*env_ovr, b, e, '\n', '\r'); )
+ {
+ // Extract the override from the current line, stripping the leading
+ // and trailing spaces.
+ //
+ string s (*env_ovr, b, e - b);
+ trim (s);
+
+ // Verify and save the override, unless the line is empty.
+ //
+ if (!s.empty ())
+ {
+ verify_glb_ovr (s, fn, false /* opt */);
+ i = cmd_vars.insert (i, move (s)) + 1;
+ }
+ }
+ }
+
+ // Load the default options files, unless --no-default-options is
+ // specified on the command line or the BUILD2_DEF_OPT environment
+ // variable is set to a value other than 'true' or '1'.
+ //
+ // If loaded, prepend the default global overrides to the variables
+ // specified on the command line, unless BUILD2_VAR_OVR is set in which
+ // case just ignore them.
+ //
+ optional<string> env_def (get_env ("BUILD2_DEF_OPT"));
+
+ // False if --no-default-options is specified on the command line. Note
+ // that we cache the flag since it can be overridden by a default
+ // options file.
+ //
+ bool cmd_def (!ops.no_default_options ());
+
+ if (cmd_def && (!env_def || *env_def == "true" || *env_def == "1"))
try
{
optional<dir_path> extra;
@@ -409,38 +508,28 @@ main (int argc, char* argv[])
//
ops = merge_default_options (def_ops, ops);
- // Merge the default and command line global overrides.
+ // Merge the default and command line global overrides, unless
+ // BUILD2_VAR_OVR is already set (in which case we assume this has
+ // already been done).
//
// Note that the "broken down" variable assignments occupying a single
// line are naturally supported.
//
- cmd_vars =
- merge_default_arguments (
- def_ops,
- cmd_vars,
- [] (const default_options_entry<options>& e, const strings&)
- {
- path_name fn (e.file);
-
- // Verify that all arguments are global overrides.
- //
- for (const string& a: e.arguments)
+ if (!env_ovr)
+ cmd_vars =
+ merge_default_arguments (
+ def_ops,
+ cmd_vars,
+ [&verify_glb_ovr] (const default_options_entry<options>& e,
+ const strings&)
{
- size_t p (a.find ('=', 1));
- if (p == string::npos || a[0] != '!')
- {
- diag_record dr (fail (fn));
- dr << "expected option or global variable override instead "
- << "of '" << a << "'";
+ path_name fn (e.file);
- if (p != string::npos)
- dr << info << "prefix variable assignment with '!'";
- }
-
- if (p == 1 || (p == 2 && a[1] == '+')) // '!=' or '!+=' ?
- fail (fn) << "missing variable name in '" << a << "'";
- }
- });
+ // Verify that all arguments are global overrides.
+ //
+ for (const string& a: e.arguments)
+ verify_glb_ovr (a, fn, true /* opt */);
+ });
}
catch (const pair<path, system_error>& e)
{
@@ -452,6 +541,41 @@ main (int argc, char* argv[])
fail << "unable to obtain home directory: " << e;
}
+ // Verify and save the global overrides present in cmd_vars (default,
+ // from the command line, etc), if any, into the BUILD2_VAR_OVR
+ // environment variable.
+ //
+ if (!cmd_vars.empty ())
+ {
+ string ovr;
+ for (const string& v: cmd_vars)
+ {
+ if (v[0] == '!')
+ {
+ if (v.find_first_of ("\n\r") != string::npos)
+ fail << "newline in global variable override '" << v << "'";
+
+ if (!ovr.empty ())
+ ovr += '\n';
+
+ ovr += v;
+ }
+ }
+
+ // Optimize for the common case.
+ //
+ // Note: cmd_vars may contain non-global overrides.
+ //
+ if (!ovr.empty () && (!env_ovr || *env_ovr != ovr))
+ set_env ("BUILD2_VAR_OVR", ovr);
+ }
+
+ // Propagate disabling of the default options files to the potential
+ // nested invocations.
+ //
+ if (!cmd_def && (!env_def || *env_def != "0"))
+ set_env ("BUILD2_DEF_OPT", "0");
+
// Validate options.
//
if (ops.progress () && ops.no_progress ())