Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 15 | #ifndef WIN32_LEAN_AND_MEAN |
| 16 | #define WIN32_LEAN_AND_MEAN |
| 17 | #endif |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 18 | #include <windows.h> |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 19 | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 20 | #include <fcntl.h> |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 21 | #include <io.h> // _open |
| 22 | #include <knownfolders.h> // FOLDERID_Profile |
| 23 | #include <lmcons.h> // UNLEN |
| 24 | #include <objbase.h> // CoTaskMemFree |
| 25 | #include <shlobj.h> // SHGetKnownFolderPath |
| 26 | #include <stdarg.h> // va_start, va_end, va_list |
Laszlo Csomor | 26f858c | 2017-02-08 17:50:10 +0000 | [diff] [blame] | 27 | |
László Csomor | e64ed19 | 2017-02-23 15:43:54 +0000 | [diff] [blame] | 28 | #include <algorithm> |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 29 | #include <cstdio> |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 30 | #include <cstdlib> |
Laszlo Csomor | ba6fbde | 2018-11-16 05:31:18 -0800 | [diff] [blame] | 31 | #include <memory> |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 32 | #include <mutex> // NOLINT |
| 33 | #include <set> |
Laszlo Csomor | f9f41c7 | 2017-01-09 12:06:45 +0000 | [diff] [blame] | 34 | #include <sstream> |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 35 | #include <thread> // NOLINT (to silence Google-internal linter) |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 36 | #include <type_traits> // static_assert |
Laszlo Csomor | e152f72 | 2017-02-03 10:05:36 +0000 | [diff] [blame] | 37 | #include <vector> |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 38 | |
Han-Wen Nienhuys | 36fbe63 | 2015-04-21 13:58:08 +0000 | [diff] [blame] | 39 | #include "src/main/cpp/blaze_util.h" |
Laszlo Csomor | a63d816 | 2019-05-21 04:35:07 -0700 | [diff] [blame] | 40 | #include "src/main/cpp/blaze_util_platform.h" |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 41 | #include "src/main/cpp/startup_options.h" |
Han-Wen Nienhuys | 36fbe63 | 2015-04-21 13:58:08 +0000 | [diff] [blame] | 42 | #include "src/main/cpp/util/errors.h" |
Thiago Farina | 7f9357f | 2015-04-23 13:57:43 +0000 | [diff] [blame] | 43 | #include "src/main/cpp/util/exit_code.h" |
Han-Wen Nienhuys | 36fbe63 | 2015-04-21 13:58:08 +0000 | [diff] [blame] | 44 | #include "src/main/cpp/util/file.h" |
Laszlo Csomor | 49970e0 | 2016-11-28 08:55:47 +0000 | [diff] [blame] | 45 | #include "src/main/cpp/util/file_platform.h" |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 46 | #include "src/main/cpp/util/logging.h" |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 47 | #include "src/main/cpp/util/md5.h" |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 48 | #include "src/main/cpp/util/numbers.h" |
ccalvarin | ac69da0 | 2018-06-05 15:27:26 -0700 | [diff] [blame] | 49 | #include "src/main/cpp/util/path.h" |
| 50 | #include "src/main/cpp/util/path_platform.h" |
László Csomor | 7287921 | 2017-02-02 15:55:18 +0000 | [diff] [blame] | 51 | #include "src/main/cpp/util/strings.h" |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 52 | #include "src/main/native/windows/file.h" |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 53 | #include "src/main/native/windows/process.h" |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 54 | #include "src/main/native/windows/util.h" |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 55 | |
| 56 | namespace blaze { |
| 57 | |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 58 | // Ensure we can safely cast (const) wchar_t* to LP(C)WSTR. |
| 59 | // This is true with MSVC but usually not with GCC. |
| 60 | static_assert(sizeof(wchar_t) == sizeof(WCHAR), |
| 61 | "wchar_t and WCHAR should be the same size"); |
| 62 | |
| 63 | // When using widechar Win32 API functions the maximum path length is 32K. |
| 64 | // Add 4 characters for potential UNC prefix and a couple more for safety. |
| 65 | static const size_t kWindowsPathBufferSize = 0x8010; |
| 66 | |
Yun Peng | 9c97bf9 | 2017-12-01 01:07:30 -0800 | [diff] [blame] | 67 | using bazel::windows::AutoAttributeList; |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 68 | using bazel::windows::AutoHandle; |
| 69 | using bazel::windows::CreateJunction; |
Laszlo Csomor | c2b70f1 | 2018-07-09 04:32:32 -0700 | [diff] [blame] | 70 | using bazel::windows::CreateJunctionResult; |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 71 | |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 72 | // TODO(bazel-team): stop using BAZEL_DIE, handle errors on the caller side. |
| 73 | // BAZEL_DIE calls exit(exitcode), which makes it difficult to follow the |
| 74 | // control flow and does not call destructors on local variables on the call |
| 75 | // stack. |
ccalvarin | c5a5880 | 2018-03-29 11:13:24 -0700 | [diff] [blame] | 76 | using blaze_util::GetLastErrorString; |
Laszlo Csomor | f00cee8 | 2016-12-15 10:58:03 +0000 | [diff] [blame] | 77 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 78 | using std::string; |
Laszlo Csomor | f00cee8 | 2016-12-15 10:58:03 +0000 | [diff] [blame] | 79 | using std::unique_ptr; |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 80 | using std::wstring; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 81 | |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 82 | namespace embedded_binaries { |
| 83 | |
| 84 | class WindowsDumper : public Dumper { |
| 85 | public: |
| 86 | static WindowsDumper* Create(string* error); |
| 87 | ~WindowsDumper() { Finish(nullptr); } |
| 88 | void Dump(const void* data, const size_t size, const string& path) override; |
| 89 | bool Finish(string* error) override; |
| 90 | |
| 91 | private: |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 92 | WindowsDumper() : threadpool_(nullptr), cleanup_group_(nullptr) {} |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 93 | |
| 94 | PTP_POOL threadpool_; |
| 95 | PTP_CLEANUP_GROUP cleanup_group_; |
| 96 | TP_CALLBACK_ENVIRON threadpool_env_; |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 97 | |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 98 | std::mutex dir_cache_lock_; |
| 99 | std::set<string> dir_cache_; |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 100 | |
| 101 | std::mutex error_lock_; |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 102 | string error_msg_; |
| 103 | }; |
| 104 | |
| 105 | namespace { |
| 106 | |
| 107 | class DumpContext { |
| 108 | public: |
| 109 | DumpContext(unique_ptr<uint8_t[]> data, const size_t size, const string path, |
| 110 | std::mutex* dir_cache_lock, std::set<string>* dir_cache, |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 111 | std::mutex* error_lock_, string* error_msg); |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 112 | void Run(); |
| 113 | |
| 114 | private: |
| 115 | void MaybeSignalError(const string& msg); |
| 116 | |
| 117 | unique_ptr<uint8_t[]> data_; |
| 118 | const size_t size_; |
| 119 | const string path_; |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 120 | |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 121 | std::mutex* dir_cache_lock_; |
| 122 | std::set<string>* dir_cache_; |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 123 | |
| 124 | std::mutex* error_lock_; |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 125 | string* error_msg_; |
| 126 | }; |
| 127 | |
| 128 | VOID CALLBACK WorkCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance, |
| 129 | _Inout_opt_ PVOID Context, _Inout_ PTP_WORK Work); |
| 130 | |
| 131 | } // namespace |
| 132 | |
| 133 | Dumper* Create(string* error) { return WindowsDumper::Create(error); } |
| 134 | |
| 135 | WindowsDumper* WindowsDumper::Create(string* error) { |
| 136 | unique_ptr<WindowsDumper> result(new WindowsDumper()); |
| 137 | |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 138 | result->threadpool_ = CreateThreadpool(nullptr); |
| 139 | if (result->threadpool_ == nullptr) { |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 140 | if (error) { |
| 141 | string msg = GetLastErrorString(); |
| 142 | *error = "CreateThreadpool failed: " + msg; |
| 143 | } |
| 144 | return nullptr; |
| 145 | } |
| 146 | |
| 147 | result->cleanup_group_ = CreateThreadpoolCleanupGroup(); |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 148 | if (result->cleanup_group_ == nullptr) { |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 149 | string msg = GetLastErrorString(); |
| 150 | CloseThreadpool(result->threadpool_); |
| 151 | if (error) { |
| 152 | string msg = GetLastErrorString(); |
| 153 | *error = "CreateThreadpoolCleanupGroup failed: " + msg; |
| 154 | } |
| 155 | return nullptr; |
| 156 | } |
| 157 | |
| 158 | // I (@laszlocsomor) experimented with different thread counts and found that |
| 159 | // 8 threads provide a significant advantage over 1 thread, but adding more |
| 160 | // threads provides only marginal speedup. |
| 161 | SetThreadpoolThreadMaximum(result->threadpool_, 16); |
| 162 | SetThreadpoolThreadMinimum(result->threadpool_, 8); |
| 163 | |
| 164 | InitializeThreadpoolEnvironment(&result->threadpool_env_); |
| 165 | SetThreadpoolCallbackPool(&result->threadpool_env_, result->threadpool_); |
| 166 | SetThreadpoolCallbackCleanupGroup(&result->threadpool_env_, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 167 | result->cleanup_group_, nullptr); |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 168 | |
| 169 | return result.release(); // release pointer ownership |
| 170 | } |
| 171 | |
| 172 | void WindowsDumper::Dump(const void* data, const size_t size, |
| 173 | const string& path) { |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 174 | { |
| 175 | std::lock_guard<std::mutex> g(error_lock_); |
| 176 | if (!error_msg_.empty()) { |
| 177 | return; |
| 178 | } |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | unique_ptr<uint8_t[]> data_copy(new uint8_t[size]); |
| 182 | memcpy(data_copy.get(), data, size); |
| 183 | unique_ptr<DumpContext> ctx(new DumpContext(std::move(data_copy), size, path, |
| 184 | &dir_cache_lock_, &dir_cache_, |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 185 | &error_lock_, &error_msg_)); |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 186 | PTP_WORK w = CreateThreadpoolWork(WorkCallback, ctx.get(), &threadpool_env_); |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 187 | if (w == nullptr) { |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 188 | string err = GetLastErrorString(); |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 189 | err = string("WindowsDumper::Dump() couldn't submit work: ") + err; |
| 190 | |
| 191 | std::lock_guard<std::mutex> g(error_lock_); |
| 192 | error_msg_ = err; |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 193 | } else { |
| 194 | ctx.release(); // release pointer ownership |
| 195 | SubmitThreadpoolWork(w); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | bool WindowsDumper::Finish(string* error) { |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 200 | if (threadpool_ == nullptr) { |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 201 | return true; |
| 202 | } |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 203 | CloseThreadpoolCleanupGroupMembers(cleanup_group_, FALSE, nullptr); |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 204 | CloseThreadpoolCleanupGroup(cleanup_group_); |
| 205 | CloseThreadpool(threadpool_); |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 206 | threadpool_ = nullptr; |
| 207 | cleanup_group_ = nullptr; |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 208 | |
| 209 | std::lock_guard<std::mutex> g(error_lock_); |
| 210 | if (!error_msg_.empty() && error) { |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 211 | *error = error_msg_; |
| 212 | } |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 213 | return error_msg_.empty(); |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 214 | } |
| 215 | |
| 216 | namespace { |
| 217 | |
| 218 | DumpContext::DumpContext(unique_ptr<uint8_t[]> data, const size_t size, |
| 219 | const string path, std::mutex* dir_cache_lock, |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 220 | std::set<string>* dir_cache, std::mutex* error_lock_, |
| 221 | string* error_msg) |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 222 | : data_(std::move(data)), |
| 223 | size_(size), |
| 224 | path_(path), |
| 225 | dir_cache_lock_(dir_cache_lock), |
| 226 | dir_cache_(dir_cache), |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 227 | error_msg_(error_msg) {} |
| 228 | |
| 229 | void DumpContext::Run() { |
| 230 | string dirname = blaze_util::Dirname(path_); |
| 231 | |
| 232 | bool success = true; |
| 233 | // Performance optimization: memoize the paths we already created a |
| 234 | // directory for, to spare a stat in attempting to recreate an already |
| 235 | // existing directory. This optimization alone shaves off seconds from the |
| 236 | // extraction time on Windows. |
| 237 | { |
| 238 | std::lock_guard<std::mutex> guard(*dir_cache_lock_); |
| 239 | if (dir_cache_->insert(dirname).second) { |
| 240 | success = blaze_util::MakeDirectories(dirname, 0777); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | if (!success) { |
| 245 | MaybeSignalError(string("Couldn't create directory '") + dirname + "'"); |
| 246 | return; |
| 247 | } |
| 248 | |
| 249 | if (!blaze_util::WriteFile(data_.get(), size_, path_, 0755)) { |
| 250 | MaybeSignalError(string("Failed to write zipped file '") + path_ + "'"); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | void DumpContext::MaybeSignalError(const string& msg) { |
Laszlo Csomor | 516282e | 2018-11-20 05:54:01 -0800 | [diff] [blame] | 255 | std::lock_guard<std::mutex> g(*error_lock_); |
| 256 | *error_msg_ = msg; |
Laszlo Csomor | ae43043f | 2018-07-17 04:18:30 -0700 | [diff] [blame] | 257 | } |
| 258 | |
| 259 | VOID CALLBACK WorkCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance, |
| 260 | _Inout_opt_ PVOID Context, _Inout_ PTP_WORK Work) { |
| 261 | unique_ptr<DumpContext> ctx(reinterpret_cast<DumpContext*>(Context)); |
| 262 | ctx->Run(); |
| 263 | } |
| 264 | |
| 265 | } // namespace |
| 266 | |
| 267 | } // namespace embedded_binaries |
| 268 | |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 269 | SignalHandler SignalHandler::INSTANCE; |
| 270 | |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 271 | class WindowsClock { |
| 272 | public: |
| 273 | uint64_t GetMilliseconds() const; |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 274 | |
| 275 | static const WindowsClock INSTANCE; |
| 276 | |
| 277 | private: |
| 278 | // Clock frequency per seconds. |
| 279 | // It's safe to cache this because (from QueryPerformanceFrequency on MSDN): |
| 280 | // "The frequency of the performance counter is fixed at system boot and is |
| 281 | // consistent across all processors. Therefore, the frequency need only be |
| 282 | // queried upon application initialization, and the result can be cached." |
| 283 | const LARGE_INTEGER kFrequency; |
| 284 | |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 285 | WindowsClock(); |
| 286 | |
| 287 | static LARGE_INTEGER GetFrequency(); |
| 288 | static LARGE_INTEGER GetMillisecondsAsLargeInt(const LARGE_INTEGER& freq); |
| 289 | }; |
| 290 | |
Dmitry Lomov | 69ad695 | 2017-03-21 16:20:37 +0000 | [diff] [blame] | 291 | BOOL WINAPI ConsoleCtrlHandler(_In_ DWORD ctrlType) { |
| 292 | static volatile int sigint_count = 0; |
| 293 | switch (ctrlType) { |
| 294 | case CTRL_C_EVENT: |
| 295 | case CTRL_BREAK_EVENT: |
| 296 | if (++sigint_count >= 3) { |
| 297 | SigPrintf( |
| 298 | "\n%s caught third Ctrl+C handler signal; killed.\n\n", |
michajlo | dc6d3b5 | 2019-06-06 14:04:46 -0700 | [diff] [blame] | 299 | SignalHandler::Get().GetProductName().c_str()); |
michajlo | f40241c | 2019-07-17 17:43:15 -0700 | [diff] [blame] | 300 | if (SignalHandler::Get().GetServerProcessInfo()->server_pid_ != -1) { |
mschaller | fd37b51 | 2017-07-11 18:21:36 +0200 | [diff] [blame] | 301 | KillServerProcess( |
michajlo | f40241c | 2019-07-17 17:43:15 -0700 | [diff] [blame] | 302 | SignalHandler::Get().GetServerProcessInfo()->server_pid_, |
michajlo | dc6d3b5 | 2019-06-06 14:04:46 -0700 | [diff] [blame] | 303 | SignalHandler::Get().GetOutputBase()); |
Dmitry Lomov | 69ad695 | 2017-03-21 16:20:37 +0000 | [diff] [blame] | 304 | } |
| 305 | _exit(1); |
| 306 | } |
| 307 | SigPrintf( |
| 308 | "\n%s Ctrl+C handler; shutting down.\n\n", |
michajlo | dc6d3b5 | 2019-06-06 14:04:46 -0700 | [diff] [blame] | 309 | SignalHandler::Get().GetProductName().c_str()); |
Dmitry Lomov | 69ad695 | 2017-03-21 16:20:37 +0000 | [diff] [blame] | 310 | SignalHandler::Get().CancelServer(); |
| 311 | return TRUE; |
| 312 | |
| 313 | case CTRL_CLOSE_EVENT: |
| 314 | SignalHandler::Get().CancelServer(); |
| 315 | return TRUE; |
| 316 | } |
| 317 | return false; |
| 318 | } |
| 319 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 320 | void SignalHandler::Install(const string& product_name, |
| 321 | const blaze_util::Path& output_base, |
| 322 | const ServerProcessInfo* server_process_info_, |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 323 | SignalHandler::Callback cancel_server) { |
michajlo | dc6d3b5 | 2019-06-06 14:04:46 -0700 | [diff] [blame] | 324 | product_name_ = product_name; |
| 325 | output_base_ = output_base; |
michajlo | f40241c | 2019-07-17 17:43:15 -0700 | [diff] [blame] | 326 | server_process_info_ = server_process_info_; |
michajlo | dc6d3b5 | 2019-06-06 14:04:46 -0700 | [diff] [blame] | 327 | cancel_server_ = cancel_server; |
Dmitry Lomov | 69ad695 | 2017-03-21 16:20:37 +0000 | [diff] [blame] | 328 | ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | ATTRIBUTE_NORETURN void SignalHandler::PropagateSignalOrExit(int exit_code) { |
Dmitry Lomov | 69ad695 | 2017-03-21 16:20:37 +0000 | [diff] [blame] | 332 | // We do not handle signals on Windows; always exit with exit_code. |
| 333 | exit(exit_code); |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 334 | } |
| 335 | |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 336 | // A signal-safe version of fprintf(stderr, ...). |
| 337 | // |
| 338 | // WARNING: any output from the blaze client may be interleaved |
| 339 | // with output from the blaze server. In --curses mode, |
| 340 | // the Blaze server often erases the previous line of output. |
| 341 | // So, be sure to end each such message with TWO newlines, |
| 342 | // otherwise it may be erased by the next message from the |
| 343 | // Blaze server. |
| 344 | // Also, it's a good idea to start each message with a newline, |
| 345 | // in case the Blaze server has written a partial line. |
Laszlo Csomor | 3b89d2d | 2016-11-28 14:04:27 +0000 | [diff] [blame] | 346 | void SigPrintf(const char *format, ...) { |
Dmitry Lomov | 5387c73 | 2017-03-22 09:33:28 +0000 | [diff] [blame] | 347 | int stderr_fileno = _fileno(stderr); |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 348 | char buf[1024]; |
| 349 | va_list ap; |
| 350 | va_start(ap, format); |
| 351 | int r = vsnprintf(buf, sizeof buf, format, ap); |
| 352 | va_end(ap); |
Dmitry Lomov | 5387c73 | 2017-03-22 09:33:28 +0000 | [diff] [blame] | 353 | if (write(stderr_fileno, buf, r) <= 0) { |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 354 | // We don't care, just placate the compiler. |
| 355 | } |
Laszlo Csomor | 3b89d2d | 2016-11-28 14:04:27 +0000 | [diff] [blame] | 356 | } |
Laszlo Csomor | 32086b2 | 2016-11-24 15:23:55 +0000 | [diff] [blame] | 357 | |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 358 | static void PrintErrorW(const wstring& op) { |
| 359 | DWORD last_error = ::GetLastError(); |
| 360 | if (last_error == 0) { |
| 361 | return; |
| 362 | } |
| 363 | |
| 364 | WCHAR* message_buffer; |
| 365 | FormatMessageW( |
| 366 | /* dwFlags */ FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 367 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 368 | /* lpSource */ nullptr, |
| 369 | /* dwMessageId */ last_error, |
| 370 | /* dwLanguageId */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| 371 | /* lpBuffer */ message_buffer, |
| 372 | /* nSize */ 0, |
| 373 | /* Arguments */ nullptr); |
| 374 | |
| 375 | fwprintf(stderr, L"ERROR: %s: %s (%d)\n", op.c_str(), message_buffer, |
| 376 | last_error); |
| 377 | LocalFree(message_buffer); |
| 378 | } |
| 379 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 380 | void WarnFilesystemType(const blaze_util::Path& output_base) {} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 381 | |
Laszlo Csomor | ae16e76 | 2016-11-18 10:16:08 +0000 | [diff] [blame] | 382 | string GetProcessIdAsString() { |
michajlo | 479c904 | 2020-03-27 11:43:39 -0700 | [diff] [blame] | 383 | return blaze_util::ToString(GetCurrentProcessId()); |
Laszlo Csomor | ae16e76 | 2016-11-18 10:16:08 +0000 | [diff] [blame] | 384 | } |
| 385 | |
Googler | 070a519 | 2019-12-06 09:21:42 -0800 | [diff] [blame] | 386 | string GetSelfPath(const char* argv0) { |
Laszlo Csomor | 31500b8 | 2016-12-15 10:15:36 +0000 | [diff] [blame] | 387 | WCHAR buffer[kWindowsPathBufferSize] = {0}; |
| 388 | if (!GetModuleFileNameW(0, buffer, kWindowsPathBufferSize)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 389 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 390 | << "GetSelfPath: GetModuleFileNameW: " << GetLastErrorString(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 391 | } |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 392 | return blaze_util::WstringToCstring(buffer); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 393 | } |
| 394 | |
Kristina Chodorow | 9396335 | 2015-03-20 19:11:19 +0000 | [diff] [blame] | 395 | string GetOutputRoot() { |
Laszlo Csomor | f083e762 | 2018-04-18 00:38:57 -0700 | [diff] [blame] | 396 | string home = GetHomeDir(); |
| 397 | if (home.empty()) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 398 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | 6da408c | 2018-11-19 05:50:58 -0800 | [diff] [blame] | 399 | << "Cannot find a good output root.\n" |
| 400 | "Set the USERPROFILE or the HOME environment variable.\n" |
| 401 | "Example (in cmd.exe):\n" |
| 402 | " set USERPROFILE=c:\\_bazel\\<YOUR-USERNAME>\n" |
| 403 | "or:\n" |
| 404 | " set HOME=c:\\_bazel\\<YOUR-USERNAME>"; |
Dmitry Lomov | bc84cc8 | 2016-04-15 14:05:24 +0000 | [diff] [blame] | 405 | } |
Laszlo Csomor | f083e762 | 2018-04-18 00:38:57 -0700 | [diff] [blame] | 406 | return home; |
Kristina Chodorow | 9396335 | 2015-03-20 19:11:19 +0000 | [diff] [blame] | 407 | } |
| 408 | |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 409 | string GetHomeDir() { |
Laszlo Csomor | b698e20 | 2019-09-24 08:26:26 -0700 | [diff] [blame] | 410 | // Check HOME, for sake of consistency with Linux / macOS. This is only set |
| 411 | // under MSYS2, or potentially in tests. |
| 412 | string home = GetPathEnv("HOME"); |
| 413 | if (IsRunningWithinTest() || !home.empty()) { |
Laszlo Csomor | 9b83bd7 | 2018-12-17 08:42:44 -0800 | [diff] [blame] | 414 | // Bazel is running inside of a test. Respect $HOME that the test setup has |
Laszlo Csomor | b698e20 | 2019-09-24 08:26:26 -0700 | [diff] [blame] | 415 | // set, even if it's empty. |
| 416 | return home; |
| 417 | } |
| 418 | |
| 419 | // Check USERPROFILE before calling SHGetKnownFolderPath. Doing so allows the |
| 420 | // user to customize (or override) the home directory. |
| 421 | // See https://github.com/bazelbuild/bazel/issues/7819#issuecomment-533050947 |
| 422 | string userprofile = GetPathEnv("USERPROFILE"); |
| 423 | if (!userprofile.empty()) { |
| 424 | return userprofile; |
Laszlo Csomor | 9b83bd7 | 2018-12-17 08:42:44 -0800 | [diff] [blame] | 425 | } |
| 426 | |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 427 | PWSTR wpath; |
Laszlo Csomor | 6da408c | 2018-11-19 05:50:58 -0800 | [diff] [blame] | 428 | // Look up the user's home directory. The default value of "FOLDERID_Profile" |
| 429 | // is the same as %USERPROFILE%, but it does not require the envvar to be set. |
Laszlo Csomor | b698e20 | 2019-09-24 08:26:26 -0700 | [diff] [blame] | 430 | // On Windows 2016 Server, Nano server: FOLDERID_Profile is unknown but |
| 431 | // %USERPROFILE% is set. See https://github.com/bazelbuild/bazel/issues/6701 |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 432 | if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, |
| 433 | nullptr, &wpath))) { |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 434 | string result = blaze_util::WstringToCstring(wpath); |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 435 | ::CoTaskMemFree(wpath); |
| 436 | return result; |
| 437 | } |
Laszlo Csomor | 6da408c | 2018-11-19 05:50:58 -0800 | [diff] [blame] | 438 | |
Laszlo Csomor | b698e20 | 2019-09-24 08:26:26 -0700 | [diff] [blame] | 439 | return ""; |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 440 | } |
| 441 | |
Laszlo Csomor | 760f786 | 2016-12-19 15:46:47 +0000 | [diff] [blame] | 442 | string FindSystemWideBlazerc() { |
László Csomor | fa27b50 | 2017-03-23 15:09:34 +0000 | [diff] [blame] | 443 | // TODO(bazel-team): figure out a good path to return here. |
Laszlo Csomor | 760f786 | 2016-12-19 15:46:47 +0000 | [diff] [blame] | 444 | return ""; |
Laszlo Csomor | 760f786 | 2016-12-19 15:46:47 +0000 | [diff] [blame] | 445 | } |
| 446 | |
Laszlo Csomor | 00549b4 | 2017-01-11 09:12:10 +0000 | [diff] [blame] | 447 | string GetJavaBinaryUnderJavabase() { return "bin/java.exe"; } |
| 448 | |
Laszlo Csomor | 943d3cf | 2016-11-07 14:27:21 +0000 | [diff] [blame] | 449 | uint64_t GetMillisecondsMonotonic() { |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 450 | return WindowsClock::INSTANCE.GetMilliseconds(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 451 | } |
| 452 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 453 | void SetScheduling(bool batch_cpu_scheduling, int io_nice_level) { |
| 454 | // TODO(bazel-team): There should be a similar function on Windows. |
| 455 | } |
| 456 | |
aldersondrive | 910079b | 2019-12-18 07:50:00 -0800 | [diff] [blame] | 457 | std::unique_ptr<blaze_util::Path> GetProcessCWD(int pid) { |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 458 | // TODO(bazel-team) 2016-11-18: decide whether we need this on Windows and |
| 459 | // implement or delete. |
aldersondrive | 910079b | 2019-12-18 07:50:00 -0800 | [diff] [blame] | 460 | return nullptr; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 461 | } |
| 462 | |
Thiago Farina | 01f3600 | 2015-04-08 15:59:08 +0000 | [diff] [blame] | 463 | bool IsSharedLibrary(const string &filename) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 464 | return blaze_util::ends_with(filename, ".dll"); |
| 465 | } |
| 466 | |
cushon | 849df36 | 2018-05-14 01:51:45 -0700 | [diff] [blame] | 467 | string GetSystemJavabase() { |
Laszlo Csomor | 0d10749 | 2019-03-13 09:04:27 -0700 | [diff] [blame] | 468 | string javahome(GetPathEnv("JAVA_HOME")); |
Philipp Wollermann | 8db450f | 2019-02-03 09:26:07 -0800 | [diff] [blame] | 469 | if (!javahome.empty()) { |
| 470 | string javac = blaze_util::JoinPath(javahome, "bin/javac.exe"); |
| 471 | if (blaze_util::PathExists(javac.c_str())) { |
| 472 | return javahome; |
| 473 | } |
| 474 | BAZEL_LOG(WARNING) |
| 475 | << "Ignoring JAVA_HOME, because it must point to a JDK, not a JRE."; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 476 | } |
Philipp Wollermann | 8db450f | 2019-02-03 09:26:07 -0800 | [diff] [blame] | 477 | |
| 478 | return ""; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 479 | } |
| 480 | |
Dmitry Lomov | b5ff50b | 2016-02-03 19:35:42 +0000 | [diff] [blame] | 481 | namespace { |
Dmitry Lomov | b5ff50b | 2016-02-03 19:35:42 +0000 | [diff] [blame] | 482 | |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 483 | // Max command line length is per CreateProcess documentation |
| 484 | // (https://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx) |
Lukacs Berki | 83c78b1 | 2016-06-24 12:35:08 +0000 | [diff] [blame] | 485 | |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 486 | static const int MAX_CMDLINE_LENGTH = 32768; |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 487 | |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 488 | struct CmdLine { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 489 | WCHAR cmdline[MAX_CMDLINE_LENGTH]; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 490 | }; |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 491 | static void CreateCommandLine(CmdLine* result, const blaze_util::Path& exe, |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 492 | const std::vector<std::wstring>& wargs_vector) { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 493 | std::wstringstream cmdline; |
Laszlo Csomor | 44ecf9a | 2017-01-10 10:41:26 +0000 | [diff] [blame] | 494 | string short_exe; |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 495 | if (!exe.IsEmpty()) { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 496 | string error; |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 497 | wstring wshort_exe; |
| 498 | if (!blaze_util::AsShortWindowsPath(exe.AsNativePath(), &wshort_exe, |
| 499 | &error)) { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 500 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 501 | << "CreateCommandLine: AsShortWindowsPath(" << exe.AsPrintablePath() |
| 502 | << "): " << error; |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 503 | } |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 504 | cmdline << L'\"' << wshort_exe << L'\"'; |
Laszlo Csomor | 44ecf9a | 2017-01-10 10:41:26 +0000 | [diff] [blame] | 505 | } |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 506 | |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 507 | bool first = true; |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 508 | for (const std::wstring& wa : wargs_vector) { |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 509 | if (first) { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 510 | // Skip first argument, it is equal to 'exe'. |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 511 | first = false; |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 512 | continue; |
| 513 | } else { |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 514 | cmdline << L' '; |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 515 | } |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 516 | cmdline << wa; |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 517 | } |
| 518 | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 519 | wstring cmdline_str = cmdline.str(); |
Laszlo Csomor | f9f41c7 | 2017-01-09 12:06:45 +0000 | [diff] [blame] | 520 | if (cmdline_str.size() >= MAX_CMDLINE_LENGTH) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 521 | BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) |
| 522 | << "Command line too long (" << cmdline_str.size() << " > " |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 523 | << MAX_CMDLINE_LENGTH |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 524 | << "): " << blaze_util::WstringToCstring(cmdline_str); |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 525 | } |
| 526 | |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 527 | // Copy command line into a mutable buffer. |
| 528 | // CreateProcess is allowed to mutate its command line argument. |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 529 | wcsncpy(result->cmdline, cmdline_str.c_str(), MAX_CMDLINE_LENGTH - 1); |
Lukacs Berki | 2236f7d | 2016-04-28 08:49:43 +0000 | [diff] [blame] | 530 | result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 531 | } |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 532 | |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 533 | } // namespace |
| 534 | |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 535 | static bool GetProcessStartupTime(HANDLE process, uint64_t* result) { |
| 536 | FILETIME creation_time, dummy1, dummy2, dummy3; |
| 537 | // GetProcessTimes cannot handle NULL arguments. |
| 538 | if (process == INVALID_HANDLE_VALUE || |
| 539 | !::GetProcessTimes(process, &creation_time, &dummy1, &dummy2, &dummy3)) { |
| 540 | return false; |
| 541 | } |
| 542 | *result = static_cast<uint64_t>(creation_time.dwHighDateTime) << 32 | |
| 543 | creation_time.dwLowDateTime; |
| 544 | return true; |
| 545 | } |
| 546 | |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 547 | static void WriteProcessStartupTime(const blaze_util::Path& server_dir, |
| 548 | HANDLE process) { |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 549 | uint64_t start_time = 0; |
| 550 | if (!GetProcessStartupTime(process, &start_time)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 551 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 552 | << "WriteProcessStartupTime(" << server_dir.AsPrintablePath() |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 553 | << "): GetProcessStartupTime failed: " << GetLastErrorString(); |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 554 | } |
| 555 | |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 556 | blaze_util::Path start_time_file = server_dir.GetRelative("server.starttime"); |
michajlo | 479c904 | 2020-03-27 11:43:39 -0700 | [diff] [blame] | 557 | if (!blaze_util::WriteFile(blaze_util::ToString(start_time), |
| 558 | start_time_file)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 559 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 560 | << "WriteProcessStartupTime(" << server_dir.AsPrintablePath() |
| 561 | << "): WriteFile(" << start_time_file.AsPrintablePath() |
| 562 | << ") failed: " << GetLastErrorString(); |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 563 | } |
| 564 | } |
| 565 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 566 | static HANDLE CreateJvmOutputFile(const blaze_util::Path& path, |
| 567 | LPSECURITY_ATTRIBUTES sa, |
michajlo | f74f9fe | 2018-01-29 09:06:44 -0800 | [diff] [blame] | 568 | bool daemon_out_append) { |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 569 | // If the previous server process was asked to be shut down (but not killed), |
| 570 | // it takes a while for it to comply, so wait until the JVM output file that |
| 571 | // it held open is closed. There seems to be no better way to wait for a file |
| 572 | // to be closed on Windows. |
| 573 | static const unsigned int timeout_sec = 60; |
| 574 | for (unsigned int waited = 0; waited < timeout_sec; ++waited) { |
| 575 | HANDLE handle = ::CreateFileW( |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 576 | /* lpFileName */ path.AsNativePath().c_str(), |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 577 | /* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE, |
Laszlo Csomor | 70ff798a | 2017-06-12 09:39:10 +0200 | [diff] [blame] | 578 | /* dwShareMode */ FILE_SHARE_READ, |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 579 | /* lpSecurityAttributes */ sa, |
michajlo | f74f9fe | 2018-01-29 09:06:44 -0800 | [diff] [blame] | 580 | /* dwCreationDisposition */ |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 581 | daemon_out_append ? OPEN_ALWAYS : CREATE_ALWAYS, |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 582 | /* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 583 | /* hTemplateFile */ nullptr); |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 584 | if (handle != INVALID_HANDLE_VALUE) { |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 585 | if (daemon_out_append && |
| 586 | !SetFilePointerEx(handle, {0}, nullptr, FILE_END)) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 587 | fprintf(stderr, "Could not seek to end of file (%s)\n", |
| 588 | path.AsPrintablePath().c_str()); |
michajlo | f74f9fe | 2018-01-29 09:06:44 -0800 | [diff] [blame] | 589 | return INVALID_HANDLE_VALUE; |
| 590 | } |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 591 | return handle; |
| 592 | } |
| 593 | if (GetLastError() != ERROR_SHARING_VIOLATION && |
| 594 | GetLastError() != ERROR_LOCK_VIOLATION) { |
| 595 | // Some other error occurred than the file being open; bail out. |
| 596 | break; |
| 597 | } |
| 598 | |
| 599 | // The file is still held open, the server is shutting down. There's a |
| 600 | // chance that another process holds it open, we don't know; in that case |
| 601 | // we just exit after the timeout expires. |
| 602 | if (waited == 5 || waited == 10 || waited == 30) { |
| 603 | fprintf(stderr, |
| 604 | "Waiting for previous Bazel server's log file to close " |
| 605 | "(waited %d seconds, waiting at most %d)\n", |
| 606 | waited, timeout_sec); |
| 607 | } |
| 608 | Sleep(1000); |
| 609 | } |
| 610 | return INVALID_HANDLE_VALUE; |
| 611 | } |
| 612 | |
Laszlo Csomor | d12fb7a | 2017-04-26 13:41:56 +0200 | [diff] [blame] | 613 | class ProcessHandleBlazeServerStartup : public BlazeServerStartup { |
| 614 | public: |
| 615 | ProcessHandleBlazeServerStartup(HANDLE _proc) : proc(_proc) {} |
| 616 | |
| 617 | bool IsStillAlive() override { |
| 618 | FILETIME dummy1, exit_time, dummy2, dummy3; |
| 619 | return GetProcessTimes(proc, &dummy1, &exit_time, &dummy2, &dummy3) && |
| 620 | exit_time.dwHighDateTime == 0 && exit_time.dwLowDateTime == 0; |
| 621 | } |
| 622 | |
| 623 | private: |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 624 | AutoHandle proc; |
Laszlo Csomor | d12fb7a | 2017-04-26 13:41:56 +0200 | [diff] [blame] | 625 | }; |
| 626 | |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 627 | int ExecuteDaemon(const blaze_util::Path& exe, |
| 628 | const std::vector<string>& args_vector, |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 629 | const std::map<string, EnvVarValue>& env, |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 630 | const blaze_util::Path& daemon_output, |
| 631 | const bool daemon_out_append, const string& binaries_dir, |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 632 | const blaze_util::Path& server_dir, |
| 633 | const StartupOptions& options, |
felly | 70eabb6 | 2017-11-30 00:40:54 -0800 | [diff] [blame] | 634 | BlazeServerStartup** server_startup) { |
Laszlo Csomor | ba6fbde | 2018-11-16 05:31:18 -0800 | [diff] [blame] | 635 | SECURITY_ATTRIBUTES inheritable_handle_sa = {sizeof(SECURITY_ATTRIBUTES), |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 636 | nullptr, TRUE}; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 637 | |
Laszlo Csomor | ba6fbde | 2018-11-16 05:31:18 -0800 | [diff] [blame] | 638 | AutoHandle devnull(::CreateFileW( |
| 639 | L"NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 640 | &inheritable_handle_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); |
Laszlo Csomor | d12fb7a | 2017-04-26 13:41:56 +0200 | [diff] [blame] | 641 | if (!devnull.IsValid()) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 642 | std::string error = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 643 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 644 | << "ExecuteDaemon(" << exe.AsPrintablePath() |
| 645 | << "): CreateFileA(NUL) failed: " << error; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 646 | } |
| 647 | |
Laszlo Csomor | ba6fbde | 2018-11-16 05:31:18 -0800 | [diff] [blame] | 648 | AutoHandle stdout_file(CreateJvmOutputFile( |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 649 | daemon_output, &inheritable_handle_sa, daemon_out_append)); |
Laszlo Csomor | 70ff798a | 2017-06-12 09:39:10 +0200 | [diff] [blame] | 650 | if (!stdout_file.IsValid()) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 651 | std::string error = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 652 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 653 | << "ExecuteDaemon(" << exe.AsPrintablePath() |
| 654 | << "): CreateJvmOutputFile(" << daemon_output.AsPrintablePath() |
| 655 | << ") failed: " << error; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 656 | } |
Laszlo Csomor | 70ff798a | 2017-06-12 09:39:10 +0200 | [diff] [blame] | 657 | HANDLE stderr_handle; |
| 658 | // We must duplicate the handle to stdout, otherwise "bazel clean --expunge" |
| 659 | // won't work, because when it tries to close stdout then stderr, the former |
| 660 | // will succeed but the latter will appear to be valid yet still fail to |
| 661 | // close. |
| 662 | if (!DuplicateHandle( |
| 663 | /* hSourceProcessHandle */ GetCurrentProcess(), |
| 664 | /* hSourceHandle */ stdout_file, |
| 665 | /* hTargetProcessHandle */ GetCurrentProcess(), |
| 666 | /* lpTargetHandle */ &stderr_handle, |
| 667 | /* dwDesiredAccess */ 0, |
| 668 | /* bInheritHandle */ TRUE, |
| 669 | /* dwOptions */ DUPLICATE_SAME_ACCESS)) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 670 | std::string error = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 671 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 672 | << "ExecuteDaemon(" << exe.AsPrintablePath() << "): DuplicateHandle(" |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 673 | << daemon_output.AsPrintablePath() << ") failed: " << error; |
Laszlo Csomor | 70ff798a | 2017-06-12 09:39:10 +0200 | [diff] [blame] | 674 | } |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 675 | AutoHandle stderr_file(stderr_handle); |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 676 | |
Laszlo Csomor | ba6fbde | 2018-11-16 05:31:18 -0800 | [diff] [blame] | 677 | // Create an attribute list. |
| 678 | wstring werror; |
| 679 | std::unique_ptr<AutoAttributeList> lpAttributeList; |
Laszlo Csomor | d235a06 | 2018-11-26 07:01:45 -0800 | [diff] [blame] | 680 | if (!AutoAttributeList::Create(devnull, stdout_file, stderr_handle, |
| 681 | &lpAttributeList, &werror)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 682 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 683 | << "ExecuteDaemon(" << exe.AsPrintablePath() |
| 684 | << "): attribute list creation failed: " |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 685 | << blaze_util::WstringToCstring(werror); |
Yun Peng | 9c97bf9 | 2017-12-01 01:07:30 -0800 | [diff] [blame] | 686 | } |
| 687 | |
| 688 | PROCESS_INFORMATION processInfo = {0}; |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 689 | STARTUPINFOEXW startupInfoEx = {0}; |
| 690 | lpAttributeList->InitStartupInfoExW(&startupInfoEx); |
Yun Peng | 9c97bf9 | 2017-12-01 01:07:30 -0800 | [diff] [blame] | 691 | |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 692 | std::vector<std::wstring> wesc_args_vector; |
| 693 | wesc_args_vector.reserve(args_vector.size()); |
| 694 | for (const string& a : args_vector) { |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 695 | std::wstring wa = blaze_util::CstringToWstring(a); |
Laszlo Csomor | dcc80fb | 2019-08-09 05:05:21 -0700 | [diff] [blame] | 696 | std::wstring wesc = bazel::windows::WindowsEscapeArg(wa); |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 697 | wesc_args_vector.push_back(wesc); |
| 698 | } |
| 699 | |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 700 | CmdLine cmdline; |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 701 | CreateCommandLine(&cmdline, exe, wesc_args_vector); |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 702 | |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 703 | BOOL ok; |
| 704 | { |
| 705 | WithEnvVars env_obj(env); |
| 706 | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 707 | ok = CreateProcessW( |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 708 | /* lpApplicationName */ nullptr, |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 709 | /* lpCommandLine */ cmdline.cmdline, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 710 | /* lpProcessAttributes */ nullptr, |
| 711 | /* lpThreadAttributes */ nullptr, |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 712 | /* bInheritHandles */ TRUE, |
| 713 | /* dwCreationFlags */ DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 714 | EXTENDED_STARTUPINFO_PRESENT, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 715 | /* lpEnvironment */ nullptr, |
| 716 | /* lpCurrentDirectory */ nullptr, |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 717 | /* lpStartupInfo */ &startupInfoEx.StartupInfo, |
| 718 | /* lpProcessInformation */ &processInfo); |
| 719 | } |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 720 | |
| 721 | if (!ok) { |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 722 | string err = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 723 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 724 | << "ExecuteDaemon(" << exe.AsPrintablePath() << "): CreateProcess(" |
| 725 | << cmdline.cmdline << ") failed: " << err; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 726 | } |
| 727 | |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 728 | WriteProcessStartupTime(server_dir, processInfo.hProcess); |
| 729 | |
Laszlo Csomor | d12fb7a | 2017-04-26 13:41:56 +0200 | [diff] [blame] | 730 | // Pass ownership of processInfo.hProcess |
| 731 | *server_startup = new ProcessHandleBlazeServerStartup(processInfo.hProcess); |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 732 | |
michajlo | 479c904 | 2020-03-27 11:43:39 -0700 | [diff] [blame] | 733 | string pid_string = blaze_util::ToString(processInfo.dwProcessId); |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 734 | blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile); |
Laszlo Csomor | 49970e0 | 2016-11-28 08:55:47 +0000 | [diff] [blame] | 735 | if (!blaze_util::WriteFile(pid_string, pid_file)) { |
Lukacs Berki | e33cf0f | 2016-04-28 11:04:59 +0000 | [diff] [blame] | 736 | // Not a lot we can do if this fails |
Laszlo Csomor | ce1f0c5 | 2019-08-26 07:57:05 -0700 | [diff] [blame] | 737 | fprintf(stderr, "Cannot write PID file %s\n", |
| 738 | pid_file.AsPrintablePath().c_str()); |
Lukacs Berki | e33cf0f | 2016-04-28 11:04:59 +0000 | [diff] [blame] | 739 | } |
| 740 | |
Laszlo Csomor | d12fb7a | 2017-04-26 13:41:56 +0200 | [diff] [blame] | 741 | // Don't close processInfo.hProcess here, it's now owned by the |
| 742 | // ProcessHandleBlazeServerStartup instance. |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 743 | CloseHandle(processInfo.hThread); |
felly | 70eabb6 | 2017-11-30 00:40:54 -0800 | [diff] [blame] | 744 | |
| 745 | return processInfo.dwProcessId; |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 746 | } |
| 747 | |
Philipp Wollermann | e4d977f | 2017-03-15 15:34:11 +0000 | [diff] [blame] | 748 | // Run the given program in the current working directory, using the given |
| 749 | // argument vector, wait for it to finish, then exit ourselves with the exitcode |
| 750 | // of that program. |
jmmv | 8cb0ec2 | 2019-09-23 12:45:28 -0700 | [diff] [blame] | 751 | ATTRIBUTE_NORETURN static void ExecuteProgram( |
| 752 | const blaze_util::Path& exe, |
| 753 | const std::vector<std::wstring>& wargs_vector) { |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 754 | CmdLine cmdline; |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 755 | CreateCommandLine(&cmdline, blaze_util::Path(), wargs_vector); |
Lukacs Berki | 7494c92 | 2016-04-27 11:17:51 +0000 | [diff] [blame] | 756 | |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 757 | bazel::windows::WaitableProcess proc; |
| 758 | std::wstring werror; |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 759 | // TODO(laszlocsomor): Fix proc.Create to accept paths with UNC prefix. |
| 760 | if (!proc.Create(blaze_util::RemoveUncPrefixMaybe(exe.AsNativePath().c_str()), |
| 761 | cmdline.cmdline, nullptr, L"", &werror) || |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 762 | proc.WaitFor(-1, nullptr, &werror) != |
| 763 | bazel::windows::WaitableProcess::kWaitSuccess) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 764 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 765 | << "ExecuteProgram(" << exe.AsPrintablePath() |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 766 | << ") failed: " << blaze_util::WstringToCstring(werror); |
Lukacs Berki | 68cb41a | 2016-07-06 11:43:37 +0000 | [diff] [blame] | 767 | } |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 768 | werror.clear(); |
| 769 | int x = proc.GetExitCode(&werror); |
| 770 | if (!werror.empty()) { |
| 771 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 772 | << "ExecuteProgram(" << exe.AsPrintablePath() |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 773 | << ") failed: " << blaze_util::WstringToCstring(werror); |
Laszlo Csomor | 1151e74 | 2019-04-25 04:55:44 -0700 | [diff] [blame] | 774 | } |
| 775 | exit(x); |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 776 | } |
| 777 | |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 778 | void ExecuteServerJvm(const blaze_util::Path& exe, |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 779 | const std::vector<string>& server_jvm_args) { |
| 780 | std::vector<std::wstring> wargs; |
| 781 | wargs.reserve(server_jvm_args.size()); |
| 782 | for (const string& a : server_jvm_args) { |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 783 | std::wstring wa = blaze_util::CstringToWstring(a); |
Laszlo Csomor | dcc80fb | 2019-08-09 05:05:21 -0700 | [diff] [blame] | 784 | std::wstring wesc = bazel::windows::WindowsEscapeArg(wa); |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 785 | wargs.push_back(wesc); |
| 786 | } |
| 787 | |
| 788 | ExecuteProgram(exe, wargs); |
| 789 | } |
| 790 | |
Laszlo Csomor | c099341 | 2019-08-28 07:34:39 -0700 | [diff] [blame] | 791 | void ExecuteRunRequest(const blaze_util::Path& exe, |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 792 | const std::vector<string>& run_request_args) { |
| 793 | std::vector<std::wstring> wargs; |
| 794 | wargs.reserve(run_request_args.size()); |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 795 | for (const string& a : run_request_args) { |
laszlocsomor | be0e36f | 2020-01-15 04:46:02 -0800 | [diff] [blame] | 796 | // The arguments are already escaped, don't escape further. |
| 797 | wargs.push_back(blaze_util::CstringToWstring(a)); |
Laszlo Csomor | da557f9 | 2019-08-09 01:51:01 -0700 | [diff] [blame] | 798 | } |
| 799 | |
| 800 | ExecuteProgram(exe, wargs); |
| 801 | } |
| 802 | |
Thiago Farina | c3aee5a | 2017-04-24 18:02:52 +0200 | [diff] [blame] | 803 | const char kListSeparator = ';'; |
Dmitry Lomov | 78c0cc7 | 2015-08-11 16:44:21 +0000 | [diff] [blame] | 804 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 805 | bool SymlinkDirectories(const string& posix_target, |
| 806 | const blaze_util::Path& name) { |
Laszlo Csomor | 38db316 | 2017-02-15 13:54:32 +0000 | [diff] [blame] | 807 | wstring target; |
Laszlo Csomor | 8c400c8 | 2018-04-24 01:44:17 -0700 | [diff] [blame] | 808 | string error; |
Laszlo Csomor | 8c400c8 | 2018-04-24 01:44:17 -0700 | [diff] [blame] | 809 | if (!blaze_util::AsAbsoluteWindowsPath(posix_target, &target, &error)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 810 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 811 | << "SymlinkDirectories(" << posix_target << ", " |
| 812 | << name.AsPrintablePath() << "): AsAbsoluteWindowsPath(" << posix_target |
| 813 | << ") failed: " << error; |
Lukacs Berki | 497d824 | 2016-04-28 07:21:26 +0000 | [diff] [blame] | 814 | return false; |
| 815 | } |
Laszlo Csomor | c2b70f1 | 2018-07-09 04:32:32 -0700 | [diff] [blame] | 816 | wstring werror; |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 817 | if (CreateJunction(name.AsNativePath(), target, &werror) != |
| 818 | CreateJunctionResult::kSuccess) { |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 819 | string error(blaze_util::WstringToCstring(werror)); |
ccalvarin | 7383976 | 2018-03-23 15:35:00 -0700 | [diff] [blame] | 820 | BAZEL_LOG(ERROR) << "SymlinkDirectories(" << posix_target << ", " |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 821 | << name.AsPrintablePath() |
| 822 | << "): CreateJunction: " << error; |
Lukacs Berki | 497d824 | 2016-04-28 07:21:26 +0000 | [diff] [blame] | 823 | return false; |
| 824 | } |
Laszlo Csomor | 38db316 | 2017-02-15 13:54:32 +0000 | [diff] [blame] | 825 | return true; |
Lukacs Berki | 497d824 | 2016-04-28 07:21:26 +0000 | [diff] [blame] | 826 | } |
| 827 | |
Laszlo Csomor | 032ddee | 2017-03-21 10:36:03 +0000 | [diff] [blame] | 828 | #ifndef STILL_ACTIVE |
| 829 | #define STILL_ACTIVE (259) // From MSDN about GetExitCodeProcess. |
| 830 | #endif |
| 831 | |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 832 | // On Windows (and Linux) we use a combination of PID and start time to identify |
| 833 | // the server process. That is supposed to be unique unless one can start more |
| 834 | // processes than there are PIDs available within a single jiffy. |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 835 | bool VerifyServerProcess(int pid, const blaze_util::Path& output_base) { |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 836 | AutoHandle process( |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 837 | ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)); |
| 838 | if (!process.IsValid()) { |
| 839 | // Cannot find the server process. Can happen if the PID file is stale. |
| 840 | return false; |
| 841 | } |
| 842 | |
Laszlo Csomor | 032ddee | 2017-03-21 10:36:03 +0000 | [diff] [blame] | 843 | DWORD exit_code = 0; |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 844 | uint64_t start_time = 0; |
Laszlo Csomor | 032ddee | 2017-03-21 10:36:03 +0000 | [diff] [blame] | 845 | if (!::GetExitCodeProcess(process, &exit_code) || exit_code != STILL_ACTIVE || |
| 846 | !GetProcessStartupTime(process, &start_time)) { |
| 847 | // Process doesn't exist or died meantime, all is good. No stale server is |
| 848 | // present. |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 849 | return false; |
| 850 | } |
| 851 | |
| 852 | string recorded_start_time; |
| 853 | bool file_present = blaze_util::ReadFile( |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 854 | output_base.GetRelative("server/server.starttime"), &recorded_start_time); |
Laszlo Csomor | d0512c4 | 2017-03-20 16:10:39 +0000 | [diff] [blame] | 855 | |
| 856 | // If start time file got deleted, but PID file didn't, assume that this is an |
| 857 | // old Bazel process that doesn't know how to write start time files yet. |
michajlo | 479c904 | 2020-03-27 11:43:39 -0700 | [diff] [blame] | 858 | return !file_present || |
| 859 | recorded_start_time == blaze_util::ToString(start_time); |
Lukacs Berki | ee44c38 | 2016-09-14 10:53:37 +0000 | [diff] [blame] | 860 | } |
| 861 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 862 | bool KillServerProcess(int pid, const blaze_util::Path& output_base) { |
Laszlo Csomor | f070234 | 2017-06-28 16:05:23 +0200 | [diff] [blame] | 863 | AutoHandle process(::OpenProcess( |
Laszlo Csomor | 032ddee | 2017-03-21 10:36:03 +0000 | [diff] [blame] | 864 | PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)); |
| 865 | DWORD exitcode = 0; |
| 866 | if (!process.IsValid() || !::GetExitCodeProcess(process, &exitcode) || |
| 867 | exitcode != STILL_ACTIVE) { |
| 868 | // Cannot find the server process (can happen if the PID file is stale) or |
| 869 | // it already exited. |
Lukacs Berki | 119dd4b | 2016-07-13 15:28:42 +0000 | [diff] [blame] | 870 | return false; |
Lukacs Berki | d825a3d | 2016-06-23 11:10:02 +0000 | [diff] [blame] | 871 | } |
| 872 | |
Laszlo Csomor | c8cd6bd | 2017-03-14 11:10:04 +0000 | [diff] [blame] | 873 | BOOL result = TerminateProcess(process, /*uExitCode*/ 0); |
mschaller | fd37b51 | 2017-07-11 18:21:36 +0200 | [diff] [blame] | 874 | if (!result || !AwaitServerProcessTermination(pid, output_base, |
| 875 | kPostKillGracePeriodSeconds)) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 876 | string err = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 877 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 878 | << "Cannot terminate server process with PID " << pid |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 879 | << ", output_base=(" << output_base.AsPrintablePath() << "): " << err; |
Lukacs Berki | d825a3d | 2016-06-23 11:10:02 +0000 | [diff] [blame] | 880 | } |
Lukacs Berki | 119dd4b | 2016-07-13 15:28:42 +0000 | [diff] [blame] | 881 | return result; |
Lukacs Berki | 1977d92 | 2016-05-02 09:31:37 +0000 | [diff] [blame] | 882 | } |
| 883 | |
mschaller | fd37b51 | 2017-07-11 18:21:36 +0200 | [diff] [blame] | 884 | void TrySleep(unsigned int milliseconds) { |
| 885 | Sleep(milliseconds); |
| 886 | } |
| 887 | |
Dave MacLachlan | 6b747ee | 2016-07-20 10:00:44 +0000 | [diff] [blame] | 888 | // Not supported. |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 889 | void ExcludePathFromBackup(const blaze_util::Path& path) {} |
Dave MacLachlan | 6b747ee | 2016-07-20 10:00:44 +0000 | [diff] [blame] | 890 | |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 891 | string GetHashedBaseDir(const string& root, const string& hashable) { |
| 892 | // Builds a shorter output base dir name for Windows. |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 893 | |
Laszlo Csomor | 9b97876 | 2018-04-19 06:50:23 -0700 | [diff] [blame] | 894 | // We create a path name representing the 128 bits of MD5 digest. To avoid |
| 895 | // platform incompatibilities we restrict the alphabet to ASCII letters and |
| 896 | // numbers. Windows paths are case-insensitive, so use only lower-case |
| 897 | // letters. These constraints yield a 5-bit alphabet. |
| 898 | // Since we only need 6 digits, ignore 0 and 1 because they look like |
| 899 | // upper-case "O" and lower-case "l". |
| 900 | static const char* alphabet = "abcdefghijklmnopqrstuvwxyz234567"; |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 901 | |
Laszlo Csomor | 9b97876 | 2018-04-19 06:50:23 -0700 | [diff] [blame] | 902 | // 128 bits of data in base-32 require 128/5 = 25 digits with 3 bits lost. |
| 903 | // Maximum path length on Windows is only 259 characters, so we'll only use |
| 904 | // a few characters characters (base-32 digits) to represent the digest. |
| 905 | // Using only 8 characters we represent 40 bits of the original 128. |
| 906 | // Since the mapping is lossy and collisions are unlikely in practice, we'll |
| 907 | // keep the mapping simple and just use the lower 5 bits of the first 8 bytes. |
| 908 | static const unsigned char kLower5BitsMask = 0x1F; |
| 909 | static const int filename_length = 8; |
| 910 | unsigned char md5[blaze_util::Md5Digest::kDigestLength]; |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 911 | char coded_name[filename_length + 1]; |
| 912 | blaze_util::Md5Digest digest; |
| 913 | digest.Update(hashable.data(), hashable.size()); |
Laszlo Csomor | 9b97876 | 2018-04-19 06:50:23 -0700 | [diff] [blame] | 914 | digest.Finish(md5); |
| 915 | for (int i = 0; i < filename_length; ++i) { |
| 916 | coded_name[i] = alphabet[md5[i] & kLower5BitsMask]; |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 917 | } |
| 918 | coded_name[filename_length] = '\0'; |
Laszlo Csomor | 760f786 | 2016-12-19 15:46:47 +0000 | [diff] [blame] | 919 | return blaze_util::JoinPath(root, string(coded_name)); |
Laszlo Csomor | 6bf9576 | 2016-11-16 13:29:22 +0000 | [diff] [blame] | 920 | } |
| 921 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 922 | void CreateSecureOutputRoot(const blaze_util::Path& path) { |
Laszlo Csomor | 5eb3ef9 | 2017-02-07 16:52:38 +0000 | [diff] [blame] | 923 | // TODO(bazel-team): implement this properly, by mimicing whatever the POSIX |
| 924 | // implementation does. |
Laszlo Csomor | 5eb3ef9 | 2017-02-07 16:52:38 +0000 | [diff] [blame] | 925 | if (!blaze_util::MakeDirectories(path, 0755)) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 926 | string err = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 927 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 928 | << "MakeDirectories(" << path.AsPrintablePath() << ") failed: " << err; |
Laszlo Csomor | 8a48f61 | 2016-11-17 10:18:34 +0000 | [diff] [blame] | 929 | } |
| 930 | |
Laszlo Csomor | 5eb3ef9 | 2017-02-07 16:52:38 +0000 | [diff] [blame] | 931 | if (!blaze_util::IsDirectory(path)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 932 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 933 | << "'" << path.AsPrintablePath() << "' is not a directory"; |
Laszlo Csomor | 8a48f61 | 2016-11-17 10:18:34 +0000 | [diff] [blame] | 934 | } |
| 935 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 936 | ExcludePathFromBackup(path); |
Laszlo Csomor | 8a48f61 | 2016-11-17 10:18:34 +0000 | [diff] [blame] | 937 | } |
| 938 | |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 939 | string GetEnv(const string& name) { |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 940 | DWORD size = ::GetEnvironmentVariableA(name.c_str(), nullptr, 0); |
Laszlo Csomor | f2b5f27 | 2017-01-18 09:51:57 +0000 | [diff] [blame] | 941 | if (size == 0) { |
Laszlo Csomor | f2b5f27 | 2017-01-18 09:51:57 +0000 | [diff] [blame] | 942 | return string(); // unset or empty envvar |
Laszlo Csomor | f2b5f27 | 2017-01-18 09:51:57 +0000 | [diff] [blame] | 943 | } |
| 944 | |
| 945 | unique_ptr<char[]> value(new char[size]); |
| 946 | ::GetEnvironmentVariableA(name.c_str(), value.get(), size); |
| 947 | return string(value.get()); |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 948 | } |
| 949 | |
Laszlo Csomor | 0d10749 | 2019-03-13 09:04:27 -0700 | [diff] [blame] | 950 | string GetPathEnv(const string& name) { |
| 951 | string value = GetEnv(name); |
| 952 | if (value.empty()) { |
| 953 | return value; |
| 954 | } |
| 955 | if (bazel::windows::HasUncPrefix(value.c_str())) { |
| 956 | value = value.substr(4); |
| 957 | } |
| 958 | string wpath, error; |
| 959 | if (!blaze_util::AsWindowsPath(value, &wpath, &error)) { |
| 960 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 961 | << "Invalid path in envvar \"" << name << "\": " << error; |
| 962 | } |
| 963 | // Callers of GetPathEnv expect a path with forward slashes. |
| 964 | std::replace(wpath.begin(), wpath.end(), '\\', '/'); |
| 965 | return wpath; |
| 966 | } |
| 967 | |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 968 | bool ExistsEnv(const string& name) { |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 969 | return ::GetEnvironmentVariableA(name.c_str(), nullptr, 0) != 0; |
lberki | d3d37f0 | 2018-04-18 11:44:14 -0700 | [diff] [blame] | 970 | } |
| 971 | |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 972 | void SetEnv(const string& name, const string& value) { |
Dmitry Lomov | 5a0d6ff | 2017-08-22 14:51:49 +0200 | [diff] [blame] | 973 | // _putenv_s both calls ::SetEnvionmentVariableA and updates environ(5). |
| 974 | _putenv_s(name.c_str(), value.c_str()); |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 975 | } |
| 976 | |
Laszlo Csomor | f2b5f27 | 2017-01-18 09:51:57 +0000 | [diff] [blame] | 977 | void UnsetEnv(const string& name) { SetEnv(name, ""); } |
Laszlo Csomor | cefa9a2 | 2016-11-22 10:50:07 +0000 | [diff] [blame] | 978 | |
Laszlo Csomor | c4fb218 | 2017-07-27 16:37:08 +0200 | [diff] [blame] | 979 | bool WarnIfStartedFromDesktop() { |
| 980 | // GetConsoleProcessList returns: |
| 981 | // 0, if no console attached (Bazel runs as a subprocess) |
| 982 | // 1, if Bazel was started by clicking on its icon |
| 983 | // 2, if Bazel was started from the command line (even if its output is |
| 984 | // redirected) |
| 985 | DWORD dummy[2] = {0}; |
| 986 | if (GetConsoleProcessList(dummy, 2) != 1) { |
| 987 | return false; |
| 988 | } |
| 989 | printf( |
| 990 | "Bazel is a command line tool.\n\n" |
| 991 | "Try opening a console, such as the Windows Command Prompt (cmd.exe) " |
| 992 | "or PowerShell, and running \"bazel help\".\n\n" |
| 993 | "Press Enter to close this window..."); |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 994 | ReadFile(GetStdHandle(STD_INPUT_HANDLE), dummy, 1, dummy, nullptr); |
Laszlo Csomor | c4fb218 | 2017-07-27 16:37:08 +0200 | [diff] [blame] | 995 | return true; |
| 996 | } |
| 997 | |
Laszlo Csomor | 41ac1e0 | 2017-03-20 15:25:44 +0000 | [diff] [blame] | 998 | #ifndef ENABLE_PROCESSED_OUTPUT |
| 999 | // From MSDN about BOOL SetConsoleMode(HANDLE, DWORD). |
| 1000 | #define ENABLE_PROCESSED_OUTPUT 0x0001 |
| 1001 | #endif // not ENABLE_PROCESSED_OUTPUT |
| 1002 | |
| 1003 | #ifndef ENABLE_WRAP_AT_EOL_OUTPUT |
| 1004 | // From MSDN about BOOL SetConsoleMode(HANDLE, DWORD). |
| 1005 | #define ENABLE_WRAP_AT_EOL_OUTPUT 0x0002 |
| 1006 | #endif // not ENABLE_WRAP_AT_EOL_OUTPUT |
| 1007 | |
| 1008 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING |
| 1009 | // From MSDN about BOOL SetConsoleMode(HANDLE, DWORD). |
| 1010 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 |
| 1011 | #endif // not ENABLE_VIRTUAL_TERMINAL_PROCESSING |
| 1012 | |
Laszlo Csomor | 74ffaf7 | 2016-11-24 12:17:20 +0000 | [diff] [blame] | 1013 | void SetupStdStreams() { |
Laszlo Csomor | 26f858c | 2017-02-08 17:50:10 +0000 | [diff] [blame] | 1014 | static const DWORD stdhandles[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, |
| 1015 | STD_ERROR_HANDLE}; |
Laszlo Csomor | 3d97f49 | 2017-03-16 12:58:23 +0000 | [diff] [blame] | 1016 | for (int i = 0; i <= 2; ++i) { |
Laszlo Csomor | 26f858c | 2017-02-08 17:50:10 +0000 | [diff] [blame] | 1017 | HANDLE handle = ::GetStdHandle(stdhandles[i]); |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 1018 | if (handle == INVALID_HANDLE_VALUE || handle == nullptr) { |
Laszlo Csomor | 26f858c | 2017-02-08 17:50:10 +0000 | [diff] [blame] | 1019 | // Ensure we have open fds to each std* stream. Otherwise we can end up |
| 1020 | // with bizarre things like stdout going to the lock file, etc. |
| 1021 | _open("NUL", (i == 0) ? _O_RDONLY : _O_WRONLY); |
| 1022 | } |
Laszlo Csomor | 41ac1e0 | 2017-03-20 15:25:44 +0000 | [diff] [blame] | 1023 | DWORD mode = 0; |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 1024 | if (i > 0 && handle != INVALID_HANDLE_VALUE && handle != nullptr && |
Laszlo Csomor | 41ac1e0 | 2017-03-20 15:25:44 +0000 | [diff] [blame] | 1025 | ::GetConsoleMode(handle, &mode)) { |
| 1026 | DWORD newmode = mode | ENABLE_PROCESSED_OUTPUT | |
| 1027 | ENABLE_WRAP_AT_EOL_OUTPUT | |
| 1028 | ENABLE_VIRTUAL_TERMINAL_PROCESSING; |
| 1029 | if (mode != newmode) { |
| 1030 | // We don't care about the success of this. Worst that can happen if |
| 1031 | // this method fails is that the console won't understand control |
| 1032 | // characters like color change or carriage return. |
| 1033 | ::SetConsoleMode(handle, newmode); |
| 1034 | } |
| 1035 | } |
Laszlo Csomor | 26f858c | 2017-02-08 17:50:10 +0000 | [diff] [blame] | 1036 | } |
Laszlo Csomor | 74ffaf7 | 2016-11-24 12:17:20 +0000 | [diff] [blame] | 1037 | } |
| 1038 | |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 1039 | LARGE_INTEGER WindowsClock::GetFrequency() { |
| 1040 | LARGE_INTEGER result; |
| 1041 | if (!QueryPerformanceFrequency(&result)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1042 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 1043 | << "WindowsClock::GetFrequency: QueryPerformanceFrequency failed: " |
| 1044 | << GetLastErrorString(); |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 1045 | } |
| 1046 | |
| 1047 | // On ancient Windows versions (pre-XP) and specific hardware the result may |
| 1048 | // be 0. Since this is pre-XP, we don't handle that, just error out. |
| 1049 | if (result.QuadPart <= 0) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1050 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 1051 | << "WindowsClock::GetFrequency: QueryPerformanceFrequency returned " |
| 1052 | "invalid result (" |
| 1053 | << result.QuadPart << "): " << GetLastErrorString(); |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 1054 | } |
| 1055 | |
| 1056 | return result; |
| 1057 | } |
| 1058 | |
| 1059 | LARGE_INTEGER WindowsClock::GetMillisecondsAsLargeInt( |
| 1060 | const LARGE_INTEGER& freq) { |
| 1061 | LARGE_INTEGER counter; |
| 1062 | if (!QueryPerformanceCounter(&counter)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1063 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 1064 | << "WindowsClock::GetMillisecondsAsLargeInt: QueryPerformanceCounter " |
| 1065 | "failed: " |
| 1066 | << GetLastErrorString(); |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 1067 | } |
| 1068 | |
| 1069 | LARGE_INTEGER result; |
| 1070 | result.QuadPart = |
| 1071 | // seconds |
| 1072 | (counter.QuadPart / freq.QuadPart) * 1000LL + |
| 1073 | // milliseconds |
| 1074 | (((counter.QuadPart % freq.QuadPart) * 1000LL) / freq.QuadPart); |
| 1075 | |
| 1076 | return result; |
| 1077 | } |
| 1078 | |
| 1079 | const WindowsClock WindowsClock::INSTANCE; |
| 1080 | |
| 1081 | WindowsClock::WindowsClock() |
twerth | f59fad7 | 2018-10-02 04:50:28 -0700 | [diff] [blame] | 1082 | : kFrequency(GetFrequency()) {} |
Laszlo Csomor | a85f52d | 2016-11-08 13:43:23 +0000 | [diff] [blame] | 1083 | |
| 1084 | uint64_t WindowsClock::GetMilliseconds() const { |
| 1085 | return GetMillisecondsAsLargeInt(kFrequency).QuadPart; |
| 1086 | } |
| 1087 | |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1088 | uint64_t AcquireLock(const blaze_util::Path& output_base, bool batch_mode, |
| 1089 | bool block, BlazeLock* blaze_lock) { |
| 1090 | blaze_util::Path lockfile = output_base.GetRelative("lock"); |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1091 | blaze_lock->handle = INVALID_HANDLE_VALUE; |
| 1092 | bool first_lock_attempt = true; |
| 1093 | uint64_t st = GetMillisecondsMonotonic(); |
| 1094 | while (true) { |
| 1095 | blaze_lock->handle = ::CreateFileW( |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1096 | /* lpFileName */ lockfile.AsNativePath().c_str(), |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1097 | /* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE, |
| 1098 | /* dwShareMode */ FILE_SHARE_READ, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 1099 | /* lpSecurityAttributes */ nullptr, |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1100 | /* dwCreationDisposition */ CREATE_ALWAYS, |
| 1101 | /* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL, |
Vertexwahn | 26c7e10 | 2021-03-10 07:25:59 -0800 | [diff] [blame] | 1102 | /* hTemplateFile */ nullptr); |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1103 | if (blaze_lock->handle != INVALID_HANDLE_VALUE) { |
| 1104 | // We could open the file, so noone else holds a lock on it. |
| 1105 | break; |
| 1106 | } |
| 1107 | if (GetLastError() == ERROR_SHARING_VIOLATION) { |
| 1108 | // Someone else has the lock. |
ccalvarin | 6c8166e | 2018-07-23 09:17:26 -0700 | [diff] [blame] | 1109 | BAZEL_LOG(USER) << "Another command holds the client lock"; |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1110 | if (!block) { |
ccalvarin | 6c8166e | 2018-07-23 09:17:26 -0700 | [diff] [blame] | 1111 | BAZEL_DIE(blaze_exit_code::LOCK_HELD_NOBLOCK_FOR_LOCK) |
| 1112 | << "Exiting because the lock is held and --noblock_for_lock was " |
| 1113 | "given."; |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1114 | } |
| 1115 | if (first_lock_attempt) { |
| 1116 | first_lock_attempt = false; |
ccalvarin | 6c8166e | 2018-07-23 09:17:26 -0700 | [diff] [blame] | 1117 | BAZEL_LOG(USER) << "Waiting for it to complete..."; |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1118 | fflush(stderr); |
| 1119 | } |
| 1120 | Sleep(/* dwMilliseconds */ 200); |
| 1121 | } else { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1122 | string err = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1123 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1124 | << "AcquireLock(" << lockfile.AsPrintablePath() |
| 1125 | << "): CreateFile failed: " << err; |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1126 | } |
| 1127 | } |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1128 | uint64_t wait_time = GetMillisecondsMonotonic() - st; |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1129 | |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1130 | // We have the lock. |
| 1131 | OVERLAPPED overlapped = {0}; |
| 1132 | if (!LockFileEx( |
| 1133 | /* hFile */ blaze_lock->handle, |
| 1134 | /* dwFlags */ LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, |
| 1135 | /* dwReserved */ 0, |
| 1136 | /* nNumberOfBytesToLockLow */ 1, |
| 1137 | /* nNumberOfBytesToLockHigh */ 0, |
| 1138 | /* lpOverlapped */ &overlapped)) { |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1139 | string err = GetLastErrorString(); |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1140 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
laszlocsomor | 6f66539 | 2019-08-27 08:15:51 -0700 | [diff] [blame] | 1141 | << "AcquireLock(" << lockfile.AsPrintablePath() |
| 1142 | << "): LockFileEx failed: " << err; |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1143 | } |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1144 | // On other platforms we write some info about this process into the lock file |
| 1145 | // such as the server PID. On Windows we don't do that because the file is |
| 1146 | // locked exclusively, meaning other processes may not open the file even for |
| 1147 | // reading. |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1148 | |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1149 | return wait_time; |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1150 | } |
| 1151 | |
| 1152 | void ReleaseLock(BlazeLock* blaze_lock) { |
laszlocsomor | ade79a7 | 2017-04-12 10:08:16 +0000 | [diff] [blame] | 1153 | OVERLAPPED overlapped = {0}; |
| 1154 | UnlockFileEx(blaze_lock->handle, 0, 1, 0, &overlapped); |
| 1155 | CloseHandle(blaze_lock->handle); |
Laszlo Csomor | 7e5fb40 | 2016-11-25 15:11:08 +0000 | [diff] [blame] | 1156 | } |
| 1157 | |
Laszlo Csomor | 99b0154 | 2017-03-02 11:43:21 +0000 | [diff] [blame] | 1158 | #ifdef GetUserName |
| 1159 | // By including <windows.h>, we have GetUserName defined either as |
| 1160 | // GetUserNameA or GetUserNameW. |
| 1161 | #undef GetUserName |
| 1162 | #endif |
| 1163 | |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1164 | string GetUserName() { |
Laszlo Csomor | b698e20 | 2019-09-24 08:26:26 -0700 | [diff] [blame] | 1165 | // Check USER, for sake of consistency with Linux / macOS. This is only set |
| 1166 | // under MSYS2, or potentially in tests. |
| 1167 | string user = GetEnv("USER"); |
| 1168 | if (!user.empty()) { |
| 1169 | return user; |
| 1170 | } |
| 1171 | |
| 1172 | // Check USERNAME before calling GetUserNameW. Doing so allows the user to |
| 1173 | // customize (or override) the user name. |
| 1174 | // See https://github.com/bazelbuild/bazel/issues/7819#issuecomment-533050947 |
| 1175 | user = GetEnv("USERNAME"); |
| 1176 | if (!user.empty()) { |
| 1177 | return user; |
| 1178 | } |
| 1179 | |
Laszlo Csomor | 41ee591 | 2016-12-15 12:26:06 +0000 | [diff] [blame] | 1180 | WCHAR buffer[UNLEN + 1]; |
| 1181 | DWORD len = UNLEN + 1; |
Laszlo Csomor | 6d47707 | 2017-07-20 10:09:44 +0200 | [diff] [blame] | 1182 | if (!::GetUserNameW(buffer, &len)) { |
ccalvarin | 8448f57 | 2018-04-06 12:42:09 -0700 | [diff] [blame] | 1183 | BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) |
| 1184 | << "GetUserNameW failed: " << GetLastErrorString(); |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1185 | } |
Laszlo Csomor | b864ced | 2019-09-23 07:39:16 -0700 | [diff] [blame] | 1186 | return blaze_util::WstringToCstring(buffer); |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1187 | } |
| 1188 | |
| 1189 | bool IsEmacsTerminal() { |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1190 | string emacs = GetEnv("EMACS"); |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1191 | // GNU Emacs <25.1 (and ~all non-GNU emacsen) set EMACS=t, but >=25.1 doesn't |
| 1192 | // do that and instead sets INSIDE_EMACS=<stuff> (where <stuff> can look like |
| 1193 | // e.g. "25.1.1,comint"). So we check both variables for maximum |
| 1194 | // compatibility. |
Laszlo Csomor | 0d10749 | 2019-03-13 09:04:27 -0700 | [diff] [blame] | 1195 | return emacs == "t" || ExistsEnv("INSIDE_EMACS"); |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1196 | } |
| 1197 | |
michajlo | ac5a971 | 2019-07-17 14:04:57 -0700 | [diff] [blame] | 1198 | bool IsStandardTerminal() { |
| 1199 | for (DWORD i : {STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}) { |
| 1200 | DWORD mode = 0; |
| 1201 | HANDLE handle = ::GetStdHandle(STD_ERROR_HANDLE); |
| 1202 | // handle may be invalid when std{out,err} is redirected |
| 1203 | if (handle == INVALID_HANDLE_VALUE || !::GetConsoleMode(handle, &mode) || |
| 1204 | !(mode & ENABLE_PROCESSED_OUTPUT) || |
| 1205 | !(mode & ENABLE_WRAP_AT_EOL_OUTPUT) || |
| 1206 | !(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { |
| 1207 | return false; |
| 1208 | } |
Laszlo Csomor | 41ac1e0 | 2017-03-20 15:25:44 +0000 | [diff] [blame] | 1209 | } |
| 1210 | return true; |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1211 | } |
| 1212 | |
michajlo | ac5a971 | 2019-07-17 14:04:57 -0700 | [diff] [blame] | 1213 | int GetTerminalColumns() { |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1214 | string columns_env = GetEnv("COLUMNS"); |
| 1215 | if (!columns_env.empty()) { |
| 1216 | char* endptr; |
| 1217 | int columns = blaze_util::strto32(columns_env.c_str(), &endptr, 10); |
| 1218 | if (*endptr == '\0') { // $COLUMNS is a valid number |
| 1219 | return columns; |
| 1220 | } |
| 1221 | } |
László Csomor | 7287921 | 2017-02-02 15:55:18 +0000 | [diff] [blame] | 1222 | |
michajlo | ac5a971 | 2019-07-17 14:04:57 -0700 | [diff] [blame] | 1223 | HANDLE stdout_handle = ::GetStdHandle(STD_OUTPUT_HANDLE); |
Laszlo Csomor | 3d97f49 | 2017-03-16 12:58:23 +0000 | [diff] [blame] | 1224 | if (stdout_handle != INVALID_HANDLE_VALUE) { |
| 1225 | // stdout_handle may be invalid when stdout is redirected. |
| 1226 | CONSOLE_SCREEN_BUFFER_INFO screen_info; |
| 1227 | if (GetConsoleScreenBufferInfo(stdout_handle, &screen_info)) { |
| 1228 | int width = 1 + screen_info.srWindow.Right - screen_info.srWindow.Left; |
| 1229 | if (width > 1) { |
| 1230 | return width; |
| 1231 | } |
László Csomor | 7287921 | 2017-02-02 15:55:18 +0000 | [diff] [blame] | 1232 | } |
| 1233 | } |
László Csomor | 7287921 | 2017-02-02 15:55:18 +0000 | [diff] [blame] | 1234 | |
| 1235 | return 80; // default if not a terminal. |
Laszlo Csomor | d0a1269 | 2016-11-28 13:35:23 +0000 | [diff] [blame] | 1236 | } |
| 1237 | |
jmmv | a96369c | 2017-07-10 18:14:36 +0200 | [diff] [blame] | 1238 | bool UnlimitResources() { |
| 1239 | return true; // Nothing to do so assume success. |
| 1240 | } |
| 1241 | |
jmmv | 8ddd782 | 2018-08-30 13:20:25 -0700 | [diff] [blame] | 1242 | bool UnlimitCoredumps() { |
| 1243 | return true; // Nothing to do so assume success. |
| 1244 | } |
| 1245 | |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1246 | static const int MAX_KEY_LENGTH = 255; |
| 1247 | // We do not care about registry values longer than MAX_PATH |
| 1248 | static const int REG_VALUE_BUFFER_SIZE = MAX_PATH; |
| 1249 | |
| 1250 | // Implements heuristics to discover msys2 installation. |
| 1251 | static string GetMsysBash() { |
| 1252 | HKEY h_uninstall; |
| 1253 | |
| 1254 | // MSYS2 installer writes its registry into HKCU, although documentation |
| 1255 | // (https://msdn.microsoft.com/en-us/library/ms954376.aspx) |
| 1256 | // clearly states that it should go to HKLM. |
Loo Rong Jie | 4bf477e | 2018-07-02 00:11:54 -0700 | [diff] [blame] | 1257 | static constexpr const char key[] = |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1258 | "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; |
| 1259 | if (RegOpenKeyExA(HKEY_CURRENT_USER, // _In_ HKEY hKey, |
| 1260 | key, // _In_opt_ LPCTSTR lpSubKey, |
| 1261 | 0, // _In_ DWORD ulOptions, |
| 1262 | KEY_ENUMERATE_SUB_KEYS | |
| 1263 | KEY_QUERY_VALUE, // _In_ REGSAM samDesired, |
| 1264 | &h_uninstall // _Out_ PHKEY phkResult |
| 1265 | )) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1266 | BAZEL_LOG(INFO) << "Cannot open HKCU\\" << key; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1267 | return string(); |
| 1268 | } |
| 1269 | AutoHandle auto_uninstall(h_uninstall); |
| 1270 | |
| 1271 | // Since MSYS2 decided to generate a new product key for each installation, |
| 1272 | // we enumerate all keys under |
| 1273 | // HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall and find the first |
| 1274 | // with MSYS2 64bit display name. |
Loo Rong Jie | 4bf477e | 2018-07-02 00:11:54 -0700 | [diff] [blame] | 1275 | static constexpr const char msys_display_name[] = "MSYS2 64bit"; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1276 | DWORD n_subkeys; |
| 1277 | |
| 1278 | if (RegQueryInfoKey(h_uninstall, // _In_ HKEY hKey, |
| 1279 | 0, // _Out_opt_ LPTSTR lpClass, |
| 1280 | 0, // _Inout_opt_ LPDWORD lpcClass, |
| 1281 | 0, // _Reserved_ LPDWORD lpReserved, |
| 1282 | &n_subkeys, // _Out_opt_ LPDWORD lpcSubKeys, |
| 1283 | 0, // _Out_opt_ LPDWORD lpcMaxSubKeyLen, |
| 1284 | 0, // _Out_opt_ LPDWORD lpcMaxClassLen, |
| 1285 | 0, // _Out_opt_ LPDWORD lpcValues, |
| 1286 | 0, // _Out_opt_ LPDWORD lpcMaxValueNameLen, |
| 1287 | 0, // _Out_opt_ LPDWORD lpcMaxValueLen, |
| 1288 | 0, // _Out_opt_ LPDWORD lpcbSecurityDescriptor, |
| 1289 | 0 // _Out_opt_ PFILETIME lpftLastWriteTime |
| 1290 | )) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1291 | BAZEL_LOG(INFO) << "Cannot query HKCU\\" << key; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1292 | return string(); |
| 1293 | } |
| 1294 | |
| 1295 | for (DWORD key_index = 0; key_index < n_subkeys; key_index++) { |
| 1296 | char subkey_name[MAX_KEY_LENGTH]; |
| 1297 | if (RegEnumKeyA(h_uninstall, // _In_ HKEY hKey, |
| 1298 | key_index, // _In_ DWORD dwIndex, |
| 1299 | subkey_name, // _Out_ LPTSTR lpName, |
| 1300 | sizeof(subkey_name) // _In_ DWORD cchName |
| 1301 | )) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1302 | BAZEL_LOG(INFO) << "Cannot get " << key_index << " subkey of HKCU\\" |
| 1303 | << key; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1304 | continue; // try next subkey |
| 1305 | } |
| 1306 | |
| 1307 | HKEY h_subkey; |
| 1308 | if (RegOpenKeyEx(h_uninstall, // _In_ HKEY hKey, |
| 1309 | subkey_name, // _In_opt_ LPCTSTR lpSubKey, |
| 1310 | 0, // _In_ DWORD ulOptions, |
| 1311 | KEY_QUERY_VALUE, // _In_ REGSAM samDesired, |
| 1312 | &h_subkey // _Out_ PHKEY phkResult |
| 1313 | )) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1314 | BAZEL_LOG(ERROR) << "Failed to open subkey HKCU\\" << key << "\\" |
| 1315 | << subkey_name; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1316 | continue; // try next subkey |
| 1317 | } |
| 1318 | AutoHandle auto_subkey(h_subkey); |
| 1319 | |
| 1320 | BYTE value[REG_VALUE_BUFFER_SIZE]; |
| 1321 | DWORD value_length = sizeof(value); |
| 1322 | DWORD value_type; |
| 1323 | |
| 1324 | if (RegQueryValueEx(h_subkey, // _In_ HKEY hKey, |
| 1325 | "DisplayName", // _In_opt_ LPCTSTR lpValueName, |
| 1326 | 0, // _Reserved_ LPDWORD lpReserved, |
| 1327 | &value_type, // _Out_opt_ LPDWORD lpType, |
| 1328 | value, // _Out_opt_ LPBYTE lpData, |
| 1329 | &value_length // _Inout_opt_ LPDWORD lpcbData |
| 1330 | )) { |
Laszlo Csomor | 1e9fc8e | 2019-01-03 00:28:25 -0800 | [diff] [blame] | 1331 | // This registry key has no DisplayName subkey, so it cannot be MSYS2, or |
| 1332 | // it cannot be a version of MSYS2 that we are looking for. |
| 1333 | continue; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1334 | } |
| 1335 | |
| 1336 | if (value_type == REG_SZ && |
| 1337 | 0 == memcmp(msys_display_name, value, sizeof(msys_display_name))) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1338 | BAZEL_LOG(INFO) << "Getting install location of HKCU\\" << key << "\\" |
| 1339 | << subkey_name; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1340 | BYTE path[REG_VALUE_BUFFER_SIZE]; |
| 1341 | DWORD path_length = sizeof(path); |
| 1342 | DWORD path_type; |
| 1343 | if (RegQueryValueEx( |
| 1344 | h_subkey, // _In_ HKEY hKey, |
| 1345 | "InstallLocation", // _In_opt_ LPCTSTR lpValueName, |
| 1346 | 0, // _Reserved_ LPDWORD lpReserved, |
| 1347 | &path_type, // _Out_opt_ LPDWORD lpType, |
| 1348 | path, // _Out_opt_ LPBYTE lpData, |
| 1349 | &path_length // _Inout_opt_ LPDWORD lpcbData |
| 1350 | )) { |
Laszlo Csomor | 1e9fc8e | 2019-01-03 00:28:25 -0800 | [diff] [blame] | 1351 | // This version of MSYS2 does not seem to create a "InstallLocation" |
| 1352 | // subkey. Let's ignore this registry key to avoid picking up an MSYS2 |
| 1353 | // version that may be different from what Bazel expects. |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1354 | continue; // try next subkey |
| 1355 | } |
| 1356 | |
| 1357 | if (path_length == 0 || path_type != REG_SZ) { |
Laszlo Csomor | 1e9fc8e | 2019-01-03 00:28:25 -0800 | [diff] [blame] | 1358 | // This version of MSYS2 seem to have recorded an empty installation |
| 1359 | // location, or the registry key was modified. Either way, let's ignore |
| 1360 | // this registry key and keep looking at the next subkey. |
| 1361 | continue; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1362 | } |
| 1363 | |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1364 | BAZEL_LOG(INFO) << "Install location of HKCU\\" << key << "\\" |
| 1365 | << subkey_name << " is " << path; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1366 | string path_as_string(path, path + path_length - 1); |
| 1367 | string bash_exe = path_as_string + "\\usr\\bin\\bash.exe"; |
| 1368 | if (!blaze_util::PathExists(bash_exe)) { |
Laszlo Csomor | 1e9fc8e | 2019-01-03 00:28:25 -0800 | [diff] [blame] | 1369 | // The supposed bash.exe does not exist. Maybe MSYS2 was deleted but not |
| 1370 | // uninstalled? We can't tell, but for all we care, this MSYS2 path is |
| 1371 | // not what we need, so ignore this registry key. |
| 1372 | continue; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1373 | } |
| 1374 | |
Laszlo Csomor | 1e9fc8e | 2019-01-03 00:28:25 -0800 | [diff] [blame] | 1375 | BAZEL_LOG(INFO) << "Detected MSYS2 Bash at " << bash_exe.c_str(); |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1376 | return bash_exe; |
| 1377 | } |
| 1378 | } |
| 1379 | return string(); |
| 1380 | } |
| 1381 | |
Yun Peng | 213a52a | 2017-09-14 11:11:48 +0200 | [diff] [blame] | 1382 | static string GetBinaryFromPath(const string& binary_name) { |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1383 | char found[MAX_PATH]; |
Laszlo Csomor | 0d10749 | 2019-03-13 09:04:27 -0700 | [diff] [blame] | 1384 | string path_list = blaze::GetPathEnv("PATH"); |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1385 | |
| 1386 | // We do not fully replicate all the quirks of search in PATH. |
| 1387 | // There is no system function to do so, and that way lies madness. |
| 1388 | size_t start = 0; |
| 1389 | do { |
| 1390 | // This ignores possibly quoted semicolons in PATH etc. |
| 1391 | size_t end = path_list.find_first_of(";", start); |
| 1392 | string path = path_list.substr( |
| 1393 | start, end != string::npos ? end - start : string::npos); |
| 1394 | // Handle one typical way of quoting (where.exe does not handle this, but |
| 1395 | // CreateProcess does). |
| 1396 | if (path.size() > 1 && path[0] == '"' && path[path.size() - 1] == '"') { |
| 1397 | path = path.substr(1, path.size() - 2); |
| 1398 | } |
Yun Peng | 213a52a | 2017-09-14 11:11:48 +0200 | [diff] [blame] | 1399 | if (SearchPathA(path.c_str(), // _In_opt_ LPCTSTR lpPath, |
| 1400 | binary_name.c_str(), // _In_ LPCTSTR lpFileName, |
| 1401 | 0, // LPCTSTR lpExtension, |
| 1402 | sizeof(found), // DWORD nBufferLength, |
| 1403 | found, // _Out_ LPTSTR lpBuffer, |
| 1404 | 0 // _Out_opt_ LPTSTR *lpFilePart |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1405 | )) { |
ccalvarin | 9eea0f9 | 2018-03-21 15:32:30 -0700 | [diff] [blame] | 1406 | BAZEL_LOG(INFO) << binary_name.c_str() << " found on PATH: " << found; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1407 | return string(found); |
| 1408 | } |
| 1409 | if (end == string::npos) { |
| 1410 | break; |
| 1411 | } |
| 1412 | start = end + 1; |
| 1413 | } while (true); |
| 1414 | |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1415 | return string(); |
| 1416 | } |
| 1417 | |
Laszlo Csomor | 087fea1 | 2019-03-07 07:19:13 -0800 | [diff] [blame] | 1418 | static string LocateBashMaybe() { |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1419 | string msys_bash = GetMsysBash(); |
Laszlo Csomor | 087fea1 | 2019-03-07 07:19:13 -0800 | [diff] [blame] | 1420 | return msys_bash.empty() ? GetBinaryFromPath("bash.exe") : msys_bash; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1421 | } |
| 1422 | |
Laszlo Csomor | 28841a6 | 2019-02-01 01:11:15 -0800 | [diff] [blame] | 1423 | string DetectBashAndExportBazelSh() { |
Laszlo Csomor | 0d10749 | 2019-03-13 09:04:27 -0700 | [diff] [blame] | 1424 | string bash = blaze::GetPathEnv("BAZEL_SH"); |
Laszlo Csomor | 28841a6 | 2019-02-01 01:11:15 -0800 | [diff] [blame] | 1425 | if (!bash.empty()) { |
| 1426 | return bash; |
| 1427 | } |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1428 | |
| 1429 | uint64_t start = blaze::GetMillisecondsMonotonic(); |
| 1430 | |
Laszlo Csomor | 087fea1 | 2019-03-07 07:19:13 -0800 | [diff] [blame] | 1431 | bash = LocateBashMaybe(); |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1432 | uint64_t end = blaze::GetMillisecondsMonotonic(); |
Laszlo Csomor | 087fea1 | 2019-03-07 07:19:13 -0800 | [diff] [blame] | 1433 | if (bash.empty()) { |
| 1434 | BAZEL_LOG(INFO) << "BAZEL_SH detection took " << end - start |
| 1435 | << " msec, not found"; |
| 1436 | } else { |
| 1437 | BAZEL_LOG(INFO) << "BAZEL_SH detection took " << end - start |
| 1438 | << " msec, found " << bash.c_str(); |
Dmitry Lomov | 5a0d6ff | 2017-08-22 14:51:49 +0200 | [diff] [blame] | 1439 | // Set process environment variable. |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1440 | blaze::SetEnv("BAZEL_SH", bash); |
Laszlo Csomor | 28841a6 | 2019-02-01 01:11:15 -0800 | [diff] [blame] | 1441 | } |
Laszlo Csomor | 28841a6 | 2019-02-01 01:11:15 -0800 | [diff] [blame] | 1442 | |
Laszlo Csomor | 087fea1 | 2019-03-07 07:19:13 -0800 | [diff] [blame] | 1443 | return bash; |
Dmitry Lomov | 891f540 | 2017-07-19 12:26:39 +0200 | [diff] [blame] | 1444 | } |
| 1445 | |
Yun Peng | 213a52a | 2017-09-14 11:11:48 +0200 | [diff] [blame] | 1446 | void EnsurePythonPathOption(std::vector<string>* options) { |
| 1447 | string python_path = GetBinaryFromPath("python.exe"); |
| 1448 | if (!python_path.empty()) { |
| 1449 | // Provide python path as coming from the least important rc file. |
| 1450 | std::replace(python_path.begin(), python_path.end(), '\\', '/'); |
| 1451 | options->push_back(string("--default_override=0:build=--python_path=") + |
| 1452 | python_path); |
| 1453 | } |
| 1454 | } |
| 1455 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1456 | } // namespace blaze |