aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Liam Miller-Cushon <cushon@google.com>2017-01-12 21:47:02 +0000
committerGravatar Marcel Hlopko <hlopko@google.com>2017-01-13 10:57:54 +0000
commit1c52600069dbfb88ae695b01034412b1e76a2699 (patch)
tree6f878e39e39196b7c72d7065f3d241a534a22fd4 /src
parentb2e6607cd9e4e168872c87c66edbef1bc2bf943a (diff)
Coalesce JavaBuilder class hierarchy
Some of these abstract classes used to have more than one subtype. -- PiperOrigin-RevId: 144366452 MOS_MIGRATED_REVID=144366452
Diffstat (limited to 'src')
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractJavaBuilder.java159
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractLibraryBuilder.java287
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java2
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java363
4 files changed, 359 insertions, 452 deletions
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractJavaBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractJavaBuilder.java
deleted file mode 100644
index 6973c320d1..0000000000
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractJavaBuilder.java
+++ /dev/null
@@ -1,159 +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.buildjar;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.Files;
-import com.google.devtools.build.buildjar.instrumentation.JacocoInstrumentationProcessor;
-import com.google.devtools.build.buildjar.javac.BlazeJavacArguments;
-import com.google.devtools.build.buildjar.javac.BlazeJavacMain;
-import com.google.devtools.build.buildjar.javac.JavacRunner;
-import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin;
-import com.sun.tools.javac.main.Main.Result;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.zip.ZipEntry;
-
-/**
- * A command line interface to compile a java_library rule using in-process javac. This allows us to
- * spawn multiple java_library compilations on a single machine or distribute Java compilations to
- * multiple machines.
- */
-public abstract class AbstractJavaBuilder extends AbstractLibraryBuilder {
-
- /** The name of the protobuf meta file. */
- private static final String PROTOBUF_META_NAME = "protobuf.meta";
-
- /** Enables more verbose output from the compiler. */
- protected boolean debug = false;
-
- /**
- * Prepares a compilation run and sets everything up so that the source files in the build request
- * can be compiled. Invokes compileSources to do the actual compilation.
- *
- * @param build A JavaLibraryBuildRequest request object describing what to compile
- * @param err PrintWriter for logging any diagnostic output
- */
- public Result compileJavaLibrary(final JavaLibraryBuildRequest build, final PrintWriter err)
- throws Exception {
- prepareSourceCompilation(build);
- if (build.getSourceFiles().isEmpty()) {
- return Result.OK;
- }
- JavacRunner javacRunner =
- new JavacRunner() {
- @Override
- public Result invokeJavac(
- ImmutableList<BlazeJavaCompilerPlugin> plugins,
- BlazeJavacArguments arguments,
- PrintWriter output) {
- return new BlazeJavacMain(output, plugins).compile(arguments);
- }
- };
- Result result = compileSources(build, javacRunner, err);
- JacocoInstrumentationProcessor processor = build.getJacocoInstrumentationProcessor();
- if (processor != null) {
- processor.processRequest(build);
- }
- return result;
- }
-
- /** Build a jar file containing source files that were generated by an annotation processor. */
- public abstract void buildGensrcJar(JavaLibraryBuildRequest build) throws IOException;
-
- /**
- * Compiles the java files specified in 'JavaLibraryBuildRequest'. Implementations can try to
- * avoid recompiling the java files. Whenever this function is invoked, it is guaranteed that the
- * build request contains files to compile.
- *
- * @param build A JavaLibraryBuildRequest request object describing what to compile
- * @param err PrintWriter for logging any diagnostic output
- * @return the exit status of the java compiler.
- */
- abstract Result compileSources(
- JavaLibraryBuildRequest build, JavacRunner javacRunner, PrintWriter err) throws IOException;
-
- /** Perform the build. */
- public Result run(JavaLibraryBuildRequest build, PrintWriter err) throws Exception {
- Result result = Result.ERROR;
- try {
- result = compileJavaLibrary(build, err);
- if (result.isOK()) {
- buildJar(build);
- }
- if (!build.getProcessors().isEmpty()) {
- if (build.getGeneratedSourcesOutputJar() != null) {
- buildGensrcJar(build);
- }
- }
- } finally {
- build.getDependencyModule().emitDependencyInformation(build.getClassPath(), result.isOK());
- build.getProcessingModule().emitManifestProto();
- }
- return result;
- }
-
- /** A SourceJarEntryListener that collects protobuf meta data files from the source jar files. */
- private static class ProtoMetaFileCollector implements SourceJarEntryListener {
-
- private final String sourceDir;
- private final String outputDir;
- private final ByteArrayOutputStream buffer;
-
- public ProtoMetaFileCollector(String sourceDir, String outputDir) {
- this.sourceDir = sourceDir;
- this.outputDir = outputDir;
- this.buffer = new ByteArrayOutputStream();
- }
-
- @Override
- public void onEntry(ZipEntry entry) throws IOException {
- String entryName = entry.getName();
- if (!entryName.equals(PROTOBUF_META_NAME)) {
- return;
- }
- Files.copy(new File(sourceDir, PROTOBUF_META_NAME), buffer);
- }
-
- /**
- * Writes the combined the meta files into the output directory. Delete the stalling meta file
- * if no meta file is collected.
- */
- @Override
- public void finish() throws IOException {
- File outputFile = new File(outputDir, PROTOBUF_META_NAME);
- if (buffer.size() > 0) {
- try (OutputStream outputStream = new FileOutputStream(outputFile)) {
- buffer.writeTo(outputStream);
- }
- } else if (outputFile.exists()) {
- // Delete stalled meta file.
- outputFile.delete();
- }
- }
- }
-
- @Override
- protected List<SourceJarEntryListener> getSourceJarEntryListeners(JavaLibraryBuildRequest build) {
- List<SourceJarEntryListener> result = super.getSourceJarEntryListeners(build);
- result.add(new ProtoMetaFileCollector(build.getTempDir(), build.getClassDir()));
- return result;
- }
-}
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractLibraryBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractLibraryBuilder.java
deleted file mode 100644
index efac18a705..0000000000
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/AbstractLibraryBuilder.java
+++ /dev/null
@@ -1,287 +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.buildjar;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.io.ByteStreams;
-import com.google.devtools.build.buildjar.jarhelper.JarCreator;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * Base class for java_library builders.
- *
- * <p>Implements common functionality like source files preparation and output jar creation.
- */
-public abstract class AbstractLibraryBuilder {
-
- /**
- * Prepares a compilation run. This involves cleaning up temporary directories and writing the
- * classpath files.
- */
- protected void prepareSourceCompilation(JavaLibraryBuildRequest build) throws IOException {
- File classDirectory = new File(build.getClassDir());
- if (classDirectory.exists()) {
- try {
- // Necessary for local builds in order to discard previous outputs
- cleanupOutputDirectory(classDirectory);
- } catch (IOException e) {
- throw new IOException("Cannot clean output directory '" + classDirectory + "'", e);
- }
- }
- classDirectory.mkdirs();
-
- setUpSourceJars(build);
- }
-
- public void buildJar(JavaLibraryBuildRequest build) throws IOException {
- JarCreator jar = new JarCreator(build.getOutputJar());
- jar.setNormalize(true);
- jar.setCompression(build.compressJar());
-
- // The easiest way to handle resource jars is to unpack them into the class directory, just
- // before we start zipping it up.
- for (String resourceJar : build.getResourceJars()) {
- setUpSourceJar(
- new File(resourceJar), build.getClassDir(), new ArrayList<SourceJarEntryListener>());
- }
-
- jar.addDirectory(build.getClassDir());
-
- jar.addRootEntries(build.getRootResourceFiles());
- addResourceEntries(jar, build.getResourceFiles());
- addMessageEntries(jar, build.getMessageFiles());
-
- jar.execute();
- }
-
- /**
- * 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();
- }
- }
-
- /**
- * Internal interface which will listen on each entry of the source jar files during the source
- * jar setup process.
- */
- protected interface SourceJarEntryListener {
- void onEntry(ZipEntry entry) throws IOException;
-
- void finish() throws IOException;
- }
-
- protected List<SourceJarEntryListener> getSourceJarEntryListeners(JavaLibraryBuildRequest build) {
- List<SourceJarEntryListener> result = new ArrayList<>();
- result.add(new SourceJavaFileCollector(build));
- return result;
- }
-
- /**
- * A SourceJarEntryListener that collects a lists of source Java files from the source jar files.
- */
- private static class SourceJavaFileCollector implements SourceJarEntryListener {
- private final List<String> sources;
- private final JavaLibraryBuildRequest build;
-
- public SourceJavaFileCollector(JavaLibraryBuildRequest build) {
- this.sources = new ArrayList<>();
- this.build = build;
- }
-
- @Override
- public void onEntry(ZipEntry entry) {
- String entryName = entry.getName();
- if (entryName.endsWith(".java")) {
- sources.add(build.getTempDir() + File.separator + entryName);
- }
- }
-
- @Override
- public void finish() {
- build.getSourceFiles().addAll(sources);
- }
- }
-
- /**
- * Extracts the all source jars from the build request into the temporary directory specified in
- * the build request. Empties the temporary directory, if it exists.
- */
- private void setUpSourceJars(JavaLibraryBuildRequest build) throws IOException {
- String sourcesDir = build.getTempDir();
-
- File sourceDirFile = new File(sourcesDir);
- if (sourceDirFile.exists()) {
- cleanupDirectory(sourceDirFile, true);
- }
-
- if (build.getSourceJars().isEmpty()) {
- return;
- }
-
- List<SourceJarEntryListener> listeners = getSourceJarEntryListeners(build);
- for (String sourceJar : build.getSourceJars()) {
- setUpSourceJar(new File(sourceJar), sourcesDir, listeners);
- }
- for (SourceJarEntryListener listener : listeners) {
- listener.finish();
- }
- }
-
- /**
- * Extracts the source jar into the directory sourceDir. Calls each of the SourceJarEntryListeners
- * for each non-directory entry to do additional work.
- */
- private void setUpSourceJar(
- File sourceJar, String sourceDir, List<SourceJarEntryListener> listeners) throws IOException {
- try (ZipFile zipFile = new ZipFile(sourceJar)) {
- Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
- while (zipEntries.hasMoreElements()) {
- ZipEntry currentEntry = zipEntries.nextElement();
- String entryName = currentEntry.getName();
- File outputFile = new File(sourceDir, entryName);
-
- outputFile.getParentFile().mkdirs();
-
- if (currentEntry.isDirectory()) {
- outputFile.mkdir();
- } else {
- // Copy the data from the zip file to the output file.
- try (InputStream in = zipFile.getInputStream(currentEntry);
- OutputStream out = new FileOutputStream(outputFile)) {
- ByteStreams.copy(in, out);
- }
-
- for (SourceJarEntryListener listener : listeners) {
- listener.onEntry(currentEntry);
- }
- }
- }
- }
- }
-
- /**
- * Recursively cleans up the files beneath the specified output directory. Does not follow
- * symbolic links. Throws IOException if any deletion fails.
- *
- * <p>Will delete all empty directories.
- *
- * @param dir the directory to clean up.
- * @return true if the directory itself was removed as well.
- */
- boolean cleanupOutputDirectory(File dir) throws IOException {
- return cleanupDirectory(dir, false);
- }
-
- /**
- * Recursively cleans up the files beneath the specified output directory. Does not follow
- * symbolic links. Throws IOException if any deletion fails. If removeEverything is false, keeps
- * .class files if keepClassFilesDuringCleanup() returns true. If removeEverything is true,
- * removes everything. Will delete all empty directories.
- *
- * @param dir the directory to clean up.
- * @param removeEverything whether to remove all files, or keep flags.xml/.class files.
- * @return true if the directory itself was removed as well.
- */
- private boolean cleanupDirectory(File dir, boolean removeEverything) throws IOException {
- boolean isEmpty = true;
- File[] files = dir.listFiles();
- if (files == null) {
- return false;
- } // avoid race condition
- for (File file : files) {
- if (file.isDirectory()) {
- isEmpty &= cleanupDirectory(file, removeEverything);
- } else if (!removeEverything
- && keepClassFilesDuringCleanup()
- && file.getName().endsWith(".class")) {
- isEmpty = false;
- } else {
- file.delete();
- }
- }
- if (isEmpty) {
- dir.delete();
- }
- return isEmpty;
- }
-
- /**
- * Returns true if cleaning the output directory should remove all .class files in the output
- * directory.
- */
- protected boolean keepClassFilesDuringCleanup() {
- return false;
- }
-}
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java
index bf73f62ace..6b9010e3e1 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BazelJavaBuilder.java
@@ -81,7 +81,7 @@ public abstract class BazelJavaBuilder {
public static int processRequest(List<String> args, PrintWriter err) {
try {
JavaLibraryBuildRequest build = parse(args);
- AbstractJavaBuilder builder =
+ SimpleJavaLibraryBuilder builder =
build.getDependencyModule().reduceClasspath()
? new ReducedClasspathJavaLibraryBuilder()
: new SimpleJavaLibraryBuilder();
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java
index 4080cbfbb6..07e2dcb6af 100644
--- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/SimpleJavaLibraryBuilder.java
@@ -14,26 +14,121 @@
package com.google.devtools.build.buildjar;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+import com.google.devtools.build.buildjar.instrumentation.JacocoInstrumentationProcessor;
import com.google.devtools.build.buildjar.jarhelper.JarCreator;
+import com.google.devtools.build.buildjar.javac.BlazeJavacArguments;
+import com.google.devtools.build.buildjar.javac.BlazeJavacMain;
import com.google.devtools.build.buildjar.javac.JavacRunner;
+import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin;
import com.sun.tools.javac.main.Main.Result;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
/** An implementation of the JavaBuilder that uses in-process javac to compile java files. */
-public class SimpleJavaLibraryBuilder extends AbstractJavaBuilder {
+public class SimpleJavaLibraryBuilder {
+
+ /** The name of the protobuf meta file. */
+ private static final String PROTOBUF_META_NAME = "protobuf.meta";
+ /** Enables more verbose output from the compiler. */
+ protected boolean debug = false;
+
+ /**
+ * 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 static List<SourceJarEntryListener> getSourceJarEntryListeners(
+ JavaLibraryBuildRequest build) {
+ return ImmutableList.of(
+ new SourceJavaFileCollector(build),
+ new ProtoMetaFileCollector(build.getTempDir(), build.getClassDir()));
+ }
- @Override
Result compileSources(JavaLibraryBuildRequest build, JavacRunner javacRunner, PrintWriter err)
throws IOException {
return javacRunner.invokeJavac(
build.getPlugins(), build.toBlazeJavacArguments(build.getClassPath()), err);
}
- @Override
protected void prepareSourceCompilation(JavaLibraryBuildRequest build) throws IOException {
- super.prepareSourceCompilation(build);
+ File classDirectory = new File(build.getClassDir());
+ if (classDirectory.exists()) {
+ try {
+ // Necessary for local builds in order to discard previous outputs
+ cleanupOutputDirectory(classDirectory);
+ } catch (IOException e) {
+ throw new IOException("Cannot clean output directory '" + classDirectory + "'", e);
+ }
+ }
+ classDirectory.mkdirs();
+
+ setUpSourceJars(build);
// Create sourceGenDir if necessary.
if (build.getSourceGenDir() != null) {
@@ -49,7 +144,6 @@ public class SimpleJavaLibraryBuilder extends AbstractJavaBuilder {
}
}
- @Override
public void buildGensrcJar(JavaLibraryBuildRequest build) throws IOException {
JarCreator jar = new JarCreator(build.getGeneratedSourcesOutputJar());
jar.setNormalize(true);
@@ -57,4 +151,263 @@ public class SimpleJavaLibraryBuilder extends AbstractJavaBuilder {
jar.addDirectory(build.getSourceGenDir());
jar.execute();
}
+
+ /**
+ * Prepares a compilation run and sets everything up so that the source files in the build request
+ * can be compiled. Invokes compileSources to do the actual compilation.
+ *
+ * @param build A JavaLibraryBuildRequest request object describing what to compile
+ * @param err PrintWriter for logging any diagnostic output
+ */
+ public Result compileJavaLibrary(final JavaLibraryBuildRequest build, final PrintWriter err)
+ throws Exception {
+ prepareSourceCompilation(build);
+ if (build.getSourceFiles().isEmpty()) {
+ return Result.OK;
+ }
+ JavacRunner javacRunner =
+ new JavacRunner() {
+ @Override
+ public Result invokeJavac(
+ ImmutableList<BlazeJavaCompilerPlugin> plugins,
+ BlazeJavacArguments arguments,
+ PrintWriter output) {
+ return new BlazeJavacMain(output, plugins).compile(arguments);
+ }
+ };
+ Result result = compileSources(build, javacRunner, err);
+ JacocoInstrumentationProcessor processor = build.getJacocoInstrumentationProcessor();
+ if (processor != null) {
+ processor.processRequest(build);
+ }
+ return result;
+ }
+
+ /** Perform the build. */
+ public Result run(JavaLibraryBuildRequest build, PrintWriter err) throws Exception {
+ Result result = Result.ERROR;
+ try {
+ result = compileJavaLibrary(build, err);
+ if (result.isOK()) {
+ buildJar(build);
+ }
+ if (!build.getProcessors().isEmpty()) {
+ if (build.getGeneratedSourcesOutputJar() != null) {
+ buildGensrcJar(build);
+ }
+ }
+ } finally {
+ build.getDependencyModule().emitDependencyInformation(build.getClassPath(), result.isOK());
+ build.getProcessingModule().emitManifestProto();
+ }
+ return result;
+ }
+
+ public void buildJar(JavaLibraryBuildRequest build) throws IOException {
+ JarCreator jar = new JarCreator(build.getOutputJar());
+ jar.setNormalize(true);
+ jar.setCompression(build.compressJar());
+
+ // The easiest way to handle resource jars is to unpack them into the class directory, just
+ // before we start zipping it up.
+ for (String resourceJar : build.getResourceJars()) {
+ setUpSourceJar(
+ new File(resourceJar), build.getClassDir(), new ArrayList<SourceJarEntryListener>());
+ }
+
+ jar.addDirectory(build.getClassDir());
+
+ jar.addRootEntries(build.getRootResourceFiles());
+ SimpleJavaLibraryBuilder.addResourceEntries(jar, build.getResourceFiles());
+ SimpleJavaLibraryBuilder.addMessageEntries(jar, build.getMessageFiles());
+
+ jar.execute();
+ }
+
+ /**
+ * Extracts the all source jars from the build request into the temporary directory specified in
+ * the build request. Empties the temporary directory, if it exists.
+ */
+ private void setUpSourceJars(JavaLibraryBuildRequest build) throws IOException {
+ String sourcesDir = build.getTempDir();
+
+ File sourceDirFile = new File(sourcesDir);
+ if (sourceDirFile.exists()) {
+ cleanupDirectory(sourceDirFile, true);
+ }
+
+ if (build.getSourceJars().isEmpty()) {
+ return;
+ }
+
+ List<SourceJarEntryListener> listeners = getSourceJarEntryListeners(build);
+ for (String sourceJar : build.getSourceJars()) {
+ setUpSourceJar(new File(sourceJar), sourcesDir, listeners);
+ }
+ for (SourceJarEntryListener listener : listeners) {
+ listener.finish();
+ }
+ }
+
+ /**
+ * Extracts the source jar into the directory sourceDir. Calls each of the SourceJarEntryListeners
+ * for each non-directory entry to do additional work.
+ */
+ private void setUpSourceJar(
+ File sourceJar, String sourceDir, List<SourceJarEntryListener> listeners) throws IOException {
+ try (ZipFile zipFile = new ZipFile(sourceJar)) {
+ Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
+ while (zipEntries.hasMoreElements()) {
+ ZipEntry currentEntry = zipEntries.nextElement();
+ String entryName = currentEntry.getName();
+ File outputFile = new File(sourceDir, entryName);
+
+ outputFile.getParentFile().mkdirs();
+
+ if (currentEntry.isDirectory()) {
+ outputFile.mkdir();
+ } else {
+ // Copy the data from the zip file to the output file.
+ try (InputStream in = zipFile.getInputStream(currentEntry);
+ OutputStream out = new FileOutputStream(outputFile)) {
+ ByteStreams.copy(in, out);
+ }
+
+ for (SourceJarEntryListener listener : listeners) {
+ listener.onEntry(currentEntry);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursively cleans up the files beneath the specified output directory. Does not follow
+ * symbolic links. Throws IOException if any deletion fails.
+ *
+ * <p>Will delete all empty directories.
+ *
+ * @param dir the directory to clean up.
+ * @return true if the directory itself was removed as well.
+ */
+ boolean cleanupOutputDirectory(File dir) throws IOException {
+ return cleanupDirectory(dir, false);
+ }
+
+ /**
+ * Recursively cleans up the files beneath the specified output directory. Does not follow
+ * symbolic links. Throws IOException if any deletion fails. If removeEverything is false, keeps
+ * .class files if keepClassFilesDuringCleanup() returns true. If removeEverything is true,
+ * removes everything. Will delete all empty directories.
+ *
+ * @param dir the directory to clean up.
+ * @param removeEverything whether to remove all files, or keep flags.xml/.class files.
+ * @return true if the directory itself was removed as well.
+ */
+ private boolean cleanupDirectory(File dir, boolean removeEverything) throws IOException {
+ boolean isEmpty = true;
+ File[] files = dir.listFiles();
+ if (files == null) {
+ return false;
+ } // avoid race condition
+ for (File file : files) {
+ if (file.isDirectory()) {
+ isEmpty &= cleanupDirectory(file, removeEverything);
+ } else if (!removeEverything
+ && keepClassFilesDuringCleanup()
+ && file.getName().endsWith(".class")) {
+ isEmpty = false;
+ } else {
+ file.delete();
+ }
+ }
+ if (isEmpty) {
+ dir.delete();
+ }
+ return isEmpty;
+ }
+
+ /**
+ * Returns true if cleaning the output directory should remove all .class files in the output
+ * directory.
+ */
+ protected boolean keepClassFilesDuringCleanup() {
+ return false;
+ }
+
+ /**
+ * Internal interface which will listen on each entry of the source jar files during the source
+ * jar setup process.
+ */
+ protected interface SourceJarEntryListener {
+ void onEntry(ZipEntry entry) throws IOException;
+
+ void finish() throws IOException;
+ }
+
+ /** A SourceJarEntryListener that collects protobuf meta data files from the source jar files. */
+ private static class ProtoMetaFileCollector implements SourceJarEntryListener {
+
+ private final String sourceDir;
+ private final String outputDir;
+ private final ByteArrayOutputStream buffer;
+
+ public ProtoMetaFileCollector(String sourceDir, String outputDir) {
+ this.sourceDir = sourceDir;
+ this.outputDir = outputDir;
+ this.buffer = new ByteArrayOutputStream();
+ }
+
+ @Override
+ public void onEntry(ZipEntry entry) throws IOException {
+ String entryName = entry.getName();
+ if (!entryName.equals(PROTOBUF_META_NAME)) {
+ return;
+ }
+ Files.copy(new File(sourceDir, PROTOBUF_META_NAME), buffer);
+ }
+
+ /**
+ * Writes the combined the meta files into the output directory. Delete the stalling meta file
+ * if no meta file is collected.
+ */
+ @Override
+ public void finish() throws IOException {
+ File outputFile = new File(outputDir, PROTOBUF_META_NAME);
+ if (buffer.size() > 0) {
+ try (OutputStream outputStream = new FileOutputStream(outputFile)) {
+ buffer.writeTo(outputStream);
+ }
+ } else if (outputFile.exists()) {
+ // Delete stalled meta file.
+ outputFile.delete();
+ }
+ }
+ }
+
+ /**
+ * A SourceJarEntryListener that collects a lists of source Java files from the source jar files.
+ */
+ private static class SourceJavaFileCollector implements SourceJarEntryListener {
+ private final List<String> sources;
+ private final JavaLibraryBuildRequest build;
+
+ public SourceJavaFileCollector(JavaLibraryBuildRequest build) {
+ this.sources = new ArrayList<>();
+ this.build = build;
+ }
+
+ @Override
+ public void onEntry(ZipEntry entry) {
+ String entryName = entry.getName();
+ if (entryName.endsWith(".java")) {
+ sources.add(build.getTempDir() + File.separator + entryName);
+ }
+ }
+
+ @Override
+ public void finish() {
+ build.getSourceFiles().addAll(sources);
+ }
+ }
}