diff options
author | 2015-09-26 20:35:57 +0000 | |
---|---|---|
committer | 2015-09-28 11:39:56 +0000 | |
commit | 22616ae8ea53df3909fd16b74e0d9210138dc2c2 (patch) | |
tree | 42740e6f6eba481ff858d7e48cc1605ece0ddb92 /src/tools/android/java/com/google/devtools/build/android/idlclass | |
parent | a72be0f1d68ec449d463f825afa0aaabc4f05246 (diff) |
Add Android IDL jar outputs.
These outputs are a jar and source jar for the results of aidl processing.
This is used to add aidl output to IDEs separate from the source code,
similar to annotation output (gen jars).
--
MOS_MIGRATED_REVID=104024453
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/idlclass')
3 files changed, 265 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/idlclass/BUILD b/src/tools/android/java/com/google/devtools/build/android/idlclass/BUILD new file mode 100644 index 0000000000..a22f833748 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/idlclass/BUILD @@ -0,0 +1,23 @@ +java_binary( + name = "IdlClass", + main_class = "com.google.devtools.build.android.idlclass.IdlClass", + visibility = ["//visibility:public"], + runtime_deps = [":idlclass_lib"], +) + +java_library( + name = "idlclass_lib", + srcs = glob(["*.java"]), + visibility = [ + "//devtools/blaze/integration:__pkg__", + "//src/test/java/com/google/devtools/build/android/idlclass:__pkg__", + ], + deps = [ + "//src/java_tools/buildjar:jarhelper", + "//src/main/java:options", + "//src/main/protobuf:proto_java_compilation", + "//src/tools/android/java/com/google/devtools/build/android:android_builder_lib", + "//third_party:guava", + "//third_party:jsr305", + ], +) diff --git a/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClass.java b/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClass.java new file mode 100644 index 0000000000..c31a66a8f9 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClass.java @@ -0,0 +1,175 @@ +// 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.android.idlclass; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.devtools.build.buildjar.JarCreator; +import com.google.devtools.build.buildjar.proto.JavaCompilation.CompilationUnit; +import com.google.devtools.build.buildjar.proto.JavaCompilation.Manifest; +import com.google.devtools.common.options.OptionsParser; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * IdlClass post-processes the output of a Java compilation, and produces + * a jar containing only the class files for sources that were generated + * from idl processing. + */ +public class IdlClass { + + public static void main(String[] args) throws IOException { + OptionsParser optionsParser = OptionsParser.newOptionsParser(IdlClassOptions.class); + optionsParser.parseAndExitUponError(args); + IdlClassOptions options = optionsParser.getOptions(IdlClassOptions.class); + Preconditions.checkNotNull(options.manifestProto); + Preconditions.checkNotNull(options.classJar); + Preconditions.checkNotNull(options.outputClassJar); + Preconditions.checkNotNull(options.outputSourceJar); + Preconditions.checkNotNull(options.tempDir); + + List<Path> idlSources = Lists.newArrayList(); + for (String idlSource : optionsParser.getResidue()) { + idlSources.add(Paths.get(idlSource)); + } + + Manifest manifest = readManifest(options.manifestProto); + writeClassJar(options, idlSources, manifest); + writeSourceJar(options, idlSources, manifest); + } + + private static void writeClassJar(IdlClassOptions options, + List<Path> idlSources, Manifest manifest) throws IOException { + Path tempDir = options.tempDir.resolve("classjar"); + Set<String> idlSourceSet = Sets.newHashSet(); + for (Path path : idlSources) { + idlSourceSet.add(path.toString()); + } + extractIdlClasses(options.classJar, manifest, tempDir, idlSourceSet); + writeOutputJar(options.outputClassJar, tempDir); + } + + private static void writeSourceJar(IdlClassOptions options, + List<Path> idlSources, Manifest manifest) throws IOException { + Path tempDir = options.tempDir.resolve("sourcejar"); + Path idlSourceBaseDir = options.idlSourceBaseDir; + + for (Path path : idlSources) { + for (CompilationUnit unit : manifest.getCompilationUnitList()) { + if (unit.getPath().equals(path.toString())) { + String pkg = unit.getPkg(); + Path source = idlSourceBaseDir != null ? idlSourceBaseDir.resolve(path) : path; + Path target = tempDir.resolve(pkg.replace('.', '/')).resolve(path.getFileName()); + Files.createDirectories(target.getParent()); + Files.copy(source, target); + break; + } + } + } + writeOutputJar(options.outputSourceJar, tempDir); + } + + /** + * Reads the compilation manifest. + */ + private static Manifest readManifest(Path path) throws IOException { + Manifest manifest; + try (InputStream inputStream = Files.newInputStream(path)) { + manifest = Manifest.parseFrom(inputStream); + } + return manifest; + } + + /** + * For each top-level class in the compilation, determine the path prefix + * of classes corresponding to that compilation unit. + * + * <p>Prefixes are used to correctly handle inner classes, e.g. the top-level + * class "c.g.Foo" may correspond to "c/g/Foo.class" and also + * "c/g/Foo$Inner.class" or "c/g/Foo$0.class". + */ + @VisibleForTesting + static ImmutableSet<String> getIdlPrefixes(Manifest manifest, Set<String> idlSources) { + ImmutableSet.Builder<String> prefixes = ImmutableSet.builder(); + for (CompilationUnit unit : manifest.getCompilationUnitList()) { + if (!idlSources.contains(unit.getPath())) { + continue; + } + String pkg; + if (unit.hasPkg()) { + pkg = unit.getPkg().replace('.', '/') + "/"; + } else { + pkg = ""; + } + for (String toplevel : unit.getTopLevelList()) { + prefixes.add(pkg + toplevel); + } + } + return prefixes.build(); + } + + /** + * Unzip all the class files that correspond to idl processor- + * generated sources into the temporary directory. + */ + private static void extractIdlClasses( + Path classJar, + Manifest manifest, + Path tempDir, + Set<String> idlSources) + throws IOException { + ImmutableSet<String> prefixes = getIdlPrefixes(manifest, idlSources); + try (JarFile jar = new JarFile(classJar.toFile())) { + Enumeration<JarEntry> entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + if (!name.endsWith(".class")) { + continue; + } + String prefix = name.substring(0, name.length() - ".class".length()); + int idx = prefix.indexOf('$'); + if (idx > 0) { + prefix = prefix.substring(0, idx); + } + if (prefixes.contains(prefix)) { + Files.createDirectories(tempDir.resolve(name).getParent()); + Files.copy(jar.getInputStream(entry), tempDir.resolve(name)); + } + } + } + } + + /** Writes the generated class files to the output jar. */ + private static void writeOutputJar(Path outputJar, Path tempDir) throws IOException { + JarCreator output = new JarCreator(outputJar.toString()); + output.setCompression(true); + output.setNormalize(true); + output.addDirectory(tempDir.toString()); + output.execute(); + } +} diff --git a/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClassOptions.java b/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClassOptions.java new file mode 100644 index 0000000000..d36bd1a1fd --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/idlclass/IdlClassOptions.java @@ -0,0 +1,67 @@ +// 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.android.idlclass; + +import com.google.devtools.build.android.Converters.ExistingPathConverter; +import com.google.devtools.build.android.Converters.PathConverter; +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionsBase; + +import java.nio.file.Path; + +/** The options for a {@IdlClass} action. */ +public final class IdlClassOptions extends OptionsBase { + @Option(name = "manifest_proto", + defaultValue = "null", + converter = ExistingPathConverter.class, + category = "input", + help = "The path to the manifest file output by the Java compiler.") + public Path manifestProto; + + @Option(name = "class_jar", + defaultValue = "null", + converter = ExistingPathConverter.class, + category = "input", + help = "The path to the class jar output by the Java compiler.") + public Path classJar; + + @Option(name = "output_class_jar", + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "The path to write the class jar output to.") + public Path outputClassJar; + + @Option(name = "output_source_jar", + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "The path to write the source jar output to.") + public Path outputSourceJar; + + @Option(name = "temp_dir", + defaultValue = "null", + converter = PathConverter.class, + category = "input", + help = "The path to a temp directory.") + public Path tempDir; + + @Option(name = "idl_source_base_dir", + defaultValue = "null", + converter = PathConverter.class, + category = "input", + help = "The path to the base directory of the idl sources. Optional; Used for testing.") + public Path idlSourceBaseDir; +} |