From cf583a38a76173029a232919cc25cf8f7a935790 Mon Sep 17 00:00:00 2001 From: Ivo List Date: Thu, 14 Jan 2021 16:47:29 +0000 Subject: [PATCH] Remove support for the Java singlejar implememntation Resolves #7365 --- scripts/ij.bazelproject | 1 - src/BUILD | 2 - .../java/com/google/testing/coverage/BUILD | 4 +- src/java_tools/singlejar/BUILD | 51 -- src/java_tools/singlejar/README | 2 - .../com/google/devtools/build/singlejar/BUILD | 102 --- .../build/singlejar/ConcatenateStrategy.java | 74 -- .../build/singlejar/CopyEntryFilter.java | 33 - .../singlejar/DefaultJarEntryFilter.java | 123 --- .../devtools/build/singlejar/JarUtils.java | 41 - .../Java8DesugarDepsJarEntryFilter.java | 144 ---- .../build/singlejar/JavaIoFileSystem.java | 48 -- .../build/singlejar/OptionFileExpander.java | 113 --- .../build/singlejar/PrefixListPathFilter.java | 40 - .../build/singlejar/SimpleFileSystem.java | 45 - .../devtools/build/singlejar/SingleJar.java | 469 ---------- .../build/singlejar/SingleJarWorker.java | 101 --- .../devtools/build/singlejar/ZipCombiner.java | 636 -------------- .../build/singlejar/ZipEntryFilter.java | 128 --- .../java/com/google/devtools/build/zip/BUILD | 7 +- .../com/google/devtools/build/singlejar/BUILD | 33 - .../singlejar/ConcatenateStrategyTest.java | 72 -- .../build/singlejar/CopyEntryFilterTest.java | 36 - .../singlejar/DefaultJarEntryFilterTest.java | 109 --- .../devtools/build/singlejar/FakeZipFile.java | 275 ------ .../build/singlejar/MockSimpleFileSystem.java | 100 --- .../singlejar/OptionFileExpanderTest.java | 80 -- .../singlejar/PrefixListPathFilterTest.java | 54 -- .../build/singlejar/RecordingCallback.java | 56 -- .../build/singlejar/SingleJarTest.java | 648 -------------- .../build/singlejar/SingleJarTests.java | 27 - .../singlejar/SlowConcatenateStrategy.java | 45 - .../build/singlejar/ZipCombinerTest.java | 800 ------------------ .../devtools/build/singlejar/ZipFactory.java | 106 --- .../devtools/build/singlejar/ZipTester.java | 412 --------- .../devtools/build/android/desugar/BUILD | 55 +- 36 files changed, 33 insertions(+), 5039 deletions(-) delete mode 100644 src/java_tools/singlejar/BUILD delete mode 100644 src/java_tools/singlejar/README delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/BUILD delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ConcatenateStrategy.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/CopyEntryFilter.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/DefaultJarEntryFilter.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JarUtils.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/Java8DesugarDepsJarEntryFilter.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JavaIoFileSystem.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/OptionFileExpander.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/PrefixListPathFilter.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SimpleFileSystem.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJar.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJarWorker.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipCombiner.java delete mode 100644 src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipEntryFilter.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/BUILD delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ConcatenateStrategyTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/CopyEntryFilterTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/DefaultJarEntryFilterTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/FakeZipFile.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/OptionFileExpanderTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/PrefixListPathFilterTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/RecordingCallback.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTests.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SlowConcatenateStrategy.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipFactory.java delete mode 100644 src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipTester.java diff --git a/scripts/ij.bazelproject b/scripts/ij.bazelproject index d4991199445512..e35d2de84abc4d 100644 --- a/scripts/ij.bazelproject +++ b/scripts/ij.bazelproject @@ -16,7 +16,6 @@ targets: //src/java_tools/buildjar/javatests/... //src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner //src/java_tools/junitrunner/javatests/... - //src/java_tools/singlejar:SingleJar //src/test/... //src/tools/remote/... //src/tools/starlark/... diff --git a/src/BUILD b/src/BUILD index 6c55a332769c54..57a7ac85db1881 100644 --- a/src/BUILD +++ b/src/BUILD @@ -433,7 +433,6 @@ filegroup( "//src/java_tools/buildjar:srcs", "//src/java_tools/import_deps_checker:srcs", "//src/java_tools/junitrunner:srcs", - "//src/java_tools/singlejar:srcs", "//src/main/cpp:srcs", "//src/main/res:srcs", "//src/main/java/com/google/devtools/build/docgen:srcs", @@ -569,7 +568,6 @@ genrule( srcs = [ "//src/java_tools/buildjar:srcs", "//src/java_tools/junitrunner:srcs", - "//src/java_tools/singlejar:srcs", "//src/tools/singlejar:embedded_java_tools", "//third_party/checker_framework_dataflow:srcs", "//third_party/checker_framework_javacutil:srcs", diff --git a/src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD b/src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD index 15a07c033d4fed..39bb35840e2589 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD +++ b/src/java_tools/junitrunner/java/com/google/testing/coverage/BUILD @@ -54,14 +54,14 @@ genrule( outs = ["JacocoCoverage_jarjar_deploy.jar"], cmd = "\n".join([ "JARJAR=\"$$(mktemp -t bazel.XXXXXXXX)\"", - "\"$(JAVA)\" -jar \"$(location //third_party/jarjar:jarjar_command_deploy.jar)\" process \"$(location :JacocoCoverage.jarjar)\" \"$(location :JacocoCoverage_deploy.jar)\" \"$${JARJAR}\"", + "\"$(location //src/tools/singlejar:singlejar_local)\" process \"$(location :JacocoCoverage.jarjar)\" \"$(location :JacocoCoverage_deploy.jar)\" \"$${JARJAR}\"", "\"$(JAVA)\" -jar \"$(location //src/java_tools/singlejar:SingleJar_deploy.jar)\" --normalize --sources \"$${JARJAR}\" --output \"$@\"", "rm -fr \"$${JARJAR}\"", ]), tags = ["manual"], toolchains = ["@bazel_tools//tools/jdk:current_host_java_runtime"], tools = [ - "//src/java_tools/singlejar:SingleJar_deploy.jar", + "//src/tools/singlejar:singlejar_local", "//third_party/jarjar:jarjar_command_deploy.jar", "@bazel_tools//tools/jdk:current_host_java_runtime", ], diff --git a/src/java_tools/singlejar/BUILD b/src/java_tools/singlejar/BUILD deleted file mode 100644 index 4eeae8848632fe..00000000000000 --- a/src/java_tools/singlejar/BUILD +++ /dev/null @@ -1,51 +0,0 @@ -# Description: -# SingleJar combines multiple zip files and additional files -# into a single zip file. -package(default_visibility = [ - "//scripts/bootstrap:__subpackages__", - "//src/java_tools:__subpackages__", -]) - -filegroup( - name = "srcs", - srcs = glob(["*"]) + [ - "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:srcs", - "//src/java_tools/singlejar/java/com/google/devtools/build/zip:srcs", - "//src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar:srcs", - "//src/java_tools/singlejar/javatests/com/google/devtools/build/zip:srcs", - ], - visibility = ["//src:__subpackages__"], -) - -package_group( - name = "singlejar_package_group", - packages = ["//src/java_tools/singlejar/..."], -) - -alias( - name = "libSingleJar", - actual = "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:libSingleJar", - visibility = ["//visibility:public"], -) - -alias( - name = "SingleJar", - actual = "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:bazel-singlejar", - visibility = ["//visibility:public"], -) - -alias( - name = "SingleJar_deploy.jar", - actual = "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:bazel-singlejar_deploy.jar", - visibility = ["//visibility:public"], -) - -alias( - name = "bootstrap", - actual = "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:bootstrap", -) - -alias( - name = "bootstrap_deploy.jar", - actual = "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:bootstrap_deploy.jar", -) diff --git a/src/java_tools/singlejar/README b/src/java_tools/singlejar/README deleted file mode 100644 index da92eb7a9bacfe..00000000000000 --- a/src/java_tools/singlejar/README +++ /dev/null @@ -1,2 +0,0 @@ -SingleJar is a tool used to combine multiple jar file into a single one. It is used by Bazel to -build java binaries that are self-contained. \ No newline at end of file diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/BUILD b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/BUILD deleted file mode 100644 index b6349019423a3c..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/BUILD +++ /dev/null @@ -1,102 +0,0 @@ -load("@rules_java//java:defs.bzl", "java_binary", "java_library") -load("//tools/build_rules:java_rules_skylark.bzl", "bootstrap_java_binary", "bootstrap_java_library") - -# Description: -# SingleJar combines multiple zip files and additional files -# into a single zip file. -package(default_visibility = ["//src/java_tools/singlejar:singlejar_package_group"]) - -filegroup( - name = "srcs", - srcs = glob(["*"]), - visibility = ["//src:__subpackages__"], -) - -# This is used as a library outside of Bazel. -java_library( - name = "libSingleJar", - srcs = [ - "ConcatenateStrategy.java", - "CopyEntryFilter.java", - "DefaultJarEntryFilter.java", - "JarUtils.java", - "ZipCombiner.java", - "ZipEntryFilter.java", - ], - visibility = [ - "//src/java_tools/singlejar:__pkg__", - "//src/java_tools/singlejar:singlejar_package_group", - "//src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar:__pkg__", - "//src/test/java/com/google/devtools/build/android:__pkg__", - "//src/tools/android/java/com/google/devtools/build/android:__pkg__", - ], - deps = [ - "//src/java_tools/singlejar/java/com/google/devtools/build/zip", - "//third_party:jsr305", - ], -) - -java_library( - name = "libSingleJarMain", - srcs = glob(["**/*.java"]), - # Avoid adding dependencies here - this is a very low-level library and we don't want to pull in - # the world, even including commons. - deps = [ - "//src/java_tools/singlejar/java/com/google/devtools/build/zip", - "//src/main/java/com/google/devtools/build/lib/shell", - "//src/main/protobuf:desugar_deps_java_proto", - "//src/main/protobuf:worker_protocol_java_proto", - "//third_party:jsr305", - "//third_party/protobuf:protobuf_java", - ], -) - -java_binary( - name = "bazel-singlejar", - srcs = glob(["*.java"]), - main_class = "com.google.devtools.build.singlejar.SingleJar", - visibility = [ - "//:__subpackages__", - "//src/java_tools/singlejar:singlejar_package_group", - ], - deps = [ - "//src/java_tools/singlejar/java/com/google/devtools/build/zip", - "//src/main/java/com/google/devtools/build/lib/shell", - "//src/main/protobuf:desugar_deps_java_proto", - "//src/main/protobuf:worker_protocol_java_proto", - "//third_party:jsr305", - "//third_party/protobuf:protobuf_java", - ], -) - -# -# Bootstrapping using Starlark rules -# - -bootstrap_java_library( - name = "starlark-deps", - jars = [ - "//third_party:jsr305-jars", - ], - tags = ["manual"], -) - -bootstrap_java_binary( - name = "bootstrap", - srcs = glob( - ["**/*.java"], - exclude = [ - "Java8DesugarDepsJarEntryFilter.java", - "SingleJarWorker.java", - ], - ) + [ - "//src/java_tools/singlejar/java/com/google/devtools/build/zip:java-srcs", - ], - main_class = "com.google.devtools.build.singlejar.SingleJar", - tags = ["manual"], - visibility = ["//visibility:public"], - deps = [ - ":starlark-deps", - "//src/main/java/com/google/devtools/build/lib/shell:shell-starlark", - ], -) diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ConcatenateStrategy.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ConcatenateStrategy.java deleted file mode 100644 index 496c8eb7653b9d..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ConcatenateStrategy.java +++ /dev/null @@ -1,74 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import javax.annotation.concurrent.NotThreadSafe; - -/** - * A strategy that merges a set of files by concatenating them. This is used - * for services files. By default, this class automatically adds a newline - * character {@code '\n'} between files if the previous file did not end with one. - * - *

