Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/main/java/com/google/devtools/build/lib/vfs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ java_library(
srcs = OS_PATH_POLICY_SOURCES,
deps = [
"//src/main/java/com/google/devtools/build/lib/util:os",
"//src/main/java/com/google/devtools/build/lib/windows:file",
"//src/main/java/com/google/devtools/build/lib/windows:windows_short_path",
"//src/main/java/com/google/devtools/build/lib/windows:windows_path_operations",
"//third_party:guava",
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.windows.WindowsFileOperations;
import com.google.devtools.build.lib.windows.WindowsShortPath;
import com.google.devtools.build.lib.windows.WindowsPathOperations;
import java.io.IOException;

@VisibleForTesting
Expand Down Expand Up @@ -49,7 +48,7 @@ public String resolveShortPath(String path) {
return path;
}
try {
return WindowsFileOperations.getLongPath(path);
return WindowsPathOperations.getLongPath(path);
} catch (IOException e) {
return path;
}
Expand Down Expand Up @@ -97,7 +96,7 @@ public int needsToNormalize(String path) {
normalizationLevel = Math.max(normalizationLevel, NEEDS_NORMALIZE);
}
if (segmentHasShortPathChar) {
if (WindowsShortPath.isShortPath(path.substring(segmentBeginIndex, i))) {
if (WindowsPathOperations.isShortPath(path.substring(segmentBeginIndex, i))) {
normalizationLevel = Math.max(normalizationLevel, NEEDS_SHORT_PATH_NORMALIZATION);
}
}
Expand All @@ -111,7 +110,7 @@ public int needsToNormalize(String path) {
prevChar = c;
}
if (segmentHasShortPathChar) {
if (WindowsShortPath.isShortPath(path.substring(segmentBeginIndex))) {
if (WindowsPathOperations.isShortPath(path.substring(segmentBeginIndex))) {
normalizationLevel = Math.max(normalizationLevel, NEEDS_SHORT_PATH_NORMALIZATION);
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/com/google/devtools/build/lib/windows/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ filegroup(
java_11_library(
name = "file",
srcs = ["WindowsFileOperations.java"],
deps = ["//src/main/java/com/google/devtools/build/lib/jni"],
deps = [
":windows_path_operations",
"//src/main/java/com/google/devtools/build/lib/jni",
],
)

java_library(
Expand All @@ -26,8 +29,9 @@ java_library(
)

java_library(
name = "windows_short_path",
srcs = ["WindowsShortPath.java"],
name = "windows_path_operations",
srcs = ["WindowsPathOperations.java"],
deps = ["//src/main/java/com/google/devtools/build/lib/jni"],
)

java_library(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ private static native int nativeIsSymlinkOrJunction(
private static native int nativeGetChangeTime(
String path, boolean followReparsePoints, long[] result, String[] error);

private static native boolean nativeGetLongPath(String path, String[] result, String[] error);

private static native int nativeCreateJunction(String name, String target, String[] error);

private static native int nativeCreateSymlink(String name, String target, String[] error);
Expand All @@ -138,7 +136,7 @@ private static native int nativeReadSymlinkOrJunction(
public static boolean isSymlinkOrJunction(String path) throws IOException {
boolean[] result = new boolean[] {false};
String[] error = new String[] {null};
switch (nativeIsSymlinkOrJunction(asLongPath(path), result, error)) {
switch (nativeIsSymlinkOrJunction(WindowsPathOperations.asLongPath(path), result, error)) {
case IS_SYMLINK_OR_JUNCTION_SUCCESS:
return result[0];
case IS_SYMLINK_OR_JUNCTION_DOES_NOT_EXIST:
Expand All @@ -157,7 +155,8 @@ public static long getLastChangeTime(String path, boolean followReparsePoints)
throws IOException {
long[] result = new long[] {0};
String[] error = new String[] {null};
switch (nativeGetChangeTime(asLongPath(path), followReparsePoints, result, error)) {
switch (nativeGetChangeTime(
WindowsPathOperations.asLongPath(path), followReparsePoints, result, error)) {
case GET_CHANGE_TIME_SUCCESS:
return result[0];
case GET_CHANGE_TIME_DOES_NOT_EXIST:
Expand All @@ -171,46 +170,6 @@ public static long getLastChangeTime(String path, boolean followReparsePoints)
throw new IOException(String.format("Cannot get last change time of '%s': %s", path, error[0]));
}

/**
* Returns the long path associated with the input `path`.
*
* <p>This method resolves all 8dot3 style components of the path and returns the long format. For
* example, if the input is "C:/progra~1/micros~1" the result may be "C:\Program Files\Microsoft
* Visual Studio 14.0". The returned path is Windows-style in that it uses backslashes, even if
* the input uses forward slashes.
*
* <p>May return an UNC path if `path` or its resolution is sufficiently long.
*
* @throws IOException if the `path` is not found or some other I/O error occurs
*/
public static String getLongPath(String path) throws IOException {
String[] result = new String[] {null};
String[] error = new String[] {null};
if (nativeGetLongPath(asLongPath(path), result, error)) {
return removeUncPrefixAndUseSlashes(result[0]);
} else {
throw new IOException(error[0]);
}
}

/** Returns a Windows-style path suitable to pass to unicode WinAPI functions. */
static String asLongPath(String path) {
return !path.startsWith("\\\\?\\")
? ("\\\\?\\" + path.replace('/', '\\'))
: path.replace('/', '\\');
}

private static String removeUncPrefixAndUseSlashes(String p) {
if (p.length() >= 4
&& p.charAt(0) == '\\'
&& (p.charAt(1) == '\\' || p.charAt(1) == '?')
&& p.charAt(2) == '?'
&& p.charAt(3) == '\\') {
p = p.substring(4);
}
return p.replace('\\', '/');
}

/**
* Creates a junction at `name`, pointing to `target`.
*
Expand All @@ -222,7 +181,8 @@ private static String removeUncPrefixAndUseSlashes(String p) {
*/
public static void createJunction(String name, String target) throws IOException {
String[] error = new String[] {null};
switch (nativeCreateJunction(asLongPath(name), asLongPath(target), error)) {
switch (nativeCreateJunction(
WindowsPathOperations.asLongPath(name), WindowsPathOperations.asLongPath(target), error)) {
case CREATE_JUNCTION_SUCCESS:
return;
case CREATE_JUNCTION_TARGET_NAME_TOO_LONG:
Expand Down Expand Up @@ -250,7 +210,8 @@ public static void createJunction(String name, String target) throws IOException

public static void createSymlink(String name, String target) throws IOException {
String[] error = new String[] {null};
switch (nativeCreateSymlink(asLongPath(name), asLongPath(target), error)) {
switch (nativeCreateSymlink(
WindowsPathOperations.asLongPath(name), WindowsPathOperations.asLongPath(target), error)) {
case CREATE_SYMLINK_SUCCESS:
return;
case CREATE_SYMLINK_TARGET_IS_DIRECTORY:
Expand All @@ -267,10 +228,11 @@ public static void createSymlink(String name, String target) throws IOException
public static ReadSymlinkOrJunctionResult readSymlinkOrJunction(String name) {
String[] target = new String[] {null};
String[] error = new String[] {null};
switch (nativeReadSymlinkOrJunction(asLongPath(name), target, error)) {
switch (nativeReadSymlinkOrJunction(WindowsPathOperations.asLongPath(name), target, error)) {
case READ_SYMLINK_OR_JUNCTION_SUCCESS:
return new ReadSymlinkOrJunctionResult(
ReadSymlinkOrJunctionResult.Status.OK, removeUncPrefixAndUseSlashes(target[0]));
ReadSymlinkOrJunctionResult.Status.OK,
WindowsPathOperations.removeUncPrefixAndUseSlashes(target[0]));
case READ_SYMLINK_OR_JUNCTION_ACCESS_DENIED:
error[0] = "access is denied";
break;
Expand All @@ -295,7 +257,7 @@ public static ReadSymlinkOrJunctionResult readSymlinkOrJunction(String name) {

public static boolean deletePath(String path) throws IOException {
String[] error = new String[] {null};
int result = nativeDeletePath(asLongPath(path), error);
int result = nativeDeletePath(WindowsPathOperations.asLongPath(path), error);
switch (result) {
case DELETE_PATH_SUCCESS:
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@
// limitations under the License.
package com.google.devtools.build.lib.windows;

import com.google.devtools.build.lib.jni.JniLoader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Support functions for Windows short paths (eg. "C:/progra~1") */
public final class WindowsShortPath {
public final class WindowsPathOperations {

private WindowsPathOperations() {}

static {
JniLoader.loadJni();
}

// Properties of 8dot3 (DOS-style) short file names:
// - they are at most 11 characters long
Expand All @@ -41,4 +49,46 @@ public static boolean isShortPath(String segment) {
&& m.groupCount() >= 2
&& (m.group(1).length() + m.group(2).length()) < 8; // the "~" makes it at most 8
}

/**
* Returns the long path associated with the input `path`.
*
* <p>This method resolves all 8dot3 style components of the path and returns the long format. For
* example, if the input is "C:/progra~1/micros~1" the result may be "C:\Program Files\Microsoft
* Visual Studio 14.0". The returned path is Windows-style in that it uses backslashes, even if
* the input uses forward slashes.
*
* <p>May return an UNC path if `path` or its resolution is sufficiently long.
*
* @throws IOException if the `path` is not found or some other I/O error occurs
*/
public static String getLongPath(String path) throws IOException {
String[] result = new String[] {null};
String[] error = new String[] {null};
if (nativeGetLongPath(asLongPath(path), result, error)) {
return removeUncPrefixAndUseSlashes(result[0]);
} else {
throw new IOException(error[0]);
}
}

/** Returns a Windows-style path suitable to pass to unicode WinAPI functions. */
static String asLongPath(String path) {
return !path.startsWith("\\\\?\\")
? ("\\\\?\\" + path.replace('/', '\\'))
: path.replace('/', '\\');
}

static String removeUncPrefixAndUseSlashes(String p) {
if (p.length() >= 4
&& p.charAt(0) == '\\'
&& (p.charAt(1) == '\\' || p.charAt(1) == '?')
&& p.charAt(2) == '?'
&& p.charAt(3) == '\\') {
p = p.substring(4);
}
return p.replace('\\', '/');
}

private static native boolean nativeGetLongPath(String path, String[] result, String[] error);
}
46 changes: 23 additions & 23 deletions src/main/native/windows/file-jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,6 @@ Java_com_google_devtools_build_lib_windows_WindowsFileOperations_nativeGetChange
return static_cast<jint>(result);
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_devtools_build_lib_windows_WindowsFileOperations_nativeGetLongPath(
JNIEnv* env, jclass clazz, jstring path, jobjectArray result_holder,
jobjectArray error_msg_holder) {
std::unique_ptr<WCHAR[]> result;
std::wstring wpath(bazel::windows::GetJavaWstring(env, path));
std::wstring error(bazel::windows::GetLongPath(wpath.c_str(), &result));
if (!error.empty()) {
if (CanReportError(env, error_msg_holder)) {
ReportLastError(
bazel::windows::MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"nativeGetLongPath", wpath, error),
env, error_msg_holder);
}
return JNI_FALSE;
}
env->SetObjectArrayElement(
result_holder, 0,
env->NewString(reinterpret_cast<const jchar*>(result.get()),
wcslen(result.get())));
return JNI_TRUE;
}

extern "C" JNIEXPORT jint JNICALL
Java_com_google_devtools_build_lib_windows_WindowsFileOperations_nativeCreateJunction(
JNIEnv* env, jclass clazz, jstring name, jstring target,
Expand Down Expand Up @@ -182,3 +159,26 @@ Java_com_google_devtools_build_lib_windows_WindowsFileOperations_nativeDeletePat
}
return result;
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_devtools_build_lib_windows_WindowsPathOperations_nativeGetLongPath(
JNIEnv* env, jclass clazz, jstring path, jobjectArray result_holder,
jobjectArray error_msg_holder) {
std::unique_ptr<WCHAR[]> result;
std::wstring wpath(bazel::windows::GetJavaWstring(env, path));
std::wstring error(bazel::windows::GetLongPath(wpath.c_str(), &result));
if (!error.empty()) {
if (CanReportError(env, error_msg_holder)) {
ReportLastError(
bazel::windows::MakeErrorMessage(WSTR(__FILE__), __LINE__,
L"nativeGetLongPath", wpath, error),
env, error_msg_holder);
}
return JNI_FALSE;
}
env->SetObjectArrayElement(
result_holder, 0,
env->NewString(reinterpret_cast<const jchar*>(result.get()),
wcslen(result.get())));
return JNI_TRUE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/util/io",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//src/main/java/com/google/devtools/build/lib/windows:windows_short_path",
"//src/main/java/com/google/devtools/build/lib/windows:windows_path_operations",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//src/main/java/net/starlark/java/eval",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey;
import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createRepositoryMapping;

import com.google.devtools.build.lib.windows.WindowsShortPath;
import com.google.devtools.build.lib.windows.WindowsPathOperations;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -97,6 +97,6 @@ public void getCanonicalRepoName_isNotAWindowsShortPath() {
}

private static void assertNotAShortPath(String name) {
assertWithMessage("For %s", name).that(WindowsShortPath.isShortPath(name)).isFalse();
assertWithMessage("For %s", name).that(WindowsPathOperations.isShortPath(name)).isFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ java_test(
"//src/main/java/com/google/devtools/build/lib/windows",
"//src/main/java/com/google/devtools/build/lib/windows:file",
"//src/main/java/com/google/devtools/build/lib/windows:processes",
"//src/main/java/com/google/devtools/build/lib/windows:windows_short_path",
"//src/main/java/com/google/devtools/build/lib/windows:windows_path_operations",
"//src/test/java/com/google/devtools/build/lib/testutil",
"//src/test/java/com/google/devtools/build/lib/windows/util",
"//third_party:guava",
Expand Down
Loading
Loading