aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2017-05-23 17:19:14 +0200
committerGravatar Irina Iancu <elenairina@google.com>2017-05-23 17:41:32 +0200
commitf6c4d6d66118410b1139a84fe34ba8134661bfa2 (patch)
tree67ee24a40c88bea1a38807155806fb957a852b9a /src/tools/android/java/com/google/devtools/build/android/GenerateRobolectricResourceSymbolsAction.java
parent81316798806b274d84c4eb1131bab8c90b0c7ba1 (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.java176
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)));
+ }
+}