aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-04-07 11:33:05 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-04-07 11:43:04 +0200
commite9f69e067da3e096e1e64be70ec2b6de30f71d2c (patch)
tree478c9f0ec86a225398424bcc9305270ca6800339
parent5e51d523e71231cb190e9ed981962df527f4ee7e (diff)
Register environment variables for hermetic build configurations
-rw-r--r--build2/b-options.cxx5
-rw-r--r--build2/b.cli8
-rw-r--r--build2/b.cxx1
-rw-r--r--libbuild2/bin/guess.cxx51
-rw-r--r--libbuild2/bin/guess.hxx14
-rw-r--r--libbuild2/bin/init.cxx8
-rw-r--r--libbuild2/cc/compile-rule.cxx2
-rw-r--r--libbuild2/cc/guess.cxx107
-rw-r--r--libbuild2/cc/guess.hxx6
-rw-r--r--libbuild2/cc/module.cxx3
-rw-r--r--libbuild2/config/init.cxx1
-rw-r--r--libbuild2/config/module.cxx7
-rw-r--r--libbuild2/config/module.hxx9
-rw-r--r--libbuild2/config/utility.cxx1
-rw-r--r--libbuild2/config/utility.hxx83
-rw-r--r--libbuild2/dist/init.cxx22
-rw-r--r--libbuild2/file.cxx30
-rw-r--r--libbuild2/install/init.cxx34
-rw-r--r--libbuild2/parser.cxx46
-rw-r--r--libbuild2/parser.hxx3
-rw-r--r--libbuild2/test/init.cxx6
-rw-r--r--libbuild2/version/snapshot-git.cxx6
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<semantic_version> 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<string> ("bin.ar.version.build") = v.build;
}
+ config::save_environment (rs, ari.ar_environment);
+
if (ranlib != nullptr)
{
rs.assign<process_path_ex> ("bin.ranlib.path") =
@@ -747,6 +749,8 @@ namespace build2
rs.assign<string> ("bin.ranlib.id") = ari.ranlib_id;
rs.assign<string> ("bin.ranlib.signature") = ari.ranlib_signature;
rs.assign<string> ("bin.ranlib.checksum") = ari.ranlib_checksum;
+
+ config::save_environment (rs, ari.ranlib_environment);
}
}
@@ -871,6 +875,8 @@ namespace build2
rs.assign<uint64_t> ("bin.ld.version.patch") = v.patch;
rs.assign<string> ("bin.ld.version.build") = v.build;
}
+
+ config::save_environment (rs, ldi.environment);
}
return true;
@@ -995,6 +1001,8 @@ namespace build2
rs.assign<string> ("bin.rc.id") = rci.id;
rs.assign<string> ("bin.rc.signature") = rci.signature;
rs.assign<string> ("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<pair<dir_paths, size_t>> sys_lib_dirs;
optional<pair<dir_paths, size_t>> sys_inc_dirs;
optional<pair<dir_paths, size_t>> 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<bool> (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> (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<uint64_t>);
+ 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<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,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<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 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 <libbuild2/parser.hxx>
#include <libbuild2/config/module.hxx> // config::module::version
-#include <libbuild2/config/utility.hxx> // config::lookup_config()
+#include <libbuild2/config/utility.hxx> // 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 <var-prefix>.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<strings> (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<bool> ("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 <name>...
+ //
+
+ // 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<strings> (
+ 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 */));