From e9f69e067da3e096e1e64be70ec2b6de30f71d2c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 7 Apr 2021 11:33:05 +0200 Subject: Register environment variables for hermetic build configurations --- build2/b-options.cxx | 5 +- build2/b.cli | 8 ++- build2/b.cxx | 1 + libbuild2/bin/guess.cxx | 51 ++++++++++++++++-- libbuild2/bin/guess.hxx | 14 ++++- libbuild2/bin/init.cxx | 8 +++ libbuild2/cc/compile-rule.cxx | 2 +- libbuild2/cc/guess.cxx | 107 +++++++++++++++++++++++++++++++++++-- libbuild2/cc/guess.hxx | 6 +++ libbuild2/cc/module.cxx | 3 ++ libbuild2/config/init.cxx | 1 + libbuild2/config/module.cxx | 7 +++ libbuild2/config/module.hxx | 9 ++++ libbuild2/config/utility.cxx | 1 + libbuild2/config/utility.hxx | 83 +++++++++++++++++++++++++++- libbuild2/dist/init.cxx | 22 ++++++++ libbuild2/file.cxx | 30 +++++++++-- libbuild2/install/init.cxx | 34 ++++++++++++ libbuild2/parser.cxx | 46 ++++++++++++++++ libbuild2/parser.hxx | 3 ++ libbuild2/test/init.cxx | 6 +++ libbuild2/version/snapshot-git.cxx | 6 +++ 22 files changed, 433 insertions(+), 20 deletions(-) diff --git a/build2/b-options.cxx b/build2/b-options.cxx index 17b791e..096aebc 100644 --- a/build2/b-options.cxx +++ b/build2/b-options.cxx @@ -1634,8 +1634,9 @@ namespace build2 << ::std::endl << "\033[1mEXIT STATUS\033[0m" << ::std::endl << ::std::endl - << "Non-zero exit status is returned in case of an error." << ::std::endl - << ::std::endl + << "Non-zero exit status is returned in case of an error." << ::std::endl; + + os << std::endl << "\033[1mENVIRONMENT\033[0m" << ::std::endl << ::std::endl << "The \033[1mHOME\033[0m environment variable is used to determine the user's home directory." << ::std::endl diff --git a/build2/b.cli b/build2/b.cli index a24837c..0ce0f9e 100644 --- a/build2/b.cli +++ b/build2/b.cli @@ -724,7 +724,12 @@ namespace build2 \h|EXIT STATUS| Non-zero exit status is returned in case of an error. + " + // NOTE: remember to update --build2-metadata output if adding any relevant + // new environment variables. + // + " \h|ENVIRONMENT| The \cb{HOME} environment variable is used to determine the user's home @@ -739,5 +744,6 @@ namespace build2 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." + 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 ba89931..b07dd0e 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -606,6 +606,7 @@ main (int argc, char* argv[]) << "b.name = [string] b" << endl << "b.version = [string] '" << LIBBUILD2_VERSION_FULL << '\'' << endl << "b.checksum = [string] '" << LIBBUILD2_VERSION_FULL << '\'' << endl + << "b.environment = [strings] BUILD2_VAR_OVR BUILD2_DEF_OPT" << endl << "b.static = [bool] " << #ifdef LIBBUILD2_STATIC "true" diff --git a/libbuild2/bin/guess.cxx b/libbuild2/bin/guess.cxx index 9f15030..219b8e8 100644 --- a/libbuild2/bin/guess.cxx +++ b/libbuild2/bin/guess.cxx @@ -326,6 +326,9 @@ namespace build2 fail << "unable to guess " << *rl << " signature"; } + // None of the ar/ranlib implementations we recognize seem to use + // environment variables (not even Microsoft lib.exe). + // return ar_cache.insert (move (key), ar_info { move (arp), @@ -333,13 +336,39 @@ namespace build2 move (arr.signature), move (arr.checksum), move (*arr.version), + nullptr, move (rlp), move (rlr.id), move (rlr.signature), - move (rlr.checksum)}); + move (rlr.checksum), + nullptr}); } + // Linker environment variables (see also the cc module which duplicates + // some of these). + // + // Notes: + // + // - GNU linkers search in LD_LIBRARY_PATH in addition to LD_RUN_PATH but + // we assume the former is part of the built-in list. Interestingly, + // LLD does not search in either. + // + // - The LLD family of linkers have a bunch of undocumented, debugging- + // related variables (LLD_REPRODUCE, LLD_VERSION, LLD_IN_TEST) that we + // ignore. + // + // - ld64 uses a ton of environment variables (according to the source + // code) but none of them are documented in the man pages. So someone + // will need to figure out what's important (some of them are clearly + // for debugging of ld itself). + // + static const char* gnu_ld_env[] = { + "LD_RUN_PATH", "GNUTARGET", "LDEMULATION", "COLLECT_NO_DEMANGLE", nullptr}; + + static const char* msvc_ld_env[] = { + "LIB", "LINK", "_LINK_", nullptr}; + // Extracting ld information requires running it which can become // expensive if done repeatedly. So we cache the result. // @@ -522,15 +551,26 @@ namespace build2 if (r.empty ()) fail << "unable to guess " << ld << " signature"; + const char* const* ld_env ((r.id == "gnu" || + r.id == "gnu-gold") ? gnu_ld_env : + (r.id == "msvc" || + r.id == "msvc-lld") ? msvc_ld_env : + nullptr); + return ld_cache.insert (move (key), ld_info { move (pp), move (r.id), move (r.signature), move (r.checksum), - move (r.version)}); + move (r.version), + ld_env}); } + // Resource compiler environment variables. + // + static const char* msvc_rc_env[] = {"INCLUDE", nullptr}; + // Extracting rc information requires running it which can become // expensive if done repeatedly. So we cache the result. // @@ -632,12 +672,17 @@ namespace build2 if (r.empty ()) fail << "unable to guess " << rc << " signature"; + const char* const* rc_env ((r.id == "msvc" || + r.id == "msvc-llvm") ? msvc_rc_env : + nullptr); + return rc_cache.insert (move (key), rc_info { move (pp), move (r.id), move (r.signature), - move (r.checksum)}); + move (r.checksum), + rc_env}); } } } diff --git a/libbuild2/bin/guess.hxx b/libbuild2/bin/guess.hxx index 9a63fa1..e5da263 100644 --- a/libbuild2/bin/guess.hxx +++ b/libbuild2/bin/guess.hxx @@ -28,6 +28,9 @@ namespace build2 // a toolchain-specific manner (usually the output of --version/-V) and // is not bulletproof. // + // The environment is an optional list of environment variables that + // affect ar/ranlib result. + // struct ar_info { process_path ar_path; @@ -35,11 +38,13 @@ namespace build2 string ar_signature; string ar_checksum; semantic_version ar_version; + const char* const* ar_environment; process_path ranlib_path; string ranlib_id; string ranlib_signature; string ranlib_checksum; + const char* const* ranlib_environment; }; // The ranlib path can be NULL, in which case no ranlib guessing will be @@ -72,6 +77,9 @@ namespace build2 // toolchain-specific manner (usually the output of --version/-version/-v) // and is not bulletproof. // + // The environment is an optional list of environment variables that + // affect the linker result. + // // Note that for now the version is extracted only for some linkers. Once // it's done for all of them, we should drop optional. // @@ -81,8 +89,8 @@ namespace build2 string id; string signature; string checksum; - optional version; + const char* const* environment; }; const ld_info& @@ -102,12 +110,16 @@ namespace build2 // toolchain-specific manner (usually the output of --version) and is not // bulletproof. // + // The environment is an optional list of environment variables that + // affect the resource compiler result. + // struct rc_info { process_path path; string id; string signature; string checksum; + const char* const* environment; }; const rc_info& diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx index 51066cb..62c7bcf 100644 --- a/libbuild2/bin/init.cxx +++ b/libbuild2/bin/init.cxx @@ -740,6 +740,8 @@ namespace build2 rs.assign ("bin.ar.version.build") = v.build; } + config::save_environment (rs, ari.ar_environment); + if (ranlib != nullptr) { rs.assign ("bin.ranlib.path") = @@ -747,6 +749,8 @@ namespace build2 rs.assign ("bin.ranlib.id") = ari.ranlib_id; rs.assign ("bin.ranlib.signature") = ari.ranlib_signature; rs.assign ("bin.ranlib.checksum") = ari.ranlib_checksum; + + config::save_environment (rs, ari.ranlib_environment); } } @@ -871,6 +875,8 @@ namespace build2 rs.assign ("bin.ld.version.patch") = v.patch; rs.assign ("bin.ld.version.build") = v.build; } + + config::save_environment (rs, ldi.environment); } return true; @@ -995,6 +1001,8 @@ namespace build2 rs.assign ("bin.rc.id") = rci.id; rs.assign ("bin.rc.signature") = rci.signature; rs.assign ("bin.rc.checksum") = rci.checksum; + + config::save_environment (rs, rci.environment); } return true; diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index a27530a..b5016bc 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -6596,7 +6596,7 @@ namespace build2 if (getenv ("IFCPATH")) { // VC's IFCPATH takes precedence over /module:stdIfcDir so unset it if - // we are using our own std modules. + // we are using our own std modules. Note: IFCPATH saved in guess.cxx. // if (!stdifc.empty ()) env.push_back ("IFCPATH"); diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index d80fd17..82be591 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -829,7 +829,9 @@ namespace build2 // // The main drawback of the latter, of course, is that the commands we // print are no longer re-runnable (even though we may have supplied - // the rest of the "environment" explicitly on the command line). + // the rest of the "environment" explicitly on the command line). Plus + // we would need to save whatever environment variables we used to + // form the fallback path in case of hermetic configuration. // // An alternative strategy is to try and obtain the corresponding // "environment" in case of the effective (absolute) path similar to @@ -1571,6 +1573,13 @@ namespace build2 const char* msvc_cpu (const string&); // msvc.cxx + // Note that LIB, LINK, and _LINK_ are technically link.exe's variables + // but we include them in case linking is done via the compiler without + // loading bin.ld. BTW, the same applies to rc.exe INCLUDE. + // + static const char* msvc_env[] = {"INCLUDE", "IFCPATH", "CL", "_CL_", + "LIB", "LINK", "_LINK_", nullptr}; + static compiler_info guess_msvc (const char* xm, lang xl, @@ -1796,9 +1805,41 @@ namespace build2 move (xsl), move (lib_dirs), move (inc_dirs), - move (mod_dirs)}; + move (mod_dirs), + msvc_env, + nullptr}; } + // See "Environment Variables Affecting GCC". + // + // @@ TODO: Yt feels like we should unset the following variables: + // + // DEPENDENCIES_OUTPUT + // SUNPRO_DEPENDENCIES + // + // Note also that we include (some) linker's variables in case linking is + // done via the compiler without loading bin.ld (to do this precisely we + // would need to detect which linker is being used at which point we might + // as well load bin.ld). + // + static const char* gcc_c_env[] = { + "CPATH", "C_INCLUDE_PATH", + "LIBRARY_PATH", "LD_RUN_PATH", + "SOURCE_DATE_EPOCH", "GCC_EXEC_PREFIX", "COMPILER_PATH", + nullptr}; + + static const char* gcc_cxx_env[] = { + "CPATH", "CPLUS_INCLUDE_PATH", + "LIBRARY_PATH", "LD_RUN_PATH", + "SOURCE_DATE_EPOCH", "GCC_EXEC_PREFIX", "COMPILER_PATH", + nullptr}; + + // Note that Clang recognizes a whole family of *_DEPLOYMENT_TARGET + // variables. + // + static const char* macos_env[] = { + "SDKROOT", "MACOSX_DEPLOYMENT_TARGET", nullptr}; + static compiler_info guess_gcc (const char* xm, lang xl, @@ -1979,6 +2020,17 @@ namespace build2 } } + // Environment. + // + const char* const* c_env (nullptr); + switch (xl) + { + case lang::c: c_env = gcc_c_env; break; + case lang::cxx: c_env = gcc_cxx_env; break; + } + + const char* const* p_env (tt.system == "darwin" ? macos_env : nullptr); + return compiler_info { move (gr.path), move (gr.id), @@ -1996,7 +2048,9 @@ namespace build2 move (xsl), nullopt, nullopt, - nullopt}; + nullopt, + c_env, + p_env}; } struct clang_msvc_info: msvc_info @@ -2218,6 +2272,22 @@ namespace build2 return r; } + // These are derived from gcc_* plus the sparse documentation (clang(1)) + // and source code. + // + // + static const char* clang_c_env[] = { + "CPATH", "C_INCLUDE_PATH", + "LIBRARY_PATH", "LD_RUN_PATH", + "COMPILER_PATH", + nullptr}; + + static const char* clang_cxx_env[] = { + "CPATH", "CPLUS_INCLUDE_PATH", + "LIBRARY_PATH", "LD_RUN_PATH", + "COMPILER_PATH", + nullptr}; + static compiler_info guess_clang (const char* xm, lang xl, @@ -2624,6 +2694,29 @@ namespace build2 } } + // Environment. + // + // Note that "Emscripten Compiler Frontend (emcc)" has a long list of + // environment variables with little explanation. So someone will need + // to figure out what's important (some of them are clearly for + // debugging of emcc itself). + // + const char* const* c_env (nullptr); + const char* const* p_env (nullptr); + if (tt.system == "win32-msvc") + c_env = msvc_env; + else + { + switch (xl) + { + case lang::c: c_env = clang_c_env; break; + case lang::cxx: c_env = clang_cxx_env; break; + } + + if (tt.system == "darwin") + p_env = macos_env; + } + return compiler_info { move (gr.path), move (gr.id), @@ -2641,7 +2734,9 @@ namespace build2 move (xsl), move (lib_dirs), nullopt, - nullopt}; + nullopt, + c_env, + p_env}; } static compiler_info @@ -2956,7 +3051,9 @@ namespace build2 move (xsl), nullopt, nullopt, - nullopt}; + nullopt, + nullptr, /* TODO */ + nullptr}; } // Compiler checks can be expensive (we often need to run the compiler diff --git a/libbuild2/cc/guess.hxx b/libbuild2/cc/guess.hxx index a141fa3..6bab649 100644 --- a/libbuild2/cc/guess.hxx +++ b/libbuild2/cc/guess.hxx @@ -236,6 +236,12 @@ namespace build2 optional> sys_lib_dirs; optional> sys_inc_dirs; optional> sys_mod_dirs; + + // Optional list of environment variables that affect the compiler and + // its target platform. + // + const char* const* compiler_environment; + const char* const* platform_environment; }; // In a sense this is analagous to the language standard which we handle diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 2300927..5bb3f60 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -706,6 +706,9 @@ namespace build2 rs.assign (x_sys_lib_dirs) = move (lib_dirs.first); rs.assign (x_sys_inc_dirs) = move (inc_dirs.first); + config::save_environment (rs, xi.compiler_environment); + config::save_environment (rs, xi.platform_environment); + // Load cc.core.config. // if (!cast_false (rs["cc.core.config.loaded"])) diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index 950579a..7b810bd 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -549,6 +549,7 @@ namespace build2 // Initialize the config entry points in the build system core. // config_save_variable = &module::save_variable; + config_save_environment = &module::save_environment; config_save_module = &module::save_module; config_preprocess_create = &preprocess_create; config_configure_post = &module::configure_post; diff --git a/libbuild2/config/module.cxx b/libbuild2/config/module.cxx index c84b1fa..713d30c 100644 --- a/libbuild2/config/module.cxx +++ b/libbuild2/config/module.cxx @@ -56,6 +56,13 @@ namespace build2 m->save_variable (var, flags); } + void module:: + save_environment (scope& rs, const char* var) + { + if (module* m = rs.find_module (module::name)) + m->save_environment (var); + } + bool module:: save_module (const char* name, int prio) { diff --git a/libbuild2/config/module.hxx b/libbuild2/config/module.hxx index 92fed32..543c486 100644 --- a/libbuild2/config/module.hxx +++ b/libbuild2/config/module.hxx @@ -173,6 +173,15 @@ namespace build2 return nullptr; } + void + save_environment (const char* var) + { + saved_environment.insert (var); + } + + static void + save_environment (scope&, const char*); + config::saved_environment saved_environment; strings old_environment; diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index f777c08..1f1ac08 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -8,6 +8,7 @@ using namespace std; namespace build2 { void (*config_save_variable) (scope&, const variable&, optional); + void (*config_save_environment) (scope&, const char*); void (*config_save_module) (scope&, const char*, int); const string& (*config_preprocess_create) (context&, values&, diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx index 45fdaba..382c22f 100644 --- a/libbuild2/config/utility.hxx +++ b/libbuild2/config/utility.hxx @@ -36,6 +36,9 @@ namespace build2 (*config_save_variable) (scope&, const variable&, optional); 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,7 +56,7 @@ namespace build2 namespace config { - // Mark the variable to be saved during configuration. + // Mark a variable to be saved during configuration. // const uint64_t save_default_commented = 0x01; // Based on value::extra. const uint64_t save_null_omitted = 0x02; // Treat NULL as undefined. @@ -67,7 +70,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. @@ -79,6 +82,82 @@ 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). While they + // can be cleared for each invocation, this is burdensome and it is + // often easier to just unset them for the entire build system process + // if we can be reasonable sure that there can be no plausible use for + // this variable (e.g., by another module or by the buildfile + // directly). The module's load() function is a natural place to do + // that. + // + 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 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. + // + 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. // diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx index a3d1e3f..2be4c3f 100644 --- a/libbuild2/dist/init.cxx +++ b/libbuild2/dist/init.cxx @@ -219,6 +219,28 @@ namespace build2 config::unsave_variable (rs, v); } + // Environment. + // + // Preparing a distribution may involve executing the following + // programs: + // + // install + // + // While some install implementations recognize environment variables, + // none of them affect our invocations (see the install module for + // analysis). + // + // *sum programs (md5sum, sha1sum, sha256sum, etc) + // + // These do not recognize any environment variables (at least the + // GNU coreutils versions). + // + // + // tar, zip, gzip, xz (and whatever tar may invoke) + // + // This is a can of worms that we currently don't touch (perhaps this + // will sort itself out if/when we switch to libarchive). + return true; } diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index e96142d..02ad71d 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -19,7 +19,7 @@ #include #include // config::module::version -#include // config::lookup_config() +#include // config::{lookup_*, save_*}() using namespace std; using namespace butl; @@ -2888,6 +2888,10 @@ namespace build2 target& t (pt->rw ()); // Load phase. + // Note that if metadata is requested via any of the import*() functions, + // then we will always end up here (see delegates to import_direct()), + // which is where we do the final verifications and processing. + // if (meta) { // The export.metadata value should start with the version followed by @@ -2924,14 +2928,30 @@ namespace build2 fail (loc) << "invalid metadata variable prefix in imported " << "target " << t; + const string& pfx (ns[1].value); + + auto& vp (ctx.var_pool.rw ()); // Load phase. + // See if we have the stable program name in the .name // variable. If its missing, set it to the metadata key (i.e., target // name as imported) by default. // - auto& vp (ctx.var_pool.rw ()); // Load phase. - value& nv (t.assign (vp.insert (ns[1].value + ".name"))); - if (!nv) - nv = *meta; + { + value& nv (t.assign (vp.insert (pfx + ".name"))); + if (!nv) + nv = *meta; + } + + // See if the program reported the use of environment variables and + // if so save them as affecting this project. + // + if (const auto* e = cast_null (t.vars[pfx + ".environment"])) + { + scope& rs (*base.root_scope ()); + + for (const string& v: *e) + config::save_environment (rs, v); + } } else fail (loc) << "no metadata for imported target " << t; diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 769b106..74e9301 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -313,6 +313,40 @@ namespace build2 vp.insert ("install.subdirs"); } + // Environment. + // + // Installation may involve executing the following programs: + // + // install + // + // GNU coreutils install recognizes the SIMPLE_BACKUP_SUFFIX and + // VERSION_CONTROL variables but they only matter with --backup which + // we do not specify and assume unlikely to be specified via .options. + // + // FreeBSD install recognizes STRIPBIN and DONTSTRIP variables that + // only matter with -s which we do not specify but which could be + // specified with .options. NetBSD and OpenBSD use STRIP (Mac OS man + // page doesn't list anything). + // + // sudo + // + // While sudo has a bunch of SUDO_* variables, none of them appear to + // matter (either not used in the modes that we invoke sudo in or do + // not affect the result). + // + // ln, rm, rmdir + // + // GNU coreutils ln recognizes the SIMPLE_BACKUP_SUFFIX and + // VERSION_CONTROL variables but they only matter with --backup which + // we do not specify. + // +#if defined(__FreeBSD__) + config::save_environment (rs, {"STRIPBIN", "DONTSTRIP"}); +#elif defined(__NetBSD__) || \ + defined(__OpenBSD__) + config::save_environment (rs, "STRIP"); +#endif + // Register our rules. // { diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 90c865b..9022d5b 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -494,6 +494,10 @@ namespace build2 { f = &parser::parse_config; } + else if (n == "config.environment") + { + f = &parser::parse_config_environment; + } if (f != nullptr) { @@ -2486,6 +2490,48 @@ namespace build2 } void parser:: + parse_config_environment (token& t, type& tt) + { + // config.environment ... + // + + // While we could allow this directive during bootstrap, it would have to + // be after loading the config module, which can be error prone. So we + // disallow it for now (it's also not clear "configuring" bootstrap with + // environment variables is a good idea; think of info, etc). + // + if (stage_ == stage::boot) + fail (t) << "config.environment during bootstrap"; + + // Parse the rest as names in the value mode to get variable expansion, + // etc. + // + mode (lexer_mode::value); + next (t, tt); + const location l (get_location (t)); + + strings ns; + try + { + ns = convert ( + tt != type::newline && tt != type::eos + ? parse_names (t, tt, + pattern_mode::ignore, + "environment variable name", + nullptr) + : names ()); + } + catch (const invalid_argument& e) + { + fail (l) << "invalid environment variable name: " << e.what (); + } + + config::save_environment (*root_, ns); + + next_after_newline (t, tt); + } + + void parser:: parse_import (token& t, type& tt) { tracer trace ("parser::parse_import", &path_); diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 915e34b..1af79bf 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -183,6 +183,9 @@ namespace build2 parse_config (token&, token_type&); void + parse_config_environment (token&, token_type&); + + void parse_import (token&, token_type&); void diff --git a/libbuild2/test/init.cxx b/libbuild2/test/init.cxx index dadbd20..539cdec 100644 --- a/libbuild2/test/init.cxx +++ b/libbuild2/test/init.cxx @@ -278,6 +278,12 @@ namespace build2 // //@@ TODO: Pring report. + // Environment. + // + // The only external program that we currently use is diff. None of the + // implementations we looked at (GNU diffutils, FreeBSD) use any + // environment variables. + // Register target types. // { diff --git a/libbuild2/version/snapshot-git.cxx b/libbuild2/version/snapshot-git.cxx index 8eb7cc8..28d96ac 100644 --- a/libbuild2/version/snapshot-git.cxx +++ b/libbuild2/version/snapshot-git.cxx @@ -37,6 +37,12 @@ namespace build2 // prevent this we pass the git's exec directory via the --exec-path // option explicitly. // + // Note also that git has quite a few GIT_* environment variables and + // stray values for some of them could break our commands. So it may + // seem like a good idea to unset them. But on the other hand, they may + // be there for a reason: after all, we are operating on user's projects + // and user's environment may be setup to handle them. + // path p ("git"); process_path pp (run_search (p, true /* init */)); -- cgit v1.1