1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

Merge topic 'update-kwsys'

77ea15b797 Tests: Add case covering `cmake -E env` with empty environment variable
209dfc51d3 Merge branch 'upstream-KWSys' into update-kwsys
92a7104600 KWSys 2025-10-08 (3bae03fe)

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !11288
This commit is contained in:
Brad King
2025-10-08 15:25:14 +00:00
committed by Kitware Robot
6 changed files with 57 additions and 29 deletions

View File

@@ -209,7 +209,6 @@ endif()
# Include helper macros. # Include helper macros.
include(${CMAKE_CURRENT_SOURCE_DIR}/kwsysPlatformTests.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/kwsysPlatformTests.cmake)
include(CheckTypeSize)
# Do full dependency headers. # Do full dependency headers.
include_regular_expression("^.*$") include_regular_expression("^.*$")

View File

@@ -675,15 +675,21 @@ char const* SystemTools::GetEnv(std::string const& key)
bool SystemTools::GetEnv(char const* key, std::string& result) bool SystemTools::GetEnv(char const* key, std::string& result)
{ {
#if defined(_WIN32) #if defined(_WIN32)
auto wide_key = Encoding::ToWide(key); std::wstring const wKey = Encoding::ToWide(key);
auto result_size = GetEnvironmentVariableW(wide_key.data(), nullptr, 0); std::vector<wchar_t> heapBuf;
if (result_size <= 0) { wchar_t stackBuf[256];
DWORD bufSz = static_cast<DWORD>(sizeof(stackBuf) / sizeof(stackBuf[0]));
wchar_t* buf = stackBuf;
DWORD r;
while ((r = GetEnvironmentVariableW(wKey.c_str(), buf, bufSz)) >= bufSz) {
heapBuf.resize(r);
bufSz = r;
buf = &heapBuf[0];
}
if (r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
return false; return false;
} }
std::wstring wide_result; result = Encoding::ToNarrow(buf);
wide_result.resize(result_size - 1);
GetEnvironmentVariableW(wide_key.data(), &wide_result[0], result_size);
result = Encoding::ToNarrow(wide_result);
return true; return true;
#else #else
char const* v = getenv(key); char const* v = getenv(key);
@@ -703,12 +709,12 @@ bool SystemTools::GetEnv(std::string const& key, std::string& result)
bool SystemTools::HasEnv(char const* key) bool SystemTools::HasEnv(char const* key)
{ {
#if defined(_WIN32) #if defined(_WIN32)
std::wstring const wkey = Encoding::ToWide(key); std::wstring const wKey = Encoding::ToWide(key);
wchar_t const* v = _wgetenv(wkey.c_str()); DWORD r = GetEnvironmentVariableW(wKey.c_str(), nullptr, 0);
return !(r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND);
#else #else
char const* v = getenv(key); return getenv(key) != nullptr;
#endif #endif
return v;
} }
bool SystemTools::HasEnv(std::string const& key) bool SystemTools::HasEnv(std::string const& key)
@@ -732,8 +738,8 @@ static int kwsysUnPutEnv(std::string const& env)
} }
#elif defined(__CYGWIN__) || defined(__GLIBC__) #elif defined(__CYGWIN__) || defined(__GLIBC__)
/* putenv("A") removes A from the environment. It must not put the // putenv("A") removes A from the environment with the GNU runtime.
memory in the environment because it does not have any "=" syntax. */ // It cannot put the memory in the environment since there is no "=" syntax.
static int kwsysUnPutEnv(std::string const& env) static int kwsysUnPutEnv(std::string const& env)
{ {
@@ -750,12 +756,7 @@ static int kwsysUnPutEnv(std::string const& env)
} }
#elif defined(_WIN32) #elif defined(_WIN32)
/* putenv("A=") places "A=" in the environment, which is as close to // putenv("A=") removes A from the environment with the MSVC runtime.
removal as we can get with the putenv API. We have to leak the
most recent value placed in the environment for each variable name
on program exit in case exit routines access it. */
static kwsysEnvSet kwsysUnPutEnvSet;
static int kwsysUnPutEnv(std::string const& env) static int kwsysUnPutEnv(std::string const& env)
{ {
@@ -763,13 +764,7 @@ static int kwsysUnPutEnv(std::string const& env)
size_t const pos = wEnv.find('='); size_t const pos = wEnv.find('=');
size_t const len = pos == std::string::npos ? wEnv.size() : pos; size_t const len = pos == std::string::npos ? wEnv.size() : pos;
wEnv.resize(len + 1, L'='); wEnv.resize(len + 1, L'=');
wchar_t* newEnv = _wcsdup(wEnv.c_str()); return _wputenv(wEnv.c_str());
if (!newEnv) {
return -1;
}
kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv));
kwsysUnPutEnvSet.insert(newEnv);
return _wputenv(newEnv);
} }
#else #else
@@ -846,7 +841,7 @@ public:
bool Put(char const* env) bool Put(char const* env)
{ {
# if defined(_WIN32) # if defined(_WIN32)
std::wstring const wEnv = Encoding::ToWide(env); std::wstring wEnv = Encoding::ToWide(env);
wchar_t* newEnv = _wcsdup(wEnv.c_str()); wchar_t* newEnv = _wcsdup(wEnv.c_str());
# else # else
char* newEnv = strdup(env); char* newEnv = strdup(env);
@@ -854,7 +849,21 @@ public:
Free oldEnv(this->Release(newEnv)); Free oldEnv(this->Release(newEnv));
this->insert(newEnv); this->insert(newEnv);
# if defined(_WIN32) # if defined(_WIN32)
return _wputenv(newEnv) == 0; // `_wputenv` updates both the C runtime library's `_wenviron` array
// and the process's environment block.
if (_wputenv(newEnv) != 0) {
return false;
}
// There seems to be no way to add an empty variable to `_wenviron`
// through the C runtime library: `_wputenv("A=")` removes "A".
// Add it directly to the process's environment block.
// This is used by child processes, and by our `GetEnv`.
std::string::size_type const eqPos = wEnv.find(L'=');
if (eqPos != std::string::npos && (eqPos + 1) == wEnv.size()) {
wEnv.resize(eqPos);
return SetEnvironmentVariableW(wEnv.c_str(), L"");
}
return true;
# else # else
return putenv(newEnv) == 0; return putenv(newEnv) == 0;
# endif # endif

View File

@@ -696,6 +696,12 @@ static bool CheckPutEnv(std::string const& env, char const* name,
<< value << "\"!" << std::endl; << value << "\"!" << std::endl;
return false; return false;
} }
bool bv = kwsys::SystemTools::HasEnv(name);
if (!bv) {
std::cerr << "HasEnv(\"" << name << "\") returned \"" << bv
<< "\", not \"true\"!" << std::endl;
return false;
}
return true; return true;
} }
@@ -711,6 +717,12 @@ static bool CheckUnPutEnv(char const* env, char const* name)
<< "\", not (null)!" << std::endl; << "\", not (null)!" << std::endl;
return false; return false;
} }
bool bv = kwsys::SystemTools::HasEnv(name);
if (bv) {
std::cerr << "HasEnv(\"" << name << "\") returned \"" << bv
<< "\", not \"false\"!" << std::endl;
return false;
}
return true; return true;
} }
@@ -721,6 +733,7 @@ static bool CheckEnvironmentOperations()
res &= CheckPutEnv("B=C", "B", "C"); res &= CheckPutEnv("B=C", "B", "C");
res &= CheckPutEnv("C=D", "C", "D"); res &= CheckPutEnv("C=D", "C", "D");
res &= CheckPutEnv("D=E", "D", "E"); res &= CheckPutEnv("D=E", "D", "E");
res &= CheckPutEnv("F=", "F", "");
res &= CheckUnPutEnv("A", "A"); res &= CheckUnPutEnv("A", "A");
res &= CheckUnPutEnv("B=", "B"); res &= CheckUnPutEnv("B=", "B");
res &= CheckUnPutEnv("C=D", "C"); res &= CheckUnPutEnv("C=D", "C");