Note: automatically inserting newline characters differs from the - * original behavior. Use {@link #ConcatenateStrategy(boolean)} to turn this - * behavior off. - */ -@NotThreadSafe -public final class ConcatenateStrategy implements CustomMergeStrategy { - - // The strategy assumes that files are generally small. This is a first guess - // about the size of the files. - private static final int BUFFER_SIZE = 4096; - - private final byte[] buffer = new byte[BUFFER_SIZE]; - private byte lastByteCopied = '\n'; - private final boolean appendNewLine; - - public ConcatenateStrategy() { - this(true); - } - - /** - * @param appendNewLine Whether to add a newline character between files if - * the previous file did not end with one. - */ - public ConcatenateStrategy(boolean appendNewLine) { - this.appendNewLine = appendNewLine; - } - - @Override - public void merge(InputStream in, OutputStream out) throws IOException { - if (appendNewLine && lastByteCopied != '\n') { - out.write('\n'); - lastByteCopied = '\n'; - } - int bytesRead; - while ((bytesRead = in.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - lastByteCopied = buffer[bytesRead - 1]; - } - } - - @Override - public void finish(OutputStream out) { - // No need to do anything. All the data was already written. - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/CopyEntryFilter.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/CopyEntryFilter.java deleted file mode 100644 index 8f94706d915ada..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/CopyEntryFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import java.io.IOException; - -import javax.annotation.concurrent.Immutable; - -/** - * A filter which invokes {@link StrategyCallback#copy} for every entry. As a - * result, the first entry for every given name is copied and further entries - * with the same name are skipped. - */ -@Immutable -public final class CopyEntryFilter implements ZipEntryFilter { - - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - callback.copy(null); - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/DefaultJarEntryFilter.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/DefaultJarEntryFilter.java deleted file mode 100644 index 4a4902f9360e39..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/DefaultJarEntryFilter.java +++ /dev/null @@ -1,123 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import java.io.IOException; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.jar.JarFile; -import javax.annotation.concurrent.Immutable; - -/** - * A default filter for JAR files. It merges all services files in the {@code META-INF/services/} - * directory. The original {@code MANIFEST} files are skipped, as are JAR signing files. Anything - * not in the supplied path filter, an arbitrary predicate, is also skipped. To use this filter - * properly, a new {@code MANIFEST} file should be explicitly added to the combined ZIP file. - */ -@Immutable -public class DefaultJarEntryFilter implements ZipEntryFilter { - - /** An interface to restrict which files are copied over and which are not. */ - public static interface PathFilter { - /** - * Returns true if an entry with the given name may be copied over. - */ - boolean allowed(String path); - } - - /** A filter that allows any path. */ - public static final PathFilter ANY_PATH = new PathFilter() { - @Override - public boolean allowed(String path) { - return true; - } - }; - - // ZIP timestamps have a resolution of 2 seconds, so this is the next timestamp after 1/1/1980. - // This is only Visible for testing. - static final Date DOS_EPOCH_PLUS_2_SECONDS = - new GregorianCalendar(1980, 0, 1, 0, 0, 2).getTime(); - - // Merge all files with a name in here: - private static final String SERVICES_DIR = "META-INF/services/"; - - // Merge all spring.handlers files. - private static final String SPRING_HANDLERS = "META-INF/spring.handlers"; - - // Merge all spring.schemas files. - private static final String SPRING_SCHEMAS = "META-INF/spring.schemas"; - - // Ignore all files with this name: - private static final String MANIFEST_NAME = JarFile.MANIFEST_NAME; - - // Merge all protobuf extension registries. - private static final String PROTOBUF_META = "protobuf.meta"; - - // Merge all reference config files. - private static final String REFERENCE_CONF = "reference.conf"; - - protected final Date date; - protected final Date classDate; - protected PathFilter allowedPaths; - - public DefaultJarEntryFilter(boolean normalize, PathFilter allowedPaths) { - this.date = normalize ? ZipCombiner.DOS_EPOCH : null; - this.classDate = normalize ? DOS_EPOCH_PLUS_2_SECONDS : null; - this.allowedPaths = allowedPaths; - } - - public DefaultJarEntryFilter(boolean normalize) { - this(normalize, ANY_PATH); - } - - public DefaultJarEntryFilter() { - this(true); - } - - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - if (!allowedPaths.allowed(filename)) { - callback.skip(); - } else if (filename.equals(SPRING_HANDLERS)) { - callback.customMerge(date, new ConcatenateStrategy()); - } else if (filename.equals(SPRING_SCHEMAS)) { - callback.customMerge(date, new ConcatenateStrategy()); - } else if (filename.equals(REFERENCE_CONF)) { - callback.customMerge(date, new ConcatenateStrategy()); - } else if (filename.startsWith(SERVICES_DIR)) { - // Merge all services files. - callback.customMerge(date, new ConcatenateStrategy()); - } else if (filename.equals(MANIFEST_NAME) || filename.endsWith(".SF") - || filename.endsWith(".DSA") || filename.endsWith(".RSA")) { - // Ignore existing manifests and any .SF, .DSA or .RSA jar signing files. - // TODO(bazel-team): I think we should be stricter and only skip signing - // files from the META-INF/ directory. - callback.skip(); - } else if (filename.endsWith(".class")) { - // Copy .class files over, but 2 seconds ahead of the dos epoch. If it finds both source and - // class files on the classpath, javac prefers the source file, if the class file is not newer - // than the source file. Since we normalize the timestamps, we need to provide timestamps for - // class files that are newer than those for the corresponding source files. - callback.copy(classDate); - } else if (filename.equals(PROTOBUF_META)) { - // Merge all protobuf meta data without inserting newlines, - // since the file is in protobuf binary format. - callback.customMerge(date, new ConcatenateStrategy(false)); - } else { - // Copy all other files over. - callback.copy(date); - } - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JarUtils.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JarUtils.java deleted file mode 100644 index a3fac8e8f681e8..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JarUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import com.google.devtools.build.zip.ExtraData; - -import java.io.IOException; -import java.util.Date; - -/** - * Provides utilities for using ZipCombiner to pack up Jar files. - */ -public final class JarUtils { - private static final String MANIFEST_DIRECTORY = "META-INF/"; - private static final short MAGIC_JAR_ID = (short) 0xCAFE; - private static final ExtraData[] MAGIC_JAR_ID_EXTRA_ENTRIES = - new ExtraData[] { new ExtraData(MAGIC_JAR_ID, new byte[0]) }; - - /** - * Adds META-INF directory through ZipCombiner with the given date and the - * magic jar ID. - * - * @throws IOException if {@link ZipCombiner#addDirectory(String, Date, ExtraData[])} - * throws an IOException. - */ - public static void addMetaInf(ZipCombiner combiner, Date date) throws IOException { - combiner.addDirectory(MANIFEST_DIRECTORY, date, MAGIC_JAR_ID_EXTRA_ENTRIES); - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/Java8DesugarDepsJarEntryFilter.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/Java8DesugarDepsJarEntryFilter.java deleted file mode 100644 index 93c6cc768e0d28..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/Java8DesugarDepsJarEntryFilter.java +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2018 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.singlejar; - -import com.google.devtools.build.android.desugar.proto.DesugarDeps.Dependency; -import com.google.devtools.build.android.desugar.proto.DesugarDeps.DesugarDepsInfo; -import com.google.devtools.build.android.desugar.proto.DesugarDeps.InterfaceDetails; -import com.google.devtools.build.android.desugar.proto.DesugarDeps.InterfaceWithCompanion; -import com.google.devtools.build.android.desugar.proto.DesugarDeps.Type; -import com.google.protobuf.ByteString; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * {@link ZipEntryFilter} that implements consistency checking of {@code META-INF/desugar_deps} - * files emitted by {@link com.google.devtools.build.android.desugar.Desugar}. This is used to - * implement singlejar's {@code --check_desugar_deps} flag. - */ -class Java8DesugarDepsJarEntryFilter - implements ZipEntryFilter, ZipEntryFilter.CustomMergeStrategy { - - private final Map neededDeps = new LinkedHashMap<>(); - private final Map missingInterfaces = new LinkedHashMap<>(); - private final Map> extendedInterfaces = new HashMap<>(); - private final Map hasDefaultMethods = new HashMap<>(); - private final Set seen = new HashSet<>(); - - private final ZipEntryFilter delegate; - - public Java8DesugarDepsJarEntryFilter(ZipEntryFilter delegate) { - this.delegate = delegate; - } - - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - if ("META-INF/desugar_deps".equals(filename)) { - callback.customMerge(null, this); - } else if (filename.startsWith("j$/")) { - throw new IOException("Unexpectedly found desugar_jdk_libs file: " + filename); - } else { - seen.add(filename); - delegate.accept(filename, callback); - } - } - - @Override - public void merge(InputStream in, OutputStream out) throws IOException { - DesugarDepsInfo depsInfo = DesugarDepsInfo.parseFrom(in); - for (Dependency assumed : depsInfo.getAssumePresentList()) { - neededDeps.putIfAbsent( - assumed.getTarget().getBinaryName() + ".class", - assumed.getOrigin().getBinaryNameBytes()); - } - for (Dependency missing : depsInfo.getMissingInterfaceList()) { - missingInterfaces.putIfAbsent( - missing.getTarget().getBinaryName(), - missing.getOrigin().getBinaryNameBytes()); - } - for (InterfaceDetails itf : depsInfo.getInterfaceWithSupertypesList()) { - if (itf.getExtendedInterfaceCount() > 0 - && !extendedInterfaces.containsKey(itf.getOrigin().getBinaryName())) { - // Avoid Guava dependency - ArrayList supertypes = new ArrayList<>(itf.getExtendedInterfaceCount()); - for (Type extended : itf.getExtendedInterfaceList()) { - supertypes.add(extended.getBinaryName()); - } - extendedInterfaces.putIfAbsent(itf.getOrigin().getBinaryName(), supertypes); - } - } - for (InterfaceWithCompanion companion : depsInfo.getInterfaceWithCompanionList()) { - if (companion.getNumDefaultMethods() > 0) { - // Only remember interfaces that definitely have default methods for now. - // For all other interfaces we'll transitively check extended interfaces - // in HasDefaultMethods. - hasDefaultMethods.putIfAbsent(companion.getOrigin().getBinaryName(), true); - } - } - // Don't write anything to out, we just want to check these files for consistency - } - - @Override - public void finish(OutputStream out) throws IOException { - for (Map.Entry need : neededDeps.entrySet()) { - if (!seen.contains(need.getKey())) { - throw new IOException(need.getKey() + " referenced by " + need.getValue().toStringUtf8() - + " but not found. Is the former defined in a neverlink library?"); - } - } - - for (Map.Entry missing : missingInterfaces.entrySet()) { - if (hasDefaultMethods(missing.getKey())) { - throw new IOException(missing.getKey() - + " needed to desugar " - + missing.getValue().toStringUtf8() - + ". Please add a dependency to the former to the library containing the latter."); - } - } - // Don't write anything to out, we just want to check these files for consistency - } - - @Override - public boolean skipEmpty() { - return true; // We never want to write these files into the output Jar - } - - private boolean hasDefaultMethods(String itf) { - Boolean cached = hasDefaultMethods.putIfAbsent(itf, false); - if (cached != null) { - return cached; // Already in the map - } - - List extended = extendedInterfaces.get(itf); - if (extended != null) { - for (String supertype : extended) { - if (hasDefaultMethods(supertype)) { - hasDefaultMethods.put(itf, true); - return true; - } - } - } - // We primed with false above in case of cycles so just return that - return false; - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JavaIoFileSystem.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JavaIoFileSystem.java deleted file mode 100644 index 878accd58869c7..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/JavaIoFileSystem.java +++ /dev/null @@ -1,48 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * An implementation based on java.io. - */ -public final class JavaIoFileSystem implements SimpleFileSystem { - - @Override - public InputStream getInputStream(String filename) throws IOException { - return new FileInputStream(filename); - } - - @Override - public OutputStream getOutputStream(String filename) throws IOException { - return new FileOutputStream(filename); - } - - @Override - public File getFile(String filename) throws IOException { - return new File(filename); - } - - @Override - public boolean delete(String filename) { - return new File(filename).delete(); - } -} \ No newline at end of file diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/OptionFileExpander.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/OptionFileExpander.java deleted file mode 100644 index aa396b5973e19d..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/OptionFileExpander.java +++ /dev/null @@ -1,113 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import static java.nio.charset.StandardCharsets.ISO_8859_1; - -import com.google.devtools.build.lib.shell.ShellUtils; -import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.concurrent.Immutable; - -/** - * A utility class to parse option files and expand them. - */ -@Immutable -final class OptionFileExpander { - - /** - * An interface that allows injecting different implementations for reading - * files. This is mostly used for testing. - */ - interface OptionFileProvider { - - /** - * Opens a file for reading and returns an input stream. - */ - InputStream getInputStream(String filename) throws IOException; - } - - private final OptionFileProvider fileSystem; - - /** - * Creates an instance with the given option file provider. - */ - public OptionFileExpander(OptionFileProvider fileSystem) { - this.fileSystem = fileSystem; - } - - /** - * Pre-processes an argument list, expanding options of the form &at;filename - * to read in the content of the file and add it to the list of arguments. - * - * @param args the List of arguments to pre-process. - * @return the List of pre-processed arguments. - * @throws IOException if one of the files containing options cannot be read. - */ - public List expandArguments(List args) throws IOException { - List expanded = new ArrayList<>(args.size()); - for (String arg : args) { - expandArgument(arg, expanded); - } - return expanded; - } - - /** - * Expands a single argument, expanding options &at;filename to read in - * the content of the file and add it to the list of processed arguments. - * - * @param arg the argument to pre-process. - * @param expanded the List of pre-processed arguments. - * @throws IOException if one of the files containing options cannot be read. - */ - private void expandArgument(String arg, List expanded) throws IOException { - if (arg.startsWith("@")) { - try (InputStreamReader reader = - new InputStreamReader(fileSystem.getInputStream(arg.substring(1)), ISO_8859_1)) { - // TODO(bazel-team): This code doesn't handle escaped newlines correctly. - // ShellUtils doesn't support them either. - for (String line : readAllLines(reader)) { - List parsedTokens = new ArrayList<>(); - try { - ShellUtils.tokenize(parsedTokens, line); - } catch (TokenizationException e) { - throw new IOException("Could not tokenize parameter file!", e); - } - for (String token : parsedTokens) { - expandArgument(token, expanded); - } - } - } - } else { - expanded.add(arg); - } - } - - private List readAllLines(Reader in) throws IOException { - List result = new ArrayList<>(); - BufferedReader reader = new BufferedReader(in); - String line; - while ((line = reader.readLine()) != null) { - result.add(line); - } - return result; - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/PrefixListPathFilter.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/PrefixListPathFilter.java deleted file mode 100644 index 7a3d6617ac2f4f..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/PrefixListPathFilter.java +++ /dev/null @@ -1,40 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import com.google.devtools.build.singlejar.DefaultJarEntryFilter.PathFilter; - -import java.util.List; - -/** - * A predicate used to filter jar entries according to a list of path prefixes. - */ -final class PrefixListPathFilter implements PathFilter { - private final List prefixes; - - public PrefixListPathFilter(List prefixes) { - this.prefixes = prefixes; - } - - @Override - public boolean allowed(String path) { - for (String prefix : prefixes) { - if (path.startsWith(prefix)) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SimpleFileSystem.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SimpleFileSystem.java deleted file mode 100644 index 300b4f5e1baa20..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SimpleFileSystem.java +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import com.google.devtools.build.singlejar.OptionFileExpander.OptionFileProvider; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A simple virtual file system interface. It's much simpler than the Blaze - * virtual file system and only to be used inside this package. - */ -public interface SimpleFileSystem extends OptionFileProvider { - - @Override - InputStream getInputStream(String filename) throws IOException; - - /** - * Opens a file for output and returns an output stream. If a file of that - * name already exists, it is overwritten. - */ - OutputStream getOutputStream(String filename) throws IOException; - - /** - * Returns the File object for this filename. - */ - File getFile(String filename) throws IOException; - - /** Delete the file with the given name and return whether deleting it was successful. */ - boolean delete(String filename); -} \ No newline at end of file diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJar.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJar.java deleted file mode 100644 index a9da39e3519bd3..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJar.java +++ /dev/null @@ -1,469 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import static com.google.devtools.build.singlejar.ZipCombiner.DOS_EPOCH; - -import com.google.devtools.build.singlejar.DefaultJarEntryFilter.PathFilter; -import com.google.devtools.build.singlejar.ZipCombiner.OutputMode; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Properties; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import javax.annotation.concurrent.NotThreadSafe; - -/** - * An application that emulates the existing SingleJar tool, using the {@link - * ZipCombiner} class. - */ -@NotThreadSafe -public class SingleJar { - - private static final byte NEWLINE_BYTE = (byte) '\n'; - private static final String MANIFEST_FILENAME = JarFile.MANIFEST_NAME; - private static final String BUILD_DATA_FILENAME = "build-data.properties"; - - private final SimpleFileSystem fileSystem; - - /** The input jar files we want to combine into the output jar. */ - private final List inputJars = new ArrayList<>(); - - /** Additional resources to be added to the output jar. */ - private final List resources = new ArrayList<>(); - - /** Additional class path resources to be added to the output jar. */ - private final List classpathResources = new ArrayList<>(); - - /** The name of the output Jar file. */ - private String outputJar; - - /** A filter for what jar entries to include */ - private PathFilter allowedPaths = DefaultJarEntryFilter.ANY_PATH; - - /** Extra manifest contents. */ - private String extraManifestContent; - /** The main class - this is put into the manifest and also into the build info. */ - private String mainClass; - - /** - * Warn about duplicate resource files, and skip them. Default behavior is to - * give an error message. - */ - private boolean warnDuplicateFiles = false; - - /** Indicates whether to set all timestamps to a fixed value. */ - private boolean normalize = false; - private boolean checkDesugarDeps = false; - private OutputMode outputMode = OutputMode.FORCE_STORED; - - /** Whether to include build-data.properties file */ - protected boolean includeBuildData = true; - - /** List of build information properties files */ - protected List buildInformationFiles = new ArrayList<>(); - - /** Extraneous build informations (key=value) */ - protected List buildInformations = new ArrayList<>(); - - /** The (optional) native executable that will be prepended to this JAR. */ - private String launcherBin = null; - - // Only visible for testing. - protected SingleJar(SimpleFileSystem fileSystem) { - this.fileSystem = fileSystem; - } - - /** - * Creates a manifest and returns an input stream for its contents. - */ - private InputStream createManifest() throws IOException { - Manifest manifest = new Manifest(); - Attributes attributes = manifest.getMainAttributes(); - attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); - attributes.put(new Attributes.Name("Created-By"), "blaze-singlejar"); - if (mainClass != null) { - attributes.put(Attributes.Name.MAIN_CLASS, mainClass); - } - if (extraManifestContent != null) { - ByteArrayInputStream in = new ByteArrayInputStream(extraManifestContent.getBytes("UTF8")); - manifest.read(in); - } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - manifest.write(out); - return new ByteArrayInputStream(out.toByteArray()); - } - - private InputStream createBuildData() throws IOException { - Properties properties = mergeBuildData(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - properties.store(outputStream, null); - byte[] output = outputStream.toByteArray(); - // Properties#store() adds a timestamp comment as first line, delete it. - output = stripFirstLine(output); - return new ByteArrayInputStream(output); - } - - static byte[] stripFirstLine(byte[] output) { - int i = 0; - while (i < output.length && output[i] != NEWLINE_BYTE) { - i++; - } - if (i < output.length) { - output = Arrays.copyOfRange(output, i + 1, output.length); - } else { - output = new byte[0]; - } - return output; - } - - private Properties mergeBuildData() throws IOException { - Properties properties = new Properties(); - for (String fileName : buildInformationFiles) { - InputStream file = fileSystem.getInputStream(fileName); - if (file != null) { - properties.load(file); - } - } - - // extra properties - for (String info : buildInformations) { - String[] split = info.split("=", 2); - String key = split[0]; - String value = ""; - if (split.length > 1) { - value = split[1]; - } - properties.put(key, value); - } - - // finally add generic information - // TODO(b/28294322): do we need to resolve the path to be absolute or canonical? - properties.put("build.target", outputJar); - if (mainClass != null) { - properties.put("main.class", mainClass); - } - return properties; - } - - private String getName(String filename) { - int index = filename.lastIndexOf('/'); - return index < 0 ? filename : filename.substring(index + 1); - } - - // Only visible for testing. - protected int run(List args) throws IOException { - List expandedArgs = new OptionFileExpander(fileSystem).expandArguments(args); - processCommandlineArgs(expandedArgs); - InputStream buildInfo = createBuildData(); - - ZipCombiner combiner = null; - try (OutputStream out = fileSystem.getOutputStream(outputJar)) { - combiner = new ZipCombiner(outputMode, createEntryFilterHelper(), out); - if (launcherBin != null) { - combiner.prependExecutable(fileSystem.getInputStream(launcherBin)); - } - Date date = normalize ? ZipCombiner.DOS_EPOCH : null; - - // Add a manifest file. - JarUtils.addMetaInf(combiner, date); - combiner.addFile(MANIFEST_FILENAME, date, createManifest()); - - if (includeBuildData) { - // Add the build data file. - combiner.addFile(BUILD_DATA_FILENAME, date, buildInfo); - } - - // Copy the resources to the top level of the jar file. - for (String classpathResource : classpathResources) { - String entryName = getName(classpathResource); - if (warnDuplicateFiles && combiner.containsFile(entryName)) { - System.err.println("File " + entryName + " clashes with a previous file"); - continue; - } - combiner.addFile(entryName, date, fileSystem.getInputStream(classpathResource)); - } - - // Copy the resources into the jar file. - for (String resource : resources) { - String from; - String to; - int i = resource.indexOf(':'); - if (i < 0) { - to = from = resource; - } else { - from = resource.substring(0, i); - to = resource.substring(i + 1); - } - if (warnDuplicateFiles && combiner.containsFile(to)) { - System.err.println("File " + from + " at " + to + " clashes with a previous file"); - continue; - } - - // Add parent directory entries. - int idx = to.indexOf('/'); - while (idx != -1) { - String dir = to.substring(0, idx + 1); - if (!combiner.containsFile(dir)) { - combiner.addDirectory(dir, DOS_EPOCH); - } - idx = to.indexOf('/', idx + 1); - } - - combiner.addFile(to, date, fileSystem.getInputStream(from)); - } - - // Copy the jars into the jar file. - for (String inputJar : inputJars) { - File jar = fileSystem.getFile(inputJar); - combiner.addZip(jar); - } - - // Close the output file. If something goes wrong here, delete the file. - combiner.close(); - combiner = null; - } finally { - // This part is only executed if an exception occurred. - if (combiner != null) { - try { - // We may end up calling close twice, but that's ok. - combiner.close(); - } catch (IOException e) { - // There's already an exception in progress - this won't add any - // additional information. - } - // Ignore return value - there's already an exception in progress. - fileSystem.delete(outputJar); - } - } - return 0; - } - - private ZipEntryFilter createEntryFilterHelper() { - ZipEntryFilter result = createEntryFilter(normalize, allowedPaths); - if (checkDesugarDeps) { - // Invocation is done through reflection so that this code will work in bazel open source - // as well. SingleJar is used for bootstrap and thus can not depend on protos (used in - // Java8DesugarDepsJarEntryFilter). - try { - return (ZipEntryFilter) - Class.forName("com.google.devtools.build.singlejar.Java8DesugarDepsJarEntryFilter") - .getConstructor(ZipEntryFilter.class).newInstance(result); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Couldn't instantiate desugar deps checker", e); - } - } else { - return (filename, callback) -> { - if ("META-INF/desugar_deps".equals(filename)) { - callback.skip(); // We never want these files in the output - } else { - result.accept(filename, callback); - } - }; - } - - } - - protected ZipEntryFilter createEntryFilter(boolean normalize, PathFilter allowedPaths) { - return new DefaultJarEntryFilter(normalize, allowedPaths); - } - - /** - * Collects the arguments for a command line flag until it finds a flag that - * starts with the terminatorPrefix. - * - * @param args - * @param startIndex the start index in the args to collect the flag arguments - * from - * @param flagArguments the collected flag arguments - * @param terminatorPrefix the terminator prefix to stop collecting of - * argument flags - * @return the index of the first argument that started with the - * terminatorPrefix - */ - private static int collectFlagArguments(List args, int startIndex, - List flagArguments, String terminatorPrefix) { - startIndex++; - while (startIndex < args.size()) { - String name = args.get(startIndex); - if (name.startsWith(terminatorPrefix)) { - return startIndex - 1; - } - flagArguments.add(name); - startIndex++; - } - return startIndex; - } - - /** - * Returns a single argument for a command line option. - * - * @throws IOException if no more arguments are available - */ - private static String getArgument(List args, int i, String arg) throws IOException { - if (i + 1 < args.size()) { - return args.get(i + 1); - } - throw new IOException(arg + ": missing argument"); - } - - /** - * Processes the command line arguments. - * - * @throws IOException if one of the files containing options cannot be read - */ - protected void processCommandlineArgs(List args) throws IOException { - List manifestLines = new ArrayList<>(); - List prefixes = new ArrayList<>(); - for (int i = 0; i < args.size(); i++) { - String arg = args.get(i); - if (arg.equals("--sources")) { - i = collectFlagArguments(args, i, inputJars, "--"); - } else if (arg.equals("--resources")) { - i = collectFlagArguments(args, i, resources, "--"); - } else if (arg.equals("--classpath_resources")) { - i = collectFlagArguments(args, i, classpathResources, "--"); - } else if (arg.equals("--deploy_manifest_lines")) { - i = collectFlagArguments(args, i, manifestLines, "--"); - } else if (arg.equals("--build_info_file")) { - buildInformationFiles.add(getArgument(args, i, arg)); - i++; - } else if (arg.equals("--extra_build_info")) { - buildInformations.add(getArgument(args, i, arg)); - i++; - } else if (arg.equals("--main_class")) { - mainClass = getArgument(args, i, arg); - i++; - } else if (arg.equals("--output")) { - outputJar = getArgument(args, i, arg); - i++; - } else if (arg.equals("--compression")) { - outputMode = OutputMode.FORCE_DEFLATE; - } else if (arg.equals("--dont_change_compression")) { - outputMode = OutputMode.DONT_CARE; - } else if (arg.equals("--normalize")) { - normalize = true; - } else if (arg.equals("--include_prefixes")) { - i = collectFlagArguments(args, i, prefixes, "--"); - } else if (arg.equals("--exclude_build_data")) { - includeBuildData = false; - } else if (arg.equals("--warn_duplicate_resources")) { - warnDuplicateFiles = true; - } else if (arg.equals("--java_launcher")) { - launcherBin = getArgument(args, i, arg); - i++; - } else if (arg.equals("--check_desugar_deps")) { - checkDesugarDeps = true; - } else { - throw new IOException("unknown option : '" + arg + "'"); - } - } - if (!manifestLines.isEmpty()) { - setExtraManifestContent(joinWithNewlines(manifestLines)); - } - if (!prefixes.isEmpty()) { - setPathPrefixes(prefixes); - } - } - - private String joinWithNewlines(Iterable lines) { - StringBuilder result = new StringBuilder(); - Iterator it = lines.iterator(); - if (it.hasNext()) { - result.append(it.next()); - } - while (it.hasNext()) { - result.append('\n'); - result.append(it.next()); - } - return result.toString(); - } - - private void setExtraManifestContent(String extraManifestContent) { - // The manifest content has to be terminated with a newline character - if (!extraManifestContent.endsWith("\n")) { - extraManifestContent = extraManifestContent + '\n'; - } - this.extraManifestContent = extraManifestContent; - } - - private void setPathPrefixes(List prefixes) throws IOException { - if (prefixes.isEmpty()) { - throw new IOException( - "Empty set of path prefixes; cowardly refusing to emit an empty jar file"); - } - allowedPaths = new PrefixListPathFilter(prefixes); - } - - static int singleRun(String[] args) throws IOException { - SingleJar singlejar = new SingleJar(new JavaIoFileSystem()); - return singlejar.run(Arrays.asList(args)); - } - - public static void main(String[] args) { - if (shouldRunInWorker(args)) { - if (!canRunInWorker()) { - System.err.println("Asked to run in a worker, but no worker support"); - System.exit(1); - } - try { - runWorker(args); - } catch (Exception e) { - System.err.println("Error running worker : " + e.getMessage()); - System.exit(1); - } - return; - } - - try { - System.exit(singleRun(args)); - } catch (IOException e) { - System.err.println("SingleJar threw exception : " + e.getMessage()); - System.exit(1); - } - } - - private static void runWorker(String[] args) throws Exception { - // Invocation is done through reflection so that this code will work in bazel open source - // as well. SingleJar is used for bootstrap and thus can not depend on protos (used in - // SingleJarWorker). - Class workerClass = Class.forName("com.google.devtools.build.singlejar.SingleJarWorker"); - workerClass.getMethod("main", String[].class).invoke(null, (Object) args); - } - - protected static boolean shouldRunInWorker(String[] args) { - return Arrays.asList(args).contains("--persistent_worker"); - } - - private static boolean canRunInWorker() { - try { - Class.forName("com.google.devtools.build.singlejar.SingleJarWorker"); - return true; - } catch (ClassNotFoundException e1) { - return false; - } - } - -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJarWorker.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJarWorker.java deleted file mode 100644 index 95554a8679c30a..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/SingleJarWorker.java +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 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.singlejar; - -import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; -import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; - -/** - * A blaze worker to run {@link SingleJar} in a warm JVM process. - */ -public class SingleJarWorker { - - public static void main(String[] args) { - new SingleJarWorker().runWorker(); - } - - private PrintStream originalStdout; - private PrintStream originalSterr; - private ByteArrayOutputStream stdoutAndStderr; - - protected void runWorker() { - trapOutputs(); - - try { - dispatchWorkRequestsForever(); - } catch (IOException e) { - // IOException will only occur if System.in has been closed - // In that case we silently exit our process - } - } - - private void trapOutputs() { - originalStdout = System.out; - originalSterr = System.err; - stdoutAndStderr = new ByteArrayOutputStream(); - System.setErr(new PrintStream(stdoutAndStderr, true)); - System.setOut(new PrintStream(stdoutAndStderr, true)); - } - - private void dispatchWorkRequestsForever() throws IOException { - while (true) { - WorkRequest workRequest = WorkRequest.parseDelimitedFrom(System.in); - - String[] args = workRequest.getArgumentsList().toArray(new String[0]); - - int returnCode = runSingleJar(args); - - outputResult(returnCode); - } - } - - private void outputResult(int returnCode) throws IOException { - WorkResponse.newBuilder() - .setExitCode(returnCode) - .setOutput(new String(stdoutAndStderr.toByteArray(), StandardCharsets.UTF_8)) - .build() - .writeDelimitedTo(originalStdout); - - // Reset output streams, we are not simply calling reset on the BAOS since this will - // still keep the full buffer allocated. - stdoutAndStderr = new ByteArrayOutputStream(); - System.setErr(new PrintStream(stdoutAndStderr, true)); - System.setOut(new PrintStream(stdoutAndStderr, true)); - } - - private int runSingleJar(String[] args) { - try { - return singleRun(args); - } catch (IOException e) { - // Some IO failures are okay no need to quit the worker - System.err.println("SingleJar threw exception : " + e.getMessage()); - return 1; - } catch (Exception e) { - // We had an actual unexpected error, lets quit the worker - originalSterr.println("SingleJar threw an unexpected exception : " + e.getMessage()); - e.printStackTrace(originalSterr); - System.exit(1); - return 1; - } - } - - protected int singleRun(String[] args) throws Exception { - return SingleJar.singleRun(args); - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipCombiner.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipCombiner.java deleted file mode 100644 index 44d4f0262b69e5..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipCombiner.java +++ /dev/null @@ -1,636 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; -import com.google.devtools.build.singlejar.ZipEntryFilter.StrategyCallback; -import com.google.devtools.build.zip.ExtraData; -import com.google.devtools.build.zip.ExtraDataList; -import com.google.devtools.build.zip.ZipFileEntry; -import com.google.devtools.build.zip.ZipFileEntry.Compression; -import com.google.devtools.build.zip.ZipReader; -import com.google.devtools.build.zip.ZipUtil; -import com.google.devtools.build.zip.ZipWriter; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.zip.CRC32; -import java.util.zip.Deflater; -import java.util.zip.DeflaterInputStream; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; -import javax.annotation.Nullable; - -/** - * An object that combines multiple ZIP files into a single file. It only - * supports a subset of the ZIP format, specifically: - *

- * - *

These restrictions are also present in the JDK implementations - * {@link java.util.jar.JarInputStream}, {@link java.util.zip.ZipInputStream}, - * though they are not documented there. - * - *

IMPORTANT NOTE: Callers must call {@link #finish()} or {@link #close()} - * at the end of processing to ensure that the output buffers are flushed and - * the ZIP file is complete. - * - *

This class performs only rudimentary data checking. If the input files - * are damaged, the output will likely also be damaged. - * - *

Also see: - * ZIP format - */ -public class ZipCombiner implements AutoCloseable { - public static final Date DOS_EPOCH = new Date(ZipUtil.DOS_EPOCH); - /** - * Whether to compress or decompress entries. - */ - public enum OutputMode { - - /** - * Output entries using any method. - */ - DONT_CARE, - - /** - * Output all entries using DEFLATE method, except directory entries. It is always more - * efficient to store directory entries uncompressed. - */ - FORCE_DEFLATE, - - /** - * Output all entries using STORED method. - */ - FORCE_STORED, - } - - /** - * The type of action to take for a ZIP file entry. - */ - private enum ActionType { - - /** - * Skip the entry. - */ - SKIP, - - /** - * Copy the entry. - */ - COPY, - - /** - * Rename the entry. - */ - RENAME, - - /** - * Merge the entry. - */ - MERGE; - } - - /** - * Encapsulates the action to take for a ZIP file entry along with optional details specific to - * the action type. The minimum requirements per type are: - *

- * - *

An action can be easily changed from one type to another by using - * {@link EntryAction#EntryAction(ActionType, EntryAction)}. - */ - private static final class EntryAction { - private final ActionType type; - @Nullable private final Date date; - @Nullable private final String newName; - @Nullable private final CustomMergeStrategy strategy; - @Nullable private final ByteArrayOutputStream mergeBuffer; - - /** - * Create an action of the specified type with no extra details. - */ - public EntryAction(ActionType type) { - this(type, null, null, null, null); - } - - /** - * Create a duplicate action with a different {@link ActionType}. - */ - public EntryAction(ActionType type, EntryAction action) { - this(type, action.getDate(), action.getNewName(), action.getStrategy(), - action.getMergeBuffer()); - } - - /** - * Create an action of the specified type and details. - * - * @param type the type of action - * @param date the custom date to set on the entry - * @param newName the custom name to create the entry as - * @param strategy the {@link CustomMergeStrategy} to use for merging this entry - * @param mergeBuffer the output stream to use for merge results - */ - public EntryAction(ActionType type, Date date, String newName, CustomMergeStrategy strategy, - ByteArrayOutputStream mergeBuffer) { - checkArgument(type != ActionType.RENAME || newName != null, - "NewName must not be null if the ActionType is RENAME."); - checkArgument(type != ActionType.MERGE || strategy != null, - "Strategy must not be null if the ActionType is MERGE."); - checkArgument(type != ActionType.MERGE || mergeBuffer != null, - "MergeBuffer must not be null if the ActionType is MERGE."); - this.type = type; - this.date = date; - this.newName = newName; - this.strategy = strategy; - this.mergeBuffer = mergeBuffer; - } - - /** Returns the type. */ - public ActionType getType() { - return type; - } - - /** Returns the date. */ - public Date getDate() { - return date; - } - - /** Returns the new name. */ - public String getNewName() { - return newName; - } - - /** Returns the strategy. */ - public CustomMergeStrategy getStrategy() { - return strategy; - } - - /** Returns the mergeBuffer. */ - public ByteArrayOutputStream getMergeBuffer() { - return mergeBuffer; - } - } - - private final class FilterCallback implements StrategyCallback { - private String filename; - private final AtomicBoolean called = new AtomicBoolean(); - - public void resetForFile(String filename) { - this.filename = filename; - this.called.set(false); - } - - @Override public void skip() throws IOException { - checkCall(); - actions.put(filename, new EntryAction(ActionType.SKIP)); - } - - @Override public void copy(Date date) throws IOException { - checkCall(); - actions.put(filename, new EntryAction(ActionType.COPY, date, null, null, null)); - } - - @Override public void rename(String newName, Date date) throws IOException { - checkCall(); - actions.put(filename, new EntryAction(ActionType.RENAME, date, newName, null, null)); - } - - @Override public void customMerge(Date date, CustomMergeStrategy strategy) throws IOException { - checkCall(); - actions.put(filename, new EntryAction(ActionType.MERGE, date, null, strategy, - new ByteArrayOutputStream())); - } - - private void checkCall() { - checkState(called.compareAndSet(false, true), "The callback was already called once."); - } - } - - /** Returns a {@link Deflater} for performing ZIP compression. */ - private static Deflater getDeflater() { - return new Deflater(Deflater.DEFAULT_COMPRESSION, true); - } - - /** Returns a {@link Inflater} for performing ZIP decompression. */ - private static Inflater getInflater() { - return new Inflater(true); - } - - /** Copies all data from the input stream to the output stream. */ - private static long copyStream(InputStream from, OutputStream to) throws IOException { - byte[] buf = new byte[0x1000]; - long total = 0; - int r; - while ((r = from.read(buf)) != -1) { - to.write(buf, 0, r); - total += r; - } - return total; - } - - private final OutputMode mode; - private final ZipEntryFilter entryFilter; - private final FilterCallback callback; - private final ZipWriter out; - - private final Map entries; - private final Map actions; - - /** - * Creates a {@link ZipCombiner} for combining ZIP files using the specified {@link OutputMode}, - * {@link ZipEntryFilter}, and destination {@link OutputStream}. - * - * @param mode the compression preference for the output ZIP file - * @param entryFilter the filter to use when adding ZIP files to the combined output - * @param out the {@link OutputStream} for writing the combined ZIP file - */ - public ZipCombiner(OutputMode mode, ZipEntryFilter entryFilter, OutputStream out) { - this.mode = mode; - this.entryFilter = entryFilter; - this.callback = new FilterCallback(); - this.out = new ZipWriter(new BufferedOutputStream(out), UTF_8); - this.entries = new HashMap<>(); - this.actions = new HashMap<>(); - } - - /** - * Creates a {@link ZipCombiner} for combining ZIP files using the specified - * {@link ZipEntryFilter}, and destination {@link OutputStream}. Uses the DONT_CARE - * {@link OutputMode}. - * - * @param entryFilter the filter to use when adding ZIP files to the combined output - * @param out the {@link OutputStream} for writing the combined ZIP file - */ - public ZipCombiner(ZipEntryFilter entryFilter, OutputStream out) { - this(OutputMode.DONT_CARE, entryFilter, out); - } - - /** - * Creates a {@link ZipCombiner} for combining ZIP files using the specified {@link OutputMode}, - * and destination {@link OutputStream}. Uses a {@link CopyEntryFilter} as the - * {@link ZipEntryFilter}. - * - * @param mode the compression preference for the output ZIP file - * @param out the {@link OutputStream} for writing the combined ZIP file - */ - public ZipCombiner(OutputMode mode, OutputStream out) { - this(mode, new CopyEntryFilter(), out); - } - - /** - * Creates a {@link ZipCombiner} for combining ZIP files using the specified destination - * {@link OutputStream}. Uses the DONT_CARE {@link OutputMode} and a {@link CopyEntryFilter} as - * the {@link ZipEntryFilter}. - * - * @param out the {@link OutputStream} for writing the combined ZIP file - */ - public ZipCombiner(OutputStream out) { - this(OutputMode.DONT_CARE, new CopyEntryFilter(), out); - } - - /** - * Write all contents from the {@link InputStream} as a prefix file for the combined ZIP file. - * - * @param in the {@link InputStream} containing the prefix file data - * @throws IOException if there is an error writing the prefix file - */ - public void prependExecutable(InputStream in) throws IOException { - out.startPrefixFile(); - copyStream(in, out); - out.endPrefixFile(); - } - - /** - * Adds a directory entry to the combined ZIP file using the specified filename and date. - * - * @param filename the name of the directory to create - * @param date the modified time to assign to the directory - * @throws IOException if there is an error writing the directory entry - */ - public void addDirectory(String filename, Date date) throws IOException { - addDirectory(filename, date, new ExtraData[0]); - } - - /** - * Adds a directory entry to the combined ZIP file using the specified filename, date, and extra - * data. - * - * @param filename the name of the directory to create - * @param date the modified time to assign to the directory - * @param extra the extra field data to add to the directory entry - * @throws IOException if there is an error writing the directory entry - */ - public void addDirectory(String filename, Date date, ExtraData[] extra) throws IOException { - checkArgument(filename.endsWith("/"), "Directory names must end with a /"); - checkState(!entries.containsKey(filename), - "Zip already contains a directory named %s", filename); - - ZipFileEntry entry = new ZipFileEntry(filename); - entry.setMethod(Compression.STORED); - entry.setCrc(0); - entry.setSize(0); - entry.setCompressedSize(0); - entry.setTime(date != null ? date.getTime() : new Date().getTime()); - entry.setExtra(new ExtraDataList(extra)); - out.putNextEntry(entry); - out.closeEntry(); - entries.put(filename, entry); - } - - /** - * Adds a file with the specified name to the combined ZIP file. - * - * @param filename the name of the file to create - * @param in the {@link InputStream} containing the file data - * @throws IOException if there is an error writing the file entry - * @throws IllegalArgumentException if the combined ZIP file already contains a file of the same - * name. - */ - public void addFile(String filename, InputStream in) throws IOException { - addFile(filename, null, in); - } - - /** - * Adds a file with the specified name and date to the combined ZIP file. - * - * @param filename the name of the file to create - * @param date the modified time to assign to the file - * @param in the {@link InputStream} containing the file data - * @throws IOException if there is an error writing the file entry - * @throws IllegalArgumentException if the combined ZIP file already contains a file of the same - * name. - */ - public void addFile(String filename, Date date, InputStream in) throws IOException { - ZipFileEntry entry = new ZipFileEntry(filename); - entry.setTime(date != null ? date.getTime() : new Date().getTime()); - addFile(entry, in); - } - - /** - * Adds a file with attributes specified by the {@link ZipFileEntry} to the combined ZIP file. - * - * @param entry the {@link ZipFileEntry} containing the entry meta-data - * @param in the {@link InputStream} containing the file data - * @throws IOException if there is an error writing the file entry - * @throws IllegalArgumentException if the combined ZIP file already contains a file of the same - * name. - */ - public void addFile(ZipFileEntry entry, InputStream in) throws IOException { - checkNotNull(entry, "Zip entry must not be null."); - checkNotNull(in, "Input stream must not be null."); - checkArgument(!entries.containsKey(entry.getName()), "Zip already contains a file named '%s'.", - entry.getName()); - - ByteArrayOutputStream uncompressed = new ByteArrayOutputStream(); - copyStream(in, uncompressed); - - writeEntryFromBuffer(new ZipFileEntry(entry), uncompressed.toByteArray()); - } - - /** - * Adds the contents of a ZIP file to the combined ZIP file using the specified - * {@link ZipEntryFilter} to determine the appropriate action for each file. - * - * @param zipFile the ZIP file to add to the combined ZIP file - * @throws IOException if there is an error reading the ZIP file or writing entries to the - * combined ZIP file - */ - public void addZip(File zipFile) throws IOException { - try (ZipReader zip = new ZipReader(zipFile)) { - for (ZipFileEntry entry : zip.entries()) { - String filename = entry.getName(); - EntryAction action = getAction(filename); - switch (action.getType()) { - case SKIP: - break; - case COPY: - case RENAME: - writeEntry(zip, entry, action); - break; - case MERGE: - entries.put(filename, null); - InputStream in = zip.getRawInputStream(entry); - if (entry.getMethod() == Compression.DEFLATED) { - in = new InflaterInputStream(in, getInflater()); - } - action.getStrategy().merge(in, action.getMergeBuffer()); - break; - } - } - } - } - - /** Returns the action to take for a file of the given filename. */ - private EntryAction getAction(String filename) throws IOException { - // If this filename has not been encountered before (no entry for filename) or this filename - // has been renamed (RENAME entry for filename), the desired action should be recomputed. - if (!actions.containsKey(filename) || actions.get(filename).getType() == ActionType.RENAME) { - callback.resetForFile(filename); - entryFilter.accept(filename, callback); - } - checkState(actions.containsKey(filename), - "Action for file '%s' should have been set by ZipEntryFilter.", filename); - - EntryAction action = actions.get(filename); - // Only copy if this is the first instance of filename. - if (action.getType() == ActionType.COPY && entries.containsKey(filename)) { - action = new EntryAction(ActionType.SKIP, action); - actions.put(filename, action); - } - // Only rename if there is not already an entry with filename or filename's action is SKIP. - if (action.getType() == ActionType.RENAME) { - if (actions.containsKey(action.getNewName()) - && actions.get(action.getNewName()).getType() == ActionType.SKIP) { - action = new EntryAction(ActionType.SKIP, action); - } - if (entries.containsKey(action.getNewName())) { - action = new EntryAction(ActionType.SKIP, action); - } - } - return action; - } - - /** Writes an entry with the given name, date and external file attributes from the buffer. */ - private void writeEntryFromBuffer(ZipFileEntry entry, byte[] uncompressed) throws IOException { - CRC32 crc = new CRC32(); - crc.update(uncompressed); - - entry.setCrc(crc.getValue()); - entry.setSize(uncompressed.length); - if (mode == OutputMode.FORCE_STORED) { - entry.setMethod(Compression.STORED); - entry.setCompressedSize(uncompressed.length); - writeEntry(entry, new ByteArrayInputStream(uncompressed)); - } else { - ByteArrayOutputStream compressed = new ByteArrayOutputStream(); - copyStream(new DeflaterInputStream(new ByteArrayInputStream(uncompressed), getDeflater()), - compressed); - entry.setMethod(Compression.DEFLATED); - entry.setCompressedSize(compressed.size()); - writeEntry(entry, new ByteArrayInputStream(compressed.toByteArray())); - } - } - - /** - * Writes an entry from the specified source {@link ZipReader} and {@link ZipFileEntry} using the - * specified {@link EntryAction}. - * - *

Writes the output entry from the input entry performing inflation or deflation as needed - * and applies any values from the {@link EntryAction} as needed. - */ - private void writeEntry(ZipReader zip, ZipFileEntry entry, EntryAction action) - throws IOException { - checkArgument(action.getType() != ActionType.SKIP, - "Cannot write a zip entry whose action is of type SKIP."); - - ZipFileEntry outEntry = new ZipFileEntry(entry); - if (action.getType() == ActionType.RENAME) { - checkNotNull(action.getNewName(), - "ZipEntryFilter actions of type RENAME must not have a null filename."); - outEntry.setName(action.getNewName()); - } - - if (action.getDate() != null) { - outEntry.setTime(action.getDate().getTime()); - } - - InputStream data; - if (mode == OutputMode.FORCE_DEFLATE && entry.getMethod() != Compression.DEFLATED) { - // The output mode is deflate, but the entry compression is not. Create a deflater stream - // from the raw file data and deflate to a temporary byte array to determine the deflated - // size. Then use this byte array as the input stream for writing the entry. - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - copyStream(new DeflaterInputStream(zip.getRawInputStream(entry), getDeflater()), tmp); - data = new ByteArrayInputStream(tmp.toByteArray()); - outEntry.setMethod(Compression.DEFLATED); - outEntry.setCompressedSize(tmp.size()); - } else if (mode == OutputMode.FORCE_STORED && entry.getMethod() != Compression.STORED) { - // The output mode is stored, but the entry compression is not; create an inflater stream - // from the raw file data. - data = new InflaterInputStream(zip.getRawInputStream(entry), getInflater()); - outEntry.setMethod(Compression.STORED); - outEntry.setCompressedSize(entry.getSize()); - } else { - // Entry compression agrees with output mode; use the raw file data as is. - data = zip.getRawInputStream(entry); - } - writeEntry(outEntry, data); - } - - /** - * Writes the specified {@link ZipFileEntry} using the data from the given {@link InputStream}. - */ - private void writeEntry(ZipFileEntry entry, InputStream data) throws IOException { - out.putNextEntry(entry); - copyStream(data, out); - out.closeEntry(); - entries.put(entry.getName(), entry); - } - - /** - * Returns true if the combined ZIP file already contains a file of the specified file name. - * - * @param filename the filename of the file whose presence in the combined ZIP file is to be - * tested - * @return true if the combined ZIP file contains the specified file - */ - public boolean containsFile(String filename) { - // TODO(apell): may be slightly different behavior because v1 returns true on skipped names. - return entries.containsKey(filename); - } - - /** - * Writes any remaining output data to the output stream and also creates the merged entries by - * calling the {@link CustomMergeStrategy} implementations given back from the - * {@link ZipEntryFilter}. - * - * @throws IOException if the output stream or the filter throws an IOException - * @throws IllegalStateException if this method was already called earlier - */ - public void finish() throws IOException { - for (Map.Entry entry : actions.entrySet()) { - String filename = entry.getKey(); - EntryAction action = entry.getValue(); - if (action.getType() == ActionType.MERGE) { - ByteArrayOutputStream uncompressed = action.getMergeBuffer(); - action.getStrategy().finish(uncompressed); - if (uncompressed.size() == 0 && action.getStrategy().skipEmpty()) { - continue; - } - - ZipFileEntry e = new ZipFileEntry(filename); - e.setTime(action.getDate() != null ? action.getDate().getTime() : new Date().getTime()); - writeEntryFromBuffer(e, uncompressed.toByteArray()); - } - } - out.finish(); - } - - /** - * Writes any remaining output data to the output stream and closes it. - * - * @throws IOException if the output stream or the filter throws an IOException - */ - @Override public void close() throws IOException { - finish(); - out.close(); - } - - /** Ensures the truth of an expression involving one or more parameters to the calling method. */ - private static void checkArgument(boolean expression, - @Nullable String errorMessageTemplate, - @Nullable Object... errorMessageArgs) { - if (!expression) { - throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** Ensures that an object reference passed as a parameter to the calling method is not null. */ - public static T checkNotNull(T reference, - @Nullable String errorMessageTemplate, - @Nullable Object... errorMessageArgs) { - if (reference == null) { - // If either of these parameters is null, the right thing happens anyway - throw new NullPointerException(String.format(errorMessageTemplate, errorMessageArgs)); - } - return reference; - } - - /** Ensures the truth of an expression involving state. */ - private static void checkState(boolean expression, - @Nullable String errorMessageTemplate, - @Nullable Object... errorMessageArgs) { - if (!expression) { - throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipEntryFilter.java b/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipEntryFilter.java deleted file mode 100644 index c4692a775c947a..00000000000000 --- a/src/java_tools/singlejar/java/com/google/devtools/build/singlejar/ZipEntryFilter.java +++ /dev/null @@ -1,128 +0,0 @@ -// 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. - -package com.google.devtools.build.singlejar; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Date; - -/** - * A custom filter for entries when combining multiple ZIP files (or even just - * copying a single ZIP file). - * - *

Implementations of this interface must be thread-safe. The {@link - * #accept} method may be called concurrently by multiple threads. - */ -public interface ZipEntryFilter { - - /** - * Strategy for a custom merge operation. The current file and all additional - * file are passed to the strategy object via {@link #merge}, which merges - * the files. At the end of the ZIP combination, {@link #finish} is called, - * which then writes the merged single entry of that name. - * - *

Implementations of this interface are not required to be thread-safe. - * Thread-safety is achieved by creating multiple instances. Each instance - * that is separately passed to {@link StrategyCallback#customMerge} is - * guaranteed not to be called by two threads at the same time. - */ - interface CustomMergeStrategy { - - /** - * Merges another file into the current state. This method is called for - * every file entry of the same name. - */ - void merge(InputStream in, OutputStream out) throws IOException; - - /** - * Outputs the merged result into the given output stream. This method is - * only called once when no further file of the same name is available. - */ - void finish(OutputStream out) throws IOException; - - /** - * Called after {@link #finish} if no output was written to check if an empty file should be - * written. Returns {@code false} by default. - * @return {@code true} to skip empty merge results, {@code false} to write them. - */ - default boolean skipEmpty() { - return false; - } - } - - /** - * A callback interface for the {@link ZipEntryFilter#accept} method. Use - * this interface to indicate the type of processing for the given file name. - * For every file name, exactly one of the methods must be called once. A - * second method call throws {@link IllegalStateException}. - * - *

There is no guarantee that the callback will perform the requested - * operation at the time of the invocation. An implementation may choose to - * defer the operation to an arbitrary later time. - * - *

IMPORTANT NOTE: Do not implement this interface. It will be modified to - * support future extensions, and all implementations in this package will be - * updated. If you violate this advice, your code will break. - */ - interface StrategyCallback { - - /** - * Skips the current entry and all entries with the same name. - */ - void skip() throws IOException; - - /** - * Copies the current entry and skips all further entries with the same - * name. If {@code date} is non-null, then the timestamp of the entry is - * overwritten with the given value. - */ - void copy(Date date) throws IOException; - - /** - * Renames and copies the current entry, and skips all further entries with - * the same name. If {@code date} is non-null, then the timestamp of the entry - * is overwritten with the given value. - */ - void rename(String filename, Date date) throws IOException; - - /** - * Merges this and all further entries with the same name with the given - * {@link CustomMergeStrategy}. This method must never be called twice with - * the same object. If {@code date} is non-null, then the timestamp of the - * generated entry is set to the given value; otherwise, it is set to the - * current time. - */ - void customMerge(Date date, CustomMergeStrategy strategy) throws IOException; - } - - /** - * Determines the policy with which to handle the ZIP file entry with the - * given name and calls the appropriate method on the callback interface - * {@link StrategyCallback}. For every unique name in the set of all ZIP file - * entries, this method is called exactly once and the result is used for all - * entries of the same name. Except, if an entry is renamed, the original name - * is not considered as having been encountered yet. - * - *

Implementations should use the filename to distinguish the desired - * processing, call one method on the callback interface and return - * immediately after that call. - * - *

There is no guarantee that the callback will perform the requested - * operation at the time of the invocation. An implementation may choose to - * defer the operation to an arbitrary later time. - */ - void accept(String filename, StrategyCallback callback) throws IOException; -} diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/zip/BUILD b/src/java_tools/singlejar/java/com/google/devtools/build/zip/BUILD index e876551507088a..e21c956cb6f5f6 100644 --- a/src/java_tools/singlejar/java/com/google/devtools/build/zip/BUILD +++ b/src/java_tools/singlejar/java/com/google/devtools/build/zip/BUILD @@ -4,7 +4,12 @@ load("@rules_java//java:defs.bzl", "java_library") # Zip provides a library for reading and writing zip files, allowing more # advanced manipulation than the JDK equivalents by providing detailed zip # entry data and raw file access. -package(default_visibility = ["//src/java_tools/singlejar:singlejar_package_group"]) +package(default_visibility = [":singlejar_package_group"]) + +package_group( + name = "singlejar_package_group", + packages = ["//src/java_tools/singlejar/..."], +) filegroup( name = "srcs", diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/BUILD b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/BUILD deleted file mode 100644 index 19d3582ed591cb..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/BUILD +++ /dev/null @@ -1,33 +0,0 @@ -load("@rules_java//java:defs.bzl", "java_library", "java_test") - -# Description: -# Tests for SingleJar -package(default_visibility = ["//src/java_tools/singlejar:singlejar_package_group"]) - -java_library( - name = "libSingleJarTesting", - testonly = 1, - srcs = glob(["*.java"]), - deps = [ - "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:libSingleJar", - "//src/java_tools/singlejar/java/com/google/devtools/build/singlejar:libSingleJarMain", - "//src/java_tools/singlejar/java/com/google/devtools/build/zip", - "//src/test/java/com/google/devtools/build/lib/testutil:TestSuite", - "//third_party:guava", - "//third_party:jsr305", - "//third_party:junit4", - "//third_party:truth", - ], -) - -java_test( - name = "SingleJarTests", - test_class = "com.google.devtools.build.singlejar.SingleJarTests", - runtime_deps = [":libSingleJarTesting"], -) - -filegroup( - name = "srcs", - srcs = glob(["*.java"]) + ["BUILD"], - visibility = ["//src:__subpackages__"], -) diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ConcatenateStrategyTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ConcatenateStrategyTest.java deleted file mode 100644 index 1d46ffa8a3d4d3..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ConcatenateStrategyTest.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.UTF_8; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link ConcatenateStrategy}. */ -@RunWith(JUnit4.class) -public class ConcatenateStrategyTest { - - private String merge(String... inputs) throws IOException { - return mergeInternal(true, inputs); - } - - private String mergeNoNewLine(String... inputs) throws IOException { - return mergeInternal(false, inputs); - } - - private String mergeInternal(boolean appendNewLine, String... inputs) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ConcatenateStrategy strategy = new ConcatenateStrategy(appendNewLine); - for (String input : inputs) { - strategy.merge(new ByteArrayInputStream(input.getBytes(UTF_8)), out); - } - strategy.finish(out); - return new String(out.toByteArray(), UTF_8); - } - - @Test - public void testSingleInput() throws IOException { - assertThat(merge("a")).isEqualTo("a"); - assertThat(mergeNoNewLine("a")).isEqualTo("a"); - } - - @Test - public void testTwoInputs() throws IOException { - assertThat(merge("a\n", "b")).isEqualTo("a\nb"); - assertThat(mergeNoNewLine("a\n", "b")).isEqualTo("a\nb"); - } - - @Test - public void testAutomaticNewline() throws IOException { - assertThat(merge("a", "b")).isEqualTo("a\nb"); - assertThat(mergeNoNewLine("a", "b")).isEqualTo("ab"); - } - - @Test - public void testAutomaticNewlineAndEmptyFile() throws IOException { - assertThat(merge("a", "", "b")).isEqualTo("a\nb"); - assertThat(mergeNoNewLine("a", "", "b")).isEqualTo("ab"); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/CopyEntryFilterTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/CopyEntryFilterTest.java deleted file mode 100644 index 47f4a361d0956d..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/CopyEntryFilterTest.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; - -import java.io.IOException; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link CopyEntryFilter}. */ -@RunWith(JUnit4.class) -public class CopyEntryFilterTest { - - @Test - public void testSingleInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new CopyEntryFilter().accept("abc", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("copy")); - } - -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/DefaultJarEntryFilterTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/DefaultJarEntryFilterTest.java deleted file mode 100644 index 2e2e910143e67d..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/DefaultJarEntryFilterTest.java +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Date; -import java.util.jar.JarFile; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link DefaultJarEntryFilter}. - */ -@RunWith(JUnit4.class) -public class DefaultJarEntryFilterTest { - - private static final Date DOS_EPOCH = ZipCombiner.DOS_EPOCH; - - @Test - public void testSingleInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("abc", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("copy")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testProtobufExtensionsInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("protobuf.meta", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("customMerge")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testManifestInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept(JarFile.MANIFEST_NAME, callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("skip")); - } - - @Test - public void testServiceInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("META-INF/services/any.service", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("customMerge")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testSpringHandlers() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("META-INF/spring.handlers", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("customMerge")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testSpringSchemas() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("META-INF/spring.schemas", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("customMerge")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testReferenceConfigs() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("reference.conf", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("customMerge")); - assertThat(callback.dates).isEqualTo(Arrays.asList(DOS_EPOCH)); - } - - @Test - public void testClassInput() throws IOException { - RecordingCallback callback = new RecordingCallback(); - new DefaultJarEntryFilter().accept("a.class", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("copy")); - assertThat(callback.dates) - .isEqualTo(Arrays.asList(DefaultJarEntryFilter.DOS_EPOCH_PLUS_2_SECONDS)); - } - - @Test - public void testOtherSkippedInputs() throws IOException { - RecordingCallback callback = new RecordingCallback(); - ZipEntryFilter filter = new DefaultJarEntryFilter(); - filter.accept("a.SF", callback); - filter.accept("a.DSA", callback); - filter.accept("a.RSA", callback); - assertThat(callback.calls).isEqualTo(Arrays.asList("skip", "skip", "skip")); - assertThat(callback.dates).isEqualTo(Arrays.asList()); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/FakeZipFile.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/FakeZipFile.java deleted file mode 100644 index 9b4a2dcb7c53a0..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/FakeZipFile.java +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.devtools.build.singlejar.SingleJarTest.EntryMode; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * A fake zip file to assert that a given {@link ZipInputStream} contains - * specified entries in a specified order. Just for unit testing. - */ -public final class FakeZipFile { - - /** - * Validates an input provided as a byte array. - */ - public static interface ByteValidator { - /** - * Check if {@code object} is the expected input. If {@code object} does not match the expected - * pattern, an assertion should fails with the necessary message. - */ - void validate(byte[] object); - } - - private static void assertSameByteArray(byte[] expected, byte[] actual) { - if (expected == null) { - assertThat(actual).isNull(); - } else { - assertThat(actual).isEqualTo(expected); - } - } - - private static byte[] readZipEntryContent(ZipInputStream zipInput) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int bytesCopied; - while ((bytesCopied = zipInput.read(buffer)) != -1) { - out.write(buffer, 0, bytesCopied); - } - return out.toByteArray(); - } - - private static final class PlainByteValidator implements ByteValidator { - private final byte[] expected; - - private PlainByteValidator(String expected) { - this.expected = expected == null ? new byte[0] : expected.getBytes(UTF_8); - } - - @Override - public void validate(byte[] object) { - assertSameByteArray(expected, object); - } - - } - - private static final class FakeZipEntry { - - private final String name; - private final ByteValidator content; - private final Date date; - private final byte[] extra; - private final EntryMode mode; - - private FakeZipEntry(String name, Date date, String content, byte[] extra, EntryMode mode) { - this.name = name; - this.date = date; - this.content = new PlainByteValidator(content); - this.extra = extra; - this.mode = mode; - } - - private FakeZipEntry(String name, Date date, ByteValidator content, byte[] extra, - EntryMode mode) { - this.name = name; - this.date = date; - this.content = content; - this.extra = extra; - this.mode = mode; - } - - public void assertNext(ZipInputStream zipInput) throws IOException { - ZipEntry zipEntry = zipInput.getNextEntry(); - assertThat(zipEntry).isNotNull(); - switch (mode) { - case EXPECT_DEFLATE: - assertThat(zipEntry.getMethod()).isEqualTo(ZipEntry.DEFLATED); - break; - case EXPECT_STORED: - assertThat(zipEntry.getMethod()).isEqualTo(ZipEntry.STORED); - break; - default: - // we don't care. - break; - } - assertThat(zipEntry.getName()).isEqualTo(name); - if (date != null) { - assertThat(zipEntry.getTime()).isEqualTo(date.getTime()); - } - assertSameByteArray(extra, zipEntry.getExtra()); - content.validate(readZipEntryContent(zipInput)); - } - } - - private final List entries = new ArrayList<>(); - - public FakeZipFile addEntry(String name, String content) { - entries.add(new FakeZipEntry(name, null, content, null, EntryMode.DONT_CARE)); - return this; - } - - public FakeZipFile addEntry(String name, String content, boolean compressed) { - entries.add(new FakeZipEntry(name, null, content, null, - compressed ? EntryMode.EXPECT_DEFLATE : EntryMode.EXPECT_STORED)); - return this; - } - - public FakeZipFile addEntry(String name, Date date, String content) { - entries.add(new FakeZipEntry(name, date, content, null, EntryMode.DONT_CARE)); - return this; - } - - public FakeZipFile addEntry(String name, Date date, String content, boolean compressed) { - entries.add(new FakeZipEntry(name, date, content, null, - compressed ? EntryMode.EXPECT_DEFLATE : EntryMode.EXPECT_STORED)); - return this; - } - - public FakeZipFile addEntry(String name, ByteValidator content) { - entries.add(new FakeZipEntry(name, null, content, null, EntryMode.DONT_CARE)); - return this; - } - - public FakeZipFile addEntry(String name, ByteValidator content, boolean compressed) { - entries.add(new FakeZipEntry(name, null, content, null, - compressed ? EntryMode.EXPECT_DEFLATE : EntryMode.EXPECT_STORED)); - return this; - } - - public FakeZipFile addEntry(String name, Date date, ByteValidator content) { - entries.add(new FakeZipEntry(name, date, content, null, EntryMode.DONT_CARE)); - return this; - } - - public FakeZipFile addEntry(String name, Date date, ByteValidator content, - boolean compressed) { - entries.add(new FakeZipEntry(name, date, content, null, - compressed ? EntryMode.EXPECT_DEFLATE : EntryMode.EXPECT_STORED)); - return this; - } - - public FakeZipFile addEntry(String name, byte[] extra) { - entries.add(new FakeZipEntry(name, null, (String) null, extra, EntryMode.DONT_CARE)); - return this; - } - - public FakeZipFile addEntry(String name, byte[] extra, boolean compressed) { - entries.add(new FakeZipEntry(name, null, (String) null, extra, - compressed ? EntryMode.EXPECT_DEFLATE : EntryMode.EXPECT_STORED)); - return this; - } - - private byte[] preamble = null; - - public FakeZipFile addPreamble(byte[] contents) { - preamble = Arrays.copyOf(contents, contents.length); - return this; - } - - private int getUnsignedShort(byte[] source, int offset) { - int a = source[offset + 0] & 0xff; - int b = source[offset + 1] & 0xff; - return (b << 8) | a; - } - - public void assertSame(byte[] data) throws IOException { - int offset = 0; - int length = data.length; - if (preamble != null) { - offset += preamble.length; - length -= offset; - byte[] maybePreamble = Arrays.copyOfRange(data, 0, offset); - assertThat(maybePreamble).isEqualTo(preamble); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(data, offset, length)); - for (FakeZipEntry entry : entries) { - entry.assertNext(zipInput); - } - assertThat(zipInput.getNextEntry()).isNull(); - // Verify that the end of central directory data is correct. - // This assumes that the end of directory is at the end of input and that there is no zip file - // comment. - int count = getUnsignedShort(data, data.length-14); - assertThat(count).isEqualTo(entries.size()); - count = getUnsignedShort(data, data.length-12); - assertThat(count).isEqualTo(entries.size()); - } - - /** - * Assert that {@code expected} is the same zip file as {@code actual}. It is similar to - * {@link org.junit.Assert#assertArrayEquals(byte[], byte[])} but should use a more - * helpful error message. - */ - public static void assertSame(byte[] expected, byte[] actual) throws IOException { - // First parse the zip files, then compare to have explicit comparison messages. - ZipInputStream expectedZip = new ZipInputStream(new ByteArrayInputStream(expected)); - ZipInputStream actualZip = new ZipInputStream(new ByteArrayInputStream(actual)); - StringBuffer actualFileList = new StringBuffer(); - StringBuffer expectedFileList = new StringBuffer(); - Map actualEntries = new HashMap(); - Map expectedEntries = new HashMap(); - Map actualEntryContents = new HashMap(); - Map expectedEntryContents = new HashMap(); - parseZipEntry(expectedZip, expectedFileList, expectedEntries, expectedEntryContents); - parseZipEntry(actualZip, actualFileList, actualEntries, actualEntryContents); - // Compare the ordered file list first. - assertThat(actualFileList.toString()).isEqualTo(expectedFileList.toString()); - - // Then compare each entry. - for (String name : expectedEntries.keySet()) { - ZipEntry expectedEntry = expectedEntries.get(name); - ZipEntry actualEntry = actualEntries.get(name); - assertWithMessage("Time differs for " + name) - .that(actualEntry.getTime()) - .isEqualTo(expectedEntry.getTime()); - assertWithMessage("Extraneous content differs for " + name) - .that(actualEntry.getExtra()) - .isEqualTo(expectedEntry.getExtra()); - assertWithMessage("Content differs for " + name) - .that(actualEntryContents.get(name)) - .isEqualTo(expectedEntryContents.get(name)); - } - - // Finally do a binary array comparison to be sure that test fails if files are different in - // some way we don't test. - assertThat(actual).isEqualTo(expected); - } - - private static void parseZipEntry(ZipInputStream expectedZip, StringBuffer expectedFileList, - Map expectedEntries, Map expectedEntryContents) - throws IOException { - ZipEntry expectedEntry; - while ((expectedEntry = expectedZip.getNextEntry()) != null) { - expectedFileList.append(expectedEntry.getName()).append("\n"); - expectedEntries.put(expectedEntry.getName(), expectedEntry); - expectedEntryContents.put(expectedEntry.getName(), readZipEntryContent(expectedZip)); - } - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java deleted file mode 100644 index 6ebef2001f59c3..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/MockSimpleFileSystem.java +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.UTF_8; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.HashMap; -import java.util.Map; - -/** - * FileSystem for testing. FileSystem supports exactly one one OutputStream for filename - * specified in constructor. - * Workflow for using this class in tests are following: - *

- */ -public final class MockSimpleFileSystem implements SimpleFileSystem { - - private final String outputFileName; - private ByteArrayOutputStream out; - private final Map files = new HashMap<>(); - - public MockSimpleFileSystem(String outputFileName) { - this.outputFileName = outputFileName; - } - - public void addFile(String name, byte[] content) { - files.put(name, content); - } - - public void addFile(String name, String content) { - files.put(name, content.getBytes(UTF_8)); - } - - @Override - public OutputStream getOutputStream(String filename) { - assertThat(filename).isEqualTo(outputFileName); - assertThat(out).isNull(); - out = new ByteArrayOutputStream(); - return out; - } - - @Override - public InputStream getInputStream(String filename) throws IOException { - byte[] data = files.get(filename); - if (data == null) { - throw new FileNotFoundException(); - } - return new ByteArrayInputStream(data); - } - - @Override - public File getFile(String filename) throws IOException { - byte[] data = files.get(filename); - if (data == null) { - throw new FileNotFoundException(); - } - File file = File.createTempFile(filename, null); - Files.copy(new ByteArrayInputStream(data), file.toPath(), StandardCopyOption.REPLACE_EXISTING); - return file; - } - - @Override - public boolean delete(String filename) { - assertThat(filename).isEqualTo(outputFileName); - assertThat(out).isNotNull(); - out = null; - return true; - } - - public byte[] toByteArray() { - assertThat(out).isNotNull(); - return out.toByteArray(); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/OptionFileExpanderTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/OptionFileExpanderTest.java deleted file mode 100644 index f6556b950ca235..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/OptionFileExpanderTest.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertThrows; - -import com.google.devtools.build.singlejar.OptionFileExpander.OptionFileProvider; -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link OptionFileExpander}. - */ -@RunWith(JUnit4.class) -public class OptionFileExpanderTest { - - private static class StoredOptionFileProvider implements OptionFileProvider { - - private Map availableFiles = new HashMap<>(); - - void addFile(String filename, String content) { - availableFiles.put(filename, content.getBytes(UTF_8)); - } - - @Override - public InputStream getInputStream(String filename) throws IOException { - byte[] result = availableFiles.get(filename); - if (result == null) { - throw new FileNotFoundException(); - } - return new ByteArrayInputStream(result); - } - } - - @Test - public void testNoExpansion() throws IOException { - OptionFileExpander expander = new OptionFileExpander(new StoredOptionFileProvider()); - assertThat(expander.expandArguments(Arrays.asList("--some", "option", "list"))) - .isEqualTo(Arrays.asList("--some", "option", "list")); - } - - @Test - public void testExpandSimpleOptionsFile() throws IOException { - StoredOptionFileProvider provider = new StoredOptionFileProvider(); - provider.addFile("options", "--some option list"); - OptionFileExpander expander = new OptionFileExpander(provider); - assertThat(expander.expandArguments(Arrays.asList("@options"))) - .isEqualTo(Arrays.asList("--some", "option", "list")); - } - - @Test - public void testIllegalOptionsFile() { - StoredOptionFileProvider provider = new StoredOptionFileProvider(); - provider.addFile("options", "'missing apostrophe"); - OptionFileExpander expander = new OptionFileExpander(provider); - assertThrows(IOException.class, () -> expander.expandArguments(Arrays.asList("@options"))); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/PrefixListPathFilterTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/PrefixListPathFilterTest.java deleted file mode 100644 index 030ae277a593ff..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/PrefixListPathFilterTest.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertWithMessage; - -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.singlejar.DefaultJarEntryFilter.PathFilter; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Tests {@link PrefixListPathFilter}. - */ -@RunWith(JUnit4.class) -public class PrefixListPathFilterTest { - private PathFilter filter; - - @Test - public void testPrefixList() { - filter = new PrefixListPathFilter(ImmutableList.of("dir1", "dir/subdir")); - assertIncluded("dir1/file1"); - assertExcluded("dir2/file1"); - assertIncluded("dir/subdir/file1"); - assertExcluded("dir2/subdir/file1"); - assertExcluded("dir/othersub/file1"); - assertExcluded("dir3/file1"); - } - - private void assertExcluded(String path) { - assertWithMessage(path + " should have been excluded, but was included") - .that(filter.allowed(path)) - .isFalse(); - } - - private void assertIncluded(String path) { - assertWithMessage(path + " should have been included but was not") - .that(filter.allowed(path)) - .isTrue(); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/RecordingCallback.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/RecordingCallback.java deleted file mode 100644 index e77dd47bfd30e3..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/RecordingCallback.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 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.singlejar; - - -import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; -import com.google.devtools.build.singlejar.ZipEntryFilter.StrategyCallback; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * A helper implementation of {@link StrategyCallback} that records callback - * invocations as string. - */ -public final class RecordingCallback implements StrategyCallback { - - public final List calls = new ArrayList<>(); - public final List dates = new ArrayList<>(); - - @Override - public void copy(Date date) { - calls.add("copy"); - dates.add(date); - } - - @Override - public void rename(String filename, Date date) { - calls.add("rename"); - dates.add(date); - } - - @Override - public void customMerge(Date date, CustomMergeStrategy strategy) { - calls.add("customMerge"); - dates.add(date); - } - - @Override - public void skip() { - calls.add("skip"); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java deleted file mode 100644 index 688ec149d41c9e..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTest.java +++ /dev/null @@ -1,648 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertThrows; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.singlejar.FakeZipFile.ByteValidator; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.jar.JarFile; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link SingleJar}. - */ -@RunWith(JUnit4.class) -public class SingleJarTest { - - public static final byte[] EXTRA_FOR_META_INF = new byte[] {(byte) 0xFE, (byte) 0xCA, 0x00, 0x00}; - - static final Joiner LINE_JOINER = Joiner.on("\r\n"); - static final Joiner LINEFEED_JOINER = Joiner.on("\n"); - - static enum EntryMode { - DONT_CARE, EXPECT_DEFLATE, EXPECT_STORED; - } - - public static final class BuildInfoValidator implements ByteValidator { - private final List buildInfoLines; - - public BuildInfoValidator(List buildInfoLines) { - this.buildInfoLines = buildInfoLines; - } - - @Override - public void validate(byte[] content) { - String actualBuildInfo = new String(content, StandardCharsets.UTF_8); - List expectedBuildInfos = new ArrayList<>(); - for (String line : buildInfoLines) { // the character : is escaped - expectedBuildInfos.add(line.replace(":", "\\:")); - } - Collections.sort(expectedBuildInfos); - String[] actualBuildInfos = actualBuildInfo.split("\n"); - Arrays.sort(actualBuildInfos); - assertThat(LINEFEED_JOINER.join(actualBuildInfos)) - .isEqualTo(LINEFEED_JOINER.join(expectedBuildInfos)); - } - - } - - // Manifest file line ordering is dependent of the ordering in HashMap (Attributes class) so - // we do a sorted comparison for Manifest. - public static final class ManifestValidator implements ByteValidator { - private final List manifestLines; - - public ManifestValidator(List manifestLines) { - this.manifestLines = new ArrayList<>(manifestLines); - Collections.sort(this.manifestLines); - } - - public ManifestValidator(String... manifestLines) { - this.manifestLines = Arrays.asList(manifestLines); - Collections.sort(this.manifestLines); - } - - @Override - public void validate(byte[] content) { - String actualManifest = new String(content, StandardCharsets.UTF_8); - String[] actualManifestLines = actualManifest.trim().split("\r\n"); - Arrays.sort(actualManifestLines); - assertThat(LINEFEED_JOINER.join(actualManifestLines)) - .isEqualTo(LINEFEED_JOINER.join(manifestLines)); - } - - } - - private BuildInfoValidator redactedBuildData(String outputJar) { - return new BuildInfoValidator(ImmutableList.of("build.target=" + outputJar)); - } - - private BuildInfoValidator redactedBuildData(String outputJar, String mainClass) { - return new BuildInfoValidator( - ImmutableList.of("build.target=" + outputJar, "main.class=" + mainClass)); - } - - static List getBuildInfo() { - return ImmutableList.of("build.build_id=11111-222-33333", - "build.version=12659499", - "build.location=user@machine.domain.com:/home/user/source", - "build.target=output.jar", - "build.time=Fri Jan 2 02:17:36 1970 (123456)", - "build.timestamp=Fri Jan 2 02:17:36 1970 (123456)", - "build.timestamp.as.int=123456" - ); - } - - private byte[] sampleZip() { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!"); - return factory.toByteArray(); - } - - private byte[] sampleUncompressedZip() { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!", false); - return factory.toByteArray(); - } - - private byte[] sampleZipWithSF() { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.SF", "Hello World!"); - return factory.toByteArray(); - } - - private byte[] sampleZipWithSubdirs() { - ZipFactory factory = new ZipFactory(); - factory.addFile("dir1/file1", "contents11"); - factory.addFile("dir1/file2", "contents12"); - factory.addFile("dir2/file1", "contents21"); - factory.addFile("dir3/file1", "contents31"); - return factory.toByteArray(); - } - - private void assertStripFirstLine(String expected, String testCase) { - byte[] result = SingleJar.stripFirstLine(testCase.getBytes(StandardCharsets.UTF_8)); - assertThat(new String(result, UTF_8)).isEqualTo(expected); - } - - @Test - public void testStripFirstLine() { - assertStripFirstLine("", ""); - assertStripFirstLine("", "no linefeed"); - assertStripFirstLine(LINEFEED_JOINER.join("toto", "titi"), - LINEFEED_JOINER.join("# timestamp comment", "toto", "titi")); - assertStripFirstLine(LINE_JOINER.join("toto", "titi"), - LINE_JOINER.join("# timestamp comment", "toto", "titi")); - } - - @Test - public void testEmptyJar() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("build-data.properties", redactedBuildData("output.jar")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - // Test that two identical calls at different time actually returns identical results - @Test - public void testDeterministicJar() throws IOException, InterruptedException { - MockSimpleFileSystem mockFs1 = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar1 = new SingleJar(mockFs1); - singleJar1.run(ImmutableList.of("--output", "output.jar", "--extra_build_info", "toto=titi", - "--normalize")); - Thread.sleep(1000); // ensure that we are not at the same seconds - - MockSimpleFileSystem mockFs2 = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar2 = new SingleJar(mockFs2); - singleJar2.run(ImmutableList.of("--output", "output.jar", "--extra_build_info", "toto=titi", - "--normalize")); - - FakeZipFile.assertSame(mockFs1.toByteArray(), mockFs2.toByteArray()); - } - - @Test - public void testExtraManifestContent() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--deploy_manifest_lines", - "Main-Class: SomeClass", "X-Other: Duh")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar", - "Main-Class: SomeClass", - "X-Other: Duh")) - .addEntry("build-data.properties", redactedBuildData("output.jar")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testMultipleExtraManifestContent() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--deploy_manifest_lines", "X-Other: Duh", - "--output", "output.jar", - "--deploy_manifest_lines", "Main-Class: SomeClass")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar", - "Main-Class: SomeClass", - "X-Other: Duh")) - .addEntry("build-data.properties", redactedBuildData("output.jar")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testMainClass() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--main_class", "SomeClass")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar", - "Main-Class: SomeClass")) - .addEntry("build-data.properties", redactedBuildData("output.jar", "SomeClass")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - // These four tests test all combinations of compressed/uncompressed input and output. - @Test - public void testSimpleZip() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("test.jar", sampleZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "test.jar")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", redactedBuildData("output.jar"), false) - .addEntry("hello.txt", "Hello World!", false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSimpleZipExpectCompressedOutput() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("test.jar", sampleZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "test.jar", - "--compression")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar"), true) - .addEntry("build-data.properties", redactedBuildData("output.jar"), true) - .addEntry("hello.txt", "Hello World!", true); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSimpleUncompressedZip() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("test.jar", sampleUncompressedZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "test.jar")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator(ImmutableList.of( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")), false) - .addEntry("build-data.properties", redactedBuildData("output.jar"), false) - .addEntry("hello.txt", "Hello World!", false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSimpleUncompressedZipExpectCompressedOutput() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("test.jar", sampleUncompressedZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "test.jar", - "--compression")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar"), true) - .addEntry("build-data.properties", redactedBuildData("output.jar"), true) - .addEntry("hello.txt", "Hello World!", true); - expectedResult.assertSame(mockFs.toByteArray()); - } - - // Integration test for option file expansion. - @Test - public void testOptionFile() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZip()); - mockFs.addFile("options", "--output output.jar --sources input.jar"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("@options")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("build-data.properties", redactedBuildData("output.jar")) - .addEntry("hello.txt", "Hello World!"); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSkipsSignatureFiles() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZipWithSF()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "input.jar")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("build-data.properties", redactedBuildData("output.jar")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSkipsUsingInputPrefixes() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZipWithSubdirs()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", - "input.jar", "--include_prefixes", "dir1", "dir2")); - - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("build-data.properties", redactedBuildData("output.jar")) - .addEntry("dir1/file1", "contents11") - .addEntry("dir1/file2", "contents12") - .addEntry("dir2/file1", "contents21"); - - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testSkipsUsingMultipleInputPrefixes() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZipWithSubdirs()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--include_prefixes", "dir2", - "--sources", "input.jar", "--include_prefixes", "dir1")); - - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("build-data.properties", redactedBuildData("output.jar")) - .addEntry("dir1/file1", "contents11") - .addEntry("dir1/file2", "contents12") - .addEntry("dir2/file1", "contents21"); - - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testNormalize() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "input.jar", - "--normalize")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, ZipCombiner.DOS_EPOCH, new ManifestValidator( - "Manifest-Version: 1.0", "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", ZipCombiner.DOS_EPOCH, - redactedBuildData("output.jar"), false) - .addEntry("hello.txt", ZipCombiner.DOS_EPOCH, "Hello World!", false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testNormalizeAndCompress() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("input.jar", sampleZip()); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--sources", "input.jar", - "--normalize", "--compression")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, ZipCombiner.DOS_EPOCH, new ManifestValidator( - "Manifest-Version: 1.0", "Created-By: blaze-singlejar"), true) - .addEntry("build-data.properties", ZipCombiner.DOS_EPOCH, - redactedBuildData("output.jar"), true) - .addEntry("hello.txt", ZipCombiner.DOS_EPOCH, "Hello World!", true); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testAddBuildInfoProperties() throws IOException { - List buildInfo = getBuildInfo(); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", new BuildInfoValidator(buildInfo), - false); - - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - List args = new ArrayList<>(); - args.add("--output"); - args.add("output.jar"); - args.addAll(infoPropertyArguments(buildInfo)); - singleJar.run(args); - expectedResult.assertSame(mockFs.toByteArray()); - } - - private static List infoPropertyArguments(List buildInfoLines) { - List args = new ArrayList<>(); - for (String s : buildInfoLines) { - if (!s.isEmpty()) { - args.add("--extra_build_info"); - args.add(s); - } - } - return args; - } - - @Test - public void testAddBuildInfoPropertiesFile() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - doTestAddBuildInfoPropertiesFile(mockFs, "output.jar", singleJar); - } - - public static void doTestAddBuildInfoPropertiesFile(MockSimpleFileSystem mockFs, String target, - SingleJar singleJar) throws IOException { - List buildInfo = getBuildInfo(); - mockFs.addFile("my.properties", makePropertyFileFromBuildInfo(buildInfo)); - singleJar.run(ImmutableList.of("--output", target, "--build_info_file", "my.properties")); - - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", new BuildInfoValidator(buildInfo), - false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - private static String makePropertyFileFromBuildInfo(List buildInfo) { - return LINEFEED_JOINER.join(buildInfo).replace(":", "\\:"); - } - - @Test - public void testAddBuildInfoPropertiesFiles() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - doTestAddBuildInfoPropertiesFiles(mockFs, "output.jar", singleJar); - } - - public static void doTestAddBuildInfoPropertiesFiles(MockSimpleFileSystem mockFs, String target, - SingleJar singleJar) throws IOException { - List buildInfo = getBuildInfo(); - - mockFs.addFile("my1.properties", makePropertyFileFromBuildInfo(buildInfo.subList(0, 4))); - mockFs.addFile("my2.properties", - makePropertyFileFromBuildInfo(buildInfo.subList(4, buildInfo.size()))); - singleJar.run(ImmutableList.of("--output", target, - "--build_info_file", "my1.properties", - "--build_info_file", "my2.properties")); - - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", new BuildInfoValidator(buildInfo), - false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testAddBuildInfoPropertiesAndFiles() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - doTestAddBuildInfoPropertiesAndFiles(mockFs, "output.jar", singleJar); - } - - public static void doTestAddBuildInfoPropertiesAndFiles(MockSimpleFileSystem mockFs, - String target, SingleJar singleJar) throws IOException { - List buildInfo = getBuildInfo(); - - mockFs.addFile("my1.properties", makePropertyFileFromBuildInfo(buildInfo.subList(0, 4))); - mockFs.addFile("my2.properties", makePropertyFileFromBuildInfo( - buildInfo.subList(4, buildInfo.size()))); - List args = ImmutableList.builder() - .add("--output").add(target) - .add("--build_info_file").add("my1.properties") - .add("--build_info_file").add("my2.properties") - .addAll(infoPropertyArguments(buildInfo.subList(4, buildInfo.size()))) - .build(); - - singleJar.run(args); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF, false) - .addEntry(JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar"), false) - .addEntry("build-data.properties", new BuildInfoValidator(buildInfo), - false); - expectedResult.assertSame(mockFs.toByteArray()); - } - - - @Test - public void testExcludeBuildData() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - SingleJar singleJar = new SingleJar(mockFs); - doTestExcludeBuildData(mockFs, "output.jar", singleJar); - } - - public static void doTestExcludeBuildData(MockSimpleFileSystem mockFs, String target, - SingleJar singleJar) throws IOException { - singleJar.run(ImmutableList.of("--output", target, "--exclude_build_data")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testResourceMapping() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("a/b/c", "Test"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--exclude_build_data", - "--resources", "a/b/c:c/b/a")); - FakeZipFile expectedResult = new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry(JarFile.MANIFEST_NAME, new ManifestValidator( - "Manifest-Version: 1.0", - "Created-By: blaze-singlejar")) - .addEntry("c/", (String) null) - .addEntry("c/b/", (String) null) - .addEntry("c/b/a", "Test"); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testResourceMappingIdentity() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("a/b/c", "Test"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--exclude_build_data", - "--resources", "a/b/c")); - FakeZipFile expectedResult = - new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry( - JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar")) - .addEntry("a/", (String) null) - .addEntry("a/b/", (String) null) - .addEntry("a/b/c", "Test"); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testResourceMappingDuplicateError() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("a/b/c", "Test"); - SingleJar singleJar = new SingleJar(mockFs); - IllegalArgumentException e = - assertThrows( - IllegalArgumentException.class, - () -> - singleJar.run( - ImmutableList.of( - "--output", - "output.jar", - "--exclude_build_data", - "--resources", - "a/b/c", - "a/b/c"))); - assertThat(e).hasMessageThat().contains("already contains a file named 'a/b/c'."); - } - - @Test - public void testResourceMappingDuplicateWarning() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - mockFs.addFile("a/b/c", "Test"); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", "--exclude_build_data", - "--warn_duplicate_resources", "--resources", "a/b/c", "a/b/c")); - FakeZipFile expectedResult = - new FakeZipFile() - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry( - JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar")) - .addEntry("a/", (String) null) - .addEntry("a/b/", (String) null) - .addEntry("a/b/c", "Test"); - expectedResult.assertSame(mockFs.toByteArray()); - } - - @Test - public void testCanAddPreamble() throws IOException { - MockSimpleFileSystem mockFs = new MockSimpleFileSystem("output.jar"); - String preamble = "WeThePeople"; - mockFs.addFile(preamble, preamble.getBytes(UTF_8)); - SingleJar singleJar = new SingleJar(mockFs); - singleJar.run(ImmutableList.of("--output", "output.jar", - "--java_launcher", preamble, - "--main_class", "SomeClass")); - FakeZipFile expectedResult = - new FakeZipFile() - .addPreamble(preamble.getBytes(UTF_8)) - .addEntry("META-INF/", EXTRA_FOR_META_INF) - .addEntry( - JarFile.MANIFEST_NAME, - new ManifestValidator("Manifest-Version: 1.0", "Created-By: blaze-singlejar", - "Main-Class: SomeClass")) - .addEntry("build-data.properties", redactedBuildData("output.jar", "SomeClass")); - expectedResult.assertSame(mockFs.toByteArray()); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTests.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTests.java deleted file mode 100644 index 2eedeb9bec53a4..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SingleJarTests.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2015 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.singlejar; - - -import com.google.devtools.build.lib.testutil.ClasspathSuite; - -import org.junit.runner.RunWith; - -/** - * A test-suite builder for this package. - */ -@RunWith(ClasspathSuite.class) -public class SingleJarTests { -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SlowConcatenateStrategy.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SlowConcatenateStrategy.java deleted file mode 100644 index c82e89ad7ebc71..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/SlowConcatenateStrategy.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 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.singlejar; - - -import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import javax.annotation.concurrent.NotThreadSafe; - -/** - * A strategy that merges a set of files by concatenating them. It inserts no - * additional characters and copies bytes one by one. Used for testing. - */ -@NotThreadSafe -final class SlowConcatenateStrategy implements CustomMergeStrategy { - - @Override - public void merge(InputStream in, OutputStream out) throws IOException { - int nextByte; - while ((nextByte = in.read()) != -1) { - out.write(nextByte); - } - } - - @Override - public void finish(OutputStream out) { - // No need to do anything. All the data was already written. - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java deleted file mode 100644 index b8e118df1bda74..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipCombinerTest.java +++ /dev/null @@ -1,800 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.ISO_8859_1; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertThrows; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; -import com.google.devtools.build.singlejar.ZipCombiner.OutputMode; -import com.google.devtools.build.singlejar.ZipEntryFilter.CustomMergeStrategy; -import com.google.devtools.build.zip.ExtraData; -import com.google.devtools.build.zip.ZipFileEntry; -import com.google.devtools.build.zip.ZipReader; -import com.google.devtools.build.zip.ZipUtil; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.jar.JarOutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link ZipCombiner}. - */ -@RunWith(JUnit4.class) -public class ZipCombinerTest { - @Rule public TemporaryFolder tmp = new TemporaryFolder(); - @Rule public ExpectedException thrown = ExpectedException.none(); - - private File sampleZip() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!"); - return writeInputStreamToFile(factory.toInputStream()); - } - - private File sampleZip2() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello2.txt", "Hello World 2!"); - return writeInputStreamToFile(factory.toInputStream()); - } - - private File sampleZipWithTwoEntries() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!"); - factory.addFile("hello2.txt", "Hello World 2!"); - return writeInputStreamToFile(factory.toInputStream()); - } - - private File sampleZipWithOneUncompressedEntry() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!", false); - return writeInputStreamToFile(factory.toInputStream()); - } - - private File sampleZipWithTwoUncompressedEntries() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", "Hello World!", false); - factory.addFile("hello2.txt", "Hello World 2!", false); - return writeInputStreamToFile(factory.toInputStream()); - } - - private File writeInputStreamToFile(InputStream in) throws IOException { - File out = tmp.newFile(); - Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING); - return out; - } - - private void assertEntry(ZipInputStream zipInput, String filename, long time, byte[] content) - throws IOException { - ZipEntry zipEntry = zipInput.getNextEntry(); - assertThat(zipEntry).isNotNull(); - assertThat(zipEntry.getName()).isEqualTo(filename); - assertThat(zipEntry.getTime()).isEqualTo(time); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int bytesCopied; - while ((bytesCopied = zipInput.read(buffer)) != -1) { - out.write(buffer, 0, bytesCopied); - } - assertThat(out.toByteArray()).isEqualTo(content); - } - - private void assertEntry(ZipInputStream zipInput, String filename, byte[] content) - throws IOException { - assertEntry(zipInput, filename, ZipUtil.DOS_EPOCH, content); - } - - private void assertEntry(ZipInputStream zipInput, String filename, String content) - throws IOException { - assertEntry(zipInput, filename, content.getBytes(ISO_8859_1)); - } - - private void assertEntry(ZipInputStream zipInput, String filename, Date date, String content) - throws IOException { - assertEntry(zipInput, filename, date.getTime(), content.getBytes(ISO_8859_1)); - } - - @Test - public void testCompressedDontCare() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZip()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testCompressedForceDeflate() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_DEFLATE, out)) { - zipCombiner.addZip(sampleZip()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testCompressedForceStored() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_STORED, out)) { - zipCombiner.addZip(sampleZip()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testUncompressedDontCare() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testUncompressedForceDeflate() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_DEFLATE, out)) { - zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", true); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testUncompressedForceStored() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(OutputMode.FORCE_STORED, out)) { - zipCombiner.addZip(sampleZipWithOneUncompressedEntry()); - } - FakeZipFile expectedResult = new FakeZipFile().addEntry("hello.txt", "Hello World!", false); - expectedResult.assertSame(out.toByteArray()); - } - - @Test - public void testCopyTwoEntries() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testCopyTwoUncompressedEntries() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testCombine() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZip2()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testDuplicateEntry() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZip()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testBadZipFileNoEntry() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - thrown.expect(ZipException.class); - thrown.expectMessage("It does not contain an end of central directory record."); - zipCombiner.addZip(writeInputStreamToFile(new ByteArrayInputStream(new byte[] {1, 2, 3, 4}))); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertThat(zipInput.getNextEntry()).isNull(); - } - - private InputStream asStream(String content) { - return new ByteArrayInputStream(content.getBytes(UTF_8)); - } - - @Test - public void testAddFile() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addFile("hello.txt", ZipCombiner.DOS_EPOCH, asStream("Hello World!")); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testAddFileAndDuplicateZipEntry() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - zipCombiner.addFile("hello.txt", ZipCombiner.DOS_EPOCH, asStream("Hello World!")); - zipCombiner.addZip(sampleZip()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - static final class MergeStrategyPlaceHolder implements CustomMergeStrategy { - - @Override - public void finish(OutputStream out) { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(InputStream in, OutputStream out) { - throw new UnsupportedOperationException(); - } - } - - private static final CustomMergeStrategy COPY_PLACEHOLDER = new MergeStrategyPlaceHolder(); - private static final CustomMergeStrategy SKIP_PLACEHOLDER = new MergeStrategyPlaceHolder(); - - /** - * A mock implementation that either uses the specified behavior or calls - * through to copy. - */ - class MockZipEntryFilter implements ZipEntryFilter { - - private Date date = ZipCombiner.DOS_EPOCH; - private final List calls = new ArrayList<>(); - // File name to merge strategy map. - private final Map behavior = - new HashMap<>(); - private final ListMultimap renameMap = ArrayListMultimap.create(); - - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - calls.add(filename); - CustomMergeStrategy strategy = behavior.get(filename); - if (strategy == null) { - callback.copy(null); - } else if (strategy == COPY_PLACEHOLDER) { - List names = renameMap.get(filename); - if (names != null && !names.isEmpty()) { - // rename to the next name in list of replacement names. - String newName = names.get(0); - callback.rename(newName, null); - // Unless this is the last replacment names, we pop the used name. - // The lastreplacement name applies any additional entries. - if (names.size() > 1) { - names.remove(0); - } - } else { - callback.copy(null); - } - } else if (strategy == SKIP_PLACEHOLDER) { - callback.skip(); - } else { - callback.customMerge(date, strategy); - } - } - } - - @Test - public void testCopyCallsFilter() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt"); - } - - @Test - public void testDuplicateEntryCallsFilterOnce() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZip()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt"); - } - - @Test - public void testMergeStrategy() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt").inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertEntry(zipInput, "hello.txt", "Hello World!\nHello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testMergeStrategyWithUncompressedFiles() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); - mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - } - assertThat(mockFilter.calls).isEqualTo(Arrays.asList("hello.txt", "hello2.txt")); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!\nHello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testMergeStrategyWithSlowCopy() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls).isEqualTo(Arrays.asList("hello.txt", "hello2.txt")); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertEntry(zipInput, "hello.txt", "Hello World!Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testMergeStrategyWithUncompressedFilesAndSlowCopy() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); - mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt").inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - private File specialZipWithMinusOne() throws IOException { - ZipFactory factory = new ZipFactory(); - factory.addFile("hello.txt", new byte[] {-1}); - return writeInputStreamToFile(factory.toInputStream()); - } - - @Test - public void testMergeStrategyWithSlowCopyAndNegativeBytes() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new SlowConcatenateStrategy()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(specialZipWithMinusOne()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt"); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", new byte[] { -1 }); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testCopyDateHandling() throws IOException { - final Date date = new GregorianCalendar(2009, 8, 2, 0, 0, 0).getTime(); - ZipEntryFilter mockFilter = - new ZipEntryFilter() { - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - assertThat(filename).isEqualTo("hello.txt"); - callback.copy(date); - } - }; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", date, "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testMergeDateHandling() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", new ConcatenateStrategy()); - mockFilter.date = new GregorianCalendar(2009, 8, 2, 0, 0, 0).getTime(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZip()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls).containsExactly("hello.txt", "hello2.txt").inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello2.txt", ZipCombiner.DOS_EPOCH, "Hello World 2!"); - assertEntry(zipInput, "hello.txt", mockFilter.date, "Hello World!\nHello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - @Test - public void testDuplicateCallThrowsException() throws IOException { - ZipEntryFilter badFilter = new ZipEntryFilter() { - @Override - public void accept(String filename, StrategyCallback callback) throws IOException { - // Duplicate callback call. - callback.skip(); - callback.copy(null); - } - }; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(badFilter, out)) { - assertThrows(IllegalStateException.class, () -> zipCombiner.addZip(sampleZip())); - } - } - - @Test - public void testNoCallThrowsException() throws IOException { - ZipEntryFilter badFilter = new ZipEntryFilter() { - @Override - public void accept(String filename, StrategyCallback callback) { - // No callback call. - } - }; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(badFilter, out)) { - assertThrows(IllegalStateException.class, () -> zipCombiner.addZip(sampleZip())); - } - } - - // This test verifies that if an entry A is renamed as A (identy mapping), - // then subsequent entries named A are still subject to filtering. - // Note: this is different from a copy, where subsequent entries are skipped. - @Test - public void testRenameIdentityMapping() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); - mockFilter.renameMap.put("hello.txt", "hello.txt"); // identity rename, not copy - mockFilter.renameMap.put("hello2.txt", "hello2.txt"); // identity rename, not copy - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls) - .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello2.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // This test verifies that multiple entries with the same name can be - // renamed to unique names. - @Test - public void testRenameNoConflictMapping() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); - mockFilter.renameMap.putAll("hello.txt", Arrays.asList("hello1.txt", "hello2.txt")); - mockFilter.renameMap.putAll("hello2.txt", Arrays.asList("world1.txt", "world2.txt")); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls) - .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello2.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello1.txt", "Hello World!"); - assertEntry(zipInput, "world1.txt", "Hello World 2!"); - assertEntry(zipInput, "hello2.txt", "Hello World!"); - assertEntry(zipInput, "world2.txt", "Hello World 2!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // This tests verifies that an attempt to rename an entry to a - // name already written, results in the entry being skipped, after - // calling the filter. - @Test - public void testRenameSkipUsedName() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); - mockFilter.renameMap.putAll("hello.txt", - Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); - mockFilter.renameMap.put("hello2.txt", "hello2.txt"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls) - .containsExactly( - "hello.txt", "hello2.txt", "hello.txt", "hello2.txt", "hello.txt", "hello2.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello1.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertEntry(zipInput, "hello3.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // This tests verifies that if an entry has been copied, then - // further entries of the same name are skipped (filter not invoked), - // and entries renamed to the same name are skipped (after calling filter). - @Test - public void testRenameAndCopy() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); - mockFilter.renameMap.putAll("hello.txt", - Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls) - .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello1.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertEntry(zipInput, "hello3.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // This tests verifies that if an entry has been skipped, then - // further entries of the same name are skipped (filter not invoked), - // and entries renamed to the same name are skipped (after calling filter). - @Test - public void testRenameAndSkip() throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", SKIP_PLACEHOLDER); - mockFilter.renameMap.putAll("hello.txt", - Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - zipCombiner.addZip(sampleZipWithTwoEntries()); - } - assertThat(mockFilter.calls) - .containsExactly("hello.txt", "hello2.txt", "hello.txt", "hello.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello1.txt", "Hello World!"); - assertEntry(zipInput, "hello3.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // This test verifies that renaming works when input and output - // disagree on compression method. This is the simple case, where - // content is read and rewritten, and no header repair is needed. - @Test - public void testRenameWithUncompressedFiles () throws IOException { - MockZipEntryFilter mockFilter = new MockZipEntryFilter(); - mockFilter.behavior.put("hello.txt", COPY_PLACEHOLDER); - mockFilter.behavior.put("hello2.txt", COPY_PLACEHOLDER); - mockFilter.renameMap.putAll("hello.txt", - Arrays.asList("hello1.txt", "hello2.txt", "hello3.txt")); - mockFilter.renameMap.put("hello2.txt", "hello2.txt"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(mockFilter, out)) { - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - zipCombiner.addZip(sampleZipWithTwoUncompressedEntries()); - } - assertThat(mockFilter.calls) - .containsExactly( - "hello.txt", "hello2.txt", "hello.txt", "hello2.txt", "hello.txt", "hello2.txt") - .inOrder(); - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - assertEntry(zipInput, "hello1.txt", "Hello World!"); - assertEntry(zipInput, "hello2.txt", "Hello World 2!"); - assertEntry(zipInput, "hello3.txt", "Hello World!"); - assertThat(zipInput.getNextEntry()).isNull(); - } - - // The next two tests check that ZipCombiner can handle a ZIP with an data - // descriptor marker in the compressed data, i.e. that it does not scan for - // the data descriptor marker. It's unfortunately a bit tricky to create such - // a ZIP. - private static final int LOCAL_FILE_HEADER_MARKER = 0x04034b50; - - // Create a ZIP with a partial entry. - private InputStream zipWithPartialEntry() { - ByteBuffer out = ByteBuffer.wrap(new byte[32]).order(ByteOrder.LITTLE_ENDIAN); - out.clear(); - // file header - out.putInt(LOCAL_FILE_HEADER_MARKER); // file header signature - out.putShort((short) 6); // version to extract - out.putShort((short) 0); // general purpose bit flag - out.putShort((short) ZipOutputStream.STORED); // compression method - out.putShort((short) 0); // mtime (00:00:00) - out.putShort((short) 0x21); // mdate (1.1.1980) - out.putInt(0); // crc32 - out.putInt(10); // compressed size - out.putInt(10); // uncompressed size - out.putShort((short) 1); // file name length - out.putShort((short) 0); // extra field length - out.put((byte) 'a'); // file name - - // file contents - out.put((byte) 0x01); - // Unexpected end of file. - - return new ByteArrayInputStream(out.array()); - } - - @Test - public void testBadZipFilePartialEntry() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = new ZipCombiner(out)) { - thrown.expect(ZipException.class); - thrown.expectMessage("It does not contain an end of central directory record."); - zipCombiner.addZip(writeInputStreamToFile(zipWithPartialEntry())); - } - } - - @Test - public void testZipCombinerAgainstJavaUtil() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (JarOutputStream jarOut = new JarOutputStream(out)) { - ZipEntry entry; - entry = new ZipEntry("META-INF/"); - entry.setTime(ZipCombiner.DOS_EPOCH.getTime()); - entry.setMethod(JarOutputStream.STORED); - entry.setSize(0); - entry.setCompressedSize(0); - entry.setCrc(0); - jarOut.putNextEntry(entry); - entry = new ZipEntry("META-INF/MANIFEST.MF"); - entry.setTime(ZipCombiner.DOS_EPOCH.getTime()); - entry.setMethod(JarOutputStream.DEFLATED); - jarOut.putNextEntry(entry); - jarOut.write(new byte[] {1, 2, 3, 4}); - } - File javaFile = writeInputStreamToFile(new ByteArrayInputStream(out.toByteArray())); - out.reset(); - - try (ZipCombiner zipcombiner = new ZipCombiner(out)) { - zipcombiner.addDirectory("META-INF/", ZipCombiner.DOS_EPOCH, - new ExtraData[] {new ExtraData((short) 0xCAFE, new byte[0])}); - zipcombiner.addFile("META-INF/MANIFEST.MF", ZipCombiner.DOS_EPOCH, - new ByteArrayInputStream(new byte[] {1, 2, 3, 4})); - } - File zipCombinerFile = writeInputStreamToFile(new ByteArrayInputStream(out.toByteArray())); - byte[] zipCombinerRaw = out.toByteArray(); - - new ZipTester(zipCombinerRaw).validate(); - assertZipFilesEquivalent(zipCombinerFile, javaFile); - } - - void assertZipFilesEquivalent(File a, File b) throws IOException { - try (ZipReader x = new ZipReader(a); - ZipReader y = new ZipReader(b)) { - Collection xEntries = x.entries(); - Collection yEntries = y.entries(); - assertThat(xEntries).hasSize(yEntries.size()); - Iterator xIter = xEntries.iterator(); - Iterator yIter = yEntries.iterator(); - for (int i = 0; i < xEntries.size(); i++) { - assertZipEntryEquivalent(xIter.next(), yIter.next()); - } - } - } - - void assertZipEntryEquivalent(ZipFileEntry x, ZipFileEntry y) { - assertThat(x.getComment()).isEqualTo(y.getComment()); - assertThat(x.getCompressedSize()).isEqualTo(y.getCompressedSize()); - assertThat(x.getCrc()).isEqualTo(y.getCrc()); - assertThat(x.getExternalAttributes()).isEqualTo(y.getExternalAttributes()); - // The JDK adds different extra data to zip files on different platforms, so we don't compare - // the extra data. - assertThat(x.getInternalAttributes()).isEqualTo(y.getInternalAttributes()); - assertThat(x.getMethod()).isEqualTo(y.getMethod()); - assertThat(x.getName()).isEqualTo(y.getName()); - assertThat(x.getSize()).isEqualTo(y.getSize()); - assertThat(x.getTime()).isEqualTo(y.getTime()); - assertThat(x.getVersion()).isEqualTo(y.getVersion()); - assertThat(x.getVersionNeeded()).isEqualTo(y.getVersionNeeded()); - // Allow general purpose bit 3 (data descriptor) used in jdk7 to differ. - // Allow general purpose bit 11 (UTF-8 encoding) used in jdk7 to differ. - assertThat(x.getFlags() | (1 << 3) | (1 << 11)) - .isEqualTo(y.getFlags() | (1 << 3) | (1 << 11)); - } - - /** - * Ensures that the code that grows the central directory and the code that patches it is not - * obviously broken. - */ - @Test - public void testLotsOfFiles() throws IOException { - int fileCount = 100; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (ZipCombiner zipCombiner = - new ZipCombiner(OutputMode.DONT_CARE, new CopyEntryFilter(), out)) { - for (int i = 0; i < fileCount; i++) { - zipCombiner.addFile("hello" + i, ZipCombiner.DOS_EPOCH, asStream("Hello " + i + "!")); - } - } - ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); - for (int i = 0; i < fileCount; i++) { - assertEntry(zipInput, "hello" + i, "Hello " + i + "!"); - } - assertThat(zipInput.getNextEntry()).isNull(); - new ZipTester(out.toByteArray()).validate(); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipFactory.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipFactory.java deleted file mode 100644 index e102d8a435aa30..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipFactory.java +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015 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.singlejar; - -import static java.nio.charset.StandardCharsets.ISO_8859_1; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -/** - * A helper class to create zip files for testing. - */ -public class ZipFactory { - - static class Entry { - private final String name; - private final byte[] content; - private final boolean compressed; - private Entry(String name, byte[] content, boolean compressed) { - this.name = name; - this.content = content; - this.compressed = compressed; - } - } - - private final List entries = new ArrayList<>(); - - // Assumes that content was created locally. Does not perform a defensive copy! - private void addEntry(String name, byte[] content, boolean compressed) { - entries.add(new Entry(name, content, compressed)); - } - - public ZipFactory addFile(String name, String content) { - addEntry(name, content.getBytes(ISO_8859_1), true); - return this; - } - - public ZipFactory addFile(String name, byte[] content) { - addEntry(name, content.clone(), true); - return this; - } - - public ZipFactory addFile(String name, String content, boolean compressed) { - addEntry(name, content.getBytes(ISO_8859_1), compressed); - return this; - } - - public ZipFactory addFile(String name, byte[] content, boolean compressed) { - addEntry(name, content.clone(), compressed); - return this; - } - - public byte[] toByteArray() { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ZipOutputStream zipper = new ZipOutputStream(out); - for (Entry entry : entries) { - ZipEntry zipEntry = new ZipEntry(entry.name); - if (entry.compressed) { - zipEntry.setMethod(ZipEntry.DEFLATED); - } else { - zipEntry.setMethod(ZipEntry.STORED); - zipEntry.setSize(entry.content.length); - zipEntry.setCrc(calculateCrc32(entry.content)); - } - zipEntry.setTime(ZipCombiner.DOS_EPOCH.getTime()); - zipper.putNextEntry(zipEntry); - zipper.write(entry.content); - zipper.closeEntry(); - } - zipper.close(); - return out.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public InputStream toInputStream() { - return new ByteArrayInputStream(toByteArray()); - } - - public static long calculateCrc32(byte[] content) { - CRC32 crc = new CRC32(); - crc.update(content); - return crc.getValue(); - } -} diff --git a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipTester.java b/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipTester.java deleted file mode 100644 index f2e8b3a8445dd6..00000000000000 --- a/src/java_tools/singlejar/javatests/com/google/devtools/build/singlejar/ZipTester.java +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2015 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.singlejar; - - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.CRC32; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * A helper class to validate zip files and provide reasonable diagnostics (better than what zip - * does). We might want to make this into a fully-fledged binary some day. - */ -final class ZipTester { - - // The following constants are ZIP-specific. - private static final int LOCAL_FILE_HEADER_MARKER = 0x04034b50; - private static final int DATA_DESCRIPTOR_MARKER = 0x08074b50; - private static final int CENTRAL_DIRECTORY_MARKER = 0x02014b50; - private static final int END_OF_CENTRAL_DIRECTORY_MARKER = 0x06054b50; - - private static final int FILE_HEADER_BUFFER_SIZE = 26; // without marker - private static final int DATA_DESCRIPTOR_BUFFER_SIZE = 12; // without marker - - private static final int DIRECTORY_ENTRY_BUFFER_SIZE = 42; // without marker - private static final int END_OF_CENTRAL_DIRECTORY_BUFFER_SIZE = 18; // without marker - - // Set if the size, compressed size and CRC are set to zero, and present in - // the data descriptor after the data. - private static final int SIZE_MASKED_FLAG = 1 << 3; - - private static final int STORED_METHOD = 0; - private static final int DEFLATE_METHOD = 8; - - private static final int VERSION_STORED = 10; // Version 1.0 - private static final int VERSION_DEFLATE = 20; // Version 2.0 - - private static class Entry { - private final long pos; - private final String name; - private final int flags; - private final int method; - private final int dosTime; - Entry(long pos, String name, int flags, int method, int dosTime) { - this.pos = pos; - this.name = name; - this.flags = flags; - this.method = method; - this.dosTime = dosTime; - } - } - - private final InputStream in; - private final byte[] buffer = new byte[1024]; - private int bufferLength; - private int bufferOffset; - private long pos; - - private List entries = new ArrayList(); - - public ZipTester(InputStream in) { - this.in = in; - } - - public ZipTester(byte[] data) { - this(new ByteArrayInputStream(data)); - } - - private void warn(String msg) { - System.err.println("WARNING: " + msg); - } - - private void readMoreData(String action) throws IOException { - if ((bufferLength > 0) && (bufferOffset > 0)) { - System.arraycopy(buffer, bufferOffset, buffer, 0, bufferLength); - } - if (bufferLength >= buffer.length) { - // The buffer size is specifically chosen to avoid this situation. - throw new AssertionError("Internal error: buffer overrun."); - } - bufferOffset = 0; - int bytesRead = in.read(buffer, bufferLength, buffer.length - bufferLength); - if (bytesRead <= 0) { - throw new IOException("Unexpected end of file, while " + action); - } - bufferLength += bytesRead; - } - - private int readByte(String action) throws IOException { - if (bufferLength == 0) { - readMoreData(action); - } - byte result = buffer[bufferOffset]; - bufferOffset++; bufferLength--; - pos++; - return result & 0xff; - } - - private long getUnsignedInt(String action) throws IOException { - int a = readByte(action); - int b = readByte(action); - int c = readByte(action); - int d = readByte(action); - return ((d << 24) | (c << 16) | (b << 8) | a) & 0xffffffffL; - } - - private long getUnsignedInt(byte[] source, int offset) { - int a = source[offset + 0] & 0xff; - int b = source[offset + 1] & 0xff; - int c = source[offset + 2] & 0xff; - int d = source[offset + 3] & 0xff; - return ((d << 24) | (c << 16) | (b << 8) | a) & 0xffffffffL; - } - - private void readFully(byte[] buffer, String action) throws IOException { - for (int i = 0; i < buffer.length; i++) { - buffer[i] = (byte) readByte(action); - } - } - - private void skip(long length, String action) throws IOException { - for (long i = 0; i < length; i++) { - readByte(action); - } - } - - private int getUnsignedShort(byte[] source, int offset) { - int a = source[offset + 0] & 0xff; - int b = source[offset + 1] & 0xff; - return (b << 8) | a; - } - - private class DeflateInputStream extends InputStream { - - private final byte[] singleByteBuffer = new byte[1]; - private int consumedBytes; - private final Inflater inflater = new Inflater(true); - private long totalBytesRead; - - private int inflateData(byte[] dest, int off, int len) - throws IOException { - consumedBytes = 0; - int bytesProduced = 0; - int bytesConsumed = 0; - while ((bytesProduced == 0) && !inflater.finished()) { - inflater.setInput(buffer, bufferOffset + bytesConsumed, bufferLength - bytesConsumed); - int remainingBefore = inflater.getRemaining(); - try { - bytesProduced = inflater.inflate(dest, off, len); - } catch (DataFormatException e) { - throw new IOException("Invalid deflate stream in ZIP file.", e); - } - bytesConsumed += remainingBefore - inflater.getRemaining(); - consumedBytes = bytesConsumed; - if (bytesProduced == 0) { - if (inflater.needsDictionary()) { - // The DEFLATE algorithm as used in the ZIP file format does not - // require an additional dictionary. - throw new AssertionError("Inflater unexpectedly requires a dictionary."); - } else if (inflater.needsInput()) { - readMoreData("need more data for deflate"); - } else if (inflater.finished()) { - return 0; - } else { - // According to the Inflater specification, this cannot happen. - throw new AssertionError("Inflater unexpectedly produced no output."); - } - } - } - return bytesProduced; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (inflater.finished()) { - return -1; - } - int length = inflateData(b, off, len); - totalBytesRead += consumedBytes; - bufferLength -= consumedBytes; - bufferOffset += consumedBytes; - pos += consumedBytes; - return length == 0 ? -1 : length; - } - - @Override - public int read() throws IOException { - int bytesRead = read(singleByteBuffer, 0, 1); - return (bytesRead == -1) ? -1 : (singleByteBuffer[0] & 0xff); - } - } - - private void readEntry() throws IOException { - long entrypos = pos - 4; - String entryDesc = "file entry at " + Long.toHexString(entrypos); - byte[] entryBuffer = new byte[FILE_HEADER_BUFFER_SIZE]; - readFully(entryBuffer, "reading file header"); - int versionToExtract = getUnsignedShort(entryBuffer, 0); - int flags = getUnsignedShort(entryBuffer, 2); - int method = getUnsignedShort(entryBuffer, 4); - int dosTime = (int) getUnsignedInt(entryBuffer, 6); - int crc32 = (int) getUnsignedInt(entryBuffer, 10); - long compressedSize = getUnsignedInt(entryBuffer, 14); - long uncompressedSize = getUnsignedInt(entryBuffer, 18); - int filenameLength = getUnsignedShort(entryBuffer, 22); - int extraLength = getUnsignedShort(entryBuffer, 24); - - byte[] filename = new byte[filenameLength]; - readFully(filename, "reading file name"); - skip(extraLength, "skipping extra data"); - - String name = new String(filename, "UTF-8"); - for (int i = 0; i < filename.length; i++) { - if ((filename[i] < ' ') || (filename[i] > 127)) { - warn(entryDesc + ": file name has unexpected non-ascii characters"); - } - } - entryDesc = "file entry '" + name + "' at " + Long.toHexString(entrypos); - - if ((method != STORED_METHOD) && (method != DEFLATE_METHOD)) { - throw new IOException(entryDesc + ": unknown method " + method); - } - if ((flags != 0) && (flags != SIZE_MASKED_FLAG)) { - throw new IOException(entryDesc + ": unknown flags " + flags); - } - if ((method == STORED_METHOD) && (versionToExtract != VERSION_STORED)) { - warn(entryDesc + ": unexpected version to extract for stored entry " + versionToExtract); - } - if ((method == DEFLATE_METHOD) && (versionToExtract != VERSION_DEFLATE)) { -// warn(entryDesc + ": unexpected version to extract for deflated entry " + versionToExtract); - } - - if (method == STORED_METHOD) { - if (compressedSize != uncompressedSize) { - throw new IOException(entryDesc + ": stored entries should have identical compressed and " - + "uncompressed sizes"); - } - skip(compressedSize, entryDesc + "skipping data"); - } else { - // No OS resources are actually allocated. - @SuppressWarnings("resource") DeflateInputStream deflater = new DeflateInputStream(); - long generatedBytes = 0; - byte[] deflated = new byte[1024]; - int readBytes; - CRC32 crc = new CRC32(); - while ((readBytes = deflater.read(deflated)) > 0) { - crc.update(deflated, 0, readBytes); - generatedBytes += readBytes; - } - int actualCrc32 = (int) crc.getValue(); - long consumedBytes = deflater.totalBytesRead; - if (flags == SIZE_MASKED_FLAG) { - long id = getUnsignedInt("reading footer marker"); - if (id != DATA_DESCRIPTOR_MARKER) { - throw new IOException(entryDesc + ": expected footer at " + Long.toHexString(pos - 4) - + ", but found " + Long.toHexString(id)); - } - byte[] footer = new byte[DATA_DESCRIPTOR_BUFFER_SIZE]; - readFully(footer, "reading footer"); - crc32 = (int) getUnsignedInt(footer, 0); - compressedSize = getUnsignedInt(footer, 4); - uncompressedSize = getUnsignedInt(footer, 8); - } - - if (consumedBytes != compressedSize) { - throw new IOException(entryDesc + ": amount of compressed data does not match value " - + "specified in the zip (specified: " + compressedSize + ", actual: " + consumedBytes - + ")"); - } - if (generatedBytes != uncompressedSize) { - throw new IOException(entryDesc + ": amount of uncompressed data does not match value " - + "specified in the zip (specified: " + uncompressedSize + ", actual: " - + generatedBytes + ")"); - } - if (crc32 != actualCrc32) { - throw new IOException(entryDesc + ": specified crc checksum does not match actual check " - + "sum"); - } - } - entries.add(new Entry(entrypos, name, flags, method, dosTime)); - } - - @SuppressWarnings("unused") // A couple of unused local variables. - private void validateCentralDirectoryEntry(Entry entry) throws IOException { - long entrypos = pos - 4; - String entryDesc = "file directory entry '" + entry.name + "' at " + Long.toHexString(entrypos); - - byte[] entryBuffer = new byte[DIRECTORY_ENTRY_BUFFER_SIZE]; - readFully(entryBuffer, "reading central directory entry"); - int versionMadeBy = getUnsignedShort(entryBuffer, 0); - int versionToExtract = getUnsignedShort(entryBuffer, 2); - int flags = getUnsignedShort(entryBuffer, 4); - int method = getUnsignedShort(entryBuffer, 6); - int dosTime = (int) getUnsignedInt(entryBuffer, 8); - int crc32 = (int) getUnsignedInt(entryBuffer, 12); - long compressedSize = getUnsignedInt(entryBuffer, 16); - long uncompressedSize = getUnsignedInt(entryBuffer, 20); - int filenameLength = getUnsignedShort(entryBuffer, 24); - int extraLength = getUnsignedShort(entryBuffer, 26); - int commentLength = getUnsignedShort(entryBuffer, 28); - int diskNumberStart = getUnsignedShort(entryBuffer, 30); - int internalAttributes = getUnsignedShort(entryBuffer, 32); - int externalAttributes = (int) getUnsignedInt(entryBuffer, 34); - long offset = getUnsignedInt(entryBuffer, 38); - - byte[] filename = new byte[filenameLength]; - readFully(filename, "reading file name"); - skip(extraLength, "skipping extra data"); - String name = new String(filename, "UTF-8"); - - if (!name.equals(entry.name)) { - throw new IOException(entryDesc + ": file name in central directory does not match original " - + "name"); - } - if (offset != entry.pos) { - throw new IOException(entryDesc); - } - if (flags != entry.flags) { - throw new IOException(entryDesc); - } - if (method != entry.method) { - throw new IOException(entryDesc); - } - if (dosTime != entry.dosTime) { - throw new IOException(entryDesc); - } - } - - private void validateCentralDirectory() throws IOException { - boolean first = true; - for (Entry entry : entries) { - if (first) { - first = false; - } else { - long id = getUnsignedInt("reading marker"); - if (id != CENTRAL_DIRECTORY_MARKER) { - throw new IOException(); - } - } - validateCentralDirectoryEntry(entry); - } - } - - @SuppressWarnings("unused") // A couple of unused local variables. - private void validateEndOfCentralDirectory() throws IOException { - long id = getUnsignedInt("expecting end of central directory"); - byte[] entryBuffer = new byte[END_OF_CENTRAL_DIRECTORY_BUFFER_SIZE]; - readFully(entryBuffer, "reading end of central directory"); - int diskNumber = getUnsignedShort(entryBuffer, 0); - int startDiskNumber = getUnsignedShort(entryBuffer, 2); - int numEntries = getUnsignedShort(entryBuffer, 4); - int numTotalEntries = getUnsignedShort(entryBuffer, 6); - long centralDirectorySize = getUnsignedInt(entryBuffer, 8); - long centralDirectoryOffset = getUnsignedInt(entryBuffer, 12); - int commentLength = getUnsignedShort(entryBuffer, 16); - if (diskNumber != 0) { - throw new IOException(String.format("diskNumber=%d", diskNumber)); - } - if (startDiskNumber != 0) { - throw new IOException(String.format("startDiskNumber=%d", diskNumber)); - } - if (numEntries != numTotalEntries) { - throw new IOException(String.format("numEntries=%d numTotalEntries=%d", - numEntries, numTotalEntries)); - } - if (numEntries != (entries.size() % 0x10000)) { - throw new IOException("bad number of entries in central directory footer"); - } - if (numTotalEntries != (entries.size() % 0x10000)) { - throw new IOException("bad number of entries in central directory footer"); - } - if (commentLength != 0) { - throw new IOException("Zip file comment is unexpected"); - } - if (id != END_OF_CENTRAL_DIRECTORY_MARKER) { - throw new IOException("Expected end of central directory marker"); - } - } - - public void validate() throws IOException { - while (true) { - long id = getUnsignedInt("reading marker"); - if (id == LOCAL_FILE_HEADER_MARKER) { - readEntry(); - } else if (id == CENTRAL_DIRECTORY_MARKER) { - validateCentralDirectory(); - validateEndOfCentralDirectory(); - return; - } else { - throw new IOException("unexpected result for marker: " - + Long.toHexString(id) + " at position " + Long.toHexString(pos - 4)); - } - } - } -} diff --git a/src/test/java/com/google/devtools/build/android/desugar/BUILD b/src/test/java/com/google/devtools/build/android/desugar/BUILD index 55c1831fa2fab2..b3108e9c832d08 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/BUILD +++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD @@ -1879,16 +1879,11 @@ grep lambda $(location subclass_disassembled.txt) > $(location subclass_lambda_s ], ) -_SINGLEJAR_IMPLS = { - "singlejar": "//src/tools/singlejar:singlejar_local", - "JavaSingleJar": "//src/java_tools/singlejar:SingleJar", -} - -[sh_test( - name = "desugar_deps_consistent_%s_test" % label, +sh_test( + name = "desugar_deps_consistent_singlejar_test", srcs = ["desugar_deps_consistency_test.sh"], args = [ - "$(location %s)" % singlejar, + "$(location //src/tools/singlejar:singlejar_local)", "$(location @bazel_tools//tools/jdk:jar)", "$(location :testdata_desugared_default_methods.jar)", "$(location :separate_java8_desugared_default_methods.jar)", @@ -1898,19 +1893,19 @@ _SINGLEJAR_IMPLS = { ":guava_at_head_desugared.jar", ":separate_java8_desugared_default_methods.jar", ":testdata_desugared_default_methods.jar", - singlejar, + "//src/tools/singlejar:singlejar_local", "@bazel_tools//tools/jdk:jar", ], tags = ["no_windows"], -) for label, singlejar in _SINGLEJAR_IMPLS.items()] +) # Tests that deps checking fails due to missing default method: libseparate_java8.jar defines # default methods which testdata_desugared_default_methods.jar assumes have been moved to companion. -[sh_test( - name = "desugar_deps_missing_%s_fail_test" % label, +sh_test( + name = "desugar_deps_missing_singlejar_fail_test", srcs = ["desugar_deps_consistency_test.sh"], args = [ - "$(location %s)" % singlejar, + "$(location //src/tools/singlejar:singlejar_local)", "$(location @bazel_tools//tools/jdk:jar)", "$(location :testdata_desugared_default_methods.jar)", "$(location :libseparate_java8.jar)", @@ -1920,36 +1915,36 @@ _SINGLEJAR_IMPLS = { ":guava_at_head_desugared.jar", ":libseparate_java8.jar", ":testdata_desugared_default_methods.jar", - singlejar, + "//src/tools/singlejar:singlejar_local", "@bazel_tools//tools/jdk:jar", ], tags = ["no_windows"], -) for label, singlejar in _SINGLEJAR_IMPLS.items()] +) # Regression test for b/68049457 with deps checking failing due to missing static method: # b68049457_caller_desugared.jar assumes presence of companion class that's missing (in fact, # the whole Jar is missing, similar to neverlink situations). -[sh_test( - name = "desugar_deps_b68049457_%s_fail_test" % label, +sh_test( + name = "desugar_deps_b68049457_singlejar_fail_test", srcs = ["desugar_deps_consistency_test.sh"], args = [ - "$(location %s)" % singlejar, + "$(location //src/tools/singlejar:singlejar_local)", "$(location @bazel_tools//tools/jdk:jar)", "$(location :b68049457_caller_desugared.jar)", ], data = [ ":b68049457_caller_desugared.jar", - singlejar, + "//src/tools/singlejar:singlejar_local", "@bazel_tools//tools/jdk:jar", ], tags = ["no_windows"], -) for label, singlejar in _SINGLEJAR_IMPLS.items()] +) -[sh_test( - name = "desugar_deps_missed_default_method_%s_fail_test" % label, +sh_test( + name = "desugar_deps_missed_default_method_singlejar_fail_test", srcs = ["desugar_deps_consistency_test.sh"], args = [ - "$(location %s)" % singlejar, + "$(location //src/tools/singlejar:singlejar_local)", "$(location @bazel_tools//tools/jdk:jar)", "$(location :testdata_desugared_with_missing_dep.jar)", "$(location :separate_java8_desugared_default_methods.jar)", @@ -1959,30 +1954,30 @@ _SINGLEJAR_IMPLS = { ":guava_at_head_desugared.jar", ":separate_java8_desugared_default_methods.jar", ":testdata_desugared_with_missing_dep.jar", - singlejar, + "//src/tools/singlejar:singlejar_local", "@bazel_tools//tools/jdk:jar", ], tags = ["no_windows"], -) for label, singlejar in _SINGLEJAR_IMPLS.items()] +) # Tests that any Jar entry starting with j$/ causes singlejar to fail when run # with --check_desugar_deps. This is a backstop against such entries being # included in deploy.jars. -[sh_test( - name = "bad_entry_jar_%s_fail_test" % label, +sh_test( + name = "bad_entry_jar_singlejar_fail_test", srcs = ["desugar_deps_consistency_test.sh"], args = [ - "$(location %s)" % singlejar, + "$(location //src/tools/singlejar:singlejar_local)", "$(location @bazel_tools//tools/jdk:jar)", "$(location :mock_bad_entry_jar)", ], data = [ ":mock_bad_entry_jar", - singlejar, + "//src/tools/singlejar:singlejar_local", "@bazel_tools//tools/jdk:jar", ], tags = ["no_windows"], -) for label, singlejar in _SINGLEJAR_IMPLS.items()] +) genrule( name = "desugar_testdata_with_missing_dep",