aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2017-04-14 21:31:43 +0200
committerGravatar Klaus Aehlig <aehlig@google.com>2017-04-18 11:28:00 +0200
commitf87b7080a399906bdf9b3a1ecdf8694eca22498e (patch)
treea2ff63c06a4122090feb81b125b4697d0e87373a /src/tools/android/java/com/google/devtools/build
parent4c8959092b44ab1359f2e2ddd0a7552baadc35b8 (diff)
Teach the RClassGenerator to merge all library symbols and reconcile ids.
Refactoring: Wrapped the SymbolLoader and SymbolWriter in a single class. RELNOTES: None PiperOrigin-RevId: 153194543
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java159
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java15
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java33
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java35
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java188
6 files changed, 270 insertions, 163 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
index 1c9982aecc..a9243d2584 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
@@ -22,8 +22,6 @@ import com.android.annotations.Nullable;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.core.VariantType;
import com.android.builder.dependency.SymbolFileProvider;
-import com.android.builder.internal.SymbolLoader;
-import com.android.builder.internal.SymbolWriter;
import com.android.builder.model.AaptOptions;
import com.android.ide.common.internal.CommandLineRunner;
import com.android.ide.common.internal.ExecutorSingleton;
@@ -33,13 +31,12 @@ import com.android.io.StreamException;
import com.android.repository.Revision;
import com.android.utils.ILogger;
import com.android.utils.StdLogger;
+import com.android.utils.StdLogger.Level;
import com.android.xml.AndroidManifest;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
-import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
@@ -47,12 +44,12 @@ import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.RevisionConverter;
import com.google.devtools.build.android.SplitConfigurationFilter.UnrecognizedSplitsException;
import com.google.devtools.build.android.resources.RClassGenerator;
+import com.google.devtools.build.android.resources.ResourceSymbols;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.TriState;
import java.io.Closeable;
-import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
@@ -62,10 +59,9 @@ import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
+import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
@@ -487,43 +483,12 @@ public class AndroidResourceProcessor {
return processedResourceDir;
}
- /** Task to parse java package from AndroidManifest.xml */
- private static final class PackageParsingTask implements Callable<String> {
-
- private final File manifest;
-
- PackageParsingTask(File manifest) {
- this.manifest = manifest;
- }
-
- @Override
- public String call() throws Exception {
- return VariantConfiguration.getManifestPackage(manifest);
- }
- }
-
- /** Task to load and parse R.txt symbols */
- private static final class SymbolLoadingTask implements Callable<Object> {
-
- private final SymbolLoader symbolLoader;
-
- SymbolLoadingTask(SymbolLoader symbolLoader) {
- this.symbolLoader = symbolLoader;
- }
-
- @Override
- public Object call() throws Exception {
- symbolLoader.load();
- return null;
- }
- }
-
- @Nullable
- public SymbolLoader loadResourceSymbolTable(
+ public ResourceSymbols loadResourceSymbolTable(
List<SymbolFileProvider> libraries,
String appPackageName,
Path primaryRTxt,
- Multimap<String, SymbolLoader> libMap) throws IOException {
+ Multimap<String, ResourceSymbols> libMap)
+ throws IOException {
// The reported availableProcessors may be higher than the actual resources
// (on a shared system). On the other hand, a lot of the work is I/O, so it's not completely
// CPU bound. As a compromise, divide by 2 the reported availableProcessors.
@@ -531,56 +496,17 @@ public class AndroidResourceProcessor {
ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(numThreads));
try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) {
- // Load the package names from the manifest files.
- Map<SymbolFileProvider, ListenableFuture<String>> packageJobs = new HashMap<>();
- for (final SymbolFileProvider lib : libraries) {
- packageJobs.put(lib, executorService.submit(new PackageParsingTask(lib.getManifest())));
- }
- Map<SymbolFileProvider, String> packageNames = new HashMap<>();
- try {
- for (Map.Entry<SymbolFileProvider, ListenableFuture<String>> entry : packageJobs
- .entrySet()) {
- packageNames.put(entry.getKey(), entry.getValue().get());
- }
- } catch (InterruptedException | ExecutionException e) {
- throw new IOException("Failed to load package name: ", e);
+ StdLogger iLogger = new StdLogger(Level.INFO);
+ for (Entry<String, ListenableFuture<ResourceSymbols>> entry :
+ ResourceSymbols.loadFrom(libraries, executorService, iLogger, appPackageName).entries()) {
+ libMap.put(entry.getKey(), entry.getValue().get());
}
- // Associate the packages with symbol files.
- for (SymbolFileProvider lib : libraries) {
- String packageName = packageNames.get(lib);
- // If the library package matches the app package skip -- the final app resource IDs are
- // stored in the primaryRTxt file.
- if (appPackageName.equals(packageName)) {
- continue;
- }
- File rFile = lib.getSymbolFile();
- // If the library has no resource, this file won't exist.
- if (rFile.isFile()) {
- SymbolLoader libSymbols = new SymbolLoader(rFile, stdLogger);
- libMap.put(packageName, libSymbols);
- }
+ if (primaryRTxt != null && Files.exists(primaryRTxt)) {
+ return ResourceSymbols.load(primaryRTxt, executorService, iLogger).get();
}
- // Even if there are no libraries, load fullSymbolValues, in case we only have resources
- // defined for the binary.
- File primaryRTxtFile = primaryRTxt.toFile();
- SymbolLoader fullSymbolValues = null;
- if (primaryRTxtFile.isFile()) {
- fullSymbolValues = new SymbolLoader(primaryRTxtFile, stdLogger);
- }
- // Now load the symbol files in parallel.
- List<ListenableFuture<?>> loadJobs = new ArrayList<>();
- Iterable<SymbolLoader> toLoad = fullSymbolValues != null
- ? Iterables.concat(libMap.values(), ImmutableList.of(fullSymbolValues))
- : libMap.values();
- for (final SymbolLoader loader : toLoad) {
- loadJobs.add(executorService.submit(new SymbolLoadingTask(loader)));
- }
- try {
- Futures.allAsList(loadJobs).get();
- } catch (InterruptedException | ExecutionException e) {
- throw new IOException("Failed to load SymbolFile: ", e);
- }
- return fullSymbolValues;
+ return ResourceSymbols.merge(libMap.values());
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IOException("Failed to load SymbolFile: ", e);
}
}
@@ -598,49 +524,44 @@ public class AndroidResourceProcessor {
if (appPackageName == null) {
appPackageName = VariantConfiguration.getManifestPackage(androidManifest.toFile());
}
- Multimap<String, SymbolLoader> libSymbolMap = ArrayListMultimap.create();
+ Multimap<String, ResourceSymbols> libSymbolMap = ArrayListMultimap.create();
Path primaryRTxt = sourceOut != null ? sourceOut.resolve("R.txt") : null;
if (primaryRTxt != null && !libraries.isEmpty()) {
- SymbolLoader fullSymbolValues = loadResourceSymbolTable(libraries,
- appPackageName, primaryRTxt, libSymbolMap);
- if (fullSymbolValues != null) {
- writePackageRJavaFiles(libSymbolMap, fullSymbolValues, sourceOut);
- }
- }
- }
-
- private void writePackageRJavaFiles(
- Multimap<String, SymbolLoader> libMap,
- SymbolLoader fullSymbolValues,
- Path sourceOut) throws IOException {
- // Loop on all the package name, merge all the symbols to write, and write.
- for (String packageName : libMap.keySet()) {
- Collection<SymbolLoader> symbols = libMap.get(packageName);
- SymbolWriter writer = new SymbolWriter(sourceOut.toString(), packageName, fullSymbolValues);
- for (SymbolLoader symbolLoader : symbols) {
- writer.addSymbolsToWrite(symbolLoader);
+ ResourceSymbols fullSymbolValues =
+ loadResourceSymbolTable(libraries, appPackageName, primaryRTxt, libSymbolMap);
+ // Loop on all the package name, merge all the symbols to write, and write.
+ for (String packageName : libSymbolMap.keySet()) {
+ Collection<ResourceSymbols> symbols = libSymbolMap.get(packageName);
+ fullSymbolValues.writeTo(sourceOut, packageName, symbols);
}
- writer.write();
}
}
void writePackageRClasses(
- Multimap<String, SymbolLoader> libMap,
- SymbolLoader fullSymbolValues,
- String appPackageName,
+ Multimap<String, ResourceSymbols> libMap,
+ ResourceSymbols fullSymbolValues,
+ @Nullable String appPackageName,
Path classesOut,
- boolean finalFields) throws IOException {
+ boolean finalFields)
+ throws IOException {
for (String packageName : libMap.keySet()) {
- Collection<SymbolLoader> symbols = libMap.get(packageName);
+ Collection<ResourceSymbols> symbols = libMap.get(packageName);
RClassGenerator classWriter = RClassGenerator.fromSymbols(
classesOut, packageName, fullSymbolValues, symbols, finalFields);
classWriter.write();
}
- // Unlike the R.java generation, we also write the app's R.class file so that the class
- // jar file can be complete (aapt doesn't generate it for us).
- RClassGenerator classWriter = RClassGenerator.fromSymbols(classesOut, appPackageName,
- fullSymbolValues, ImmutableList.of(fullSymbolValues), finalFields);
- classWriter.write();
+ if (appPackageName != null) {
+ // Unlike the R.java generation, we also write the app's R.class file so that the class
+ // jar file can be complete (aapt doesn't generate it for us).
+ RClassGenerator classWriter =
+ RClassGenerator.fromSymbols(
+ classesOut,
+ appPackageName,
+ fullSymbolValues,
+ ImmutableList.of(fullSymbolValues),
+ finalFields);
+ classWriter.write();
+ }
}
/** Finds aapt's split outputs and renames them according to the input flags. */
diff --git a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
index 88a2f88233..64a8fb2388 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java
@@ -92,6 +92,21 @@ class DependencyAndroidData extends SerializedAndroidData {
public boolean isOptional() {
return false;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getManifest(), getSymbolFile());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SymbolFileProvider) {
+ SymbolFileProvider other = (SymbolFileProvider) obj;
+ return Objects.equals(getManifest(), other.getManifest())
+ && Objects.equals(getSymbolFile(), other.getSymbolFile());
+ }
+ return false;
+ }
};
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
index 1cb816c01e..399b9b34e6 100644
--- a/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.android;
import com.android.ide.common.res2.MergingException;
-import com.android.utils.StdLogger;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
@@ -43,8 +42,6 @@ public class LibraryRClassGeneratorAction {
private static final Logger logger =
Logger.getLogger(LibraryRClassGeneratorAction.class.getName());
- private static final StdLogger stdLogger = new StdLogger(StdLogger.Level.WARNING);
-
/** Flag specifications for this action. */
public static final class Options extends OptionsBase {
@Option(
diff --git a/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java
index 1e97859311..6ed0d8ff05 100644
--- a/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java
@@ -15,7 +15,6 @@ package com.google.devtools.build.android;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.dependency.SymbolFileProvider;
-import com.android.builder.internal.SymbolLoader;
import com.android.utils.StdLogger;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
@@ -23,14 +22,13 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.devtools.build.android.Converters.DependencySymbolFileProviderListConverter;
import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.resources.ResourceSymbols;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
-import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -106,11 +104,6 @@ public class RClassGeneratorAction {
final Stopwatch timer = Stopwatch.createStarted();
OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
optionsParser.enableParamsFileSupport(FileSystems.getDefault());
- if (args.length == 1 && args[0].startsWith("@")) {
- args = Files.readAllLines(Paths.get(args[0].substring(1)), StandardCharsets.UTF_8)
- .toArray(new String[0]);
- }
-
optionsParser.parseAndExitUponError(args);
Options options = optionsParser.getOptions(Options.class);
Preconditions.checkNotNull(options.classJarOutput);
@@ -133,18 +126,28 @@ public class RClassGeneratorAction {
appPackageName = VariantConfiguration
.getManifestPackage(options.primaryManifest.toFile());
}
- Multimap<String, SymbolLoader> libSymbolMap = ArrayListMultimap.create();
- SymbolLoader fullSymbolValues = resourceProcessor.loadResourceSymbolTable(
- libraries, appPackageName, options.primaryRTxt, libSymbolMap);
+ Multimap<String, ResourceSymbols> libSymbolMap = ArrayListMultimap.create();
+ ResourceSymbols fullSymbolValues =
+ resourceProcessor.loadResourceSymbolTable(
+ libraries, appPackageName, options.primaryRTxt, libSymbolMap);
logger.fine(
String.format("Load symbols finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
// For now, assuming not used for libraries and setting final access for fields.
- if (fullSymbolValues != null) {
resourceProcessor.writePackageRClasses(libSymbolMap, fullSymbolValues, appPackageName,
classOutPath, true /* finalFields */);
- logger.fine(
- String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
- }
+ logger.fine(
+ String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ } else if (!libraries.isEmpty()) {
+ Multimap<String, ResourceSymbols> libSymbolMap = ArrayListMultimap.create();
+ ResourceSymbols fullSymbolValues =
+ resourceProcessor.loadResourceSymbolTable(libraries, null, null, libSymbolMap);
+ logger.fine(
+ String.format("Load symbols finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ // For now, assuming not used for libraries and setting final access for fields.
+ resourceProcessor.writePackageRClasses(
+ libSymbolMap, fullSymbolValues, null, classOutPath, true /* finalFields */);
+ logger.fine(
+ String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
} else {
Files.createDirectories(classOutPath);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
index 5342a83901..2d92e783cc 100644
--- a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
+++ b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.android.resources;
import com.android.SdkConstants;
-import com.android.builder.internal.SymbolLoader;
import com.android.builder.internal.SymbolLoader.SymbolEntry;
import com.android.resources.ResourceType;
import com.google.common.base.Preconditions;
@@ -22,7 +21,6 @@ import com.google.common.base.Splitter;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.io.IOException;
-import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -69,12 +67,12 @@ public class RClassGenerator {
public static RClassGenerator fromSymbols(
Path outFolder,
String packageName,
- SymbolLoader values,
- Collection<SymbolLoader> packageSymbols,
+ ResourceSymbols values,
+ Collection<ResourceSymbols> packageSymbols,
boolean finalFields)
throws IOException {
Table<String, String, SymbolEntry> symbolsTable = getAllSymbols(packageSymbols);
- Table<String, String, SymbolEntry> valuesTable = getSymbols(values);
+ Table<String, String, SymbolEntry> valuesTable = values.asTable();
Map<ResourceType, List<FieldInitializer>> initializers =
getInitializers(symbolsTable, valuesTable);
return new RClassGenerator(outFolder, packageName, initializers, finalFields);
@@ -100,30 +98,15 @@ public class RClassGenerator {
}
private static Table<String, String, SymbolEntry> getAllSymbols(
- Collection<SymbolLoader> symbolLoaders) throws IOException {
- Table<String, String, SymbolEntry> symbols = HashBasedTable.create();
- for (SymbolLoader symbolLoader : symbolLoaders) {
- symbols.putAll(getSymbols(symbolLoader));
+ Collection<ResourceSymbols> symbols) throws IOException {
+ Table<String, String, SymbolEntry> mergedSymbols = HashBasedTable.create();
+ for (ResourceSymbols tableProvider : symbols) {
+ mergedSymbols.putAll(tableProvider.asTable());
}
- return symbols;
+ return mergedSymbols;
}
- private static Table<String, String, SymbolEntry> getSymbols(SymbolLoader symbolLoader)
- throws IOException {
- // TODO(bazel-team): remove when we update android_ide_common to a version w/ public visibility
- try {
- Method getSymbols = SymbolLoader.class.getDeclaredMethod("getSymbols");
- getSymbols.setAccessible(true);
- @SuppressWarnings("unchecked")
- Table<String, String, SymbolEntry> result =
- (Table<String, String, SymbolEntry>) getSymbols.invoke(symbolLoader);
- return result;
- } catch (ReflectiveOperationException e) {
- throw new IOException(e);
- }
- }
-
- /** Convert the {@link SymbolLoader} data, to a map of {@link FieldInitializer}. */
+ /** Convert the {@link SymbolTableProvider} data, to a map of {@link FieldInitializer}. */
private static Map<ResourceType, List<FieldInitializer>> getInitializers(
Table<String, String, SymbolEntry> symbols, Table<String, String, SymbolEntry> values) {
Map<ResourceType, List<FieldInitializer>> initializers = new EnumMap<>(ResourceType.class);
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java
new file mode 100644
index 0000000000..1860e228a3
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java
@@ -0,0 +1,188 @@
+// 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.android.resources;
+
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.dependency.SymbolFileProvider;
+import com.android.builder.internal.SymbolLoader;
+import com.android.builder.internal.SymbolLoader.SymbolEntry;
+import com.android.builder.internal.SymbolWriter;
+import com.android.utils.ILogger;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Table;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import javax.annotation.Nullable;
+
+/**
+ * Wraps the {@link SymbolLoader} and {@link SymbolWriter} classes.
+ * This provides a unified interface for working with R.txts.
+ */
+public class ResourceSymbols {
+ /** Task to load and parse R.txt symbols */
+ private static final class SymbolLoadingTask implements Callable<ResourceSymbols> {
+
+ private final SymbolLoader symbolLoader;
+
+ SymbolLoadingTask(SymbolLoader symbolLoader) {
+ this.symbolLoader = symbolLoader;
+ }
+
+ @Override
+ public ResourceSymbols call() throws Exception {
+ symbolLoader.load();
+ return ResourceSymbols.wrap(symbolLoader);
+ }
+ }
+
+ private static final class PackageParsingTask implements Callable<String> {
+
+ private final File manifest;
+
+ PackageParsingTask(File manifest) {
+ this.manifest = manifest;
+ }
+
+ @Override
+ public String call() throws Exception {
+ return VariantConfiguration.getManifestPackage(manifest);
+ }
+ }
+
+ /**
+ * Loads the SymbolTables from a list of SymbolFileProviders.
+ *
+ * @param dependencies The full set of library symbols to load.
+ * @param executor The executor use during loading.
+ * @param iLogger Android logger to use.
+ * @param packageToExclude A string package to elide if it exists in the providers.
+ * @return A list of loading {@link ResourceSymbols} instances.
+ * @throws ExecutionException
+ * @throws InterruptedException when there is an error loading the symbols.
+ */
+ public static Multimap<String, ListenableFuture<ResourceSymbols>> loadFrom(
+ Collection<SymbolFileProvider> dependencies,
+ ListeningExecutorService executor,
+ ILogger iLogger,
+ @Nullable String packageToExclude)
+ throws InterruptedException, ExecutionException {
+ Map<SymbolFileProvider, ListenableFuture<String>> providerToPackage = new HashMap<>();
+ for (SymbolFileProvider dependency : dependencies) {
+ providerToPackage.put(
+ dependency, executor.submit(new PackageParsingTask(dependency.getManifest())));
+ }
+ Multimap<String, ListenableFuture<ResourceSymbols>> packageToTable = HashMultimap.create();
+ for (Entry<SymbolFileProvider, ListenableFuture<String>> entry : providerToPackage.entrySet()) {
+ File symbolFile = entry.getKey().getSymbolFile();
+ if (!Objects.equals(entry.getValue().get(), packageToExclude)) {
+ packageToTable.put(entry.getValue().get(), load(executor, iLogger, symbolFile));
+ }
+ }
+ return packageToTable;
+ }
+
+ public static ResourceSymbols merge(Collection<ResourceSymbols> symbolTables) throws IOException {
+ final Table<String, String, SymbolEntry> mergedTable = HashBasedTable.create();
+ for (ResourceSymbols symbolTableProvider : symbolTables) {
+ mergedTable.putAll(symbolTableProvider.asTable());
+ }
+ try {
+ SymbolLoader nullLoader = new SymbolLoader(null, null);
+ Field declaredField = SymbolLoader.class.getDeclaredField("mSymbols");
+ declaredField.setAccessible(true);
+ declaredField.set(nullLoader, mergedTable);
+ return wrap(nullLoader);
+ } catch (NoSuchFieldException
+ | SecurityException
+ | IllegalArgumentException
+ | IllegalAccessException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /** Read the symbols from the provided symbol file. */
+ public static ListenableFuture<ResourceSymbols> load(
+ Path primaryRTxt, ListeningExecutorService executorService, ILogger iLogger) {
+ return load(executorService, iLogger, primaryRTxt.toFile());
+ }
+
+ public static ListenableFuture<ResourceSymbols> load(
+ ListeningExecutorService executor, ILogger iLogger, File symbolFile) {
+ return executor.submit(new SymbolLoadingTask(new SymbolLoader(symbolFile, iLogger)));
+ }
+
+ static ResourceSymbols of(Path rTxt, ILogger logger) {
+ return of(rTxt.toFile(), logger);
+ }
+
+ public static ResourceSymbols of(File rTxt, ILogger logger) {
+ return wrap(new SymbolLoader(rTxt, logger));
+ }
+
+ private static ResourceSymbols wrap(SymbolLoader input) {
+ return new ResourceSymbols(input);
+ }
+
+ private final SymbolLoader symbolLoader;
+
+ private ResourceSymbols(SymbolLoader symbolLoader) {
+ this.symbolLoader = symbolLoader;
+ }
+
+ public Table<String, String, SymbolEntry> asTable() throws IOException {
+ // TODO(bazel-team): remove when we update android_ide_common to a version w/ public visibility
+ try {
+ Method getSymbols = SymbolLoader.class.getDeclaredMethod("getSymbols");
+ getSymbols.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Table<String, String, SymbolEntry> result =
+ (Table<String, String, SymbolEntry>) getSymbols.invoke(symbolLoader);
+ return result;
+ } catch (ReflectiveOperationException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Writes the java sources for a given package.
+ *
+ * @param sourceOut The directory to write the java package structures and sources to.
+ * @param packageName The name of the package to write.
+ * @param packageSymbols The symbols defined in the given package.
+ * @throws IOException when encountering an error during writing.
+ */
+ public void writeTo(
+ Path sourceOut, String packageName, Collection<ResourceSymbols> packageSymbols)
+ throws IOException {
+ SymbolWriter writer = new SymbolWriter(sourceOut.toString(), packageName, symbolLoader);
+ for (ResourceSymbols packageSymbol : packageSymbols) {
+ writer.addSymbolsToWrite(packageSymbol.symbolLoader);
+ }
+ writer.write();
+ }
+}