blob: 7cd188164dc247b76a356b2613579abd56632554 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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 Csomor1151e742019-04-25 04:55:44 -070015#ifndef WIN32_LEAN_AND_MEAN
16#define WIN32_LEAN_AND_MEAN
17#endif
László Csomorfa27b502017-03-23 15:09:34 +000018#include <windows.h>
Dmitry Lomov78c0cc72015-08-11 16:44:21 +000019
Laszlo Csomor1151e742019-04-25 04:55:44 -070020#include <fcntl.h>
Laszlo Csomorda557f92019-08-09 01:51:01 -070021#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 Csomor26f858c2017-02-08 17:50:10 +000027
László Csomore64ed192017-02-23 15:43:54 +000028#include <algorithm>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029#include <cstdio>
Laszlo Csomor31500b82016-12-15 10:15:36 +000030#include <cstdlib>
Laszlo Csomorba6fbde2018-11-16 05:31:18 -080031#include <memory>
Laszlo Csomorae43043f2018-07-17 04:18:30 -070032#include <mutex> // NOLINT
33#include <set>
Laszlo Csomorf9f41c72017-01-09 12:06:45 +000034#include <sstream>
Laszlo Csomorae43043f2018-07-17 04:18:30 -070035#include <thread> // NOLINT (to silence Google-internal linter)
Laszlo Csomor31500b82016-12-15 10:15:36 +000036#include <type_traits> // static_assert
Laszlo Csomore152f722017-02-03 10:05:36 +000037#include <vector>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038
Han-Wen Nienhuys36fbe632015-04-21 13:58:08 +000039#include "src/main/cpp/blaze_util.h"
Laszlo Csomora63d8162019-05-21 04:35:07 -070040#include "src/main/cpp/blaze_util_platform.h"
Laszlo Csomor32086b22016-11-24 15:23:55 +000041#include "src/main/cpp/startup_options.h"
Han-Wen Nienhuys36fbe632015-04-21 13:58:08 +000042#include "src/main/cpp/util/errors.h"
Thiago Farina7f9357f2015-04-23 13:57:43 +000043#include "src/main/cpp/util/exit_code.h"
Han-Wen Nienhuys36fbe632015-04-21 13:58:08 +000044#include "src/main/cpp/util/file.h"
Laszlo Csomor49970e02016-11-28 08:55:47 +000045#include "src/main/cpp/util/file_platform.h"
ccalvarin9eea0f92018-03-21 15:32:30 -070046#include "src/main/cpp/util/logging.h"
Laszlo Csomor6bf95762016-11-16 13:29:22 +000047#include "src/main/cpp/util/md5.h"
Laszlo Csomord0a12692016-11-28 13:35:23 +000048#include "src/main/cpp/util/numbers.h"
ccalvarinac69da02018-06-05 15:27:26 -070049#include "src/main/cpp/util/path.h"
50#include "src/main/cpp/util/path_platform.h"
László Csomor72879212017-02-02 15:55:18 +000051#include "src/main/cpp/util/strings.h"
Laszlo Csomorf0702342017-06-28 16:05:23 +020052#include "src/main/native/windows/file.h"
Laszlo Csomor1151e742019-04-25 04:55:44 -070053#include "src/main/native/windows/process.h"
Laszlo Csomorf0702342017-06-28 16:05:23 +020054#include "src/main/native/windows/util.h"
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055
56namespace blaze {
57
Laszlo Csomor31500b82016-12-15 10:15:36 +000058// Ensure we can safely cast (const) wchar_t* to LP(C)WSTR.
59// This is true with MSVC but usually not with GCC.
60static_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.
65static const size_t kWindowsPathBufferSize = 0x8010;
66
Yun Peng9c97bf92017-12-01 01:07:30 -080067using bazel::windows::AutoAttributeList;
Laszlo Csomorf0702342017-06-28 16:05:23 +020068using bazel::windows::AutoHandle;
69using bazel::windows::CreateJunction;
Laszlo Csomorc2b70f12018-07-09 04:32:32 -070070using bazel::windows::CreateJunctionResult;
Laszlo Csomorf0702342017-06-28 16:05:23 +020071
ccalvarin8448f572018-04-06 12:42:09 -070072// 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.
ccalvarinc5a58802018-03-29 11:13:24 -070076using blaze_util::GetLastErrorString;
Laszlo Csomorf00cee82016-12-15 10:58:03 +000077
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010078using std::string;
Laszlo Csomorf00cee82016-12-15 10:58:03 +000079using std::unique_ptr;
Laszlo Csomor31500b82016-12-15 10:15:36 +000080using std::wstring;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010081
Laszlo Csomorae43043f2018-07-17 04:18:30 -070082namespace embedded_binaries {
83
84class 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:
Vertexwahn26c7e102021-03-10 07:25:59 -080092 WindowsDumper() : threadpool_(nullptr), cleanup_group_(nullptr) {}
Laszlo Csomorae43043f2018-07-17 04:18:30 -070093
94 PTP_POOL threadpool_;
95 PTP_CLEANUP_GROUP cleanup_group_;
96 TP_CALLBACK_ENVIRON threadpool_env_;
Laszlo Csomor516282e2018-11-20 05:54:01 -080097
Laszlo Csomorae43043f2018-07-17 04:18:30 -070098 std::mutex dir_cache_lock_;
99 std::set<string> dir_cache_;
Laszlo Csomor516282e2018-11-20 05:54:01 -0800100
101 std::mutex error_lock_;
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700102 string error_msg_;
103};
104
105namespace {
106
107class 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 Csomor516282e2018-11-20 05:54:01 -0800111 std::mutex* error_lock_, string* error_msg);
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700112 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 Csomor516282e2018-11-20 05:54:01 -0800120
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700121 std::mutex* dir_cache_lock_;
122 std::set<string>* dir_cache_;
Laszlo Csomor516282e2018-11-20 05:54:01 -0800123
124 std::mutex* error_lock_;
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700125 string* error_msg_;
126};
127
128VOID CALLBACK WorkCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance,
129 _Inout_opt_ PVOID Context, _Inout_ PTP_WORK Work);
130
131} // namespace
132
133Dumper* Create(string* error) { return WindowsDumper::Create(error); }
134
135WindowsDumper* WindowsDumper::Create(string* error) {
136 unique_ptr<WindowsDumper> result(new WindowsDumper());
137
Vertexwahn26c7e102021-03-10 07:25:59 -0800138 result->threadpool_ = CreateThreadpool(nullptr);
139 if (result->threadpool_ == nullptr) {
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700140 if (error) {
141 string msg = GetLastErrorString();
142 *error = "CreateThreadpool failed: " + msg;
143 }
144 return nullptr;
145 }
146
147 result->cleanup_group_ = CreateThreadpoolCleanupGroup();
Vertexwahn26c7e102021-03-10 07:25:59 -0800148 if (result->cleanup_group_ == nullptr) {
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700149 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_,
Vertexwahn26c7e102021-03-10 07:25:59 -0800167 result->cleanup_group_, nullptr);
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700168
169 return result.release(); // release pointer ownership
170}
171
172void WindowsDumper::Dump(const void* data, const size_t size,
173 const string& path) {
Laszlo Csomor516282e2018-11-20 05:54:01 -0800174 {
175 std::lock_guard<std::mutex> g(error_lock_);
176 if (!error_msg_.empty()) {
177 return;
178 }
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700179 }
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 Csomor516282e2018-11-20 05:54:01 -0800185 &error_lock_, &error_msg_));
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700186 PTP_WORK w = CreateThreadpoolWork(WorkCallback, ctx.get(), &threadpool_env_);
Vertexwahn26c7e102021-03-10 07:25:59 -0800187 if (w == nullptr) {
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700188 string err = GetLastErrorString();
Laszlo Csomor516282e2018-11-20 05:54:01 -0800189 err = string("WindowsDumper::Dump() couldn't submit work: ") + err;
190
191 std::lock_guard<std::mutex> g(error_lock_);
192 error_msg_ = err;
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700193 } else {
194 ctx.release(); // release pointer ownership
195 SubmitThreadpoolWork(w);
196 }
197}
198
199bool WindowsDumper::Finish(string* error) {
Vertexwahn26c7e102021-03-10 07:25:59 -0800200 if (threadpool_ == nullptr) {
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700201 return true;
202 }
Vertexwahn26c7e102021-03-10 07:25:59 -0800203 CloseThreadpoolCleanupGroupMembers(cleanup_group_, FALSE, nullptr);
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700204 CloseThreadpoolCleanupGroup(cleanup_group_);
205 CloseThreadpool(threadpool_);
Vertexwahn26c7e102021-03-10 07:25:59 -0800206 threadpool_ = nullptr;
207 cleanup_group_ = nullptr;
Laszlo Csomor516282e2018-11-20 05:54:01 -0800208
209 std::lock_guard<std::mutex> g(error_lock_);
210 if (!error_msg_.empty() && error) {
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700211 *error = error_msg_;
212 }
Laszlo Csomor516282e2018-11-20 05:54:01 -0800213 return error_msg_.empty();
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700214}
215
216namespace {
217
218DumpContext::DumpContext(unique_ptr<uint8_t[]> data, const size_t size,
219 const string path, std::mutex* dir_cache_lock,
Laszlo Csomor516282e2018-11-20 05:54:01 -0800220 std::set<string>* dir_cache, std::mutex* error_lock_,
221 string* error_msg)
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700222 : data_(std::move(data)),
223 size_(size),
224 path_(path),
225 dir_cache_lock_(dir_cache_lock),
226 dir_cache_(dir_cache),
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700227 error_msg_(error_msg) {}
228
229void 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
254void DumpContext::MaybeSignalError(const string& msg) {
Laszlo Csomor516282e2018-11-20 05:54:01 -0800255 std::lock_guard<std::mutex> g(*error_lock_);
256 *error_msg_ = msg;
Laszlo Csomorae43043f2018-07-17 04:18:30 -0700257}
258
259VOID 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 Csomor32086b22016-11-24 15:23:55 +0000269SignalHandler SignalHandler::INSTANCE;
270
Laszlo Csomora85f52d2016-11-08 13:43:23 +0000271class WindowsClock {
272 public:
273 uint64_t GetMilliseconds() const;
Laszlo Csomora85f52d2016-11-08 13:43:23 +0000274
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 Csomora85f52d2016-11-08 13:43:23 +0000285 WindowsClock();
286
287 static LARGE_INTEGER GetFrequency();
288 static LARGE_INTEGER GetMillisecondsAsLargeInt(const LARGE_INTEGER& freq);
289};
290
Dmitry Lomov69ad6952017-03-21 16:20:37 +0000291BOOL 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",
michajlodc6d3b52019-06-06 14:04:46 -0700299 SignalHandler::Get().GetProductName().c_str());
michajlof40241c2019-07-17 17:43:15 -0700300 if (SignalHandler::Get().GetServerProcessInfo()->server_pid_ != -1) {
mschallerfd37b512017-07-11 18:21:36 +0200301 KillServerProcess(
michajlof40241c2019-07-17 17:43:15 -0700302 SignalHandler::Get().GetServerProcessInfo()->server_pid_,
michajlodc6d3b52019-06-06 14:04:46 -0700303 SignalHandler::Get().GetOutputBase());
Dmitry Lomov69ad6952017-03-21 16:20:37 +0000304 }
305 _exit(1);
306 }
307 SigPrintf(
308 "\n%s Ctrl+C handler; shutting down.\n\n",
michajlodc6d3b52019-06-06 14:04:46 -0700309 SignalHandler::Get().GetProductName().c_str());
Dmitry Lomov69ad6952017-03-21 16:20:37 +0000310 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
laszlocsomor6f665392019-08-27 08:15:51 -0700320void SignalHandler::Install(const string& product_name,
321 const blaze_util::Path& output_base,
322 const ServerProcessInfo* server_process_info_,
Laszlo Csomor32086b22016-11-24 15:23:55 +0000323 SignalHandler::Callback cancel_server) {
michajlodc6d3b52019-06-06 14:04:46 -0700324 product_name_ = product_name;
325 output_base_ = output_base;
michajlof40241c2019-07-17 17:43:15 -0700326 server_process_info_ = server_process_info_;
michajlodc6d3b52019-06-06 14:04:46 -0700327 cancel_server_ = cancel_server;
Dmitry Lomov69ad6952017-03-21 16:20:37 +0000328 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE);
Laszlo Csomor32086b22016-11-24 15:23:55 +0000329}
330
331ATTRIBUTE_NORETURN void SignalHandler::PropagateSignalOrExit(int exit_code) {
Dmitry Lomov69ad6952017-03-21 16:20:37 +0000332 // We do not handle signals on Windows; always exit with exit_code.
333 exit(exit_code);
Laszlo Csomor32086b22016-11-24 15:23:55 +0000334}
335
Laszlo Csomor32086b22016-11-24 15:23:55 +0000336// 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 Csomor3b89d2d2016-11-28 14:04:27 +0000346void SigPrintf(const char *format, ...) {
Dmitry Lomov5387c732017-03-22 09:33:28 +0000347 int stderr_fileno = _fileno(stderr);
Laszlo Csomor32086b22016-11-24 15:23:55 +0000348 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 Lomov5387c732017-03-22 09:33:28 +0000353 if (write(stderr_fileno, buf, r) <= 0) {
Laszlo Csomor32086b22016-11-24 15:23:55 +0000354 // We don't care, just placate the compiler.
355 }
Laszlo Csomor3b89d2d2016-11-28 14:04:27 +0000356}
Laszlo Csomor32086b22016-11-24 15:23:55 +0000357
Laszlo Csomor31500b82016-12-15 10:15:36 +0000358static 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
laszlocsomor6f665392019-08-27 08:15:51 -0700380void WarnFilesystemType(const blaze_util::Path& output_base) {}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100381
Laszlo Csomorae16e762016-11-18 10:16:08 +0000382string GetProcessIdAsString() {
michajlo479c9042020-03-27 11:43:39 -0700383 return blaze_util::ToString(GetCurrentProcessId());
Laszlo Csomorae16e762016-11-18 10:16:08 +0000384}
385
Googler070a5192019-12-06 09:21:42 -0800386string GetSelfPath(const char* argv0) {
Laszlo Csomor31500b82016-12-15 10:15:36 +0000387 WCHAR buffer[kWindowsPathBufferSize] = {0};
388 if (!GetModuleFileNameW(0, buffer, kWindowsPathBufferSize)) {
ccalvarin8448f572018-04-06 12:42:09 -0700389 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
390 << "GetSelfPath: GetModuleFileNameW: " << GetLastErrorString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100391 }
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700392 return blaze_util::WstringToCstring(buffer);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100393}
394
Kristina Chodorow93963352015-03-20 19:11:19 +0000395string GetOutputRoot() {
Laszlo Csomorf083e7622018-04-18 00:38:57 -0700396 string home = GetHomeDir();
397 if (home.empty()) {
ccalvarin8448f572018-04-06 12:42:09 -0700398 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomor6da408c2018-11-19 05:50:58 -0800399 << "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 Lomovbc84cc82016-04-15 14:05:24 +0000405 }
Laszlo Csomorf083e7622018-04-18 00:38:57 -0700406 return home;
Kristina Chodorow93963352015-03-20 19:11:19 +0000407}
408
László Csomorfa27b502017-03-23 15:09:34 +0000409string GetHomeDir() {
Laszlo Csomorb698e202019-09-24 08:26:26 -0700410 // 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 Csomor9b83bd72018-12-17 08:42:44 -0800414 // Bazel is running inside of a test. Respect $HOME that the test setup has
Laszlo Csomorb698e202019-09-24 08:26:26 -0700415 // 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 Csomor9b83bd72018-12-17 08:42:44 -0800425 }
426
László Csomorfa27b502017-03-23 15:09:34 +0000427 PWSTR wpath;
Laszlo Csomor6da408c2018-11-19 05:50:58 -0800428 // 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 Csomorb698e202019-09-24 08:26:26 -0700430 // On Windows 2016 Server, Nano server: FOLDERID_Profile is unknown but
431 // %USERPROFILE% is set. See https://github.com/bazelbuild/bazel/issues/6701
Vertexwahn26c7e102021-03-10 07:25:59 -0800432 if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT,
433 nullptr, &wpath))) {
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700434 string result = blaze_util::WstringToCstring(wpath);
László Csomorfa27b502017-03-23 15:09:34 +0000435 ::CoTaskMemFree(wpath);
436 return result;
437 }
Laszlo Csomor6da408c2018-11-19 05:50:58 -0800438
Laszlo Csomorb698e202019-09-24 08:26:26 -0700439 return "";
László Csomorfa27b502017-03-23 15:09:34 +0000440}
441
Laszlo Csomor760f7862016-12-19 15:46:47 +0000442string FindSystemWideBlazerc() {
László Csomorfa27b502017-03-23 15:09:34 +0000443 // TODO(bazel-team): figure out a good path to return here.
Laszlo Csomor760f7862016-12-19 15:46:47 +0000444 return "";
Laszlo Csomor760f7862016-12-19 15:46:47 +0000445}
446
Laszlo Csomor00549b42017-01-11 09:12:10 +0000447string GetJavaBinaryUnderJavabase() { return "bin/java.exe"; }
448
Laszlo Csomor943d3cf2016-11-07 14:27:21 +0000449uint64_t GetMillisecondsMonotonic() {
Laszlo Csomora85f52d2016-11-08 13:43:23 +0000450 return WindowsClock::INSTANCE.GetMilliseconds();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100451}
452
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100453void SetScheduling(bool batch_cpu_scheduling, int io_nice_level) {
454 // TODO(bazel-team): There should be a similar function on Windows.
455}
456
aldersondrive910079b2019-12-18 07:50:00 -0800457std::unique_ptr<blaze_util::Path> GetProcessCWD(int pid) {
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000458 // TODO(bazel-team) 2016-11-18: decide whether we need this on Windows and
459 // implement or delete.
aldersondrive910079b2019-12-18 07:50:00 -0800460 return nullptr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461}
462
Thiago Farina01f36002015-04-08 15:59:08 +0000463bool IsSharedLibrary(const string &filename) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 return blaze_util::ends_with(filename, ".dll");
465}
466
cushon849df362018-05-14 01:51:45 -0700467string GetSystemJavabase() {
Laszlo Csomor0d107492019-03-13 09:04:27 -0700468 string javahome(GetPathEnv("JAVA_HOME"));
Philipp Wollermann8db450f2019-02-03 09:26:07 -0800469 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 Nienhuysd08b27f2015-02-25 16:45:20 +0100476 }
Philipp Wollermann8db450f2019-02-03 09:26:07 -0800477
478 return "";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100479}
480
Dmitry Lomovb5ff50b2016-02-03 19:35:42 +0000481namespace {
Dmitry Lomovb5ff50b2016-02-03 19:35:42 +0000482
Lukacs Berki7494c922016-04-27 11:17:51 +0000483// Max command line length is per CreateProcess documentation
484// (https://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx)
Lukacs Berki83c78b12016-06-24 12:35:08 +0000485
Lukacs Berki7494c922016-04-27 11:17:51 +0000486static const int MAX_CMDLINE_LENGTH = 32768;
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000487
Lukacs Berki7494c922016-04-27 11:17:51 +0000488struct CmdLine {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700489 WCHAR cmdline[MAX_CMDLINE_LENGTH];
Lukacs Berki7494c922016-04-27 11:17:51 +0000490};
Laszlo Csomorc0993412019-08-28 07:34:39 -0700491static void CreateCommandLine(CmdLine* result, const blaze_util::Path& exe,
Laszlo Csomorda557f92019-08-09 01:51:01 -0700492 const std::vector<std::wstring>& wargs_vector) {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700493 std::wstringstream cmdline;
Laszlo Csomor44ecf9a2017-01-10 10:41:26 +0000494 string short_exe;
Laszlo Csomorc0993412019-08-28 07:34:39 -0700495 if (!exe.IsEmpty()) {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700496 string error;
Laszlo Csomorc0993412019-08-28 07:34:39 -0700497 wstring wshort_exe;
498 if (!blaze_util::AsShortWindowsPath(exe.AsNativePath(), &wshort_exe,
499 &error)) {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700500 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700501 << "CreateCommandLine: AsShortWindowsPath(" << exe.AsPrintablePath()
502 << "): " << error;
Laszlo Csomor1151e742019-04-25 04:55:44 -0700503 }
Laszlo Csomor1151e742019-04-25 04:55:44 -0700504 cmdline << L'\"' << wshort_exe << L'\"';
Laszlo Csomor44ecf9a2017-01-10 10:41:26 +0000505 }
Laszlo Csomor1151e742019-04-25 04:55:44 -0700506
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000507 bool first = true;
Laszlo Csomorda557f92019-08-09 01:51:01 -0700508 for (const std::wstring& wa : wargs_vector) {
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000509 if (first) {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700510 // Skip first argument, it is equal to 'exe'.
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000511 first = false;
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000512 continue;
513 } else {
Laszlo Csomor1151e742019-04-25 04:55:44 -0700514 cmdline << L' ';
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000515 }
Laszlo Csomorda557f92019-08-09 01:51:01 -0700516 cmdline << wa;
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000517 }
518
Laszlo Csomor1151e742019-04-25 04:55:44 -0700519 wstring cmdline_str = cmdline.str();
Laszlo Csomorf9f41c72017-01-09 12:06:45 +0000520 if (cmdline_str.size() >= MAX_CMDLINE_LENGTH) {
ccalvarin8448f572018-04-06 12:42:09 -0700521 BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
522 << "Command line too long (" << cmdline_str.size() << " > "
Laszlo Csomor1151e742019-04-25 04:55:44 -0700523 << MAX_CMDLINE_LENGTH
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700524 << "): " << blaze_util::WstringToCstring(cmdline_str);
Lukacs Berki7494c922016-04-27 11:17:51 +0000525 }
526
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000527 // Copy command line into a mutable buffer.
528 // CreateProcess is allowed to mutate its command line argument.
Laszlo Csomor1151e742019-04-25 04:55:44 -0700529 wcsncpy(result->cmdline, cmdline_str.c_str(), MAX_CMDLINE_LENGTH - 1);
Lukacs Berki2236f7d2016-04-28 08:49:43 +0000530 result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
Lukacs Berki7494c922016-04-27 11:17:51 +0000531}
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000532
Lukacs Berki7494c922016-04-27 11:17:51 +0000533} // namespace
534
Laszlo Csomord0512c42017-03-20 16:10:39 +0000535static 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 Csomorce1f0c52019-08-26 07:57:05 -0700547static void WriteProcessStartupTime(const blaze_util::Path& server_dir,
548 HANDLE process) {
Laszlo Csomord0512c42017-03-20 16:10:39 +0000549 uint64_t start_time = 0;
550 if (!GetProcessStartupTime(process, &start_time)) {
ccalvarin8448f572018-04-06 12:42:09 -0700551 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700552 << "WriteProcessStartupTime(" << server_dir.AsPrintablePath()
ccalvarin8448f572018-04-06 12:42:09 -0700553 << "): GetProcessStartupTime failed: " << GetLastErrorString();
Laszlo Csomord0512c42017-03-20 16:10:39 +0000554 }
555
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700556 blaze_util::Path start_time_file = server_dir.GetRelative("server.starttime");
michajlo479c9042020-03-27 11:43:39 -0700557 if (!blaze_util::WriteFile(blaze_util::ToString(start_time),
558 start_time_file)) {
ccalvarin8448f572018-04-06 12:42:09 -0700559 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700560 << "WriteProcessStartupTime(" << server_dir.AsPrintablePath()
561 << "): WriteFile(" << start_time_file.AsPrintablePath()
562 << ") failed: " << GetLastErrorString();
Laszlo Csomord0512c42017-03-20 16:10:39 +0000563 }
564}
565
laszlocsomor6f665392019-08-27 08:15:51 -0700566static HANDLE CreateJvmOutputFile(const blaze_util::Path& path,
567 LPSECURITY_ATTRIBUTES sa,
michajlof74f9fe2018-01-29 09:06:44 -0800568 bool daemon_out_append) {
Laszlo Csomord0512c42017-03-20 16:10:39 +0000569 // 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(
laszlocsomor6f665392019-08-27 08:15:51 -0700576 /* lpFileName */ path.AsNativePath().c_str(),
Laszlo Csomord0512c42017-03-20 16:10:39 +0000577 /* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE,
Laszlo Csomor70ff798a2017-06-12 09:39:10 +0200578 /* dwShareMode */ FILE_SHARE_READ,
Laszlo Csomord0512c42017-03-20 16:10:39 +0000579 /* lpSecurityAttributes */ sa,
michajlof74f9fe2018-01-29 09:06:44 -0800580 /* dwCreationDisposition */
laszlocsomor6f665392019-08-27 08:15:51 -0700581 daemon_out_append ? OPEN_ALWAYS : CREATE_ALWAYS,
Laszlo Csomord0512c42017-03-20 16:10:39 +0000582 /* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
Vertexwahn26c7e102021-03-10 07:25:59 -0800583 /* hTemplateFile */ nullptr);
Laszlo Csomord0512c42017-03-20 16:10:39 +0000584 if (handle != INVALID_HANDLE_VALUE) {
Vertexwahn26c7e102021-03-10 07:25:59 -0800585 if (daemon_out_append &&
586 !SetFilePointerEx(handle, {0}, nullptr, FILE_END)) {
laszlocsomor6f665392019-08-27 08:15:51 -0700587 fprintf(stderr, "Could not seek to end of file (%s)\n",
588 path.AsPrintablePath().c_str());
michajlof74f9fe2018-01-29 09:06:44 -0800589 return INVALID_HANDLE_VALUE;
590 }
Laszlo Csomord0512c42017-03-20 16:10:39 +0000591 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 Csomord12fb7a2017-04-26 13:41:56 +0200613class 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 Csomorf0702342017-06-28 16:05:23 +0200624 AutoHandle proc;
Laszlo Csomord12fb7a2017-04-26 13:41:56 +0200625};
626
Laszlo Csomorc0993412019-08-28 07:34:39 -0700627int ExecuteDaemon(const blaze_util::Path& exe,
628 const std::vector<string>& args_vector,
lberkid3d37f02018-04-18 11:44:14 -0700629 const std::map<string, EnvVarValue>& env,
laszlocsomor6f665392019-08-27 08:15:51 -0700630 const blaze_util::Path& daemon_output,
631 const bool daemon_out_append, const string& binaries_dir,
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700632 const blaze_util::Path& server_dir,
633 const StartupOptions& options,
felly70eabb62017-11-30 00:40:54 -0800634 BlazeServerStartup** server_startup) {
Laszlo Csomorba6fbde2018-11-16 05:31:18 -0800635 SECURITY_ATTRIBUTES inheritable_handle_sa = {sizeof(SECURITY_ATTRIBUTES),
Vertexwahn26c7e102021-03-10 07:25:59 -0800636 nullptr, TRUE};
Lukacs Berki7494c922016-04-27 11:17:51 +0000637
Laszlo Csomorba6fbde2018-11-16 05:31:18 -0800638 AutoHandle devnull(::CreateFileW(
639 L"NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
Vertexwahn26c7e102021-03-10 07:25:59 -0800640 &inheritable_handle_sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
Laszlo Csomord12fb7a2017-04-26 13:41:56 +0200641 if (!devnull.IsValid()) {
laszlocsomor6f665392019-08-27 08:15:51 -0700642 std::string error = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700643 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700644 << "ExecuteDaemon(" << exe.AsPrintablePath()
645 << "): CreateFileA(NUL) failed: " << error;
Lukacs Berki7494c922016-04-27 11:17:51 +0000646 }
647
Laszlo Csomorba6fbde2018-11-16 05:31:18 -0800648 AutoHandle stdout_file(CreateJvmOutputFile(
laszlocsomor6f665392019-08-27 08:15:51 -0700649 daemon_output, &inheritable_handle_sa, daemon_out_append));
Laszlo Csomor70ff798a2017-06-12 09:39:10 +0200650 if (!stdout_file.IsValid()) {
laszlocsomor6f665392019-08-27 08:15:51 -0700651 std::string error = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700652 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700653 << "ExecuteDaemon(" << exe.AsPrintablePath()
654 << "): CreateJvmOutputFile(" << daemon_output.AsPrintablePath()
655 << ") failed: " << error;
Lukacs Berki7494c922016-04-27 11:17:51 +0000656 }
Laszlo Csomor70ff798a2017-06-12 09:39:10 +0200657 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)) {
laszlocsomor6f665392019-08-27 08:15:51 -0700670 std::string error = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700671 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700672 << "ExecuteDaemon(" << exe.AsPrintablePath() << "): DuplicateHandle("
laszlocsomor6f665392019-08-27 08:15:51 -0700673 << daemon_output.AsPrintablePath() << ") failed: " << error;
Laszlo Csomor70ff798a2017-06-12 09:39:10 +0200674 }
Laszlo Csomorf0702342017-06-28 16:05:23 +0200675 AutoHandle stderr_file(stderr_handle);
Lukacs Berki7494c922016-04-27 11:17:51 +0000676
Laszlo Csomorba6fbde2018-11-16 05:31:18 -0800677 // Create an attribute list.
678 wstring werror;
679 std::unique_ptr<AutoAttributeList> lpAttributeList;
Laszlo Csomord235a062018-11-26 07:01:45 -0800680 if (!AutoAttributeList::Create(devnull, stdout_file, stderr_handle,
681 &lpAttributeList, &werror)) {
ccalvarin8448f572018-04-06 12:42:09 -0700682 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700683 << "ExecuteDaemon(" << exe.AsPrintablePath()
684 << "): attribute list creation failed: "
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700685 << blaze_util::WstringToCstring(werror);
Yun Peng9c97bf92017-12-01 01:07:30 -0800686 }
687
688 PROCESS_INFORMATION processInfo = {0};
Laszlo Csomor1151e742019-04-25 04:55:44 -0700689 STARTUPINFOEXW startupInfoEx = {0};
690 lpAttributeList->InitStartupInfoExW(&startupInfoEx);
Yun Peng9c97bf92017-12-01 01:07:30 -0800691
Laszlo Csomorda557f92019-08-09 01:51:01 -0700692 std::vector<std::wstring> wesc_args_vector;
693 wesc_args_vector.reserve(args_vector.size());
694 for (const string& a : args_vector) {
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700695 std::wstring wa = blaze_util::CstringToWstring(a);
Laszlo Csomordcc80fb2019-08-09 05:05:21 -0700696 std::wstring wesc = bazel::windows::WindowsEscapeArg(wa);
Laszlo Csomorda557f92019-08-09 01:51:01 -0700697 wesc_args_vector.push_back(wesc);
698 }
699
Lukacs Berki7494c922016-04-27 11:17:51 +0000700 CmdLine cmdline;
Laszlo Csomorda557f92019-08-09 01:51:01 -0700701 CreateCommandLine(&cmdline, exe, wesc_args_vector);
Lukacs Berki7494c922016-04-27 11:17:51 +0000702
lberkid3d37f02018-04-18 11:44:14 -0700703 BOOL ok;
704 {
705 WithEnvVars env_obj(env);
706
Laszlo Csomor1151e742019-04-25 04:55:44 -0700707 ok = CreateProcessW(
Vertexwahn26c7e102021-03-10 07:25:59 -0800708 /* lpApplicationName */ nullptr,
lberkid3d37f02018-04-18 11:44:14 -0700709 /* lpCommandLine */ cmdline.cmdline,
Vertexwahn26c7e102021-03-10 07:25:59 -0800710 /* lpProcessAttributes */ nullptr,
711 /* lpThreadAttributes */ nullptr,
lberkid3d37f02018-04-18 11:44:14 -0700712 /* bInheritHandles */ TRUE,
713 /* dwCreationFlags */ DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP |
Laszlo Csomor1151e742019-04-25 04:55:44 -0700714 EXTENDED_STARTUPINFO_PRESENT,
Vertexwahn26c7e102021-03-10 07:25:59 -0800715 /* lpEnvironment */ nullptr,
716 /* lpCurrentDirectory */ nullptr,
lberkid3d37f02018-04-18 11:44:14 -0700717 /* lpStartupInfo */ &startupInfoEx.StartupInfo,
718 /* lpProcessInformation */ &processInfo);
719 }
Lukacs Berki7494c922016-04-27 11:17:51 +0000720
721 if (!ok) {
Laszlo Csomorc0993412019-08-28 07:34:39 -0700722 string err = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700723 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700724 << "ExecuteDaemon(" << exe.AsPrintablePath() << "): CreateProcess("
725 << cmdline.cmdline << ") failed: " << err;
Lukacs Berki7494c922016-04-27 11:17:51 +0000726 }
727
Laszlo Csomord0512c42017-03-20 16:10:39 +0000728 WriteProcessStartupTime(server_dir, processInfo.hProcess);
729
Laszlo Csomord12fb7a2017-04-26 13:41:56 +0200730 // Pass ownership of processInfo.hProcess
731 *server_startup = new ProcessHandleBlazeServerStartup(processInfo.hProcess);
Lukacs Berki7494c922016-04-27 11:17:51 +0000732
michajlo479c9042020-03-27 11:43:39 -0700733 string pid_string = blaze_util::ToString(processInfo.dwProcessId);
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700734 blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile);
Laszlo Csomor49970e02016-11-28 08:55:47 +0000735 if (!blaze_util::WriteFile(pid_string, pid_file)) {
Lukacs Berkie33cf0f2016-04-28 11:04:59 +0000736 // Not a lot we can do if this fails
Laszlo Csomorce1f0c52019-08-26 07:57:05 -0700737 fprintf(stderr, "Cannot write PID file %s\n",
738 pid_file.AsPrintablePath().c_str());
Lukacs Berkie33cf0f2016-04-28 11:04:59 +0000739 }
740
Laszlo Csomord12fb7a2017-04-26 13:41:56 +0200741 // Don't close processInfo.hProcess here, it's now owned by the
742 // ProcessHandleBlazeServerStartup instance.
Lukacs Berki7494c922016-04-27 11:17:51 +0000743 CloseHandle(processInfo.hThread);
felly70eabb62017-11-30 00:40:54 -0800744
745 return processInfo.dwProcessId;
Lukacs Berki7494c922016-04-27 11:17:51 +0000746}
747
Philipp Wollermanne4d977f2017-03-15 15:34:11 +0000748// 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.
jmmv8cb0ec22019-09-23 12:45:28 -0700751ATTRIBUTE_NORETURN static void ExecuteProgram(
752 const blaze_util::Path& exe,
753 const std::vector<std::wstring>& wargs_vector) {
Lukacs Berki7494c922016-04-27 11:17:51 +0000754 CmdLine cmdline;
Laszlo Csomorc0993412019-08-28 07:34:39 -0700755 CreateCommandLine(&cmdline, blaze_util::Path(), wargs_vector);
Lukacs Berki7494c922016-04-27 11:17:51 +0000756
Laszlo Csomor1151e742019-04-25 04:55:44 -0700757 bazel::windows::WaitableProcess proc;
758 std::wstring werror;
Laszlo Csomorc0993412019-08-28 07:34:39 -0700759 // 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 Csomor1151e742019-04-25 04:55:44 -0700762 proc.WaitFor(-1, nullptr, &werror) !=
763 bazel::windows::WaitableProcess::kWaitSuccess) {
ccalvarin8448f572018-04-06 12:42:09 -0700764 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700765 << "ExecuteProgram(" << exe.AsPrintablePath()
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700766 << ") failed: " << blaze_util::WstringToCstring(werror);
Lukacs Berki68cb41a2016-07-06 11:43:37 +0000767 }
Laszlo Csomor1151e742019-04-25 04:55:44 -0700768 werror.clear();
769 int x = proc.GetExitCode(&werror);
770 if (!werror.empty()) {
771 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
Laszlo Csomorc0993412019-08-28 07:34:39 -0700772 << "ExecuteProgram(" << exe.AsPrintablePath()
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700773 << ") failed: " << blaze_util::WstringToCstring(werror);
Laszlo Csomor1151e742019-04-25 04:55:44 -0700774 }
775 exit(x);
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000776}
777
Laszlo Csomorc0993412019-08-28 07:34:39 -0700778void ExecuteServerJvm(const blaze_util::Path& exe,
Laszlo Csomorda557f92019-08-09 01:51:01 -0700779 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 Csomorb864ced2019-09-23 07:39:16 -0700783 std::wstring wa = blaze_util::CstringToWstring(a);
Laszlo Csomordcc80fb2019-08-09 05:05:21 -0700784 std::wstring wesc = bazel::windows::WindowsEscapeArg(wa);
Laszlo Csomorda557f92019-08-09 01:51:01 -0700785 wargs.push_back(wesc);
786 }
787
788 ExecuteProgram(exe, wargs);
789}
790
Laszlo Csomorc0993412019-08-28 07:34:39 -0700791void ExecuteRunRequest(const blaze_util::Path& exe,
Laszlo Csomorda557f92019-08-09 01:51:01 -0700792 const std::vector<string>& run_request_args) {
793 std::vector<std::wstring> wargs;
794 wargs.reserve(run_request_args.size());
Laszlo Csomorda557f92019-08-09 01:51:01 -0700795 for (const string& a : run_request_args) {
laszlocsomorbe0e36f2020-01-15 04:46:02 -0800796 // The arguments are already escaped, don't escape further.
797 wargs.push_back(blaze_util::CstringToWstring(a));
Laszlo Csomorda557f92019-08-09 01:51:01 -0700798 }
799
800 ExecuteProgram(exe, wargs);
801}
802
Thiago Farinac3aee5a2017-04-24 18:02:52 +0200803const char kListSeparator = ';';
Dmitry Lomov78c0cc72015-08-11 16:44:21 +0000804
laszlocsomor6f665392019-08-27 08:15:51 -0700805bool SymlinkDirectories(const string& posix_target,
806 const blaze_util::Path& name) {
Laszlo Csomor38db3162017-02-15 13:54:32 +0000807 wstring target;
Laszlo Csomor8c400c82018-04-24 01:44:17 -0700808 string error;
Laszlo Csomor8c400c82018-04-24 01:44:17 -0700809 if (!blaze_util::AsAbsoluteWindowsPath(posix_target, &target, &error)) {
ccalvarin8448f572018-04-06 12:42:09 -0700810 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
laszlocsomor6f665392019-08-27 08:15:51 -0700811 << "SymlinkDirectories(" << posix_target << ", "
812 << name.AsPrintablePath() << "): AsAbsoluteWindowsPath(" << posix_target
813 << ") failed: " << error;
Lukacs Berki497d8242016-04-28 07:21:26 +0000814 return false;
815 }
Laszlo Csomorc2b70f12018-07-09 04:32:32 -0700816 wstring werror;
laszlocsomor6f665392019-08-27 08:15:51 -0700817 if (CreateJunction(name.AsNativePath(), target, &werror) !=
818 CreateJunctionResult::kSuccess) {
Laszlo Csomorb864ced2019-09-23 07:39:16 -0700819 string error(blaze_util::WstringToCstring(werror));
ccalvarin73839762018-03-23 15:35:00 -0700820 BAZEL_LOG(ERROR) << "SymlinkDirectories(" << posix_target << ", "
laszlocsomor6f665392019-08-27 08:15:51 -0700821 << name.AsPrintablePath()
822 << "): CreateJunction: " << error;
Lukacs Berki497d8242016-04-28 07:21:26 +0000823 return false;
824 }
Laszlo Csomor38db3162017-02-15 13:54:32 +0000825 return true;
Lukacs Berki497d8242016-04-28 07:21:26 +0000826}
827
Laszlo Csomor032ddee2017-03-21 10:36:03 +0000828#ifndef STILL_ACTIVE
829#define STILL_ACTIVE (259) // From MSDN about GetExitCodeProcess.
830#endif
831
Laszlo Csomord0512c42017-03-20 16:10:39 +0000832// 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.
laszlocsomor6f665392019-08-27 08:15:51 -0700835bool VerifyServerProcess(int pid, const blaze_util::Path& output_base) {
Laszlo Csomorf0702342017-06-28 16:05:23 +0200836 AutoHandle process(
Laszlo Csomord0512c42017-03-20 16:10:39 +0000837 ::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 Csomor032ddee2017-03-21 10:36:03 +0000843 DWORD exit_code = 0;
Laszlo Csomord0512c42017-03-20 16:10:39 +0000844 uint64_t start_time = 0;
Laszlo Csomor032ddee2017-03-21 10:36:03 +0000845 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 Csomord0512c42017-03-20 16:10:39 +0000849 return false;
850 }
851
852 string recorded_start_time;
853 bool file_present = blaze_util::ReadFile(
laszlocsomor6f665392019-08-27 08:15:51 -0700854 output_base.GetRelative("server/server.starttime"), &recorded_start_time);
Laszlo Csomord0512c42017-03-20 16:10:39 +0000855
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.
michajlo479c9042020-03-27 11:43:39 -0700858 return !file_present ||
859 recorded_start_time == blaze_util::ToString(start_time);
Lukacs Berkiee44c382016-09-14 10:53:37 +0000860}
861
laszlocsomor6f665392019-08-27 08:15:51 -0700862bool KillServerProcess(int pid, const blaze_util::Path& output_base) {
Laszlo Csomorf0702342017-06-28 16:05:23 +0200863 AutoHandle process(::OpenProcess(
Laszlo Csomor032ddee2017-03-21 10:36:03 +0000864 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 Berki119dd4b2016-07-13 15:28:42 +0000870 return false;
Lukacs Berkid825a3d2016-06-23 11:10:02 +0000871 }
872
Laszlo Csomorc8cd6bd2017-03-14 11:10:04 +0000873 BOOL result = TerminateProcess(process, /*uExitCode*/ 0);
mschallerfd37b512017-07-11 18:21:36 +0200874 if (!result || !AwaitServerProcessTermination(pid, output_base,
875 kPostKillGracePeriodSeconds)) {
laszlocsomor6f665392019-08-27 08:15:51 -0700876 string err = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700877 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
878 << "Cannot terminate server process with PID " << pid
laszlocsomor6f665392019-08-27 08:15:51 -0700879 << ", output_base=(" << output_base.AsPrintablePath() << "): " << err;
Lukacs Berkid825a3d2016-06-23 11:10:02 +0000880 }
Lukacs Berki119dd4b2016-07-13 15:28:42 +0000881 return result;
Lukacs Berki1977d922016-05-02 09:31:37 +0000882}
883
mschallerfd37b512017-07-11 18:21:36 +0200884void TrySleep(unsigned int milliseconds) {
885 Sleep(milliseconds);
886}
887
Dave MacLachlan6b747ee2016-07-20 10:00:44 +0000888// Not supported.
laszlocsomor6f665392019-08-27 08:15:51 -0700889void ExcludePathFromBackup(const blaze_util::Path& path) {}
Dave MacLachlan6b747ee2016-07-20 10:00:44 +0000890
Laszlo Csomor6bf95762016-11-16 13:29:22 +0000891string GetHashedBaseDir(const string& root, const string& hashable) {
892 // Builds a shorter output base dir name for Windows.
Laszlo Csomor6bf95762016-11-16 13:29:22 +0000893
Laszlo Csomor9b978762018-04-19 06:50:23 -0700894 // 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 Csomor6bf95762016-11-16 13:29:22 +0000901
Laszlo Csomor9b978762018-04-19 06:50:23 -0700902 // 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 Csomor6bf95762016-11-16 13:29:22 +0000911 char coded_name[filename_length + 1];
912 blaze_util::Md5Digest digest;
913 digest.Update(hashable.data(), hashable.size());
Laszlo Csomor9b978762018-04-19 06:50:23 -0700914 digest.Finish(md5);
915 for (int i = 0; i < filename_length; ++i) {
916 coded_name[i] = alphabet[md5[i] & kLower5BitsMask];
Laszlo Csomor6bf95762016-11-16 13:29:22 +0000917 }
918 coded_name[filename_length] = '\0';
Laszlo Csomor760f7862016-12-19 15:46:47 +0000919 return blaze_util::JoinPath(root, string(coded_name));
Laszlo Csomor6bf95762016-11-16 13:29:22 +0000920}
921
laszlocsomor6f665392019-08-27 08:15:51 -0700922void CreateSecureOutputRoot(const blaze_util::Path& path) {
Laszlo Csomor5eb3ef92017-02-07 16:52:38 +0000923 // TODO(bazel-team): implement this properly, by mimicing whatever the POSIX
924 // implementation does.
Laszlo Csomor5eb3ef92017-02-07 16:52:38 +0000925 if (!blaze_util::MakeDirectories(path, 0755)) {
laszlocsomor6f665392019-08-27 08:15:51 -0700926 string err = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -0700927 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
laszlocsomor6f665392019-08-27 08:15:51 -0700928 << "MakeDirectories(" << path.AsPrintablePath() << ") failed: " << err;
Laszlo Csomor8a48f612016-11-17 10:18:34 +0000929 }
930
Laszlo Csomor5eb3ef92017-02-07 16:52:38 +0000931 if (!blaze_util::IsDirectory(path)) {
ccalvarin8448f572018-04-06 12:42:09 -0700932 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
laszlocsomor6f665392019-08-27 08:15:51 -0700933 << "'" << path.AsPrintablePath() << "' is not a directory";
Laszlo Csomor8a48f612016-11-17 10:18:34 +0000934 }
935
laszlocsomor6f665392019-08-27 08:15:51 -0700936 ExcludePathFromBackup(path);
Laszlo Csomor8a48f612016-11-17 10:18:34 +0000937}
938
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000939string GetEnv(const string& name) {
Vertexwahn26c7e102021-03-10 07:25:59 -0800940 DWORD size = ::GetEnvironmentVariableA(name.c_str(), nullptr, 0);
Laszlo Csomorf2b5f272017-01-18 09:51:57 +0000941 if (size == 0) {
Laszlo Csomorf2b5f272017-01-18 09:51:57 +0000942 return string(); // unset or empty envvar
Laszlo Csomorf2b5f272017-01-18 09:51:57 +0000943 }
944
945 unique_ptr<char[]> value(new char[size]);
946 ::GetEnvironmentVariableA(name.c_str(), value.get(), size);
947 return string(value.get());
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000948}
949
Laszlo Csomor0d107492019-03-13 09:04:27 -0700950string 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
lberkid3d37f02018-04-18 11:44:14 -0700968bool ExistsEnv(const string& name) {
Vertexwahn26c7e102021-03-10 07:25:59 -0800969 return ::GetEnvironmentVariableA(name.c_str(), nullptr, 0) != 0;
lberkid3d37f02018-04-18 11:44:14 -0700970}
971
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000972void SetEnv(const string& name, const string& value) {
Dmitry Lomov5a0d6ff2017-08-22 14:51:49 +0200973 // _putenv_s both calls ::SetEnvionmentVariableA and updates environ(5).
974 _putenv_s(name.c_str(), value.c_str());
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000975}
976
Laszlo Csomorf2b5f272017-01-18 09:51:57 +0000977void UnsetEnv(const string& name) { SetEnv(name, ""); }
Laszlo Csomorcefa9a22016-11-22 10:50:07 +0000978
Laszlo Csomorc4fb2182017-07-27 16:37:08 +0200979bool 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...");
Vertexwahn26c7e102021-03-10 07:25:59 -0800994 ReadFile(GetStdHandle(STD_INPUT_HANDLE), dummy, 1, dummy, nullptr);
Laszlo Csomorc4fb2182017-07-27 16:37:08 +0200995 return true;
996}
997
Laszlo Csomor41ac1e02017-03-20 15:25:44 +0000998#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 Csomor74ffaf72016-11-24 12:17:20 +00001013void SetupStdStreams() {
Laszlo Csomor26f858c2017-02-08 17:50:10 +00001014 static const DWORD stdhandles[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
1015 STD_ERROR_HANDLE};
Laszlo Csomor3d97f492017-03-16 12:58:23 +00001016 for (int i = 0; i <= 2; ++i) {
Laszlo Csomor26f858c2017-02-08 17:50:10 +00001017 HANDLE handle = ::GetStdHandle(stdhandles[i]);
Vertexwahn26c7e102021-03-10 07:25:59 -08001018 if (handle == INVALID_HANDLE_VALUE || handle == nullptr) {
Laszlo Csomor26f858c2017-02-08 17:50:10 +00001019 // 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 Csomor41ac1e02017-03-20 15:25:44 +00001023 DWORD mode = 0;
Vertexwahn26c7e102021-03-10 07:25:59 -08001024 if (i > 0 && handle != INVALID_HANDLE_VALUE && handle != nullptr &&
Laszlo Csomor41ac1e02017-03-20 15:25:44 +00001025 ::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 Csomor26f858c2017-02-08 17:50:10 +00001036 }
Laszlo Csomor74ffaf72016-11-24 12:17:20 +00001037}
1038
Laszlo Csomora85f52d2016-11-08 13:43:23 +00001039LARGE_INTEGER WindowsClock::GetFrequency() {
1040 LARGE_INTEGER result;
1041 if (!QueryPerformanceFrequency(&result)) {
ccalvarin8448f572018-04-06 12:42:09 -07001042 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
1043 << "WindowsClock::GetFrequency: QueryPerformanceFrequency failed: "
1044 << GetLastErrorString();
Laszlo Csomora85f52d2016-11-08 13:43:23 +00001045 }
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) {
ccalvarin8448f572018-04-06 12:42:09 -07001050 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
1051 << "WindowsClock::GetFrequency: QueryPerformanceFrequency returned "
1052 "invalid result ("
1053 << result.QuadPart << "): " << GetLastErrorString();
Laszlo Csomora85f52d2016-11-08 13:43:23 +00001054 }
1055
1056 return result;
1057}
1058
1059LARGE_INTEGER WindowsClock::GetMillisecondsAsLargeInt(
1060 const LARGE_INTEGER& freq) {
1061 LARGE_INTEGER counter;
1062 if (!QueryPerformanceCounter(&counter)) {
ccalvarin8448f572018-04-06 12:42:09 -07001063 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
1064 << "WindowsClock::GetMillisecondsAsLargeInt: QueryPerformanceCounter "
1065 "failed: "
1066 << GetLastErrorString();
Laszlo Csomora85f52d2016-11-08 13:43:23 +00001067 }
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
1079const WindowsClock WindowsClock::INSTANCE;
1080
1081WindowsClock::WindowsClock()
twerthf59fad72018-10-02 04:50:28 -07001082 : kFrequency(GetFrequency()) {}
Laszlo Csomora85f52d2016-11-08 13:43:23 +00001083
1084uint64_t WindowsClock::GetMilliseconds() const {
1085 return GetMillisecondsAsLargeInt(kFrequency).QuadPart;
1086}
1087
laszlocsomor6f665392019-08-27 08:15:51 -07001088uint64_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");
laszlocsomorade79a72017-04-12 10:08:16 +00001091 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(
laszlocsomor6f665392019-08-27 08:15:51 -07001096 /* lpFileName */ lockfile.AsNativePath().c_str(),
laszlocsomorade79a72017-04-12 10:08:16 +00001097 /* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE,
1098 /* dwShareMode */ FILE_SHARE_READ,
Vertexwahn26c7e102021-03-10 07:25:59 -08001099 /* lpSecurityAttributes */ nullptr,
laszlocsomorade79a72017-04-12 10:08:16 +00001100 /* dwCreationDisposition */ CREATE_ALWAYS,
1101 /* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
Vertexwahn26c7e102021-03-10 07:25:59 -08001102 /* hTemplateFile */ nullptr);
laszlocsomorade79a72017-04-12 10:08:16 +00001103 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.
ccalvarin6c8166e2018-07-23 09:17:26 -07001109 BAZEL_LOG(USER) << "Another command holds the client lock";
laszlocsomorade79a72017-04-12 10:08:16 +00001110 if (!block) {
ccalvarin6c8166e2018-07-23 09:17:26 -07001111 BAZEL_DIE(blaze_exit_code::LOCK_HELD_NOBLOCK_FOR_LOCK)
1112 << "Exiting because the lock is held and --noblock_for_lock was "
1113 "given.";
laszlocsomorade79a72017-04-12 10:08:16 +00001114 }
1115 if (first_lock_attempt) {
1116 first_lock_attempt = false;
ccalvarin6c8166e2018-07-23 09:17:26 -07001117 BAZEL_LOG(USER) << "Waiting for it to complete...";
laszlocsomorade79a72017-04-12 10:08:16 +00001118 fflush(stderr);
1119 }
1120 Sleep(/* dwMilliseconds */ 200);
1121 } else {
laszlocsomor6f665392019-08-27 08:15:51 -07001122 string err = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -07001123 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
laszlocsomor6f665392019-08-27 08:15:51 -07001124 << "AcquireLock(" << lockfile.AsPrintablePath()
1125 << "): CreateFile failed: " << err;
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001126 }
1127 }
laszlocsomorade79a72017-04-12 10:08:16 +00001128 uint64_t wait_time = GetMillisecondsMonotonic() - st;
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001129
laszlocsomorade79a72017-04-12 10:08:16 +00001130 // 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)) {
laszlocsomor6f665392019-08-27 08:15:51 -07001139 string err = GetLastErrorString();
ccalvarin8448f572018-04-06 12:42:09 -07001140 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
laszlocsomor6f665392019-08-27 08:15:51 -07001141 << "AcquireLock(" << lockfile.AsPrintablePath()
1142 << "): LockFileEx failed: " << err;
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001143 }
laszlocsomorade79a72017-04-12 10:08:16 +00001144 // 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 Csomor7e5fb402016-11-25 15:11:08 +00001148
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001149 return wait_time;
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001150}
1151
1152void ReleaseLock(BlazeLock* blaze_lock) {
laszlocsomorade79a72017-04-12 10:08:16 +00001153 OVERLAPPED overlapped = {0};
1154 UnlockFileEx(blaze_lock->handle, 0, 1, 0, &overlapped);
1155 CloseHandle(blaze_lock->handle);
Laszlo Csomor7e5fb402016-11-25 15:11:08 +00001156}
1157
Laszlo Csomor99b01542017-03-02 11:43:21 +00001158#ifdef GetUserName
1159// By including <windows.h>, we have GetUserName defined either as
1160// GetUserNameA or GetUserNameW.
1161#undef GetUserName
1162#endif
1163
Laszlo Csomord0a12692016-11-28 13:35:23 +00001164string GetUserName() {
Laszlo Csomorb698e202019-09-24 08:26:26 -07001165 // 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 Csomor41ee5912016-12-15 12:26:06 +00001180 WCHAR buffer[UNLEN + 1];
1181 DWORD len = UNLEN + 1;
Laszlo Csomor6d477072017-07-20 10:09:44 +02001182 if (!::GetUserNameW(buffer, &len)) {
ccalvarin8448f572018-04-06 12:42:09 -07001183 BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
1184 << "GetUserNameW failed: " << GetLastErrorString();
Laszlo Csomord0a12692016-11-28 13:35:23 +00001185 }
Laszlo Csomorb864ced2019-09-23 07:39:16 -07001186 return blaze_util::WstringToCstring(buffer);
Laszlo Csomord0a12692016-11-28 13:35:23 +00001187}
1188
1189bool IsEmacsTerminal() {
Laszlo Csomord0a12692016-11-28 13:35:23 +00001190 string emacs = GetEnv("EMACS");
Laszlo Csomord0a12692016-11-28 13:35:23 +00001191 // 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 Csomor0d107492019-03-13 09:04:27 -07001195 return emacs == "t" || ExistsEnv("INSIDE_EMACS");
Laszlo Csomord0a12692016-11-28 13:35:23 +00001196}
1197
michajloac5a9712019-07-17 14:04:57 -07001198bool 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 Csomor41ac1e02017-03-20 15:25:44 +00001209 }
1210 return true;
Laszlo Csomord0a12692016-11-28 13:35:23 +00001211}
1212
michajloac5a9712019-07-17 14:04:57 -07001213int GetTerminalColumns() {
Laszlo Csomord0a12692016-11-28 13:35:23 +00001214 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ó Csomor72879212017-02-02 15:55:18 +00001222
michajloac5a9712019-07-17 14:04:57 -07001223 HANDLE stdout_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
Laszlo Csomor3d97f492017-03-16 12:58:23 +00001224 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ó Csomor72879212017-02-02 15:55:18 +00001232 }
1233 }
László Csomor72879212017-02-02 15:55:18 +00001234
1235 return 80; // default if not a terminal.
Laszlo Csomord0a12692016-11-28 13:35:23 +00001236}
1237
jmmva96369c2017-07-10 18:14:36 +02001238bool UnlimitResources() {
1239 return true; // Nothing to do so assume success.
1240}
1241
jmmv8ddd7822018-08-30 13:20:25 -07001242bool UnlimitCoredumps() {
1243 return true; // Nothing to do so assume success.
1244}
1245
Dmitry Lomov891f5402017-07-19 12:26:39 +02001246static const int MAX_KEY_LENGTH = 255;
1247// We do not care about registry values longer than MAX_PATH
1248static const int REG_VALUE_BUFFER_SIZE = MAX_PATH;
1249
1250// Implements heuristics to discover msys2 installation.
1251static 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 Jie4bf477e2018-07-02 00:11:54 -07001257 static constexpr const char key[] =
Dmitry Lomov891f5402017-07-19 12:26:39 +02001258 "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 )) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001266 BAZEL_LOG(INFO) << "Cannot open HKCU\\" << key;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001267 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 Jie4bf477e2018-07-02 00:11:54 -07001275 static constexpr const char msys_display_name[] = "MSYS2 64bit";
Dmitry Lomov891f5402017-07-19 12:26:39 +02001276 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 )) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001291 BAZEL_LOG(INFO) << "Cannot query HKCU\\" << key;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001292 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 )) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001302 BAZEL_LOG(INFO) << "Cannot get " << key_index << " subkey of HKCU\\"
1303 << key;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001304 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 )) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001314 BAZEL_LOG(ERROR) << "Failed to open subkey HKCU\\" << key << "\\"
1315 << subkey_name;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001316 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 Csomor1e9fc8e2019-01-03 00:28:25 -08001331 // 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 Lomov891f5402017-07-19 12:26:39 +02001334 }
1335
1336 if (value_type == REG_SZ &&
1337 0 == memcmp(msys_display_name, value, sizeof(msys_display_name))) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001338 BAZEL_LOG(INFO) << "Getting install location of HKCU\\" << key << "\\"
1339 << subkey_name;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001340 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 Csomor1e9fc8e2019-01-03 00:28:25 -08001351 // 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 Lomov891f5402017-07-19 12:26:39 +02001354 continue; // try next subkey
1355 }
1356
1357 if (path_length == 0 || path_type != REG_SZ) {
Laszlo Csomor1e9fc8e2019-01-03 00:28:25 -08001358 // 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 Lomov891f5402017-07-19 12:26:39 +02001362 }
1363
ccalvarin9eea0f92018-03-21 15:32:30 -07001364 BAZEL_LOG(INFO) << "Install location of HKCU\\" << key << "\\"
1365 << subkey_name << " is " << path;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001366 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 Csomor1e9fc8e2019-01-03 00:28:25 -08001369 // 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 Lomov891f5402017-07-19 12:26:39 +02001373 }
1374
Laszlo Csomor1e9fc8e2019-01-03 00:28:25 -08001375 BAZEL_LOG(INFO) << "Detected MSYS2 Bash at " << bash_exe.c_str();
Dmitry Lomov891f5402017-07-19 12:26:39 +02001376 return bash_exe;
1377 }
1378 }
1379 return string();
1380}
1381
Yun Peng213a52a2017-09-14 11:11:48 +02001382static string GetBinaryFromPath(const string& binary_name) {
Dmitry Lomov891f5402017-07-19 12:26:39 +02001383 char found[MAX_PATH];
Laszlo Csomor0d107492019-03-13 09:04:27 -07001384 string path_list = blaze::GetPathEnv("PATH");
Dmitry Lomov891f5402017-07-19 12:26:39 +02001385
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 Peng213a52a2017-09-14 11:11:48 +02001399 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 Lomov891f5402017-07-19 12:26:39 +02001405 )) {
ccalvarin9eea0f92018-03-21 15:32:30 -07001406 BAZEL_LOG(INFO) << binary_name.c_str() << " found on PATH: " << found;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001407 return string(found);
1408 }
1409 if (end == string::npos) {
1410 break;
1411 }
1412 start = end + 1;
1413 } while (true);
1414
Dmitry Lomov891f5402017-07-19 12:26:39 +02001415 return string();
1416}
1417
Laszlo Csomor087fea12019-03-07 07:19:13 -08001418static string LocateBashMaybe() {
Dmitry Lomov891f5402017-07-19 12:26:39 +02001419 string msys_bash = GetMsysBash();
Laszlo Csomor087fea12019-03-07 07:19:13 -08001420 return msys_bash.empty() ? GetBinaryFromPath("bash.exe") : msys_bash;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001421}
1422
Laszlo Csomor28841a62019-02-01 01:11:15 -08001423string DetectBashAndExportBazelSh() {
Laszlo Csomor0d107492019-03-13 09:04:27 -07001424 string bash = blaze::GetPathEnv("BAZEL_SH");
Laszlo Csomor28841a62019-02-01 01:11:15 -08001425 if (!bash.empty()) {
1426 return bash;
1427 }
Dmitry Lomov891f5402017-07-19 12:26:39 +02001428
1429 uint64_t start = blaze::GetMillisecondsMonotonic();
1430
Laszlo Csomor087fea12019-03-07 07:19:13 -08001431 bash = LocateBashMaybe();
Dmitry Lomov891f5402017-07-19 12:26:39 +02001432 uint64_t end = blaze::GetMillisecondsMonotonic();
Laszlo Csomor087fea12019-03-07 07:19:13 -08001433 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 Lomov5a0d6ff2017-08-22 14:51:49 +02001439 // Set process environment variable.
Dmitry Lomov891f5402017-07-19 12:26:39 +02001440 blaze::SetEnv("BAZEL_SH", bash);
Laszlo Csomor28841a62019-02-01 01:11:15 -08001441 }
Laszlo Csomor28841a62019-02-01 01:11:15 -08001442
Laszlo Csomor087fea12019-03-07 07:19:13 -08001443 return bash;
Dmitry Lomov891f5402017-07-19 12:26:39 +02001444}
1445
Yun Peng213a52a2017-09-14 11:11:48 +02001446void 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001456} // namespace blaze