View File

@@ -0,0 +1 @@
^-- TEST_ENV is correctly set in environment: ''$

View File

@@ -0,0 +1,5 @@
if(DEFINED ENV{TEST_ENV})
message(STATUS "TEST_ENV is correctly set in environment: '$ENV{TEST_ENV}'")
else()
message(FATAL_ERROR "TEST_ENV is incorrectly not set in environment")
endif()

View File

@@ -820,6 +820,7 @@ run_cmake_command(E_env-no-command0 ${CMAKE_COMMAND} -E env)
run_cmake_command(E_env-no-command1 ${CMAKE_COMMAND} -E env TEST_ENV=1) run_cmake_command(E_env-no-command1 ${CMAKE_COMMAND} -E env TEST_ENV=1)
run_cmake_command(E_env-bad-arg1 ${CMAKE_COMMAND} -E env -bad-arg1) run_cmake_command(E_env-bad-arg1 ${CMAKE_COMMAND} -E env -bad-arg1)
run_cmake_command(E_env-set ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-set.cmake) run_cmake_command(E_env-set ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-set.cmake)
run_cmake_command(E_env-empty ${CMAKE_COMMAND} -E env TEST_ENV= ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-empty.cmake)
run_cmake_command(E_env-unset ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -E env --unset=TEST_ENV ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-unset.cmake) run_cmake_command(E_env-unset ${CMAKE_COMMAND} -E env TEST_ENV=1 ${CMAKE_COMMAND} -E env --unset=TEST_ENV ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-unset.cmake)
run_cmake_command(E_env-stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/E_env-stdin.cmake) run_cmake_command(E_env-stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/E_env-stdin.cmake)