aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2015-12-03 15:17:55 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-12-03 18:37:59 +0000
commit5e34a3d4ea30525971e320ce283ba8cd67c74e3c (patch)
tree29c755c006c6b5f1fb82f131a72c44e406a18c5a /src/tools
parent84fb4612dd4203425b59809ea5e3302ff55723fa (diff)
ASwB aspect: parse java packages during execution
-- MOS_MIGRATED_REVID=109305952
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/BUILD2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Converters.java28
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD38
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD.tools12
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java206
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParserIoProvider.java49
6 files changed, 335 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD b/src/tools/android/java/com/google/devtools/build/android/BUILD
index 74c12552bf..9abd062082 100644
--- a/src/tools/android/java/com/google/devtools/build/android/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/BUILD
@@ -37,10 +37,12 @@ java_library(
srcs = glob(["*.java"]),
deps = [
"//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:package_manifest_proto",
"//third_party:android_common",
"//third_party:apache_commons_compress",
"//third_party:asm",
"//third_party:guava",
"//third_party:jsr305",
+ "//third_party:protobuf",
],
)
diff --git a/src/tools/android/java/com/google/devtools/build/android/Converters.java b/src/tools/android/java/com/google/devtools/build/android/Converters.java
index 8a10c52285..f69aac162d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/Converters.java
+++ b/src/tools/android/java/com/google/devtools/build/android/Converters.java
@@ -26,6 +26,8 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -154,4 +156,30 @@ public final class Converters {
super(VariantConfiguration.Type.class, "variant configuration type");
}
}
+
+ /**
+ * Validating converter for a list of Paths.
+ * A Path is considered valid if it resolves to a file.
+ */
+ public static class PathListConverter implements Converter<List<Path>> {
+
+ final PathConverter baseConverter = new PathConverter();
+
+ @Override
+ public List<Path> convert(String input) throws OptionsParsingException {
+ List<Path> list = new ArrayList<>();
+ for (String piece : input.split(":")) {
+ if (!piece.isEmpty()) {
+ list.add(baseConverter.convert(piece));
+ }
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "a colon-separated list of paths";
+ }
+ }
+
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD b/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD
new file mode 100644
index 0000000000..b520a81b3a
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD
@@ -0,0 +1,38 @@
+filegroup(
+ name = "embedded_tools",
+ srcs = [
+ "BUILD.tools",
+ "classes_deploy.jar",
+ ],
+ visibility = ["//src:__pkg__"],
+)
+
+java_binary(
+ name = "classes",
+ main_class = "does.not.exist",
+ runtime_deps = [":package_parser_lib"],
+)
+
+java_binary(
+ name = "PackageParser",
+ main_class = "com.google.devtools.build.android.ideinfo.PackageParser",
+ visibility = ["//visibility:public"],
+ runtime_deps = [":package_parser_lib"],
+)
+
+java_library(
+ name = "package_parser_lib",
+ srcs = glob(["*.java"]),
+ visibility = [
+ "//devtools/blaze/integration:__pkg__",
+ "//src/test/java/com/google/devtools/build/android/ideinfo:__pkg__",
+ ],
+ deps = [
+ "//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:package_manifest_proto",
+ "//src/tools/android/java/com/google/devtools/build/android:android_builder_lib",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ "//third_party:protobuf",
+ ],
+)
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD.tools b/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD.tools
new file mode 100644
index 0000000000..efa64fb710
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/BUILD.tools
@@ -0,0 +1,12 @@
+package(default_visibility = ["//visibility:public"])
+
+java_import(
+ name = "classes",
+ jars = [":classes_deploy.jar"],
+)
+
+java_binary(
+ name = "PackageParser",
+ main_class = "com.google.devtools.build.android.ideinfo.PackageParser",
+ runtime_deps = [":classes"],
+)
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java
new file mode 100644
index 0000000000..5e84d3c20e
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java
@@ -0,0 +1,206 @@
+// 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.android.ideinfo;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
+import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.JavaSourcePackage;
+import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.PackageManifest;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionsBase;
+import com.google.devtools.common.options.OptionsParser;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Parses the package string from each of the source .java files
+ */
+public class PackageParser {
+
+ /** The options for a {@PackageParser} action. */
+ public static final class PackageParserOptions extends OptionsBase {
+ @Option(name = "sources_absolute_paths",
+ defaultValue = "null",
+ converter = PathListConverter.class,
+ category = "input",
+ help = "The absolute paths of the java source files. The expected format is a "
+ + "colon-separated list.")
+ public List<Path> sourcesAbsolutePaths;
+
+ @Option(name = "sources_execution_paths",
+ defaultValue = "null",
+ converter = PathListConverter.class,
+ category = "input",
+ help = "The execution paths of the java source files. The expected format is a "
+ + "colon-separated list.")
+ public List<Path> sourcesExecutionPaths;
+
+ @Option(name = "output_manifest",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "The path to the manifest file this parser writes to.")
+ public Path outputManifest;
+ }
+
+ private static final Logger logger = Logger.getLogger(PackageParser.class.getName());
+
+ private static final Pattern JAVA_PACKAGE_PATTERN =
+ Pattern.compile("^\\s*package\\s+([\\w\\.]+);");
+
+ public static void main(String[] args) throws Exception {
+ PackageParserOptions options = parseArgs(args);
+ Preconditions.checkNotNull(options.sourcesAbsolutePaths);
+ Preconditions.checkNotNull(options.sourcesExecutionPaths);
+ Preconditions.checkState(
+ options.sourcesAbsolutePaths.size() == options.sourcesExecutionPaths.size());
+ Preconditions.checkNotNull(options.outputManifest);
+
+ try {
+ PackageParser parser = new PackageParser(PackageParserIoProvider.INSTANCE);
+ Map<Path, String> outputMap = parser.parsePackageStrings(options.sourcesAbsolutePaths,
+ options.sourcesExecutionPaths);
+ parser.writeManifest(outputMap, options.outputManifest);
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, "Error parsing package strings", e);
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ @VisibleForTesting
+ public static PackageParserOptions parseArgs(String[] args) {
+ args = parseParamFileIfUsed(args);
+ OptionsParser optionsParser = OptionsParser.newOptionsParser(PackageParserOptions.class);
+ optionsParser.parseAndExitUponError(args);
+ return optionsParser.getOptions(PackageParserOptions.class);
+ }
+
+ private static String[] parseParamFileIfUsed(@Nonnull String[] args) {
+ if (args.length != 1 || !args[0].startsWith("@")) {
+ return args;
+ }
+ File paramFile = new File(args[0].substring(1));
+ try {
+ return Files.readLines(paramFile, StandardCharsets.UTF_8).toArray(new String[0]);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing param file: " + args[0], e);
+ }
+ }
+
+ private final PackageParserIoProvider ioProvider;
+
+ @VisibleForTesting
+ public PackageParser(@Nonnull PackageParserIoProvider ioProvider) {
+ this.ioProvider = ioProvider;
+ }
+
+ @VisibleForTesting
+ public void writeManifest(@Nonnull Map<Path, String> sourceToPackageMap, Path outputFile)
+ throws IOException {
+ if (sourceToPackageMap.isEmpty()) {
+ return;
+ }
+ PackageManifest.Builder builder = PackageManifest.newBuilder();
+ for (Entry<Path, String> entry : sourceToPackageMap.entrySet()) {
+ builder.addSources(JavaSourcePackage.newBuilder()
+ .setAbsolutePath(entry.getKey().toAbsolutePath().toString())
+ .setPackageString(entry.getValue()));
+ }
+
+ try {
+ ioProvider.writeProto(builder.build(), outputFile);
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Error writing package manifest", e);
+ throw e;
+ }
+ }
+
+ @Nonnull
+ @VisibleForTesting
+ public Map<Path, String> parsePackageStrings(@Nonnull List<Path> absolutePaths,
+ @Nonnull List<Path> executionPaths) throws Exception {
+
+ ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
+ Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
+
+ Map<Path, ListenableFuture<String>> futures = Maps.newHashMap();
+ for (int i = 0; i < absolutePaths.size(); i++) {
+ final Path source = executionPaths.get(i);
+ futures.put(absolutePaths.get(i), executorService.submit(new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return getDeclaredPackageOfJavaFile(source);
+ }
+ }));
+ }
+ Map<Path, String> map = Maps.newHashMap();
+ for (Entry<Path, ListenableFuture<String>> entry : futures.entrySet()) {
+ String value = entry.getValue().get();
+ if (value != null) {
+ map.put(entry.getKey(), value);
+ }
+ }
+ return map;
+ }
+
+ @Nullable
+ private String getDeclaredPackageOfJavaFile(@Nonnull Path source) {
+ try (BufferedReader reader = ioProvider.getReader(source)) {
+ return parseDeclaredPackage(reader);
+
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error parsing package string from java source: " + source, e);
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public static String parseDeclaredPackage(@Nonnull BufferedReader reader) throws IOException {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Matcher packageMatch = JAVA_PACKAGE_PATTERN.matcher(line);
+ if (packageMatch.find()) {
+ return packageMatch.group(1);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParserIoProvider.java b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParserIoProvider.java
new file mode 100644
index 0000000000..be555c8d0e
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParserIoProvider.java
@@ -0,0 +1,49 @@
+// 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.android.ideinfo;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.protobuf.MessageLite;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Provides a BufferedReader for the source java files,
+ * and a writer for the output proto
+ */
+@VisibleForTesting
+public class PackageParserIoProvider {
+
+ public static final PackageParserIoProvider INSTANCE = new PackageParserIoProvider();
+
+ public void writeProto(@Nonnull MessageLite message, @Nonnull Path file) throws IOException {
+ try (OutputStream out = Files.newOutputStream(file)) {
+ message.writeTo(out);
+ }
+ }
+
+ @Nonnull
+ public BufferedReader getReader(Path file) throws IOException {
+ return Files.newBufferedReader(file, StandardCharsets.UTF_8);
+ }
+
+}