aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Laszlo Csomor <laszlocsomor@google.com>2016-09-28 15:02:13 +0000
committerGravatar Yun Peng <pcloudy@google.com>2016-09-28 16:06:31 +0000
commit8896dad21f6993d4b9fd74d3a332143e75080d06 (patch)
tree04b4f5fddeeaf96feecd04421328f9980437ee81
parentad1a77e296fe5f05624109f3af04c5bc3257e9b6 (diff)
Windows, native: add windows_msvc config_setting
Update `select` statements in BUILD files with the new config_setting. This is a first step on a long path that leads to us being able to compile bazel on Windows with --cpu=x64_windows_msvc. Needless to say, we're not there yet. Tested: on Linux, Darwin, Windows/MSYS -- MOS_MIGRATED_REVID=134534613
-rw-r--r--scripts/packages/BUILD1
-rw-r--r--src/BUILD7
-rw-r--r--src/main/cpp/BUILD3
-rw-r--r--src/main/cpp/blaze_util_msvc.cc783
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD1
-rw-r--r--src/main/tools/BUILD1
-rw-r--r--src/test/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/repository/BUILD18
-rw-r--r--third_party/ijar/BUILD3
9 files changed, 812 insertions, 7 deletions
diff --git a/scripts/packages/BUILD b/scripts/packages/BUILD
index fc70dd49ca..44dc15a9fe 100644
--- a/scripts/packages/BUILD
+++ b/scripts/packages/BUILD
@@ -8,6 +8,7 @@ filegroup(
name = "packages",
srcs = select({
"//src:windows": [],
+ "//src:windows_msvc": [],
"//src:darwin": [
":install.sh",
":generate-package-info",
diff --git a/src/BUILD b/src/BUILD
index c13767ddad..29054f6ad0 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -9,6 +9,7 @@ filegroup(
":darwin": ["//src/main/native:libunix.dylib"],
":darwin_x86_64": ["//src/main/native:libunix.dylib"],
":windows": ["//src/main/native:windows_jni.dll"],
+ ":windows_msvc": ["//src/main/native:windows_jni.dll"],
"//conditions:default": ["//src/main/native:libunix.so"],
}),
visibility = [
@@ -273,6 +274,12 @@ config_setting(
visibility = ["//visibility:public"],
)
+config_setting(
+ name = "windows_msvc",
+ values = {"cpu": "x64_windows_msvc"},
+ visibility = ["//visibility:public"],
+)
+
filegroup(
name = "srcs",
srcs = glob(["**"]) + [
diff --git a/src/main/cpp/BUILD b/src/main/cpp/BUILD
index 64de8eb688..b52b292d05 100644
--- a/src/main/cpp/BUILD
+++ b/src/main/cpp/BUILD
@@ -24,6 +24,9 @@ cc_library(
"//src:windows": [
"blaze_util_mingw.cc",
],
+ "//src:windows_msvc": [
+ "blaze_util_msvc.cc",
+ ],
"//conditions:default": [
"blaze_util_linux.cc",
"blaze_util_posix.cc",
diff --git a/src/main/cpp/blaze_util_msvc.cc b/src/main/cpp/blaze_util_msvc.cc
new file mode 100644
index 0000000000..9136c61a5b
--- /dev/null
+++ b/src/main/cpp/blaze_util_msvc.cc
@@ -0,0 +1,783 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h> // errno, ENAMETOOLONG
+#include <limits.h>
+#include <string.h> // strerror
+#include <sys/cygwin.h>
+#include <sys/socket.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+
+#include <windows.h>
+
+#include <cstdlib>
+#include <cstdio>
+#include <thread> // NOLINT (to slience Google-internal linter)
+
+#include "src/main/cpp/blaze_util.h"
+#include "src/main/cpp/blaze_util_platform.h"
+#include "src/main/cpp/util/errors.h"
+#include "src/main/cpp/util/exit_code.h"
+#include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/strings.h"
+
+namespace blaze {
+
+using blaze_util::die;
+using blaze_util::pdie;
+using std::string;
+using std::vector;
+
+static void PrintError(const string& op) {
+ DWORD last_error = ::GetLastError();
+ if (last_error == 0) {
+ return;
+ }
+
+ char* message_buffer;
+ size_t size = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ last_error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &message_buffer,
+ 0,
+ NULL);
+
+ fprintf(stderr, "ERROR: %s: %s (%d)\n",
+ op.c_str(), message_buffer, last_error);
+ LocalFree(message_buffer);
+}
+
+void WarnFilesystemType(const string& output_base) {
+}
+
+string GetSelfPath() {
+ char buffer[PATH_MAX] = {};
+ if (!GetModuleFileName(0, buffer, sizeof(buffer))) {
+ pdie(255, "Error %u getting executable file name\n", GetLastError());
+ }
+ return string(buffer);
+}
+
+string GetOutputRoot() {
+ char* tmpdir = getenv("TMPDIR");
+ if (tmpdir == 0 || strlen(tmpdir) == 0) {
+ return "/var/tmp";
+ } else {
+ return string(tmpdir);
+ }
+}
+
+uint64_t MonotonicClock() {
+ struct timespec ts = {};
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+}
+
+uint64_t ProcessClock() {
+ struct timespec ts = {};
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+}
+
+void SetScheduling(bool batch_cpu_scheduling, int io_nice_level) {
+ // TODO(bazel-team): There should be a similar function on Windows.
+}
+
+string GetProcessCWD(int pid) {
+ char server_cwd[PATH_MAX] = {};
+ if (readlink(
+ ("/proc/" + ToString(pid) + "/cwd").c_str(),
+ server_cwd, sizeof(server_cwd)) < 0) {
+ return "";
+ }
+
+ return string(server_cwd);
+}
+
+bool IsSharedLibrary(const string &filename) {
+ return blaze_util::ends_with(filename, ".dll");
+}
+
+string GetDefaultHostJavabase() {
+ const char *javahome = getenv("JAVA_HOME");
+ if (javahome == NULL) {
+ die(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "Error: JAVA_HOME not set.");
+ }
+ return javahome;
+}
+
+namespace {
+void ReplaceAll(
+ std::string* s, const std::string& pattern, const std::string with) {
+ size_t pos = 0;
+ while (true) {
+ size_t pos = s->find(pattern, pos);
+ if (pos == std::string::npos) return;
+ *s = s->replace(pos, pattern.length(), with);
+ pos += with.length();
+ }
+}
+
+// Max command line length is per CreateProcess documentation
+// (https://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx)
+//
+// Quoting rules are described here:
+// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
+
+static const int MAX_CMDLINE_LENGTH = 32768;
+
+struct CmdLine {
+ char cmdline[MAX_CMDLINE_LENGTH];
+};
+static void CreateCommandLine(CmdLine* result, const string& exe,
+ const vector<string>& args_vector) {
+ string cmdline;
+ bool first = true;
+ for (const auto& s : args_vector) {
+ if (first) {
+ first = false;
+ // Skip first argument, instead use quoted executable name with ".exe"
+ // suffix.
+ cmdline.append("\"");
+ cmdline.append(exe);
+ cmdline.append(".exe");
+ cmdline.append("\"");
+ continue;
+ } else {
+ cmdline.append(" ");
+ }
+
+ bool has_space = s.find(" ") != string::npos;
+
+ if (has_space) {
+ cmdline.append("\"");
+ }
+
+ std::string::const_iterator it = s.begin();
+ while (it != s.end()) {
+ char ch = *it++;
+ switch (ch) {
+ case '"':
+ // Escape double quotes
+ cmdline.append("\\\"");
+ break;
+
+ case '\\':
+ if (it == s.end()) {
+ // Backslashes at the end of the string are quoted if we add quotes
+ cmdline.append(has_space ? "\\\\" : "\\");
+ } else {
+ // Backslashes everywhere else are quoted if they are followed by a
+ // quote or a backslash
+ cmdline.append(*it == '"' || *it == '\\' ? "\\\\" : "\\");
+ }
+ break;
+
+ default:
+ cmdline.append(1, ch);
+ }
+ }
+
+ if (has_space) {
+ cmdline.append("\"");
+ }
+ }
+
+ if (cmdline.size() >= MAX_CMDLINE_LENGTH) {
+ pdie(blaze_exit_code::INTERNAL_ERROR,
+ "Command line too long: %s", cmdline.c_str());
+ }
+
+ // Copy command line into a mutable buffer.
+ // CreateProcess is allowed to mutate its command line argument.
+ strncpy(result->cmdline, cmdline.c_str(), MAX_CMDLINE_LENGTH - 1);
+ result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
+}
+
+} // namespace
+
+string RunProgram(
+ const string& exe, const vector<string>& args_vector) {
+ SECURITY_ATTRIBUTES sa = {0};
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ HANDLE pipe_read, pipe_write;
+ if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreatePipe");
+ }
+
+ if (!SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "SetHandleInformation");
+ }
+
+ PROCESS_INFORMATION processInfo = {0};
+ STARTUPINFO startupInfo = {0};
+
+ startupInfo.hStdError = pipe_write;
+ startupInfo.hStdOutput = pipe_write;
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ CmdLine cmdline;
+ CreateCommandLine(&cmdline, exe, args_vector);
+
+ bool ok = CreateProcess(
+ NULL, // _In_opt_ LPCTSTR lpApplicationName,
+ // _Inout_opt_ LPTSTR lpCommandLine,
+ cmdline.cmdline,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ true, // _In_ BOOL bInheritHandles,
+ 0, // _In_ DWORD dwCreationFlags,
+ NULL, // _In_opt_ LPVOID lpEnvironment,
+ NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
+ &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
+ &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+
+ if (!ok) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "RunProgram/CreateProcess: Error %d while executing %s",
+ GetLastError(), cmdline.cmdline);
+ }
+
+ CloseHandle(pipe_write);
+ std::string result = "";
+ DWORD bytes_read;
+ CHAR buf[1024];
+
+ for (;;) {
+ ok = ::ReadFile(pipe_read, buf, 1023, &bytes_read, NULL);
+ if (!ok || bytes_read == 0) {
+ break;
+ }
+ buf[bytes_read] = 0;
+ result = result + buf;
+ }
+
+ CloseHandle(pipe_read);
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+
+ return result;
+}
+
+// If we pass DETACHED_PROCESS to CreateProcess(), cmd.exe appropriately
+// returns the command prompt when the client terminates. msys2, however, in
+// its infinite wisdom, waits until the *server* terminates and cannot be
+// convinced otherwise.
+//
+// So, we first pretend to be a POSIX daemon so that msys2 knows about our
+// intentions and *then* we call CreateProcess(). Life ain't easy.
+static bool DaemonizeOnWindows() {
+ if (fork() > 0) {
+ // We are the original client process.
+ return true;
+ }
+
+ if (fork() > 0) {
+ // We are the child of the original client process. Terminate so that the
+ // actual server is not a child process of the client.
+ exit(0);
+ }
+
+ setsid();
+ // Contrary to the POSIX version, we are not closing the three standard file
+ // descriptors here. CreateProcess() will take care of that and it's useful
+ // to see the error messages in ExecuteDaemon() on the console of the client.
+ return false;
+}
+
+// Keeping an eye on the server process on Windows is not implemented yet.
+// TODO(lberki): Implement this, because otherwise if we can't start up a server
+// process, the client will hang until it times out.
+class DummyBlazeServerStartup : public BlazeServerStartup {
+ public:
+ DummyBlazeServerStartup() {}
+ virtual ~DummyBlazeServerStartup() {}
+ virtual bool IsStillAlive() { return true; }
+};
+
+void ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
+ const string& daemon_output, const string& server_dir,
+ BlazeServerStartup** server_startup) {
+ if (DaemonizeOnWindows()) {
+ // We are the client process
+ *server_startup = new DummyBlazeServerStartup();
+ return;
+ }
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ // We redirect stdout and stderr by telling CreateProcess to use a file handle
+ // we open below and these handles must be inheriatable
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ HANDLE output_file = CreateFile(
+ ConvertPath(daemon_output).c_str(), // lpFileName
+ GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
+ // So that the file can be read while the server is running
+ FILE_SHARE_READ, // dwShareMode
+ &sa, // lpSecurityAttributes
+ CREATE_ALWAYS, // dwCreationDisposition
+ FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
+ NULL); // hTemplateFile
+
+ if (output_file == INVALID_HANDLE_VALUE) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreateFile");
+ }
+
+ HANDLE pipe_read, pipe_write;
+ if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreatePipe");
+ }
+
+ if (!SetHandleInformation(pipe_write, HANDLE_FLAG_INHERIT, 0)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "SetHandleInformation");
+ }
+
+ PROCESS_INFORMATION processInfo = {0};
+ STARTUPINFO startupInfo = {0};
+
+ startupInfo.hStdInput = pipe_read;
+ startupInfo.hStdError = output_file;
+ startupInfo.hStdOutput = output_file;
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ CmdLine cmdline;
+ CreateCommandLine(&cmdline, exe, args_vector);
+
+ // Propagate BAZEL_SH environment variable to a sub-process.
+ // todo(dslomov): More principled approach to propagating
+ // environment variables.
+ SetEnvironmentVariable("BAZEL_SH", getenv("BAZEL_SH"));
+
+ bool ok = CreateProcess(
+ NULL, // _In_opt_ LPCTSTR lpApplicationName,
+ // _Inout_opt_ LPTSTR lpCommandLine,
+ cmdline.cmdline,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ TRUE, // _In_ BOOL bInheritHandles,
+ // _In_ DWORD dwCreationFlags,
+ DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
+ NULL, // _In_opt_ LPVOID lpEnvironment,
+ NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
+ &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
+ &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+
+ if (!ok) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "ExecuteDaemon/CreateProcess: error %u executing: %s\n",
+ GetLastError(), cmdline.cmdline);
+ }
+
+ CloseHandle(output_file);
+ CloseHandle(pipe_write);
+ CloseHandle(pipe_read);
+
+ string pid_string = ToString(processInfo.dwProcessId);
+ string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
+ if (!WriteFile(pid_string, pid_file)) {
+ // Not a lot we can do if this fails
+ fprintf(stderr, "Cannot write PID file %s\n", pid_file.c_str());
+ }
+
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+
+ exit(0);
+}
+
+void BatchWaiterThread(HANDLE java_handle) {
+ WaitForSingleObject(java_handle, INFINITE);
+}
+
+static void MingwSignalHandler(int signum) {
+ // Java process will be terminated because we set the job to terminate if its
+ // handle is closed.
+ //
+ // Note that this is different how interruption is handled on Unix, where the
+ // Java process sets up a signal handler for SIGINT itself. That cannot be
+ // done on Windows without using native code, and it's better to have as
+ // little JNI as possible. The most important part of the cleanup after
+ // termination (killing all child processes) happens automatically on Windows
+ // anyway, since we put the batch Java process in its own job which does not
+ // allow breakaway processes.
+ exit(blaze_exit_code::ExitCode::INTERRUPTED);
+}
+
+// Returns whether assigning the given process to a job failed because nested
+// jobs are not available on the current system.
+static bool IsFailureDueToNestedJobsNotSupported(HANDLE process) {
+ BOOL is_in_job;
+ if (!IsProcessInJob(process, NULL, &is_in_job)) {
+ PrintError("IsProcessInJob()");
+ return false;
+ }
+
+ if (!is_in_job) {
+ // Not in a job.
+ return false;
+ }
+
+ OSVERSIONINFOEX version_info;
+ version_info.dwOSVersionInfoSize = sizeof(version_info);
+ if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) {
+ PrintError("GetVersionEx()");
+ return false;
+ }
+
+ return version_info.dwMajorVersion < 6
+ || version_info.dwMajorVersion == 6 && version_info.dwMinorVersion <= 1;
+}
+
+// Run the given program in the current working directory,
+// using the given argument vector.
+void ExecuteProgram(
+ const string& exe, const vector<string>& args_vector) {
+ CmdLine cmdline;
+ CreateCommandLine(&cmdline, exe, args_vector);
+
+ STARTUPINFO startupInfo = {0};
+ PROCESS_INFORMATION processInfo = {0};
+
+ // Propagate BAZEL_SH environment variable to a sub-process.
+ // todo(dslomov): More principled approach to propagating
+ // environment variables.
+ SetEnvironmentVariable("BAZEL_SH", getenv("BAZEL_SH"));
+
+ HANDLE job = CreateJobObject(NULL, NULL);
+ if (job == NULL) {
+ pdie(255, "Error %u while creating job\n", GetLastError());
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
+ job_info.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if (!SetInformationJobObject(
+ job,
+ JobObjectExtendedLimitInformation,
+ &job_info,
+ sizeof(job_info))) {
+ pdie(255, "Error %u while setting up job\n", GetLastError());
+ }
+
+ bool success = CreateProcess(
+ NULL, // _In_opt_ LPCTSTR lpApplicationName,
+ // _Inout_opt_ LPTSTR lpCommandLine,
+ cmdline.cmdline,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ true, // _In_ BOOL bInheritHandles,
+ // _In_ DWORD dwCreationFlags,
+ CREATE_NEW_PROCESS_GROUP // So that Ctrl-Break does not affect it
+ | CREATE_BREAKAWAY_FROM_JOB // We'll put it in a new job
+ | CREATE_SUSPENDED, // So that it doesn't start a new job itself
+ NULL, // _In_opt_ LPVOID lpEnvironment,
+ NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
+ &startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
+ &processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
+
+ if (!success) {
+ pdie(255, "ExecuteProgram/CreateProcess: error %u executing: %s\n",
+ GetLastError(), cmdline.cmdline);
+ }
+
+ if (!AssignProcessToJobObject(job, processInfo.hProcess)) {
+ if (!IsFailureDueToNestedJobsNotSupported(processInfo.hProcess)) {
+ pdie(255, "Error %u while assigning process to job\n", GetLastError());
+ }
+
+ // Otherwise, the OS doesn't support nested jobs so we'll just have to
+ // make do without.
+ }
+
+ // Now that we put the process in a new job object, we can start executing it
+ if (ResumeThread(processInfo.hThread) == -1) {
+ pdie(255, "Error %u while starting Java process\n", GetLastError());
+ }
+
+ // msys doesn't deliver signals while a Win32 call is pending so we need to
+ // do the blocking call in another thread
+ signal(SIGINT, MingwSignalHandler);
+ std::thread batch_waiter_thread([=]() {
+ BatchWaiterThread(processInfo.hProcess);
+ });
+
+ // The output base lock is held while waiting
+ batch_waiter_thread.join();
+ DWORD exit_code;
+ GetExitCodeProcess(processInfo.hProcess, &exit_code);
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ exit(exit_code);
+}
+
+string ListSeparator() { return ";"; }
+
+string ConvertPath(const string& path) {
+ // If the path looks like %USERPROFILE%/foo/bar, don't convert.
+ if (path.empty() || path[0] == '%') {
+ return path;
+ }
+ char* wpath = static_cast<char*>(cygwin_create_path(
+ CCP_POSIX_TO_WIN_A, static_cast<const void*>(path.c_str())));
+ string result(wpath);
+ free(wpath);
+ return result;
+}
+
+// Convert a Unix path list to Windows path list
+string ConvertPathList(const string& path_list) {
+ string w_list = "";
+ int start = 0;
+ int pos;
+ while ((pos = path_list.find(":", start)) != string::npos) {
+ w_list += ConvertPath(path_list.substr(start, pos - start)) + ";";
+ start = pos + 1;
+ }
+ if (start < path_list.size()) {
+ w_list += ConvertPath(path_list.substr(start));
+ }
+ return w_list;
+}
+
+string ConvertPathToPosix(const string& win_path) {
+ char* posix_path = static_cast<char*>(cygwin_create_path(
+ CCP_WIN_A_TO_POSIX, static_cast<const void*>(win_path.c_str())));
+ string result(posix_path);
+ free(posix_path);
+ return result;
+}
+
+// Cribbed from ntifs.h, not present in windows.h
+
+#define REPARSE_MOUNTPOINT_HEADER_SIZE 8
+
+typedef struct {
+ DWORD ReparseTag;
+ WORD ReparseDataLength;
+ WORD Reserved;
+ WORD SubstituteNameOffset;
+ WORD SubstituteNameLength;
+ WORD PrintNameOffset;
+ WORD PrintNameLength;
+ WCHAR PathBuffer[ANYSIZE_ARRAY];
+} REPARSE_MOUNTPOINT_DATA_BUFFER, *PREPARSE_MOUNTPOINT_DATA_BUFFER;
+
+HANDLE OpenDirectory(const string& path, bool readWrite) {
+ HANDLE result = ::CreateFile(
+ path.c_str(),
+ readWrite ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (result == INVALID_HANDLE_VALUE) {
+ PrintError("CreateFile(" + path + ")");
+ }
+
+ return result;
+}
+
+bool SymlinkDirectories(const string &posix_target, const string &posix_name) {
+ string target = ConvertPath(posix_target);
+ string name = ConvertPath(posix_name);
+
+ // Junctions are directories, so create one
+ if (!::CreateDirectory(name.c_str(), NULL)) {
+ PrintError("CreateDirectory(" + name + ")");
+ return false;
+ }
+
+ HANDLE directory = OpenDirectory(name, true);
+ if (directory == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ char reparse_buffer_bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ REPARSE_MOUNTPOINT_DATA_BUFFER* reparse_buffer =
+ reinterpret_cast<REPARSE_MOUNTPOINT_DATA_BUFFER *>(reparse_buffer_bytes);
+ memset(reparse_buffer_bytes, 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+
+ // non-parsed path prefix. Required for junction targets.
+ string prefixed_target = "\\??\\" + target;
+ int prefixed_target_length = ::MultiByteToWideChar(
+ CP_ACP,
+ 0,
+ prefixed_target.c_str(),
+ -1,
+ reparse_buffer->PathBuffer,
+ MAX_PATH);
+ if (prefixed_target_length == 0) {
+ PrintError("MultiByteToWideChar(" + prefixed_target + ")");
+ CloseHandle(directory);
+ return false;
+ }
+
+ // In addition to their target, junctions also have another string which
+ // tells which target to show to the user. mklink cuts of the \??\ part, so
+ // that's what we do, too.
+ int target_length = ::MultiByteToWideChar(
+ CP_UTF8,
+ 0,
+ target.c_str(),
+ -1,
+ reparse_buffer->PathBuffer + prefixed_target_length,
+ MAX_PATH);
+ if (target_length == 0) {
+ PrintError("MultiByteToWideChar(" + target + ")");
+ CloseHandle(directory);
+ return false;
+ }
+
+ reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ reparse_buffer->PrintNameOffset = prefixed_target_length * sizeof(WCHAR);
+ reparse_buffer->PrintNameLength = (target_length - 1) * sizeof(WCHAR);
+ reparse_buffer->SubstituteNameLength =
+ (prefixed_target_length - 1) * sizeof(WCHAR);
+ reparse_buffer->SubstituteNameOffset = 0;
+ reparse_buffer->Reserved = 0;
+ reparse_buffer->ReparseDataLength =
+ reparse_buffer->SubstituteNameLength +
+ reparse_buffer->PrintNameLength + 12;
+
+ DWORD bytes_returned;
+ bool result = ::DeviceIoControl(
+ directory,
+ FSCTL_SET_REPARSE_POINT,
+ reparse_buffer,
+ reparse_buffer->ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,
+ NULL,
+ 0,
+ &bytes_returned,
+ NULL);
+ if (!result) {
+ PrintError("DeviceIoControl(FSCTL_SET_REPARSE_POINT, " + name + ")");
+ }
+ CloseHandle(directory);
+ return result;
+}
+
+bool ReadDirectorySymlink(const string &posix_name, string* result) {
+ string name = ConvertPath(posix_name);
+ HANDLE directory = OpenDirectory(name, false);
+ if (directory == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ char reparse_buffer_bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ REPARSE_MOUNTPOINT_DATA_BUFFER* reparse_buffer =
+ reinterpret_cast<REPARSE_MOUNTPOINT_DATA_BUFFER *>(reparse_buffer_bytes);
+ memset(reparse_buffer_bytes, 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+
+ reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ DWORD bytes_returned;
+ bool ok = ::DeviceIoControl(
+ directory,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ 0,
+ reparse_buffer,
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
+ &bytes_returned,
+ NULL);
+ if (!ok) {
+ PrintError("DeviceIoControl(FSCTL_GET_REPARSE_POINT, " + name + ")");
+ }
+
+ CloseHandle(directory);
+ if (!ok) {
+ return false;
+ }
+
+ vector<char> print_name(reparse_buffer->PrintNameLength * sizeof(WCHAR) + 1);
+ int count = ::WideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ reparse_buffer->PathBuffer +
+ (reparse_buffer->PrintNameOffset / sizeof(WCHAR)),
+ reparse_buffer->PrintNameLength,
+ &print_name[0],
+ print_name.size(),
+ NULL,
+ NULL);
+ if (count == 0) {
+ PrintError("WideCharToMultiByte()");
+ *result = "";
+ return false;
+ } else {
+ *result = ConvertPathToPosix(&print_name[0]);
+ return true;
+ }
+}
+
+static bool IsAbsoluteWindowsPath(const string& p) {
+ if (p.size() < 3) {
+ return false;
+ }
+
+ if (p.substr(1, 2) == ":/") {
+ return true;
+ }
+
+ if (p.substr(1, 2) == ":\\") {
+ return true;
+ }
+
+ return false;
+}
+
+bool CompareAbsolutePaths(const string& a, const string& b) {
+ string a_real = IsAbsoluteWindowsPath(a) ? ConvertPathToPosix(a) : a;
+ string b_real = IsAbsoluteWindowsPath(b) ? ConvertPathToPosix(b) : b;
+ return a_real == b_real;
+}
+
+bool VerifyServerProcess(
+ int pid, const string& output_base, const string& install_base) {
+ // TODO(lberki): This might accidentally kill an unrelated process if the
+ // server died and the PID got reused.
+ return true;
+}
+
+bool KillServerProcess(int pid) {
+ HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+ if (process == NULL) {
+ // Cannot find the server process. Can happen if the PID file is stale.
+ return false;
+ }
+
+ bool result = TerminateProcess(process, /*uExitCode*/0);
+ if (!result) {
+ fprintf(stderr, "Cannot terminate server process with PID %d\n", pid);
+ }
+
+ CloseHandle(process);
+ return result;
+}
+
+// Not supported.
+void ExcludePathFromBackup(const string &path) {
+}
+
+} // namespace blaze
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index eb7c8d7828..f9fe01e718 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -63,6 +63,7 @@ java_library(
resources = select({
# todo(dslomov): Get rid of this whole library on Windows.
"//src:windows": [],
+ "//src:windows_msvc": [],
"//conditions:default": ["//src/main/native:libunix.so"],
}),
deps = [
diff --git a/src/main/tools/BUILD b/src/main/tools/BUILD
index 23a4e319d1..2c52acf6db 100644
--- a/src/main/tools/BUILD
+++ b/src/main/tools/BUILD
@@ -23,6 +23,7 @@ cc_binary(
"//src:darwin_x86_64": ["dummy-sandbox.c"],
"//src:freebsd": ["dummy-sandbox.c"],
"//src:windows": ["dummy-sandbox.c"],
+ "//src:windows_msvc": ["dummy-sandbox.c"],
"//conditions:default": [
"linux-sandbox.cc",
"linux-sandbox.h",
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index c676d7d49a..67efd8385e 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -78,6 +78,7 @@ java_library(
]),
data = select({
"//src:windows": [],
+ "//src:windows_msvc": [],
"//conditions:default": [
"//src/main/native:libunix.dylib",
"//src/main/native:libunix.so",
@@ -211,6 +212,7 @@ java_test(
":MockSubprocess_deploy.jar",
] + select({
"//src:windows": ["//src/main/native:windows_jni.dll"],
+ "//src:windows_msvc": ["//src/main/native:windows_jni.dll"],
"//conditions:default": [
"//src/main/native:libunix.dylib",
"//src/main/native:libunix.so",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
index 9086a6be91..530fd558ba 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
@@ -7,13 +7,9 @@ filegroup(
java_test(
name = "RepositoryTests",
srcs = select({
- "//src:windows": glob(
- ["*.java"],
- exclude = ["CompressedTarFunctionTest.java"],
- ),
- "//conditions:default": glob(
- ["*.java"],
- ),
+ "//src:windows": [":RepositoryTests_windows_srcs"],
+ "//src:windows_msvc": [":RepositoryTests_windows_srcs"],
+ "//conditions:default": glob(["*.java"]),
}),
data = ["test_decompress_archive.tar.gz"],
tags = ["rules"],
@@ -42,3 +38,11 @@ java_test(
"//third_party/protobuf",
],
)
+
+filegroup(
+ name = "RepositoryTests_windows_srcs",
+ srcs = glob(
+ ["*.java"],
+ exclude = ["CompressedTarFunctionTest.java"],
+ ),
+)
diff --git a/third_party/ijar/BUILD b/third_party/ijar/BUILD
index 9abfd4d46c..63d1182e63 100644
--- a/third_party/ijar/BUILD
+++ b/third_party/ijar/BUILD
@@ -15,6 +15,9 @@ cc_library(
"//src:windows": [
"mapped_file_windows.cc",
],
+ "//src:windows_msvc": [
+ "mapped_file_windows.cc",
+ ],
"//conditions:default": [
"mapped_file_unix.cc",
],