From 9298908b00c1977fcce5a3ac8e29e982cc28c97f Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 15 Sep 2020 19:28:08 +0300 Subject: Add support for BUILD2_VAR_OVR and BUILD2_DEF_OPT environment variables --- build2/b-options.cxx | 10 ++- build2/b.cli | 9 ++- build2/b.cxx | 186 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 172 insertions(+), 33 deletions(-) (limited to 'build2') 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 r (getenv (nm)); + + if (verbosity () >= 5) + { + if (r) + trace << nm << ": '" << *r << "'"; + else + trace << nm << ": "; + } + + 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 env_ovr (get_env ("BUILD2_VAR_OVR")); + if (env_ovr) + { + path_name fn (""); + + 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 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 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& 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& 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& 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 ()) -- cgit v1.1