Bazel client, Windows: implement envvar handling
Implement GetEnv, SetEnv, UnsetEnv, and use it in
some locations, plus add some tests.
See https://github.com/bazelbuild/bazel/issues/2107
--
PiperOrigin-RevId: 144808435
MOS_MIGRATED_REVID=144808435
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 0d8bdb2..23cf626 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -284,15 +284,15 @@
// for TEMP under MSYS, though it can retrieve WINDIR.
WCHAR buffer[kWindowsPathBufferSize] = {0};
- if (!GetTempPathW(kWindowsPathBufferSize, buffer)) {
+ if (!::GetTempPathW(kWindowsPathBufferSize, buffer)) {
PrintErrorW(L"GetTempPathW");
pdie(255, "Could not retrieve the temp directory path");
}
return string(blaze_util::WstringToCstring(buffer).get());
#else // not COMPILER_MSVC
for (const char* i : {"TMPDIR", "TEMPDIR", "TMP", "TEMP"}) {
- char* tmpdir = getenv(i);
- if (tmpdir != NULL && strlen(tmpdir) > 0) {
+ string tmpdir(GetEnv(i));
+ if (!tmpdir.empty()) {
return tmpdir;
}
}
@@ -351,8 +351,8 @@
}
string GetDefaultHostJavabase() {
- const char *javahome = getenv("JAVA_HOME");
- if (javahome == NULL) {
+ string javahome(GetEnv("JAVA_HOME"));
+ if (javahome.empty()) {
die(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"Error: JAVA_HOME not set.");
}
@@ -1123,33 +1123,36 @@
}
string GetEnv(const string& name) {
+ DWORD size = ::GetEnvironmentVariableA(name.c_str(), NULL, 0);
+ if (size == 0) {
#ifdef COMPILER_MSVC
- // TODO(bazel-team): implement this.
- pdie(255, "blaze::GetEnv is not implemented on Windows");
- return "";
+ return string(); // unset or empty envvar
#else // not COMPILER_MSVC
- char* result = getenv(name.c_str());
- return result != NULL ? string(result) : "";
+ char* result = getenv(name.c_str());
+ return result != NULL ? string(result) : string();
#endif // COMPILER_MSVC
+ }
+
+ unique_ptr<char[]> value(new char[size]);
+ ::GetEnvironmentVariableA(name.c_str(), value.get(), size);
+ return string(value.get());
}
void SetEnv(const string& name, const string& value) {
-#ifdef COMPILER_MSVC
- // TODO(bazel-team): implement this.
- pdie(255, "blaze::SetEnv is not implemented on Windows");
-#else // not COMPILER_MSVC
- setenv(name.c_str(), value.c_str(), 1);
-#endif // COMPILER_MSVC
+ if (value.empty()) {
+ ::SetEnvironmentVariableA(name.c_str(), NULL);
+#ifndef COMPILER_MSVC
+ unsetenv(name.c_str());
+#endif // not COMPILER_MSVC
+ } else {
+ ::SetEnvironmentVariableA(name.c_str(), value.c_str());
+#ifndef COMPILER_MSVC
+ setenv(name.c_str(), value.c_str(), 1);
+#endif // not COMPILER_MSVC
+ }
}
-void UnsetEnv(const string& name) {
-#ifdef COMPILER_MSVC
- // TODO(bazel-team): implement this.
- pdie(255, "blaze::UnsetEnv is not implemented on Windows");
-#else // not COMPILER_MSVC
- unsetenv(name.c_str());
-#endif // COMPILER_MSVC
-}
+void UnsetEnv(const string& name) { SetEnv(name, ""); }
void SetupStdStreams() {
#ifdef COMPILER_MSVC
diff --git a/src/test/cpp/BUILD b/src/test/cpp/BUILD
index acc5461..057c721 100644
--- a/src/test/cpp/BUILD
+++ b/src/test/cpp/BUILD
@@ -10,7 +10,18 @@
cc_test(
name = "blaze_util_test",
- srcs = ["blaze_util_test.cc"],
+ srcs = select({
+ "//src:windows": [
+ "blaze_util_test.cc",
+ "blaze_util_windows_test.cc",
+ ],
+ "//src:windows_msvc": [
+ "blaze_util_windows_test.cc",
+ ],
+ "//conditions:default": [
+ "blaze_util_test.cc",
+ ],
+ }),
deps = [
"//src/main/cpp:blaze_util",
"//src/main/cpp/util",
diff --git a/src/test/cpp/blaze_util_windows_test.cc b/src/test/cpp/blaze_util_windows_test.cc
new file mode 100644
index 0000000..e558617
--- /dev/null
+++ b/src/test/cpp/blaze_util_windows_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <windows.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "src/main/cpp/blaze_util.h"
+#include "src/main/cpp/blaze_util_platform.h"
+#include "src/main/cpp/util/strings.h"
+#include "gtest/gtest.h"
+
+namespace blaze {
+
+using std::string;
+using std::unique_ptr;
+using std::wstring;
+
+// Asserts that the envvar named `key` is unset.
+// Exercises GetEnvironmentVariable{A,W}, both with `key` and its lower case
+// version, to make sure that envvar retrieval is case-insensitive (envvar names
+// are case-insensitive on Windows).
+//
+// This is a macro so the assertions will have the correct line number.
+#define ASSERT_ENVVAR_UNSET(/* const char* */ key) \
+ { \
+ ASSERT_EQ(::GetEnvironmentVariableA(key, NULL, 0), (DWORD)0); \
+ ASSERT_EQ( \
+ ::GetEnvironmentVariableA(blaze_util::AsLower(key).c_str(), NULL, 0), \
+ (DWORD)0); \
+ ASSERT_EQ(::GetEnvironmentVariableW( \
+ blaze_util::CstringToWstring(key).get(), NULL, 0), \
+ (DWORD)0); \
+ ASSERT_EQ(::GetEnvironmentVariableW(blaze_util::CstringToWstring( \
+ blaze_util::AsLower(key).c_str()) \
+ .get(), \
+ NULL, 0), \
+ (DWORD)0); \
+ }
+
+// Asserts that the envvar named `key` is set to the `expected` value.
+// Exercises GetEnvironmentVariable{A,W}, both with `key` and its lower case
+// version, to make sure that envvar retrieval is case-insensitive (envvar names
+// are case-insensitive on Windows).
+//
+// This is a macro so the assertions will have the correct line number.
+#define ASSERT_ENVVAR(/* const (char* | string&) */ _key, \
+ /* const (char* | string&) */ _expected) \
+ { \
+ string key(_key); \
+ string expected(_expected); \
+ DWORD size = ::GetEnvironmentVariableA(key.c_str(), NULL, 0); \
+ ASSERT_GT(size, (DWORD)0); \
+ unique_ptr<char[]> buf(new char[size]); \
+ \
+ /* Assert that GetEnvironmentVariableA can retrieve the value. */ \
+ ASSERT_EQ(::GetEnvironmentVariableA(key.c_str(), buf.get(), size), \
+ size - 1); \
+ ASSERT_EQ(string(buf.get()), expected); \
+ \
+ /* Assert that envvar keys are case-insensitive. */ \
+ string lkey(blaze_util::AsLower(key)); \
+ ASSERT_EQ(::GetEnvironmentVariableA(lkey.c_str(), buf.get(), size), \
+ size - 1); \
+ ASSERT_EQ(string(buf.get()), expected); \
+ \
+ /* Assert that GetEnvironmentVariableW can retrieve the value. */ \
+ wstring wkey(blaze_util::CstringToWstring(key.c_str()).get()); \
+ wstring wexpected(blaze_util::CstringToWstring(expected.c_str()).get()); \
+ size = ::GetEnvironmentVariableW(wkey.c_str(), NULL, 0); \
+ ASSERT_GT(size, (DWORD)0); \
+ unique_ptr<WCHAR[]> wbuf(new WCHAR[size]); \
+ ASSERT_EQ(::GetEnvironmentVariableW(wkey.c_str(), wbuf.get(), size), \
+ size - 1); \
+ ASSERT_EQ(wstring(wbuf.get()), wexpected); \
+ \
+ /* Assert that widechar envvar keys are case-insensitive. */ \
+ wstring wlkey(blaze_util::CstringToWstring(lkey.c_str()).get()); \
+ ASSERT_EQ(::GetEnvironmentVariableW(wlkey.c_str(), wbuf.get(), size), \
+ size - 1); \
+ ASSERT_EQ(wstring(wbuf.get()), wexpected); \
+ }
+
+TEST(BlazeUtilWindowsTest, TestGetEnv) {
+ ASSERT_ENVVAR_UNSET("DOES_not_EXIST");
+
+ string actual(GetEnv("TEST_SRCDIR"));
+ ASSERT_NE(actual, "");
+
+ std::replace(actual.begin(), actual.end(), '/', '\\');
+ ASSERT_NE(actual.find(":\\"), string::npos);
+
+ ASSERT_ENVVAR_UNSET("Bazel_TEST_Key1");
+ ASSERT_TRUE(::SetEnvironmentVariableA("Bazel_TEST_Key1", "some_VALUE"));
+ ASSERT_ENVVAR("Bazel_TEST_Key1", "some_VALUE");
+ ASSERT_TRUE(::SetEnvironmentVariableA("Bazel_TEST_Key1", NULL));
+
+ string long_string(MAX_PATH, 'a');
+ string long_key = string("Bazel_TEST_Key2_") + long_string;
+ string long_value = string("Bazel_TEST_Value2_") + long_string;
+
+ ASSERT_ENVVAR_UNSET(long_key.c_str());
+ ASSERT_TRUE(::SetEnvironmentVariableA(long_key.c_str(), long_value.c_str()));
+ ASSERT_ENVVAR(long_key, long_value);
+ ASSERT_TRUE(::SetEnvironmentVariableA(long_key.c_str(), NULL));
+}
+
+TEST(BlazeUtilWindowsTest, TestSetEnv) {
+ ASSERT_ENVVAR_UNSET("Bazel_TEST_Key1");
+ SetEnv("Bazel_TEST_Key1", "some_VALUE");
+ ASSERT_ENVVAR("Bazel_TEST_Key1", "some_VALUE");
+ SetEnv("Bazel_TEST_Key1", "");
+ ASSERT_ENVVAR_UNSET("Bazel_TEST_Key1");
+
+ string long_string(MAX_PATH, 'a');
+ string long_key = string("Bazel_TEST_Key2_") + long_string;
+ string long_value = string("Bazel_TEST_Value2_") + long_string;
+
+ ASSERT_ENVVAR_UNSET(long_key.c_str());
+ SetEnv(long_key, long_value);
+ ASSERT_ENVVAR(long_key.c_str(), long_value.c_str());
+ SetEnv(long_key, "");
+ ASSERT_ENVVAR_UNSET(long_key.c_str());
+}
+
+TEST(BlazeUtilWindowsTest, TestUnsetEnv) {
+ ASSERT_ENVVAR_UNSET("Bazel_TEST_Key1");
+ SetEnv("Bazel_TEST_Key1", "some_VALUE");
+ ASSERT_ENVVAR("Bazel_TEST_Key1", "some_VALUE");
+ UnsetEnv("Bazel_TEST_Key1");
+ ASSERT_ENVVAR_UNSET("Bazel_TEST_Key1");
+
+ string long_string(MAX_PATH, 'a');
+ string long_key = string("Bazel_TEST_Key2_") + long_string;
+ string long_value = string("Bazel_TEST_Value2_") + long_string;
+
+ ASSERT_ENVVAR_UNSET(long_key.c_str());
+ SetEnv(long_key, long_value);
+ ASSERT_ENVVAR(long_key.c_str(), long_value.c_str());
+ UnsetEnv(long_key);
+ ASSERT_ENVVAR_UNSET(long_key.c_str());
+}
+
+} // namespace blaze