From b3bc26dc284cf73e97b88c9979d49368d07e686c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 26 Mar 2021 16:03:29 +0200 Subject: Add support for propagating project environment --- libbuild2/config/init.cxx | 86 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) (limited to 'libbuild2/config') diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index 9911670..1e6b36b 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -4,6 +4,8 @@ #include #include +#include // getenv() +#include // strncmp() #include #include @@ -28,6 +30,24 @@ namespace build2 void functions (function_map&); // functions.cxx + // Compare environment variable names. Note that on Windows they are + // case-insensitive. + // + static inline bool + compare_env (const string& x, size_t xn, const string& y, size_t yn) + { + if (xn == string::npos) xn = x.size (); + if (yn == string::npos) yn = y.size (); + + return xn == yn && +#ifdef _WIN32 + icasecmp (x.c_str (), y.c_str (), xn) == 0 +#else + strncmp (x.c_str (), y.c_str (), xn) == 0 +#endif + ; + } + // Custom save function for config.config.environment. // // It tries to optimize the storage in subprojects by appending the @@ -62,7 +82,7 @@ namespace build2 if (find_if (ds.rbegin (), i, [&v, p] (const string& v1) { - return v.compare (0, p, v1, 0, v1.find ('=')) == 0; + return compare_env (v, p, v1, v1.find ('=')); }) != i) continue; @@ -71,7 +91,7 @@ namespace build2 auto j (find_if (bs.rbegin (), bs.rend (), [&v, p] (const string& v1) { - return v.compare (0, p, v1, 0, v1.find ('=')) == 0; + return compare_env (v, p, v1, v1.find ('=')); })); if (j == bs.rend () || *j != v) @@ -375,6 +395,68 @@ namespace build2 rs["config.config.persist"]); } + // Copy config.config.environment to scope::root_extra::environment. + // + // Note that we store shallow copies that point to the c.c.environment + // value which means it shall not change. + // + if (const strings* src = cast_null ( + rs["config.config.environment"])) + { + vector& dst (rs.root_extra->environment); + + // The idea is to only copy entries that are effective, that is those + // that actually override something in the environment. This should be + // both more efficient and less noisy (e.g., if we decide to print + // this in diagnostics). + // + // Note that config.config.environment may contain duplicates and the + // last entry should have effect. + // + // Note also that we use std::getenv() instead of butl::getenv() to + // disregard any thread environment overrides. + // + for (auto i (src->rbegin ()), e (src->rend ()); i != e; ++i) + { + // Note that we must only consider variable names (up to first '=' + // if any). + // + const string& v (*i); + size_t p (v.find ('=')); + + // Check if we have already seen this variable. + // + if (find_if (src->rbegin (), i, + [&v, p] (const string& v1) + { + return compare_env (v, p, v1, v1.find ('=')); + }) != i) + continue; + + // If it's an unset, see if it actually unsets anything. + // + if (p == string::npos) + { + if (std::getenv (v.c_str ()) == nullptr) + continue; + } + // + // And if it's a set, see if it sets a different value. + // + else + { + const char* v1 (std::getenv (string (v, 0, p).c_str ())); + if (v1 != nullptr && v.compare (p + 1, string::npos, v1) == 0) + continue; + } + + dst.push_back (v.c_str ()); + } + + if (!dst.empty ()) + dst.push_back (nullptr); + } + // Register alias and fallback rule for the configure meta-operation. // // We need this rule for out-of-any-project dependencies (e.g., -- cgit v1.1