diff options
author | 2015-06-08 21:05:29 +0000 | |
---|---|---|
committer | 2015-06-09 09:57:14 +0000 | |
commit | 438126ce44e3c5c8313329e7d579f4c13c3e210f (patch) | |
tree | 24cc2e98a93024fe6f734b759a5acc89f3e770ae /src/java_tools/buildjar/java/com/google/devtools/build | |
parent | 50e3f04c17bb5d6d8adecbe2849b4ab887803d5d (diff) |
Add --generated_class_output to JavaBuilder.
If enabled, JavaBuilder will write a jar containing classes generated by
annotation processors to the given path.
--
MOS_MIGRATED_REVID=95470914
Diffstat (limited to 'src/java_tools/buildjar/java/com/google/devtools/build')
6 files changed, 275 insertions, 17 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 index 15349a1789..1ba28bf0bc 100644 --- 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 @@ -168,6 +168,9 @@ public abstract class AbstractJavaBuilder extends AbstractLibraryBuilder { if (build.getGeneratedSourcesOutputJar() != null) { buildGensrcJar(build, err); } + if (build.getGeneratedClassOutputJar() != null) { + buildGenclassJar(build); + } } successful = true; } finally { 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 index cf1c985b0f..c402cc07a9 100644 --- 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 @@ -22,6 +22,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +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.ArrayList; import java.util.Collection; import java.util.Enumeration; @@ -78,6 +84,30 @@ public abstract class AbstractLibraryBuilder extends CommonJavaLibraryProcessor } /** + * Build a jar file containing class files that were generated by an annotation processor. + */ + public void buildGenclassJar(final JavaLibraryBuildRequest build) throws IOException { + final JarCreator jar = new JarCreator(build.getGeneratedClassOutputJar()); + jar.setNormalize(true); + jar.setCompression(build.compressJar()); + final Path classDir = Paths.get(build.getClassDir()).toAbsolutePath(); + Files.walkFileTree( + classDir, + new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (build.getProcessingModule().isGeneratedClass(file)) { + file = file.toAbsolutePath(); + jar.addEntry(classDir.relativize(file).toString(), file.toString()); + } + return FileVisitResult.CONTINUE; + } + }); + 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 diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java index 350da64e0e..4d14071d93 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/JavaLibraryBuildRequest.java @@ -17,8 +17,10 @@ package com.google.devtools.build.buildjar; import com.google.common.collect.ImmutableList; import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; import com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule; +import com.google.devtools.build.buildjar.javac.plugins.processing.AnnotationProcessingModule; import java.io.IOException; +import java.nio.file.Paths; import java.util.List; import java.util.Map.Entry; @@ -38,6 +40,11 @@ public final class JavaLibraryBuildRequest { */ private final String generatedSourcesOutputJar; + /** + * The path to an output jar for classfiles generated by annotation processors. + */ + private final String generatedClassOutputJar; + private final List<String> sourceFiles; private final ImmutableList<String> sourceJars; private final ImmutableList<String> messageFiles; @@ -69,6 +76,11 @@ public final class JavaLibraryBuildRequest { private final DependencyModule dependencyModule; /** + * Repository for information about annotation processor-generated symbols. + */ + private final AnnotationProcessingModule processingModule; + + /** * List of plugins that are given to javac. */ private final ImmutableList<BlazeJavaCompilerPlugin> plugins; @@ -92,41 +104,54 @@ public final class JavaLibraryBuildRequest { * * @param args the list of command line args * @param extraPlugins extraneous plugins to use in addition to the strict dependency module. - * @param builder a preconstructed dependency module builder. + * @param depsBuilder a preconstructed dependency module builder. * @throws InvalidCommandLineException on any command line error */ - public JavaLibraryBuildRequest(List<String> args, List<BlazeJavaCompilerPlugin> extraPlugins, - DependencyModule.Builder builder) + public JavaLibraryBuildRequest( + List<String> args, + List<BlazeJavaCompilerPlugin> extraPlugins, + DependencyModule.Builder depsBuilder) throws InvalidCommandLineException, IOException { OptionsParser optionsParser = new OptionsParser(args); - builder.addDirectMappings(optionsParser.getDirectMappings()); - builder.addIndirectMappings(optionsParser.getIndirectMappings()); + depsBuilder.addDirectMappings(optionsParser.getDirectMappings()); + depsBuilder.addIndirectMappings(optionsParser.getIndirectMappings()); if (optionsParser.getStrictJavaDeps() != null) { - builder.setStrictJavaDeps(optionsParser.getStrictJavaDeps()); + depsBuilder.setStrictJavaDeps(optionsParser.getStrictJavaDeps()); } if (optionsParser.getOutputDepsFile() != null) { - builder.setOutputDepsFile(optionsParser.getOutputDepsFile()); + depsBuilder.setOutputDepsFile(optionsParser.getOutputDepsFile()); } if (optionsParser.getOutputDepsProtoFile() != null) { - builder.setOutputDepsProtoFile(optionsParser.getOutputDepsProtoFile()); + depsBuilder.setOutputDepsProtoFile(optionsParser.getOutputDepsProtoFile()); } - builder.addDepsArtifacts(optionsParser.getDepsArtifacts()); + depsBuilder.addDepsArtifacts(optionsParser.getDepsArtifacts()); if (optionsParser.reduceClasspath()) { - builder.setReduceClasspath(); + depsBuilder.setReduceClasspath(); } if (optionsParser.getRuleKind() != null) { - builder.setRuleKind(optionsParser.getRuleKind()); + depsBuilder.setRuleKind(optionsParser.getRuleKind()); } if (optionsParser.getTargetLabel() != null) { - builder.setTargetLabel(optionsParser.getTargetLabel()); + depsBuilder.setTargetLabel(optionsParser.getTargetLabel()); } - this.dependencyModule = builder.build(); + this.dependencyModule = depsBuilder.build(); + + AnnotationProcessingModule.Builder processingBuilder = AnnotationProcessingModule.builder(); + if (optionsParser.getSourceGenDir() != null) { + processingBuilder.setSourceGenDir(Paths.get(optionsParser.getSourceGenDir())); + } + if (optionsParser.getClassDir() != null) { + processingBuilder.setClassDir(Paths.get(optionsParser.getClassDir())); + } + this.processingModule = processingBuilder.build(); + + ImmutableList.Builder<BlazeJavaCompilerPlugin> pluginsBuilder = + ImmutableList.<BlazeJavaCompilerPlugin>builder().add(dependencyModule.getPlugin()); + processingModule.registerPlugin(pluginsBuilder); + pluginsBuilder.addAll(extraPlugins); + this.plugins = pluginsBuilder.build(); - this.plugins = ImmutableList.<BlazeJavaCompilerPlugin>builder() - .add(dependencyModule.getPlugin()) - .addAll(extraPlugins) - .build(); this.compressJar = optionsParser.compressJar(); this.sourceFiles = optionsParser.getSourceFiles(); this.sourceJars = ImmutableList.copyOf(optionsParser.getSourceJars()); @@ -159,6 +184,7 @@ public final class JavaLibraryBuildRequest { this.javacOpts = ImmutableList.copyOf(optionsParser.getJavacOpts()); this.sourceGenDir = optionsParser.getSourceGenDir(); this.generatedSourcesOutputJar = optionsParser.getGeneratedSourcesOutputJar(); + this.generatedClassOutputJar = optionsParser.getGeneratedClassOutputJar(); } public ImmutableList<String> getJavacOpts() { @@ -177,6 +203,10 @@ public final class JavaLibraryBuildRequest { return generatedSourcesOutputJar; } + public String getGeneratedClassOutputJar() { + return generatedClassOutputJar; + } + public List<String> getSourceFiles() { // TODO(bazel-team): This is being modified after parsing to add files from source jars. return sourceFiles; @@ -244,6 +274,10 @@ public final class JavaLibraryBuildRequest { return dependencyModule; } + public AnnotationProcessingModule getProcessingModule() { + return processingModule; + } + public ImmutableList<BlazeJavaCompilerPlugin> getPlugins() { return plugins; } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java index 99962bcca3..93c6c94576 100644 --- a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/OptionsParser.java @@ -51,6 +51,7 @@ public final class OptionsParser { private String sourceGenDir; private String generatedSourcesOutputJar; + private String generatedClassOutputJar; private final List<String> sourceFiles = new ArrayList<>(); private final List<String> sourceJars = new ArrayList<>(); @@ -138,6 +139,9 @@ public final class OptionsParser { case "--generated_sources_output": generatedSourcesOutputJar = getArgument(argQueue, arg); break; + case "--classes_from_generated_sources_output": + generatedClassOutputJar = getArgument(argQueue, arg); + break; case "--sources": collectFlagArguments(sourceFiles, argQueue, "-"); break; @@ -335,6 +339,10 @@ public final class OptionsParser { return generatedSourcesOutputJar; } + public String getGeneratedClassOutputJar() { + return generatedClassOutputJar; + } + public List<String> getSourceFiles() { return sourceFiles; } diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingModule.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingModule.java new file mode 100644 index 0000000000..e9d4c1832a --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingModule.java @@ -0,0 +1,108 @@ +// Copyright 2015 Google Inc. 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.javac.plugins.processing; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; + +import java.nio.file.Path; +import java.util.HashSet; + +/** + * A module for information about the compilation's annotation processing. + */ +public class AnnotationProcessingModule { + + /** + * A builder for {@link AnnotationProcessingModule}s. + */ + public static class Builder { + private Path sourceGenDir; + private Path classDir; + + private Builder() {} + + public AnnotationProcessingModule build() { + return new AnnotationProcessingModule(sourceGenDir, classDir); + } + + public void setSourceGenDir(Path sourceGenDir) { + this.sourceGenDir = sourceGenDir.toAbsolutePath(); + } + + public void setClassDir(Path classDir) { + this.classDir = classDir.toAbsolutePath(); + } + } + + private final boolean enabled; + private final Path sourceGenDir; + private final Path classDir; + + public Path sourceGenDir() { + return sourceGenDir; + } + + private AnnotationProcessingModule(Path sourceGenDir, Path classDir) { + this.sourceGenDir = sourceGenDir; + this.classDir = classDir; + this.enabled = sourceGenDir != null && classDir != null; + } + + public static Builder builder() { + return new Builder(); + } + + public void registerPlugin(ImmutableList.Builder<BlazeJavaCompilerPlugin> builder) { + if (enabled) { + builder.add(new AnnotationProcessingPlugin(this)); + } + } + + /** + * The set of prefixes of generated class files. + */ + private final HashSet<String> pathPrefixes = new HashSet<>(); + + /** + * Record the prefix of a group of generated class files. + * + * <p>Prefixes are used to handle generated inner classes. Since + * e.g. j/c/g/Foo.class and j/c/g/Foo$Inner.class both correspond to the + * same generated source file, only the prefix "j/c/g/Foo" is recorded. + */ + void recordPrefix(String pathPrefix) { + pathPrefixes.add(pathPrefix); + } + + /** Returns true if the given path is to a generated source file. */ + public boolean isGeneratedSource(Path sourcePath) { + return sourcePath.toAbsolutePath().startsWith(sourceGenDir); + } + + /** Returns true if the given path is to a generated class file. */ + public boolean isGeneratedClass(Path path) { + if (!path.getFileName().toString().endsWith(".class")) { + return false; + } + String prefix = classDir.relativize(path.toAbsolutePath()).toString(); + prefix = prefix.substring(0, prefix.length() - ".class".length()); + int idx = prefix.lastIndexOf('$'); + if (idx > 0) { + prefix = prefix.substring(0, idx); + } + return pathPrefixes.contains(prefix); + } +} diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingPlugin.java b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingPlugin.java new file mode 100644 index 0000000000..315e6fed88 --- /dev/null +++ b/src/java_tools/buildjar/java/com/google/devtools/build/buildjar/javac/plugins/processing/AnnotationProcessingPlugin.java @@ -0,0 +1,75 @@ +// Copyright 2015 Google Inc. 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.javac.plugins.processing; + +import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin; + +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; + +/** + * A plugin that records information about sources generated during annotation + * processing. + */ +public class AnnotationProcessingPlugin extends BlazeJavaCompilerPlugin { + + private final HashSet<JCCompilationUnit> toplevels = new HashSet<>(); + private final AnnotationProcessingModule processingModule; + + public AnnotationProcessingPlugin(AnnotationProcessingModule processingModule) { + this.processingModule = processingModule; + } + + @Override + public void postAttribute(Env<AttrContext> env) { + if (toplevels.add(env.toplevel)) { + recordSymbols(env.toplevel); + } + } + + /** + * For each top-level type, record the path prefixes of that type's class, + * and all inner and anonymous classes declared inside that type. + * + * <p>e.g. for j.c.g.Foo we record the prefix j/c/g/Foo, which will match + * j/c/g/Foo.class, j/c/g/Foo$Inner.class, j/c/g/Foo$1.class, etc. + */ + private void recordSymbols(JCCompilationUnit toplevel) { + if (toplevel.sourcefile == null) { + return; + } + Path sourcePath = Paths.get(toplevel.sourcefile.toUri()); + if (!processingModule.isGeneratedSource(sourcePath)) { + return; + } + String packageBase = ""; + if (toplevel.getPackageName() != null) { + packageBase = toplevel.getPackageName().toString().replace('.', '/') + "/"; + } + for (JCTree decl : toplevel.defs) { + if (decl instanceof JCClassDecl) { + String pathPrefix = packageBase + ((JCClassDecl) decl).getSimpleName(); + processingModule.recordPrefix(pathPrefix); + } + } + } +} |