From f87b7080a399906bdf9b3a1ecdf8694eca22498e Mon Sep 17 00:00:00 2001 From: corysmith Date: Fri, 14 Apr 2017 21:31:43 +0200 Subject: 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 --- .../build/android/resources/ResourceSymbols.java | 188 +++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java (limited to 'src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java') 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 { + + 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 { + + 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> loadFrom( + Collection dependencies, + ListeningExecutorService executor, + ILogger iLogger, + @Nullable String packageToExclude) + throws InterruptedException, ExecutionException { + Map> providerToPackage = new HashMap<>(); + for (SymbolFileProvider dependency : dependencies) { + providerToPackage.put( + dependency, executor.submit(new PackageParsingTask(dependency.getManifest()))); + } + Multimap> packageToTable = HashMultimap.create(); + for (Entry> 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 symbolTables) throws IOException { + final Table 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 load( + Path primaryRTxt, ListeningExecutorService executorService, ILogger iLogger) { + return load(executorService, iLogger, primaryRTxt.toFile()); + } + + public static ListenableFuture 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 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 result = + (Table) 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 packageSymbols) + throws IOException { + SymbolWriter writer = new SymbolWriter(sourceOut.toString(), packageName, symbolLoader); + for (ResourceSymbols packageSymbol : packageSymbols) { + writer.addSymbolsToWrite(packageSymbol.symbolLoader); + } + writer.write(); + } +} -- cgit v1.2.3