diff options
author | 2017-07-10 13:30:04 +0200 | |
---|---|---|
committer | 2017-07-10 14:37:35 +0200 | |
commit | 8a9be26492651afd8c71e5da98751e36d948b4e5 (patch) | |
tree | 7ad723c34dbba601d987a5201df49fd6483648d2 /src/tools/android/java/com/google/devtools/build/android | |
parent | 3fbd7c43fe329c7052b7105d6941205680fb1a3d (diff) |
Windows, Android BusyBox: create JunctionCreator
Introduce the JunctionCreator classes that the
Android BusyBox can use to work around path length
limitations on Windows.
See https://github.com/bazelbuild/bazel/issues/3264
Change-Id: Ia5ee39f0635dcc2690ffb1755dc56d21e7bc7536
PiperOrigin-RevId: 161378422
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android')
5 files changed, 206 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD b/src/tools/android/java/com/google/devtools/build/android/BUILD index d3bc605e3e..5f1de9dbc3 100644 --- a/src/tools/android/java/com/google/devtools/build/android/BUILD +++ b/src/tools/android/java/com/google/devtools/build/android/BUILD @@ -9,6 +9,7 @@ filegroup( "classes_deploy.jar", "//src/tools/android/java/com/google/devtools/build/android/desugar:embedded_tools", "//src/tools/android/java/com/google/devtools/build/android/incrementaldeployment:embedded_tools", + "//src/tools/android/java/com/google/devtools/build/android/junctions:embedded_tools", "//src/tools/android/java/com/google/devtools/build/android/proto:srcs", ], ) @@ -38,6 +39,7 @@ java_library( "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:option_filters_java_proto", "//src/main/protobuf:package_manifest_java_proto", + "//src/tools/android/java/com/google/devtools/build/android/junctions", "//src/tools/android/java/com/google/devtools/build/android/proto:serialize_format_java_pb", "//src/tools/android/java/com/google/devtools/build/android/resources", "//third_party:android_common_25_0_0", @@ -58,6 +60,7 @@ filegroup( "//src/tools/android/java/com/google/devtools/build/android/ideinfo:srcs", "//src/tools/android/java/com/google/devtools/build/android/idlclass:srcs", "//src/tools/android/java/com/google/devtools/build/android/incrementaldeployment:srcs", + "//src/tools/android/java/com/google/devtools/build/android/junctions:srcs", "//src/tools/android/java/com/google/devtools/build/android/proto:srcs", "//src/tools/android/java/com/google/devtools/build/android/resources:srcs", "//src/tools/android/java/com/google/devtools/build/android/ziputils:srcs", diff --git a/src/tools/android/java/com/google/devtools/build/android/junctions/BUILD b/src/tools/android/java/com/google/devtools/build/android/junctions/BUILD new file mode 100644 index 0000000000..d54aecd406 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/junctions/BUILD @@ -0,0 +1,47 @@ +package(default_visibility = ["//visibility:private"]) + +package_group( + name = "android-prod", + packages = [ + "//src/tools/android/java/com/google/devtools/build/android", + ], +) + +package_group( + name = "android-tests", + packages = [ + "//src/test/java/com/google/devtools/build/android/...", + ], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = [":android-prod"], +) + +filegroup( + name = "embedded_tools", + srcs = glob(["*.java"]), + visibility = [":android-prod"], +) + +java_library( + name = "junctions", + srcs = glob(["*.java"]), + data = select({ + "//src:windows": ["//src/main/native/windows:windows_jni"], + "//src:windows_msvc": ["//src/main/native/windows:windows_jni"], + "//src:windows_msys": ["//src/main/native/windows:windows_jni"], + "//conditions:default": [], + }), + visibility = [ + ":android-prod", + ":android-tests", + ], + deps = [ + "//src/main/java/com/google/devtools/build/lib/windows/jni:file", + "//third_party:guava", + "//third_party:jsr305", + ], +) diff --git a/src/tools/android/java/com/google/devtools/build/android/junctions/JunctionCreator.java b/src/tools/android/java/com/google/devtools/build/android/junctions/JunctionCreator.java new file mode 100644 index 0000000000..fe82e34180 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/junctions/JunctionCreator.java @@ -0,0 +1,46 @@ +// Copyright 2017 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. +package com.google.devtools.build.android.junctions; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Path; +import javax.annotation.Nullable; + +/** + * Interface to create junctions (directory symlinks). + * + * <p>Junctions are directory symlinks on NTFS filesystems. They are useful on Windows, because + * creating them doesn't require any privileges, as opposed to the creation of file symlinks which + * does. + * + * <p>On Windows, Bazel and the Android BusyBox uses junctions to work around path length + * limitations of the Windows Shell and of tools like aapt.exe and the PNG cruncher. The limit is + * 260 characters for all paths. The filesystem supports longer paths than that, but the tools + * usually don't. To work around that limitation, we create junctions that have short paths but + * point to long paths (this is allowed). + * + * <p>On Linux/MacOS the junction creator may have a no-op implementation. + */ +public interface JunctionCreator extends Closeable { + /** + * Returns an equivalent path to `target`, which may or may not be the same as `target`. + * + * <p>Depending on the implementation, this method may return `target` itself, or may create a + * junction that points to `target` (if `target` is a directory) or the parent of it (if `target` + * is a file). + */ + @Nullable + public abstract Path create(@Nullable Path target) throws IOException; +} diff --git a/src/tools/android/java/com/google/devtools/build/android/junctions/NoopJunctionCreator.java b/src/tools/android/java/com/google/devtools/build/android/junctions/NoopJunctionCreator.java new file mode 100644 index 0000000000..5b061f3fbd --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/junctions/NoopJunctionCreator.java @@ -0,0 +1,30 @@ +// Copyright 2017 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. +package com.google.devtools.build.android.junctions; + +import java.io.IOException; +import java.nio.file.Path; +import javax.annotation.Nullable; + +/** A no-op JunctionCreator implementation that just returns the input path. */ +public final class NoopJunctionCreator implements JunctionCreator { + @Nullable + @Override + public Path create(@Nullable Path path) throws IOException { + return path; + } + + @Override + public void close() throws IOException {} +} diff --git a/src/tools/android/java/com/google/devtools/build/android/junctions/WindowsJunctionCreator.java b/src/tools/android/java/com/google/devtools/build/android/junctions/WindowsJunctionCreator.java new file mode 100644 index 0000000000..278b55d34c --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/junctions/WindowsJunctionCreator.java @@ -0,0 +1,80 @@ +// Copyright 2017 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. +package com.google.devtools.build.android.junctions; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.windows.jni.WindowsFileOperations; +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * Junction creator implementation for Windows. + * + * <p>Creates a junction (or uses a cached one) for a path. If the path is a directory, the junction + * points to it, and the returned path is the junction's path. If the path is a file, the junction + * points to its parent, and the returned path is the file's path through the junction. + * + * <p>The `close` method deletes all junctions that this object created, along with the `dir` + * directory where the junctions are created. The purpose of this is to avoid other methods (such as + * ScopedTemporaryDirectory.close) to traverse these junctions believing they are regular + * directories and deleting files in them that are actually outside of the directory tree. + */ +public final class WindowsJunctionCreator implements JunctionCreator { + private final Path dir; + private Map<Path, Path> paths; // allocated lazily, but semantically final + private int junctionIndex = 0; + + public WindowsJunctionCreator(Path dir) { + this.dir = Preconditions.checkNotNull(dir); + } + + @Nullable + public Path create(@Nullable Path path) throws IOException { + if (path == null) { + return null; + } + + if (paths == null) { + paths = new HashMap<>(); + } + path = path.toAbsolutePath(); + if (path.toFile().isDirectory()) { + Path link = paths.get(path); + if (link == null) { + link = dir.resolve(Integer.toString(junctionIndex++)); + WindowsFileOperations.createJunction(link.toString(), path.toString()); + paths.put(path, link); + } + return link; + } + + Path parent = path.getParent(); + return (parent == null) ? path : create(parent).resolve(path.getFileName()); + } + + @Override + public void close() throws IOException { + // Delete all junctions, otherwise the temp directory deleter would follow them and delete files + // from directories they point to. + if (paths != null) { + for (Path link : paths.values()) { + link.toFile().delete(); + } + } + dir.toFile().delete(); + } +} |