diff options
author | corysmith <corysmith@google.com> | 2017-05-23 17:19:14 +0200 |
---|---|---|
committer | Irina Iancu <elenairina@google.com> | 2017-05-23 17:41:32 +0200 |
commit | f6c4d6d66118410b1139a84fe34ba8134661bfa2 (patch) | |
tree | 67ee24a40c88bea1a38807155806fb957a852b9a /src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java | |
parent | 81316798806b274d84c4eb1131bab8c90b0c7ba1 (diff) |
Add a new action for generating reconciled R classes for Robolectric.
This includes some refactoring:
* Move the symbol deserialization our of the merger and into the ParsedAndroidData (probably move again.)
* Change the FailedFutureAggregator generics to work more callables
RELNOTES: None
PiperOrigin-RevId: 156863698
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java')
-rw-r--r-- | src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java b/src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java new file mode 100644 index 0000000000..e95b646d6d --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java @@ -0,0 +1,176 @@ +// 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; + +import com.android.builder.dependency.SymbolFileProvider; +import com.android.resources.ResourceType; +import com.google.common.base.Optional; +import com.google.common.base.Stopwatch; +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.AndroidResourceProcessor.AaptConfigOptions; +import com.google.devtools.build.android.Converters.DependencyAndroidDataListConverter; +import com.google.devtools.build.android.Converters.PathConverter; +import com.google.devtools.build.android.resources.RClassGenerator; +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.io.Closeable; +import java.nio.file.FileSystems; +import java.nio.file.Path; +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.Level; +import java.util.logging.Logger; + +/** + * This action generates consistant ids R.class files for use in robolectric tests. + */ +public class GenerateRobolectricResourceSymbolsAction { + + private static final Logger logger = + Logger.getLogger(GenerateRobolectricResourceSymbolsAction.class.getName()); + + private static final class WriteLibraryRClass implements Callable<Boolean> { + private final Entry<String, ListenableFuture<ResourceSymbols>> librarySymbolEntry; + private final RClassGenerator generator; + + private WriteLibraryRClass( + Entry<String, ListenableFuture<ResourceSymbols>> librarySymbolEntry, + RClassGenerator generator) { + this.librarySymbolEntry = librarySymbolEntry; + this.generator = generator; + } + + @Override + public Boolean call() throws Exception { + generator.write( + librarySymbolEntry.getKey(), librarySymbolEntry.getValue().get().asInitializers()); + return true; + } + } + + /** Flag specifications for this action. */ + public static final class Options extends OptionsBase { + + @Option( + name = "data", + defaultValue = "", + converter = DependencyAndroidDataListConverter.class, + category = "input", + help = + "Data dependencies. The expected format is " + + DependencyAndroidData.EXPECTED_FORMAT + + "[&...]" + ) + public List<DependencyAndroidData> data; + + @Option( + name = "classJarOutput", + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "Path for the generated java class jar." + ) + public Path classJarOutput; + } + + public static void main(String[] args) throws Exception { + + final Stopwatch timer = Stopwatch.createStarted(); + OptionsParser optionsParser = + OptionsParser.newOptionsParser(Options.class, AaptConfigOptions.class); + optionsParser.enableParamsFileSupport(FileSystems.getDefault()); + optionsParser.parseAndExitUponError(args); + AaptConfigOptions aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class); + Options options = optionsParser.getOptions(Options.class); + + try (ScopedTemporaryDirectory scopedTmp = + new ScopedTemporaryDirectory("robolectric_resources_tmp")) { + Path tmp = scopedTmp.getPath(); + Path generatedSources = tmp.resolve("generated_resources"); + // 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. + int numThreads = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); + ListeningExecutorService executorService = + MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(numThreads)); + try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) { + + logger.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + final PlaceholderIdFieldInitializerBuilder robolectricIds = + PlaceholderIdFieldInitializerBuilder.from(aaptConfigOptions.androidJar); + ParsedAndroidData.loadedFrom( + options.data, executorService, AndroidDataDeserializer.create()) + .writeResourcesTo( + new AndroidResourceSymbolSink() { + + @Override + public void acceptSimpleResource(ResourceType type, String name) { + robolectricIds.addSimpleResource(type, name); + } + + @Override + public void acceptPublicResource( + ResourceType type, String name, Optional<Integer> value) { + robolectricIds.addPublicResource(type, name, value); + } + + @Override + public void acceptStyleableResource( + FullyQualifiedName key, Map<FullyQualifiedName, Boolean> attrs) { + robolectricIds.addStyleableResource(key, attrs); + } + }); + + final RClassGenerator generator = + RClassGenerator.with(generatedSources, robolectricIds.build(), false); + + List<SymbolFileProvider> libraries = new ArrayList<>(); + for (DependencyAndroidData dataDep : options.data) { + SymbolFileProvider library = dataDep.asSymbolFileProvider(); + libraries.add(library); + } + List<ListenableFuture<Boolean>> writeSymbolsTask = new ArrayList<>(); + for (final Entry<String, ListenableFuture<ResourceSymbols>> librarySymbolEntry : + ResourceSymbols.loadFrom(libraries, executorService, null).entries()) { + writeSymbolsTask.add( + executorService.submit(new WriteLibraryRClass(librarySymbolEntry, generator))); + } + FailedFutureAggregator.forIOExceptionsWithMessage("Errors writing symbols.") + .aggregateAndMaybeThrow(writeSymbolsTask); + } + + logger.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + AndroidResourceOutputs.createClassJar(generatedSources, options.classJarOutput); + System.out.println(options.classJarOutput); + logger.fine( + String.format("Create classJar finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + } catch (Exception e) { + logger.log(Level.SEVERE, "Unexpected", e); + throw e; + } + logger.fine(String.format("Resources merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + } +} |