aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Liam Miller-Cushon <cushon@google.com>2017-01-31 08:10:11 +0000
committerGravatar Yun Peng <pcloudy@google.com>2017-01-31 09:00:38 +0000
commita006146535460b2fba821847e13816bec4540ba4 (patch)
tree1e81ed1ada9c14ceba678f1d12ed49a49c2eb55d
parentde59c450f6c5af3204596e6b1d869476276fb59f (diff)
Create a tool for building resource jars
Move the functionality for building resources jars out of JavaBuilder, in preparation for building resource jars as a separate action. -- PiperOrigin-RevId: 146086774 MOS_MIGRATED_REVID=146086774
-rw-r--r--src/java_tools/buildjar/BUILD1
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD1
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/BUILD27
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilder.java170
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptions.java113
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptionsParser.java121
-rw-r--r--src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/BUILD17
-rw-r--r--src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilderTest.java232
8 files changed, 682 insertions, 0 deletions
diff --git a/src/java_tools/buildjar/BUILD b/src/java_tools/buildjar/BUILD
index 880c13dc6f..b47b17ccc3 100644
--- a/src/java_tools/buildjar/BUILD
+++ b/src/java_tools/buildjar/BUILD
@@ -43,6 +43,7 @@ filegroup(
"//src/java_tools/buildjar/java/com/google/devtools/build/java/turbine:srcs",
"//src/java_tools/buildjar/javatests/com/google/devtools/build/java/bazel:srcs",
"//src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine:srcs",
+ "//src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar:srcs",
],
visibility = ["//src:__pkg__"],
)
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD
index 5e948d2ee9..d05bdeec40 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD
@@ -179,6 +179,7 @@ filegroup(
srcs = glob(["**"]) + [
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass:srcs",
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/jarhelper:srcs",
+ "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar:srcs",
"//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins:srcs",
],
visibility = ["//src/java_tools/buildjar:__pkg__"],
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/BUILD b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/BUILD
new file mode 100644
index 0000000000..5ef3e21919
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/BUILD
@@ -0,0 +1,27 @@
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/java_tools/buildjar/java/com/google/devtools/build/buildjar:__pkg__"],
+)
+
+java_binary(
+ name = "ResourceJarBuilder",
+ main_class = "com.google.devtools.build.buildjar.resourcejar.ResourceJarBuilder",
+ runtime_deps = [":resourcejar"],
+)
+
+java_library(
+ name = "resourcejar",
+ srcs = [
+ "ResourceJarBuilder.java",
+ "ResourceJarOptions.java",
+ "ResourceJarOptionsParser.java",
+ ],
+ visibility = [
+ "//src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar:__pkg__",
+ ],
+ deps = [
+ "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/jarhelper",
+ "//third_party:guava",
+ ],
+)
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilder.java
new file mode 100644
index 0000000000..480f929b82
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilder.java
@@ -0,0 +1,170 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.buildjar.resourcejar;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.buildjar.jarhelper.JarCreator;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Constructs a jar file of Java resources. */
+public class ResourceJarBuilder implements Closeable {
+
+ public static void main(String[] args) throws Exception {
+ build(ResourceJarOptionsParser.parse(Arrays.asList(args)));
+ }
+
+ public static void build(ResourceJarOptions options) throws Exception {
+ try (ResourceJarBuilder builder = new ResourceJarBuilder(options)) {
+ builder.build();
+ }
+ }
+
+ /** Cache of opened zip filesystems. */
+ private final Map<Path, FileSystem> filesystems = new HashMap<>();
+
+ private final ResourceJarOptions options;
+
+ private ResourceJarBuilder(ResourceJarOptions options) {
+ this.options = options;
+ }
+
+ public void build() throws IOException {
+ final JarCreator jar = new JarCreator(options.output());
+ jar.setNormalize(true);
+ jar.setCompression(true);
+
+ addResourceJars(jar, options.resourceJars());
+ jar.addRootEntries(options.classpathResources());
+ addResourceEntries(jar, options.resources());
+ addMessageEntries(jar, options.messages());
+
+ jar.execute();
+ }
+
+ private void addResourceJars(final JarCreator jar, ImmutableList<String> resourceJars)
+ throws IOException {
+ for (String resourceJar : resourceJars) {
+ for (final Path root : getJarFileSystem(Paths.get(resourceJar)).getRootDirectories()) {
+ Files.walkFileTree(
+ root,
+ new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+ throws IOException {
+ // TODO(b/28452451): omit directories entries from jar files
+ if (dir.getNameCount() > 0) {
+ jar.addEntry(root.relativize(dir).toString(), dir);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
+ throws IOException {
+ jar.addEntry(root.relativize(path).toString(), path);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Adds a collection of resource entries. Each entry is a string composed of a pair of parts
+ * separated by a colon ':'. The name of the resource comes from the second part, and the path to
+ * the resource comes from the whole string with the colon replaced by a slash '/'.
+ *
+ * <pre>
+ * prefix:name => (name, prefix/name)
+ * </pre>
+ */
+ private static void addResourceEntries(JarCreator jar, Collection<String> resources)
+ throws IOException {
+ for (String resource : resources) {
+ int colon = resource.indexOf(':');
+ if (colon < 0) {
+ throw new IOException("" + resource + ": Illegal resource entry.");
+ }
+ String prefix = resource.substring(0, colon);
+ String name = resource.substring(colon + 1);
+ String path = colon > 0 ? prefix + "/" + name : name;
+ addEntryWithParents(jar, name, path);
+ }
+ }
+
+ private static void addMessageEntries(JarCreator jar, List<String> messages) throws IOException {
+ for (String message : messages) {
+ int colon = message.indexOf(':');
+ if (colon < 0) {
+ throw new IOException("" + message + ": Illegal message entry.");
+ }
+ String prefix = message.substring(0, colon);
+ String name = message.substring(colon + 1);
+ String path = colon > 0 ? prefix + "/" + name : name;
+ File messageFile = new File(path);
+ // Ignore empty messages. They get written by the translation importer
+ // when there is no translation for a particular language.
+ if (messageFile.length() != 0L) {
+ addEntryWithParents(jar, name, path);
+ }
+ }
+ }
+
+ /**
+ * Adds an entry to the jar, making sure that all the parent dirs up to the base of {@code entry}
+ * are also added.
+ *
+ * @param entry the PathFragment of the entry going into the Jar file
+ * @param file the PathFragment of the input file for the entry
+ */
+ @VisibleForTesting
+ static void addEntryWithParents(JarCreator creator, String entry, String file) {
+ while ((entry != null) && creator.addEntry(entry, file)) {
+ entry = new File(entry).getParent();
+ file = new File(file).getParent();
+ }
+ }
+
+ private FileSystem getJarFileSystem(Path sourceJar) throws IOException {
+ FileSystem fs = filesystems.get(sourceJar);
+ if (fs == null) {
+ filesystems.put(sourceJar, fs = FileSystems.newFileSystem(sourceJar, null));
+ }
+ return fs;
+ }
+
+ @Override
+ public void close() throws IOException {
+ for (FileSystem fs : filesystems.values()) {
+ fs.close();
+ }
+ }
+}
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptions.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptions.java
new file mode 100644
index 0000000000..bf1afe47c4
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptions.java
@@ -0,0 +1,113 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.buildjar.resourcejar;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+
+/** Resource jar builder options. */
+public class ResourceJarOptions {
+ private final String output;
+ private final ImmutableList<String> messages;
+ private final ImmutableList<String> resources;
+ private final ImmutableList<String> resourceJars;
+ private final ImmutableList<String> classpathResources;
+
+ public ResourceJarOptions(
+ String output,
+ ImmutableList<String> messages,
+ ImmutableList<String> resources,
+ ImmutableList<String> resourceJars,
+ ImmutableList<String> classpathResources) {
+ this.output = checkNotNull(output);
+ this.messages = messages;
+ this.resources = resources;
+ this.resourceJars = resourceJars;
+ this.classpathResources = classpathResources;
+ }
+
+ public String output() {
+ return output;
+ }
+
+ /**
+ * Resources to include in the jar.
+ *
+ * <p>The format is {@code <prefix>:<name>}, where {@code <prefix>/<name>} is the path to the
+ * resource file, and {code <name>} is the relative name that will be used for the resource jar
+ * entry.
+ */
+ public ImmutableList<String> resources() {
+ return resources;
+ }
+
+ /** Message files to include in the resource jar. The format is the same as {@link #resources}. */
+ public ImmutableList<String> messages() {
+ return messages;
+ }
+
+ /** Jar files of resources to append to the resource jar. */
+ public ImmutableList<String> resourceJars() {
+ return resourceJars;
+ }
+
+ /** Files to include as top-level entries in the resource jar. */
+ public ImmutableList<String> classpathResources() {
+ return classpathResources;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** A builder for {@link ResourceJarOptions}. */
+ public static class Builder {
+ private String output;
+ private ImmutableList<String> messages = ImmutableList.of();
+ private ImmutableList<String> resources = ImmutableList.of();
+ private ImmutableList<String> resourceJars = ImmutableList.of();
+ private ImmutableList<String> classpathResources = ImmutableList.of();
+
+ public ResourceJarOptions build() {
+ return new ResourceJarOptions(output, messages, resources, resourceJars, classpathResources);
+ }
+
+ public Builder setOutput(String output) {
+ this.output = checkNotNull(output);
+ return this;
+ }
+
+ public Builder setMessages(ImmutableList<String> messages) {
+ this.messages = checkNotNull(messages);
+ return this;
+ }
+
+ public Builder setResources(ImmutableList<String> resources) {
+ this.resources = checkNotNull(resources);
+ return this;
+ }
+
+ public Builder setResourceJars(ImmutableList<String> resourceJars) {
+ this.resourceJars = checkNotNull(resourceJars);
+ return this;
+ }
+
+ public Builder setClasspathResources(ImmutableList<String> classpathResources) {
+ this.classpathResources = checkNotNull(classpathResources);
+ return this;
+ }
+ }
+}
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptionsParser.java
new file mode 100644
index 0000000000..f84b88394b
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar/ResourceJarOptionsParser.java
@@ -0,0 +1,121 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.buildjar.resourcejar;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.annotation.Nullable;
+
+/** A command line options parser for {@link ResourceJarOptions}. */
+public class ResourceJarOptionsParser {
+
+ /**
+ * Parses command line options into {@link ResourceJarOptions}, expanding any {@code @params}
+ * files.
+ */
+ public static ResourceJarOptions parse(Iterable<String> args) throws IOException {
+ ResourceJarOptions.Builder builder = ResourceJarOptions.builder();
+ parse(builder, args);
+ return builder.build();
+ }
+
+ /**
+ * Parses command line options into a {@link ResourceJarOptions.Builder}, expanding any
+ * {@code @params} files.
+ */
+ public static void parse(ResourceJarOptions.Builder builder, Iterable<String> args)
+ throws IOException {
+ Deque<String> argumentDeque = new ArrayDeque<>();
+ expandParamsFiles(argumentDeque, args);
+ parse(builder, argumentDeque);
+ }
+
+ private static final Splitter ARG_SPLITTER =
+ Splitter.on(CharMatcher.breakingWhitespace()).omitEmptyStrings().trimResults();
+
+ /**
+ * Pre-processes an argument list, expanding arguments of the form {@code @filename} by reading
+ * the content of the file and appending whitespace-delimited options to {@code argumentDeque}.
+ */
+ private static void expandParamsFiles(Deque<String> argumentDeque, Iterable<String> args)
+ throws IOException {
+ for (String arg : args) {
+ if (arg.isEmpty()) {
+ continue;
+ }
+ if (arg.startsWith("@") && !arg.startsWith("@@")) {
+ Path paramsPath = Paths.get(arg.substring(1));
+ expandParamsFiles(
+ argumentDeque, ARG_SPLITTER.split(new String(Files.readAllBytes(paramsPath), UTF_8)));
+ } else {
+ argumentDeque.addLast(arg);
+ }
+ }
+ }
+
+ private static void parse(ResourceJarOptions.Builder builder, Deque<String> argumentDeque) {
+ while (!argumentDeque.isEmpty()) {
+ String next = argumentDeque.pollFirst();
+ switch (next) {
+ case "--output":
+ builder.setOutput(readOne(argumentDeque));
+ break;
+ case "--messages":
+ builder.setMessages(readList(argumentDeque));
+ break;
+ case "--resources":
+ builder.setResources(readList(argumentDeque));
+ break;
+ case "--resource_jars":
+ builder.setResourceJars(readList(argumentDeque));
+ break;
+ case "--classpath_resources":
+ builder.setClasspathResources(readList(argumentDeque));
+ break;
+ default:
+ if (next.isEmpty() && !argumentDeque.isEmpty()) {
+ throw new IllegalArgumentException("unknown option: " + next);
+ }
+ }
+ }
+ }
+
+ /** Returns the value of an option, or {@code null}. */
+ @Nullable
+ private static String readOne(Deque<String> argumentDeque) {
+ if (argumentDeque.isEmpty() || argumentDeque.peekFirst().startsWith("-")) {
+ return null;
+ }
+ return argumentDeque.pollFirst();
+ }
+
+ /** Returns a list of option values. */
+ private static ImmutableList<String> readList(Deque<String> argumentDeque) {
+ ImmutableList.Builder<String> result = ImmutableList.builder();
+ while (!argumentDeque.isEmpty() && !argumentDeque.peekFirst().startsWith("--")) {
+ result.add(argumentDeque.pollFirst());
+ }
+ return result.build();
+ }
+}
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/BUILD b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/BUILD
new file mode 100644
index 0000000000..a587c7cb73
--- /dev/null
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/BUILD
@@ -0,0 +1,17 @@
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/java_tools/buildjar:__pkg__"],
+)
+
+java_test(
+ name = "ResourceJarBuilderTest",
+ srcs = ["ResourceJarBuilderTest.java"],
+ deps = [
+ "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/resourcejar",
+ "//third_party:guava",
+ "//third_party:junit4",
+ "//third_party:truth",
+ "//third_party/java/jdk/langtools:javac",
+ ],
+)
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilderTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilderTest.java
new file mode 100644
index 0000000000..8b89377053
--- /dev/null
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/buildjar/resourcejar/ResourceJarBuilderTest.java
@@ -0,0 +1,232 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.buildjar.resourcejar;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** {@link com.google.devtools.build.buildjar.resourcejar.ResourceJarBuilder}Test. */
+@RunWith(JUnit4.class)
+public class ResourceJarBuilderTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void options() throws IOException {
+ ResourceJarOptions options =
+ ResourceJarOptionsParser.parse(
+ ImmutableList.of(
+ "--output",
+ "resource.jar",
+ "--messages",
+ "m1",
+ "m2",
+ "--resources",
+ "r1",
+ "r2",
+ "--resource_jars",
+ "rj1",
+ "rj2",
+ "--classpath_resources",
+ "cr1",
+ "cr2"));
+ assertThat(options.output()).isEqualTo("resource.jar");
+ assertThat(options.messages()).containsExactly("m1", "m2");
+ assertThat(options.resources()).containsExactly("r1", "r2");
+ assertThat(options.resourceJars()).containsExactly("rj1", "rj2");
+ assertThat(options.classpathResources()).containsExactly("cr1", "cr2");
+ }
+
+ @Test
+ public void resourceJars() throws Exception {
+ File output = temporaryFolder.newFile("resources.jar");
+
+ File jar1 = temporaryFolder.newFile("jar1.jar");
+ try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jar1))) {
+ jos.putNextEntry(new JarEntry("one/a.properties"));
+ jos.putNextEntry(new JarEntry("one/b.properties"));
+ }
+
+ File jar2 = temporaryFolder.newFile("jar2.jar");
+ try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jar2))) {
+ jos.putNextEntry(new JarEntry("two/c.properties"));
+ jos.putNextEntry(new JarEntry("two/d.properties"));
+ }
+
+ ResourceJarBuilder.build(
+ ResourceJarOptions.builder()
+ .setOutput(output.toString())
+ .setResourceJars(ImmutableList.of(jar1.toString(), jar2.toString()))
+ .build());
+
+ List<String> entries = new ArrayList<>();
+ try (JarFile jf = new JarFile(output)) {
+ Enumeration<JarEntry> jes = jf.entries();
+ while (jes.hasMoreElements()) {
+ entries.add(jes.nextElement().getName());
+ }
+ }
+
+ assertThat(entries)
+ .containsExactly(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "one/",
+ "one/a.properties",
+ "one/b.properties",
+ "two/",
+ "two/c.properties",
+ "two/d.properties")
+ .inOrder();
+ }
+
+ @Test
+ public void resources() throws Exception {
+ File output = temporaryFolder.newFile("resources.jar");
+
+ Path root = temporaryFolder.newFolder().toPath();
+
+ Path r1 = root.resolve("one/a.properties");
+ Files.createDirectories(r1.getParent());
+ Files.write(r1, "hello".getBytes(UTF_8));
+
+ Path r2 = root.resolve("two/b.properties");
+ Files.createDirectories(r2.getParent());
+ Files.write(r2, "goodbye".getBytes(UTF_8));
+
+ ResourceJarBuilder.build(
+ ResourceJarOptions.builder()
+ .setOutput(output.toString())
+ .setResources(
+ ImmutableList.of(
+ root + ":" + root.relativize(r1), root + ":" + root.relativize(r2)))
+ .build());
+
+ List<String> entries = new ArrayList<>();
+ try (JarFile jf = new JarFile(output)) {
+ Enumeration<JarEntry> jes = jf.entries();
+ while (jes.hasMoreElements()) {
+ entries.add(jes.nextElement().getName());
+ }
+ }
+
+ assertThat(entries)
+ .containsExactly(
+ "META-INF/",
+ "META-INF/MANIFEST.MF",
+ "one/",
+ "one/a.properties",
+ "two/",
+ "two/b.properties");
+ }
+
+ @Test
+ public void rootEntries() throws Exception {
+ File output = temporaryFolder.newFile("resources.jar");
+
+ Path root = temporaryFolder.newFolder().toPath();
+
+ Path r1 = root.resolve("one/a.properties");
+ Files.createDirectories(r1.getParent());
+ Files.write(r1, "hello".getBytes(UTF_8));
+
+ Path r2 = root.resolve("two/b.properties");
+ Files.createDirectories(r2.getParent());
+ Files.write(r2, "goodbye".getBytes(UTF_8));
+
+ ResourceJarBuilder.build(
+ ResourceJarOptions.builder()
+ .setOutput(output.toString())
+ .setClasspathResources(ImmutableList.of(r1.toString(), r2.toString()))
+ .build());
+
+ List<String> entries = new ArrayList<>();
+ try (JarFile jf = new JarFile(output)) {
+ Enumeration<JarEntry> jes = jf.entries();
+ while (jes.hasMoreElements()) {
+ entries.add(jes.nextElement().getName());
+ }
+ }
+
+ assertThat(entries)
+ .containsExactly("META-INF/", "META-INF/MANIFEST.MF", "a.properties", "b.properties");
+ }
+
+ @Test
+ public void messages() throws Exception {
+ File output = temporaryFolder.newFile("resources.jar");
+
+ Path root = temporaryFolder.newFolder().toPath();
+
+ Path r1 = root.resolve("one/a.xmb");
+ Files.createDirectories(r1.getParent());
+ Files.write(r1, "hello".getBytes(UTF_8));
+
+ Path r2 = root.resolve("two/b.xmb");
+ Files.createDirectories(r2.getParent());
+ Files.write(r2, "goodbye".getBytes(UTF_8));
+
+ // empty messages are omitted
+ Path r3 = root.resolve("three/c.xmb");
+ Files.createDirectories(r3.getParent());
+ Files.write(r3, new byte[0]);
+
+ ResourceJarBuilder.build(
+ ResourceJarOptions.builder()
+ .setOutput(output.toString())
+ .setMessages(
+ ImmutableList.of(
+ root + ":" + root.relativize(r1),
+ root + ":" + root.relativize(r2),
+ root + ":" + root.relativize(r3)))
+ .build());
+
+ List<String> entries = new ArrayList<>();
+ try (JarFile jf = new JarFile(output)) {
+ Enumeration<JarEntry> jes = jf.entries();
+ while (jes.hasMoreElements()) {
+ entries.add(jes.nextElement().getName());
+ }
+ }
+
+ assertThat(entries)
+ .containsExactly(
+ "META-INF/", //
+ "META-INF/MANIFEST.MF",
+ "one/",
+ "one/a.xmb",
+ "two/",
+ "two/b.xmb")
+ .inOrder();
+ }
+}