diff options
author | 2017-04-14 21:31:43 +0200 | |
---|---|---|
committer | 2017-04-18 11:28:00 +0200 | |
commit | f87b7080a399906bdf9b3a1ecdf8694eca22498e (patch) | |
tree | a2ff63c06a4122090feb81b125b4697d0e87373a /src/tools/android/java/com/google/devtools/build | |
parent | 4c8959092b44ab1359f2e2ddd0a7552baadc35b8 (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')
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(); + } +} |