diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java index 516db565c06943..7c340694cb9ac9 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java @@ -99,18 +99,17 @@ public ConfiguredTarget create(RuleContext ruleContext) Artifact resourcesZip = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP); - ResourceApk resourceApk = - androidManifest.packWithDataAndResources( - ruleContext, - new LocalResourceContainer.Builder(ruleContext) - .withResources(ImmutableList.of(resourcesProvider)) - .build(), - ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LOCAL_SYMBOLS), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), - resourcesZip, - /* alwaysExportManifest = */ true); + ResourceApk resourceApk = androidManifest.packWithDataAndResources( + ruleContext, + new LocalResourceContainer.Builder(ruleContext) + .withResources(ImmutableList.of(resourcesProvider)) + .build(), + ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_SYMBOLS), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), + resourcesZip, + /* alwaysExportManifest = */ true); // There isn't really any use case for building an aar_import target on its own, so the files to // build could be empty. The resources zip and merged jars are added here as a sanity check for diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java index c70b10780a3f15..f194aa559cc341 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java @@ -90,26 +90,23 @@ public ConfiguredTarget create(RuleContext ruleContext) if (definesLocalResources) { ApplicationManifest applicationManifest = androidSemantics.getManifestForRule(ruleContext) .renamePackage(ruleContext, AndroidCommon.getJavaPackage(ruleContext)); - resourceApk = - applicationManifest.packWithDataAndResources( - null, /* resourceApk -- not needed for library */ - ruleContext, - true, /* isLibrary */ - ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LOCAL_SYMBOLS), - ResourceConfigurationFilter.empty(ruleContext), - ImmutableList.of(), /* uncompressedExtensions */ - false, /* crunchPng */ - ImmutableList.of(), /* densities */ - false, /* incremental */ - null, /* proguardCfgOut */ - null, /* mainDexProguardCfg */ - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP), - DataBinding.isEnabled(ruleContext) - ? DataBinding.getLayoutInfoFile(ruleContext) - : null); + resourceApk = applicationManifest.packWithDataAndResources( + null, /* resourceApk -- not needed for library */ + ruleContext, + true, /* isLibrary */ + ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_SYMBOLS), + ResourceConfigurationFilter.empty(ruleContext), + ImmutableList.of(), /* uncompressedExtensions */ + false, /* crunchPng */ + ImmutableList.of(), /* densities */ + false, /* incremental */ + null, /* proguardCfgOut */ + null, /* mainDexProguardCfg */ + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP), + DataBinding.isEnabled(ruleContext) ? DataBinding.getLayoutInfoFile(ruleContext) : null); if (ruleContext.hasErrors()) { return null; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java index dc84ee5dc5cc90..3d7886d3688af4 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java @@ -144,11 +144,10 @@ public ResourceContainer build(ActionConstructionContext context) { ResourceContainerConverter.convertDependencies( dependencies, builder, inputs, RESOURCE_CONTAINER_TO_ARG, RESOURCE_CONTAINER_TO_ARTIFACTS); + Preconditions.checkNotNull(classJarOut); List outs = new ArrayList<>(); - if (classJarOut != null) { - builder.addExecPath("--classJarOutput", classJarOut); - outs.add(classJarOut); - } + builder.addExecPath("--classJarOutput", classJarOut); + outs.add(classJarOut); if (mergedResourcesOut != null) { builder.addExecPath("--resourcesOutput", mergedResourcesOut); @@ -188,11 +187,8 @@ public ResourceContainer build(ActionConstructionContext context) { .build(context)); // Return the full set of processed transitive dependencies. - ResourceContainer.Builder result = primary.toBuilder(); - if (classJarOut != null) { - // ensure the classJar is propgated if it exists. Otherwise, AndroidCommon tries to make it. - result.setJavaClassJar(classJarOut); - } + ResourceContainer.Builder result = primary.toBuilder() + .setJavaClassJar(classJarOut); if (manifestOut != null) { result.setManifest(manifestOut); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java index 8ab8794eb9a043..36eb19c3aaf2c1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java @@ -116,8 +116,8 @@ public final class AndroidRuleClasses { fromTemplates("%{name}_symbols/R.txt"); public static final SafeImplicitOutputsFunction ANDROID_LOCAL_SYMBOLS = fromTemplates("%{name}_symbols/local.bin"); - public static final SafeImplicitOutputsFunction ANDROID_MERGED_SYMBOLS = - fromTemplates("%{name}_symbols/merged.bin"); + public static final SafeImplicitOutputsFunction ANDROID_SYMBOLS = + fromTemplates("%{name}_symbols/symbols.bin"); public static final ImplicitOutputsFunction ANDROID_PROCESSED_MANIFEST = fromTemplates("%{name}_processed_manifest/AndroidManifest.xml"); public static final SafeImplicitOutputsFunction MOBILE_INSTALL_STUB_APPLICATION_MANIFEST = diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java index 5f08ee6c882adc..4467c823c74607 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java @@ -453,43 +453,38 @@ private ResourceApk createApk( if (isLibrary && AndroidCommon.getAndroidConfig(ruleContext).useParallelResourceProcessing()) { // android_library should only build the APK one way (!incremental). Preconditions.checkArgument(!incremental); - Artifact rJavaClassJar = - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); - - ResourceContainer parsed = - new AndroidResourceParsingActionBuilder(ruleContext) - .setParse(data) - .withPrimary(resourceContainer) - .setOutput(resourceContainer.getSymbols()) - .build(ruleContext); - - ResourceContainer generated = - new LibraryRGeneratorActionBuilder() - .setJavaPackage(resourceContainer.getJavaPackage()) - .withPrimary(parsed) - .withDependencies(resourceDeps) - .setClassJarOut(rJavaClassJar) - .build(ruleContext); + Artifact rJavaClassJar = ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); + + if (resourceContainer.getSymbols() != null) { + new AndroidResourceParsingActionBuilder(ruleContext) + .withPrimary(resourceContainer) + .setParse(data) + .setOutput(resourceContainer.getSymbols()) + .build(ruleContext); + } - ResourceContainer merged = + AndroidResourceMergingActionBuilder resourcesMergerBuilder = new AndroidResourceMergingActionBuilder(ruleContext) - .setJavaPackage(generated.getJavaPackage()) - .withPrimary(generated) + .setJavaPackage(resourceContainer.getJavaPackage()) + .withPrimary(resourceContainer) .withDependencies(resourceDeps) .setMergedResourcesOut(mergedResources) .setManifestOut(manifestOut) - .setDataBindingInfoZip(dataBindingInfoZip) - .build(ruleContext); + .setClassJarOut(rJavaClassJar) + .setDataBindingInfoZip(dataBindingInfoZip); + ResourceContainer merged = resourcesMergerBuilder.build(ruleContext); - processed = + AndroidResourceValidatorActionBuilder validatorBuilder = new AndroidResourceValidatorActionBuilder(ruleContext) .setJavaPackage(merged.getJavaPackage()) - .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) + .setDebug( + ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .setMergedResources(mergedResources) .withPrimary(merged) .setSourceJarOut(merged.getJavaSourceJar()) - .setRTxtOut(merged.getRTxt()) - .build(ruleContext); + .setRTxtOut(merged.getRTxt()); + processed = validatorBuilder.build(ruleContext); } else { AndroidResourcesProcessorBuilder builder = new AndroidResourcesProcessorBuilder(ruleContext) @@ -635,16 +630,14 @@ public ResourceApk packWithResources( aaptActionHelper.createGenerateApkAction(resourceApk, resourceContainer.getRenameManifestPackage(), additionalAaptOpts.build(), densities); - ResourceContainer updatedResources = - resourceContainer - .toBuilder() - .setLabel(ruleContext.getLabel()) - .setApk(resourceApk) - .setManifest(getManifest()) - .setJavaSourceJar(javaSourcesJar) - .setJavaClassJar(null) - .setSymbols(null) - .build(); + ResourceContainer updatedResources = resourceContainer.toBuilder() + .setLabel(ruleContext.getLabel()) + .setApk(resourceApk) + .setManifest(getManifest()) + .setJavaSourceJar(javaSourcesJar) + .setJavaClassJar(null) + .setSymbols(null) + .build(); aaptActionHelper.createGenerateProguardAction(proguardCfg, mainDexProguardCfg); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java deleted file mode 100644 index 361a70a8037404..00000000000000 --- a/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java +++ /dev/null @@ -1,117 +0,0 @@ -// 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.lib.rules.android; - -import com.google.common.base.Function; -import com.google.common.base.Strings; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; -import com.google.devtools.build.lib.analysis.FilesToRunProvider; -import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; -import com.google.devtools.build.lib.analysis.RuleContext; -import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; -import com.google.devtools.build.lib.analysis.actions.SpawnAction; -import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; -import com.google.devtools.build.lib.collect.nestedset.Order; - -/** Builder for the action that generates the R class for libraries. */ -public class LibraryRGeneratorActionBuilder { - static final Function TO_SYMBOL_ARTIFACT = - new Function() { - @Override - public Artifact apply(ResourceContainer input) { - return input.getSymbols(); - } - }; - static final Function TO_SYMBOL_PATH = - new Function() { - @Override - public String apply(ResourceContainer container) { - return container.getSymbols().getExecPathString(); - } - }; - - private String javaPackage; - private Iterable deps = ImmutableList.of(); - private ResourceContainer resourceContainer; - private Artifact rJavaClassJar; - - public LibraryRGeneratorActionBuilder setJavaPackage(String javaPackage) { - this.javaPackage = javaPackage; - return this; - } - - public LibraryRGeneratorActionBuilder withPrimary(ResourceContainer resourceContainer) { - this.resourceContainer = resourceContainer; - return this; - } - - public LibraryRGeneratorActionBuilder withDependencies(ResourceDependencies resourceDeps) { - this.deps = resourceDeps.getResources(); - return this; - } - - public LibraryRGeneratorActionBuilder setClassJarOut(Artifact rJavaClassJar) { - this.rJavaClassJar = rJavaClassJar; - return this; - } - - public ResourceContainer build(RuleContext ruleContext) { - AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); - - CustomCommandLine.Builder builder = new CustomCommandLine.Builder(); - NestedSetBuilder inputs = NestedSetBuilder.naiveLinkOrder(); - FilesToRunProvider executable = - ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST); - inputs.addAll(executable.getRunfilesSupport().getRunfilesArtifactsWithoutMiddlemen()); - - builder.add("--tool").add("GENERATE_LIBRARY_R").add("--"); - - if (!Strings.isNullOrEmpty(javaPackage)) { - builder.add("--packageForR").add(javaPackage); - } - - FluentIterable symbolProviders = - FluentIterable.from(deps).append(resourceContainer); - - builder.addJoinStrings( - "--symbols", - ruleContext.getConfiguration().getHostPathSeparator(), - symbolProviders.transform(TO_SYMBOL_PATH)); - inputs.addTransitive( - NestedSetBuilder.wrap( - Order.NAIVE_LINK_ORDER, symbolProviders.transform(TO_SYMBOL_ARTIFACT))); - - builder.addExecPath("--classJarOutput", rJavaClassJar); - - builder.addExecPath("--androidJar", sdk.getAndroidJar()); - inputs.add(sdk.getAndroidJar()); - - // Create the spawn action. - SpawnAction.Builder spawnActionBuilder = new SpawnAction.Builder(); - ruleContext.registerAction( - spawnActionBuilder - .addTransitiveInputs(inputs.build()) - .addOutputs(ImmutableList.of(rJavaClassJar)) - .useParameterFile(ParameterFileType.UNQUOTED) - .setCommandLine(builder.build()) - .setExecutable(executable) - .setProgressMessage("Generating Library R Classes: " + ruleContext.getLabel()) - .setMnemonic("LibraryRClassGenerator") - .build(ruleContext)); - return resourceContainer.toBuilder().setJavaClassJar(rJavaClassJar).build(); - } -} diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java index e9e1f3e22c3efd..c42e870bbd070c 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java @@ -13,63 +13,24 @@ // limitations under the License. package com.google.devtools.build.android; -import com.android.ide.common.res2.MergingException; import com.google.common.base.Stopwatch; import com.google.common.collect.Maps; -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.ParsedAndroidData.Builder; import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.Header; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -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.concurrent.TimeUnit; import java.util.logging.Logger; /** Deserializes {@link DataKey}, {@link DataValue} entries from a binary file. */ public class AndroidDataDeserializer { - /** Task to deserialize resources from a path. */ - static final class Deserialize implements Callable { - - private final Path symbolPath; - - private final Builder finalDataBuilder; - private final AndroidDataDeserializer deserializer; - - private Deserialize( - AndroidDataDeserializer deserializer, Path symbolPath, Builder finalDataBuilder) { - this.deserializer = deserializer; - this.symbolPath = symbolPath; - this.finalDataBuilder = finalDataBuilder; - } - - @Override - public Boolean call() throws Exception { - final Builder parsedDataBuilder = ParsedAndroidData.Builder.newBuilder(); - deserializer.read(symbolPath, parsedDataBuilder.consumers()); - // The builder isn't threadsafe, so synchronize the copyTo call. - synchronized (finalDataBuilder) { - // All the resources are sorted before writing, so they can be aggregated in - // whatever order here. - parsedDataBuilder.copyTo(finalDataBuilder); - } - return Boolean.TRUE; - } - } - private static final Logger logger = Logger.getLogger(AndroidDataDeserializer.class.getName()); public static AndroidDataDeserializer create() { @@ -153,35 +114,4 @@ private void readEntriesSegment( } } } - - /** Deserializes a list of serialized resource paths to a {@link ParsedAndroidData}. */ - public static ParsedAndroidData deserializeSymbolsToData(List symbolPaths) - throws IOException, MergingException { - AndroidDataDeserializer deserializer = create(); - final ListeningExecutorService executorService = - MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15)); - final Builder deserializedDataBuilder = ParsedAndroidData.Builder.newBuilder(); - try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) { - List> deserializing = new ArrayList<>(); - for (final Path symbolPath : symbolPaths) { - deserializing.add( - executorService.submit( - new AndroidDataDeserializer.Deserialize( - deserializer, symbolPath, deserializedDataBuilder))); - } - FailedFutureAggregator aggregator = - FailedFutureAggregator.createForMergingExceptionWithMessage( - "Failure(s) during dependency parsing"); - aggregator.aggregateAndMaybeThrow(deserializing); - } - return deserializedDataBuilder.build(); - } - - public static ParsedAndroidData deserializeSingleAndroidData(SerializedAndroidData data) - throws MergingException { - final ParsedAndroidData.Builder builder = ParsedAndroidData.Builder.newBuilder(); - final AndroidDataDeserializer deserializer = create(); - data.deserialize(deserializer, builder.consumers()); - return builder.build(); - } } diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java index e0a40362f747a9..0ab4c322755b1a 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java @@ -28,13 +28,12 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; -/** Collects all the functionality for an action to merge resources. */ -// TODO(bazel-team): Turn into an instance object, in order to use an external ExecutorService. +/** Collects all the functionationality for an action to merge resources. */ public class AndroidResourceMerger { static final Logger logger = Logger.getLogger(AndroidResourceProcessor.class.getName()); /** Merges all secondary resources with the primary resources. */ - public static MergedAndroidData mergeData( + static MergedAndroidData mergeData( final ParsedAndroidData primary, final Path primaryManifest, final List direct, @@ -50,14 +49,11 @@ public static MergedAndroidData mergeData( final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15)); try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) { + AndroidDataMerger merger = AndroidDataMerger.createWithPathDeduplictor(executorService); UnwrittenMergedAndroidData merged = - mergeData( - executorService, - transitive, - direct, - primary, - primaryManifest, - type != VariantType.LIBRARY); + merger.loadAndMerge( + transitive, direct, primary, primaryManifest, type != VariantType.LIBRARY); + logger.fine(String.format("merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); timer.reset().start(); if (symbolsOut != null) { AndroidDataSerializer serializer = AndroidDataSerializer.create(); @@ -86,24 +82,6 @@ public static MergedAndroidData mergeData( } } - public static UnwrittenMergedAndroidData mergeData( - ListeningExecutorService executorService, - List transitive, - List direct, - ParsedAndroidData primary, - Path primaryManifest, - boolean allowPrimaryOverrideAll) - throws MergingException { - Stopwatch timer = Stopwatch.createStarted(); - try { - AndroidDataMerger merger = AndroidDataMerger.createWithPathDeduplictor(executorService); - return merger.loadAndMerge( - transitive, direct, primary, primaryManifest, allowPrimaryOverrideAll); - } finally { - logger.fine(String.format("merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); - } - } - /** * Merges all secondary resources with the primary resources, given that the primary resources * have not yet been parsed and serialized. 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 4da08f1ee3b3d0..e33928d19c6306 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 @@ -28,6 +28,7 @@ import com.android.ide.common.internal.CommandLineRunner; import com.android.ide.common.internal.ExecutorSingleton; import com.android.ide.common.internal.LoggedErrorException; +import com.android.ide.common.res2.MergingException; import com.android.io.FileWrapper; import com.android.io.StreamException; import com.android.repository.Revision; @@ -45,6 +46,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.devtools.build.android.Converters.ExistingPathConverter; import com.google.devtools.build.android.Converters.RevisionConverter; +import com.google.devtools.build.android.ParsedAndroidData.Builder; import com.google.devtools.build.android.SplitConfigurationFilter.UnrecognizedSplitsException; import com.google.devtools.build.android.resources.RClassGenerator; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; @@ -705,4 +707,55 @@ private Path prepareOutputPath(@Nullable Path out) throws IOException { } return Files.createDirectories(out); } + + /** Deserializes a list of serialized resource paths to a {@link ParsedAndroidData}. */ + public ParsedAndroidData deserializeSymbolsToData(List symbolPaths) + throws IOException, MergingException { + AndroidDataDeserializer deserializer = AndroidDataDeserializer.create(); + final ListeningExecutorService executorService = + MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15)); + final Builder deserializedDataBuilder = ParsedAndroidData.Builder.newBuilder(); + try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) { + List> deserializing = new ArrayList<>(); + for (final Path symbolPath : symbolPaths) { + deserializing.add( + executorService.submit( + new Deserialize(deserializer, symbolPath, deserializedDataBuilder))); + } + FailedFutureAggregator aggregator = + FailedFutureAggregator.createForMergingExceptionWithMessage( + "Failure(s) during dependency parsing"); + aggregator.aggregateAndMaybeThrow(deserializing); + } + return deserializedDataBuilder.build(); + } + + /** Task to deserialize resources from a path. */ + private static final class Deserialize implements Callable { + + private final Path symbolPath; + + private final Builder finalDataBuilder; + private final AndroidDataDeserializer deserializer; + + private Deserialize( + AndroidDataDeserializer deserializer, Path symbolPath, Builder finalDataBuilder) { + this.deserializer = deserializer; + this.symbolPath = symbolPath; + this.finalDataBuilder = finalDataBuilder; + } + + @Override + public Boolean call() throws Exception { + final Builder parsedDataBuilder = ParsedAndroidData.Builder.newBuilder(); + deserializer.read(symbolPath, parsedDataBuilder.consumers()); + // The builder isn't threadsafe, so synchronize the copyTo call. + synchronized (finalDataBuilder) { + // All the resources are sorted before writing, so they can be aggregated in + // whatever order here. + parsedDataBuilder.copyTo(finalDataBuilder); + } + return Boolean.TRUE; + } + } } 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 1cb816c01e4225..51c771946b8008 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 @@ -93,10 +93,11 @@ public static void main(String[] args) throws Exception { Strings.nullToEmpty(options.packageForR)); resourceClassWriter.setIncludeClassFile(true); resourceClassWriter.setIncludeJavaFile(false); + final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(stdLogger); logger.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); final ParsedAndroidData data = - AndroidDataDeserializer.deserializeSymbolsToData(options.symbols); + resourceProcessor.deserializeSymbolsToData(options.symbols); logger.fine( String.format("Deserialization finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));