Windows, JNI: create process from relative path
The JNI library now supports creating processes
from a relative path.
The path is absolutized (relative to the current
directory) and normalized if necessary.
With this, we can "bazel run" tests with
--incompatible_windows_bashless_run_command and
--incompatible_windows_native_test_wrapper (but
not yet with
--noincompatible_windows_native_test_wrapper).
Closes #8476.
PiperOrigin-RevId: 250445511
diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc
index b7fb4b3..cc79ead 100644
--- a/src/main/native/windows/file.cc
+++ b/src/main/native/windows/file.cc
@@ -45,14 +45,6 @@
return bazel::windows::HasUncPrefix(path.c_str()) ? path.substr(4) : path;
}
-bool HasDriveSpecifierPrefix(const wstring& p) {
- if (HasUncPrefix(p.c_str())) {
- return p.size() >= 7 && iswalpha(p[4]) && p[5] == ':' && p[6] == '\\';
- } else {
- return p.size() >= 3 && iswalpha(p[0]) && p[1] == ':' && p[2] == '\\';
- }
-}
-
bool IsAbsoluteNormalizedWindowsPath(const wstring& p) {
if (p.empty()) {
return false;
@@ -64,7 +56,7 @@
return false;
}
- return HasDriveSpecifierPrefix(p) && p.find(L".\\") != 0 &&
+ return HasDriveSpecifierPrefix(p.c_str()) && p.find(L".\\") != 0 &&
p.find(L"\\.\\") == wstring::npos && p.find(L"\\.") != p.size() - 2 &&
p.find(L"..\\") != 0 && p.find(L"\\..\\") == wstring::npos &&
p.find(L"\\..") != p.size() - 3;
@@ -773,5 +765,21 @@
std::wstring Normalize(const std::wstring& p) { return NormalizeImpl(p); }
+bool GetCwd(std::wstring* result, DWORD* err_code) {
+ // Maximum path is 32767 characters, with null terminator that is 0x8000.
+ static constexpr DWORD kMaxPath = 0x8000;
+ WCHAR buf[kMaxPath];
+ DWORD len = GetCurrentDirectoryW(kMaxPath, buf);
+ if (len > 0 && len < kMaxPath) {
+ *result = buf;
+ return true;
+ } else {
+ if (err_code) {
+ *err_code = GetLastError();
+ }
+ return false;
+ }
+}
+
} // namespace windows
} // namespace bazel
diff --git a/src/main/native/windows/file.h b/src/main/native/windows/file.h
index 5b73c3e..f8651e6 100644
--- a/src/main/native/windows/file.h
+++ b/src/main/native/windows/file.h
@@ -45,6 +45,15 @@
(path[2] == 'L' || path[2] == 'l');
}
+template <typename char_type>
+bool HasDriveSpecifierPrefix(const char_type* p) {
+ if (HasUncPrefix(p)) {
+ return iswalpha(p[4]) && p[5] == ':' && (p[6] == '\\' || p[6] == '/');
+ } else {
+ return iswalpha(p[0]) && p[1] == ':' && (p[2] == '\\' || p[2] == '/');
+ }
+}
+
std::wstring AddUncPrefixMaybe(const std::wstring& path);
std::wstring RemoveUncPrefixMaybe(const std::wstring& path);
@@ -184,6 +193,8 @@
std::string Normalize(const std::string& p);
std::wstring Normalize(const std::wstring& p);
+bool GetCwd(std::wstring* result, DWORD* err_code);
+
} // namespace windows
} // namespace bazel
diff --git a/src/main/native/windows/util.cc b/src/main/native/windows/util.cc
index 0aac172..0e46dee 100644
--- a/src/main/native/windows/util.cc
+++ b/src/main/native/windows/util.cc
@@ -27,6 +27,8 @@
#include <sstream>
#include <string>
+#include "src/main/native/windows/file.h"
+
namespace bazel {
namespace windows {
@@ -214,8 +216,7 @@
return MakeErrorMessage(WSTR(__FILE__), __LINE__, L"AsShortPath", path,
L"path is just a file name but too long");
}
- if (HasSeparator(path) &&
- !(isalpha(path[0]) && path[1] == L':' && IsSeparator(path[2]))) {
+ if (HasSeparator(path) && !HasDriveSpecifierPrefix(path.c_str())) {
return MakeErrorMessage(WSTR(__FILE__), __LINE__, L"AsShortPath", path,
L"path is not absolute");
}
@@ -261,12 +262,26 @@
return L"";
}
-wstring AsExecutablePathForCreateProcess(const wstring& path, wstring* result) {
+wstring AsExecutablePathForCreateProcess(wstring path, wstring* result) {
if (path.empty()) {
return MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"AsExecutablePathForCreateProcess", path,
L"path should not be empty");
}
+ if (IsSeparator(path[0])) {
+ return MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"AsExecutablePathForCreateProcess", path,
+ L"path is absolute without a drive letter");
+ }
+ if (HasSeparator(path) && !HasDriveSpecifierPrefix(path.c_str())) {
+ wstring cwd;
+ DWORD err;
+ if (!GetCwd(&cwd, &err)) {
+ return MakeErrorMessage(WSTR(__FILE__), __LINE__,
+ L"AsExecutablePathForCreateProcess", path, err);
+ }
+ path = cwd + L"\\" + path;
+ }
wstring error = AsShortPath(path, result);
if (!error.empty()) {
return MakeErrorMessage(WSTR(__FILE__), __LINE__,
diff --git a/src/main/native/windows/util.h b/src/main/native/windows/util.h
index 2380314..16357a1 100644
--- a/src/main/native/windows/util.h
+++ b/src/main/native/windows/util.h
@@ -148,7 +148,7 @@
// `path`, and if that succeeds and the result is at most MAX_PATH - 1 long (not
// including null terminator), then that will be the result (plus quotes).
// Otherwise this function fails and returns an error message.
-wstring AsExecutablePathForCreateProcess(const wstring& path, wstring* result);
+wstring AsExecutablePathForCreateProcess(wstring path, wstring* result);
} // namespace windows
} // namespace bazel
diff --git a/src/test/native/windows/util_test.cc b/src/test/native/windows/util_test.cc
index 9f6a3ca..857518c 100644
--- a/src/test/native/windows/util_test.cc
+++ b/src/test/native/windows/util_test.cc
@@ -256,7 +256,6 @@
ASSERT_SHORTENING_FAILS(L"\"cmd.exe\"", L"path should not be quoted");
ASSERT_SHORTENING_FAILS(L"/dev/null", L"path is absolute without a drive");
ASSERT_SHORTENING_FAILS(L"/usr/bin/bash", L"path is absolute without a");
- ASSERT_SHORTENING_FAILS(L"foo\\bar.exe", L"path is not absolute");
ASSERT_SHORTENING_FAILS(L"foo\\..\\bar.exe", L"path is not normalized");
ASSERT_SHORTENING_FAILS(L"\\bar.exe", L"path is absolute");
@@ -266,6 +265,13 @@
}
dummy += L".exe";
ASSERT_SHORTENING_FAILS(dummy.c_str(), L"a file name but too long");
+
+ // Relative paths are fine, they are absolutized.
+ std::wstring rel(L"foo\\bar.exe");
+ std::wstring actual;
+ EXPECT_EQ(AsExecutablePathForCreateProcess(rel, &actual), L"");
+ EXPECT_GT(actual.size(), rel.size());
+ EXPECT_EQ(actual.rfind(rel), actual.size() - rel.size() - 1);
}
TEST(WindowsUtilTest, TestAsExecutablePathForCreateProcessConversions) {