diff options
author | Dmitry Lomov <dslomov@google.com> | 2017-07-19 12:26:39 +0200 |
---|---|---|
committer | Klaus Aehlig <aehlig@google.com> | 2017-07-19 16:48:24 +0200 |
commit | 891f540a1bc6977f25e77a29f24cdcf939fb2e2e (patch) | |
tree | 915a969f6f3a0af308339f2361b51562cfb3ce96 | |
parent | 9d61bad6a0595ffd64faa5d0889d3f9d9ace3fc3 (diff) |
Implement bash.exe detection logic.
The logic is as follows:
1) search for msys installation
2) search got git-on-Windows installation
3) search in PATH.
This happens on every client startup unless BAZEL_SH enviornment
variable is set.
My measurements show that the time required for this detection is
negligible (<10 msec in the worst case).
Change-Id: If130e2491a9df5a23954d303f2ccdb932eeed1db
PiperOrigin-RevId: 162466913
-rw-r--r-- | src/main/cpp/blaze.cc | 21 | ||||
-rw-r--r-- | src/main/cpp/blaze_util.cc | 18 | ||||
-rw-r--r-- | src/main/cpp/blaze_util.h | 8 | ||||
-rw-r--r-- | src/main/cpp/blaze_util_platform.h | 2 | ||||
-rw-r--r-- | src/main/cpp/blaze_util_posix.cc | 4 | ||||
-rw-r--r-- | src/main/cpp/blaze_util_windows.cc | 259 |
6 files changed, 296 insertions, 16 deletions
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc index 3571b236d2..0ac61a0b3e 100644 --- a/src/main/cpp/blaze.cc +++ b/src/main/cpp/blaze.cc @@ -248,20 +248,6 @@ class GrpcBlazeServer : public BlazeServer { //////////////////////////////////////////////////////////////////////// // Logic -void debug_log(const char *format, ...) { - if (!globals->options->client_debug) { - return; - } - - fprintf(stderr, "CLIENT: "); - va_list arglist; - va_start(arglist, format); - vfprintf(stderr, format, arglist); - va_end(arglist); - fprintf(stderr, "%s", "\n"); - fflush(stderr); -} - // A devtools_ijar::ZipExtractorProcessor to extract the InstallKeyFile class GetInstallKeyFileProcessor : public devtools_ijar::ZipExtractorProcessor { public: @@ -1241,7 +1227,7 @@ static void ComputeBaseDirectories(const WorkspaceLayout *workspace_layout, blaze_util::JoinPath(globals->options->output_base, "server/jvm.out"); } -static void CheckEnvironment() { +static void CheckEnvironmentOrDie() { if (!blaze::GetEnv("http_proxy").empty()) { PrintWarning("ignoring http_proxy in environment."); blaze::UnsetEnv("http_proxy"); @@ -1283,6 +1269,8 @@ static void CheckEnvironment() { blaze::SetEnv("LANGUAGE", "en_US.ISO-8859-1"); blaze::SetEnv("LC_ALL", "en_US.ISO-8859-1"); blaze::SetEnv("LC_CTYPE", "en_US.ISO-8859-1"); + + blaze::DetectBashOrDie(); } static string CheckAndGetBinaryPath(const string &argv0) { @@ -1347,9 +1335,10 @@ int Main(int argc, const char *argv[], WorkspaceLayout *workspace_layout, globals->binary_path = CheckAndGetBinaryPath(argv[0]); ParseOptions(argc, argv); + blaze::SetDebugLog(globals->options->client_debug); debug_log("Debug logging active"); - CheckEnvironment(); + CheckEnvironmentOrDie(); blaze::CreateSecureOutputRoot(globals->options->output_user_root); const string self_path = GetSelfPath(); diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc index be3e0ebfd9..d8a5af6b7b 100644 --- a/src/main/cpp/blaze_util.cc +++ b/src/main/cpp/blaze_util.cc @@ -208,4 +208,22 @@ bool AwaitServerProcessTermination(int pid, const string& output_base, return true; } +static bool is_debug_log_enabled = false; + +void SetDebugLog(bool enabled) { is_debug_log_enabled = enabled; } + +void debug_log(const char *format, ...) { + if (!is_debug_log_enabled) { + return; + } + + fprintf(stderr, "CLIENT: "); + va_list arglist; + va_start(arglist, format); + vfprintf(stderr, format, arglist); + va_end(arglist); + fprintf(stderr, "%s", "\n"); + fflush(stderr); +} + } // namespace blaze diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h index f2c3a2f187..7c85d82dbf 100644 --- a/src/main/cpp/blaze_util.h +++ b/src/main/cpp/blaze_util.h @@ -112,6 +112,14 @@ std::string ToString(const T &value) { return oss.str(); } +// Control the output of debug information by debug_log. +// Revisit once client logging is fixed (b/32939567). +void SetDebugLog(bool enabled); + +// Output debug information from client. +// Revisit once client logging is fixed (b/32939567). +void debug_log(const char *format, ...); + } // namespace blaze #endif // BAZEL_SRC_MAIN_CPP_BLAZE_UTIL_H_ diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h index b842ae8385..bb69541a6e 100644 --- a/src/main/cpp/blaze_util_platform.h +++ b/src/main/cpp/blaze_util_platform.h @@ -230,6 +230,8 @@ int32_t GetExplicitSystemLimit(const int resource); // raised; false otherwise. bool UnlimitResources(); +void DetectBashOrDie(); + } // namespace blaze #endif // BAZEL_SRC_MAIN_CPP_BLAZE_UTIL_PLATFORM_H_ diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc index f5c7241db8..42fa76206c 100644 --- a/src/main/cpp/blaze_util_posix.cc +++ b/src/main/cpp/blaze_util_posix.cc @@ -762,4 +762,8 @@ bool UnlimitResources() { return success; } +void DetectBashOrDie() { + // do nothing. +} + } // namespace blaze. diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc index 802b3c2395..7f0ca75742 100644 --- a/src/main/cpp/blaze_util_windows.cc +++ b/src/main/cpp/blaze_util_windows.cc @@ -1448,4 +1448,263 @@ bool UnlimitResources() { return true; // Nothing to do so assume success. } +static const int MAX_KEY_LENGTH = 255; +// We do not care about registry values longer than MAX_PATH +static const int REG_VALUE_BUFFER_SIZE = MAX_PATH; + +// Implements heuristics to discover msys2 installation. +static string GetMsysBash() { + HKEY h_uninstall; + + // MSYS2 installer writes its registry into HKCU, although documentation + // (https://msdn.microsoft.com/en-us/library/ms954376.aspx) + // clearly states that it should go to HKLM. + static const char* const key = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + if (RegOpenKeyExA(HKEY_CURRENT_USER, // _In_ HKEY hKey, + key, // _In_opt_ LPCTSTR lpSubKey, + 0, // _In_ DWORD ulOptions, + KEY_ENUMERATE_SUB_KEYS | + KEY_QUERY_VALUE, // _In_ REGSAM samDesired, + &h_uninstall // _Out_ PHKEY phkResult + )) { + debug_log("Cannot open HKCU\\%s", key); + return string(); + } + AutoHandle auto_uninstall(h_uninstall); + + // Since MSYS2 decided to generate a new product key for each installation, + // we enumerate all keys under + // HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall and find the first + // with MSYS2 64bit display name. + static const char* const msys_display_name = "MSYS2 64bit"; + DWORD n_subkeys; + + if (RegQueryInfoKey(h_uninstall, // _In_ HKEY hKey, + 0, // _Out_opt_ LPTSTR lpClass, + 0, // _Inout_opt_ LPDWORD lpcClass, + 0, // _Reserved_ LPDWORD lpReserved, + &n_subkeys, // _Out_opt_ LPDWORD lpcSubKeys, + 0, // _Out_opt_ LPDWORD lpcMaxSubKeyLen, + 0, // _Out_opt_ LPDWORD lpcMaxClassLen, + 0, // _Out_opt_ LPDWORD lpcValues, + 0, // _Out_opt_ LPDWORD lpcMaxValueNameLen, + 0, // _Out_opt_ LPDWORD lpcMaxValueLen, + 0, // _Out_opt_ LPDWORD lpcbSecurityDescriptor, + 0 // _Out_opt_ PFILETIME lpftLastWriteTime + )) { + debug_log("Cannot query HKCU\\%s", key); + return string(); + } + + for (DWORD key_index = 0; key_index < n_subkeys; key_index++) { + char subkey_name[MAX_KEY_LENGTH]; + if (RegEnumKeyA(h_uninstall, // _In_ HKEY hKey, + key_index, // _In_ DWORD dwIndex, + subkey_name, // _Out_ LPTSTR lpName, + sizeof(subkey_name) // _In_ DWORD cchName + )) { + debug_log("Cannot get %d subkey of HKCU\\%s", key_index, key); + continue; // try next subkey + } + + HKEY h_subkey; + if (RegOpenKeyEx(h_uninstall, // _In_ HKEY hKey, + subkey_name, // _In_opt_ LPCTSTR lpSubKey, + 0, // _In_ DWORD ulOptions, + KEY_QUERY_VALUE, // _In_ REGSAM samDesired, + &h_subkey // _Out_ PHKEY phkResult + )) { + debug_log("Failed to open subkey HKCU\\%s\\%s", key, subkey_name); + continue; // try next subkey + } + AutoHandle auto_subkey(h_subkey); + + BYTE value[REG_VALUE_BUFFER_SIZE]; + DWORD value_length = sizeof(value); + DWORD value_type; + + if (RegQueryValueEx(h_subkey, // _In_ HKEY hKey, + "DisplayName", // _In_opt_ LPCTSTR lpValueName, + 0, // _Reserved_ LPDWORD lpReserved, + &value_type, // _Out_opt_ LPDWORD lpType, + value, // _Out_opt_ LPBYTE lpData, + &value_length // _Inout_opt_ LPDWORD lpcbData + )) { + debug_log("Failed to query DisplayName of HKCU\\%s\\%s", key, + subkey_name); + continue; // try next subkey + } + + if (value_type == REG_SZ && + 0 == memcmp(msys_display_name, value, sizeof(msys_display_name))) { + debug_log("Getting install location of HKCU\\%s\\%s", key, subkey_name); + BYTE path[REG_VALUE_BUFFER_SIZE]; + DWORD path_length = sizeof(path); + DWORD path_type; + if (RegQueryValueEx( + h_subkey, // _In_ HKEY hKey, + "InstallLocation", // _In_opt_ LPCTSTR lpValueName, + 0, // _Reserved_ LPDWORD lpReserved, + &path_type, // _Out_opt_ LPDWORD lpType, + path, // _Out_opt_ LPBYTE lpData, + &path_length // _Inout_opt_ LPDWORD lpcbData + )) { + debug_log("Failed to query InstallLocation of HKCU\\%s\\%s", key, + subkey_name); + continue; // try next subkey + } + + if (path_length == 0 || path_type != REG_SZ) { + debug_log("Zero-length (%d) install location or wrong type (%d)", + path_length, path_type); + continue; // try next subkey + } + + debug_log("Install location of HKCU\\%s\\%s is %s", key, subkey_name, + path); + string path_as_string(path, path + path_length - 1); + string bash_exe = path_as_string + "\\usr\\bin\\bash.exe"; + if (!blaze_util::PathExists(bash_exe)) { + debug_log("%s does not exist", bash_exe.c_str()); + continue; // try next subkey + } + + debug_log("Detected msys bash at %s", bash_exe.c_str()); + return bash_exe; + } + } + return string(); +} + +// Implements heuristics to discover Git-on-Win installation. +static string GetBashFromGitOnWin() { + HKEY h_GitOnWin_uninstall; + + // Well-known registry key for Git-on-Windows. + static const char* const key = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, // _In_ HKEY hKey, + key, // _In_opt_ LPCTSTR lpSubKey, + 0, // _In_ DWORD ulOptions, + KEY_QUERY_VALUE, // _In_ REGSAM samDesired, + &h_GitOnWin_uninstall // _Out_ PHKEY phkResult + )) { + debug_log("Cannot open HKCU\\%s", key); + return string(); + } + AutoHandle auto_h_GitOnWin_uninstall(h_GitOnWin_uninstall); + + debug_log("Getting install location of HKLM\\%s", key); + BYTE path[REG_VALUE_BUFFER_SIZE]; + DWORD path_length = sizeof(path); + DWORD path_type; + if (RegQueryValueEx(h_GitOnWin_uninstall, // _In_ HKEY hKey, + "InstallLocation", // _In_opt_ LPCTSTR lpValueName, + 0, // _Reserved_ LPDWORD lpReserved, + &path_type, // _Out_opt_ LPDWORD lpType, + path, // _Out_opt_ LPBYTE lpData, + &path_length // _Inout_opt_ LPDWORD lpcbData + )) { + debug_log("Failed to query InstallLocation of HKLM\\%s", key); + return string(); + } + + if (path_length == 0 || path_type != REG_SZ) { + debug_log("Zero-length (%d) install location or wrong type (%d)", + path_length, path_type); + return string(); + } + + debug_log("Install location of HKLM\\%s is %s", key, path); + string path_as_string(path, path + path_length - 1); + string bash_exe = path_as_string + "\\usr\\bin\\bash.exe"; + if (!blaze_util::PathExists(bash_exe)) { + debug_log("%s does not exist", bash_exe.c_str()); + return string(); + } + + debug_log("Detected msys bash at %s", bash_exe.c_str()); + return bash_exe; +} + +static string GetBashFromPath() { + char found[MAX_PATH]; + string path_list = blaze::GetEnv("PATH"); + + // We do not fully replicate all the quirks of search in PATH. + // There is no system function to do so, and that way lies madness. + size_t start = 0; + do { + // This ignores possibly quoted semicolons in PATH etc. + size_t end = path_list.find_first_of(";", start); + string path = path_list.substr( + start, end != string::npos ? end - start : string::npos); + // Handle one typical way of quoting (where.exe does not handle this, but + // CreateProcess does). + if (path.size() > 1 && path[0] == '"' && path[path.size() - 1] == '"') { + path = path.substr(1, path.size() - 2); + } + if (SearchPathA(path.c_str(), // _In_opt_ LPCTSTR lpPath, + "bash.exe", // _In_ LPCTSTR lpFileName, + 0, // LPCTSTR lpExtension, + sizeof(found), // DWORD nBufferLength, + found, // _Out_ LPTSTR lpBuffer, + 0 // _Out_opt_ LPTSTR *lpFilePart + )) { + debug_log("bash.exe found on PATH: %s", found); + return string(found); + } + if (end == string::npos) { + break; + } + start = end + 1; + } while (true); + + debug_log("bash.exe not found on PATH"); + return string(); +} + +static string LocateBash() { + string msys_bash = GetMsysBash(); + if (!msys_bash.empty()) { + return msys_bash; + } + + string git_on_win_bash = GetBashFromGitOnWin(); + if (!git_on_win_bash.empty()) { + return git_on_win_bash; + } + + return GetBashFromPath(); +} + +void DetectBashOrDie() { + if (!blaze::GetEnv("BAZEL_SH").empty()) return; + + uint64_t start = blaze::GetMillisecondsMonotonic(); + + string bash = LocateBash(); + uint64_t end = blaze::GetMillisecondsMonotonic(); + debug_log("BAZEL_SH detection took %lu msec", end - start); + + if (!bash.empty()) { + blaze::SetEnv("BAZEL_SH", bash); + } else { + printf( + "Bazel on Windows requires bash.exe and other Unix tools, but we could " + "not find them.\n" + "If you do not have them installed, the easiest is to install MSYS2 " + "from\n" + " http://repo.msys2.org/distrib/msys2-x86_64-latest.exe\n" + "or git-on-Windows from\n" + " https://git-scm.com/download/win\n" + "\n" + "If you already have bash.exe installed but Bazel cannot find it,\n" + "set BAZEL_SH environment variable to it's location:\n" + " set BAZEL_SH=c:\\path\\to\\bash.exe\n"); + exit(1); + } +} + } // namespace blaze |