aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-06-21 01:41:17 +0000
committerGravatar Philipp Wollermann <philwo@google.com>2016-06-21 09:59:19 +0000
commit32c6c15c8b9bc4e203529f60bedbc5cd8a496a36 (patch)
tree12662ce5f6f786bcc79f7f241d0aa40f40f580e0 /src/tools/android/java/com/google/devtools
parentcecca155c50b2b58890c7b84448d72c28a0ee7cf (diff)
*** Reason for rollback *** Doesn't handle aapt that doesn't generate R.txt properly. -- MOS_MIGRATED_REVID=125405481
Diffstat (limited to 'src/tools/android/java/com/google/devtools')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java20
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceCompilationAction.java160
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java377
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/BUILD10
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/BUILD.tools8
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Converters.java32
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DependencySymbolFileProvider.java86
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ScopedTemporaryDirectory.java56
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/BUILD24
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/RClassWriter.java317
12 files changed, 110 insertions, 998 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
index 711c97677e..f84783a90b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
@@ -124,7 +124,7 @@ public class AarGeneratorAction {
public boolean strictMerge;
}
- public static void main(String[] args) throws IOException {
+ public static void main(String[] args) {
Stopwatch timer = Stopwatch.createStarted();
OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
optionsParser.parseAndExitUponError(args);
@@ -138,14 +138,15 @@ public class AarGeneratorAction {
AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(
new StdLogger(com.android.utils.StdLogger.Level.VERBOSE));
- try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("aar_gen_tmp")) {
- Path tmp = scopedTmp.getPath();
- Path resourcesOut = tmp.resolve("merged_resources");
- Files.createDirectories(resourcesOut);
- Path assetsOut = tmp.resolve("merged_assets");
- Files.createDirectories(assetsOut);
- Path expandedOut = tmp.resolve("tmp-expanded");
- Path deduplicatedOut = tmp.resolve("tmp-deduplicated");
+ try {
+ Path resourcesOut = Files.createTempDirectory("tmp-resources");
+ resourcesOut.toFile().deleteOnExit();
+ Path assetsOut = Files.createTempDirectory("tmp-assets");
+ assetsOut.toFile().deleteOnExit();
+ Path expandedOut = Files.createTempDirectory("tmp-expanded");
+ expandedOut.toFile().deleteOnExit();
+ Path deduplicatedOut = Files.createTempDirectory("tmp-deduplicated");
+ deduplicatedOut.toFile().deleteOnExit();
logger.fine(String.format("Setup finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS)));
@@ -164,6 +165,7 @@ public class AarGeneratorAction {
writeAar(options.aarOutput, mergedData, options.manifest, options.rtxt, options.classes);
logger.fine(
String.format("Packaging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS)));
+
} catch (IOException | MergingException e) {
logger.log(Level.SEVERE, "Error during merging resources", e);
System.exit(1);
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceCompilationAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceCompilationAction.java
deleted file mode 100644
index 4967d0c91a..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceCompilationAction.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2016 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.google.common.base.Preconditions;
-import com.google.common.base.Stopwatch;
-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.common.options.Option;
-import com.google.devtools.common.options.OptionsBase;
-import com.google.devtools.common.options.OptionsParser;
-
-import com.android.builder.core.VariantConfiguration;
-import com.android.builder.dependency.SymbolFileProvider;
-import com.android.builder.internal.SymbolLoader;
-import com.android.utils.StdLogger;
-
-import java.nio.charset.StandardCharsets;
-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;
-import java.util.logging.Logger;
-
-
-/**
- * Provides an entry point for the compiling resource classes using a custom compiler (simply parse
- * R.txt and make a jar, which is simpler than parsing R.java and running errorprone, etc.).
- *
- * For now, we assume this is only worthwhile for android_binary and not libraries.
- *
- * <pre>
- * Example Usage:
- * java/com/google/build/android/AndroidResourceCompilationAction\
- * --primaryRTxt path/to/R.txt\
- * --primaryManifest path/to/AndroidManifest.xml\
- * --libraries p/t/1/AndroidManifest.txt:p/t/1/R.txt,\
- * p/t/2/AndroidManifest.txt:p/t/2/R.txt\
- * --classJarOutput path/to/write/archive_resources.jar
- * </pre>
- */
-public class AndroidResourceCompilationAction {
-
- private static final StdLogger STD_LOGGER =
- new StdLogger(StdLogger.Level.WARNING);
-
- private static final Logger LOGGER =
- Logger.getLogger(AndroidResourceCompilationAction.class.getName());
-
- /**
- * Flag specifications for this action.
- */
- public static final class Options extends OptionsBase {
-
- @Option(name = "primaryRTxt",
- defaultValue = "null",
- converter = PathConverter.class,
- category = "input",
- help = "The path to the binary's R.txt file")
- public Path primaryRTxt;
-
- @Option(name = "primaryManifest",
- defaultValue = "null",
- converter = PathConverter.class,
- category = "input",
- help = "The path to the binary's AndroidManifest.xml file. This helps provide the package.")
- public Path primaryManifest;
-
- @Option(name = "packageForR",
- defaultValue = "null",
- category = "config",
- help = "Custom java package to generate the R class files.")
- public String packageForR;
-
- @Option(name = "libraries",
- defaultValue = "",
- converter = DependencySymbolFileProviderListConverter.class,
- category = "input",
- help = "R.txt and manifests for the libraries in this binary's deps. We will write "
- + "class files for the libraries as well. Expected format: lib1/R.txt[:lib2/R.txt]")
- public List<DependencySymbolFileProvider> libraries;
-
- @Option(name = "classJarOutput",
- defaultValue = "null",
- converter = PathConverter.class,
- category = "output",
- help = "Path for the generated jar of R.class files.")
- public Path classJarOutput;
-
- }
-
- public static void main(String[] args) throws Exception {
- final Stopwatch timer = Stopwatch.createStarted();
- OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
- 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);
- final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(STD_LOGGER);
- try (ScopedTemporaryDirectory scopedTmp =
- new ScopedTemporaryDirectory("android_res_compile_tmp")) {
- Path tmp = scopedTmp.getPath();
- Path classOutPath = tmp.resolve("compiled_classes");
-
- LOGGER.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
- List<SymbolFileProvider> libraries = new ArrayList<>();
- for (DependencySymbolFileProvider library : options.libraries) {
- libraries.add(library);
- }
- // Note that we need to write the R class for the main binary (so proceed even if there
- // are no libraries).
- if (options.primaryRTxt != null) {
- String appPackageName = options.packageForR;
- if (appPackageName == null) {
- appPackageName = VariantConfiguration
- .getManifestPackage(options.primaryManifest.toFile());
- }
- Multimap<String, SymbolLoader> libSymbolMap = ArrayListMultimap.create();
- SymbolLoader 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.
- resourceProcessor.writePackageRClasses(libSymbolMap, fullSymbolValues, appPackageName,
- classOutPath, true /* finalFields */);
- LOGGER.fine(
- String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
- } else {
- Files.createDirectories(classOutPath);
- }
- // We write .class files to temp, then jar them up after (we create a dummy jar, even if
- // there are no class files).
- resourceProcessor.createClassJar(classOutPath, options.classJarOutput);
- LOGGER.fine(
- String.format("createClassJar finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
- } finally {
- resourceProcessor.shutdown();
- }
- LOGGER.fine(String.format("Compile action done in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
- }
-}
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index 69f58d6fbb..373f1612e1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -40,6 +40,7 @@ import com.android.utils.StdLogger;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -216,9 +217,11 @@ public class AndroidResourceProcessingAction {
Path working = fileSystem.getPath("").toAbsolutePath();
final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(STD_LOGGER);
- try (ScopedTemporaryDirectory scopedTmp =
- new ScopedTemporaryDirectory("android_resources_tmp")) {
- final Path tmp = scopedTmp.getPath();
+ try {
+ final Path tmp = Files.createTempDirectory("android_resources_tmp");
+ // Clean up the tmp file on exit to keep diskspace low.
+ tmp.toFile().deleteOnExit();
+
final Path expandedOut = tmp.resolve("tmp-expanded");
final Path deduplicatedOut = tmp.resolve("tmp-deduplicated");
final Path mergedAssets = tmp.resolve("merged_assets");
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 9bc1ae5d57..33aa697b8e 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
@@ -21,16 +21,10 @@ import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
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;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.FullRevisionConverter;
-import com.google.devtools.build.android.resources.RClassWriter;
import com.google.devtools.common.options.Converters.ColonSeparatedOptionListConverter;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
import com.google.devtools.common.options.Option;
@@ -69,8 +63,6 @@ import com.android.utils.StdLogger;
import org.xml.sax.SAXException;
import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -88,12 +80,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.jar.Attributes;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
@@ -231,31 +217,9 @@ public class AndroidResourceProcessor {
}
}
- /** Shutdowns and verifies that no tasks are running in the executor service. */
- private static final class ExecutorServiceCloser implements Closeable {
- private final ListeningExecutorService executorService;
- private ExecutorServiceCloser(ListeningExecutorService executorService) {
- this.executorService = executorService;
- }
-
- @Override
- public void close() throws IOException {
- List<Runnable> unfinishedTasks = executorService.shutdownNow();
- if (!unfinishedTasks.isEmpty()) {
- throw new IOException(
- "Shutting down the executor with unfinished tasks:" + unfinishedTasks);
- }
- }
-
- public static Closeable createWith(ListeningExecutorService executorService) {
- return new ExecutorServiceCloser(executorService);
- }
- }
-
private static final ImmutableMap<SystemProperty, String> SYSTEM_PROPERTY_NAMES = Maps.toMap(
Arrays.asList(SystemProperty.values()), new Function<SystemProperty, String>() {
- @Override
- public String apply(SystemProperty property) {
+ @Override public String apply(SystemProperty property) {
if (property == SystemProperty.PACKAGE) {
return "applicationId";
} else {
@@ -273,7 +237,6 @@ public class AndroidResourceProcessor {
/**
* Copies the R.txt to the expected place.
- *
* @param generatedSourceRoot The path to the generated R.txt.
* @param rOutput The Path to write the R.txt.
* @param staticIds Boolean that indicates if the ids should be set to 0x1 for caching purposes.
@@ -310,9 +273,8 @@ public class AndroidResourceProcessor {
Files.createDirectories(srcJar.getParent());
try (final ZipOutputStream zip = new ZipOutputStream(
new BufferedOutputStream(Files.newOutputStream(srcJar)))) {
- SymbolFileSrcJarBuildingVisitor visitor =
- new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot, staticIds);
- Files.walkFileTree(generatedSourcesRoot, visitor);
+ Files.walkFileTree(generatedSourcesRoot,
+ new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot, staticIds));
}
// Set to the epoch for caching purposes.
Files.setLastModifiedTime(srcJar, FileTime.fromMillis(0L));
@@ -322,25 +284,6 @@ public class AndroidResourceProcessor {
}
/**
- * Creates a zip archive from all found R.class (and inner class) files.
- */
- public void createClassJar(Path generatedClassesRoot, Path classJar) {
- try {
- Files.createDirectories(classJar.getParent());
- try (final ZipOutputStream zip = new ZipOutputStream(
- new BufferedOutputStream(Files.newOutputStream(classJar)))) {
- ClassJarBuildingVisitor visitor = new ClassJarBuildingVisitor(zip, generatedClassesRoot);
- Files.walkFileTree(generatedClassesRoot, visitor);
- visitor.writeManifestContent();
- }
- // Set to the epoch for caching purposes.
- Files.setLastModifiedTime(classJar, FileTime.fromMillis(0L));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
* Copies the AndroidManifest.xml to the specified output location.
*
* @param androidData The MergedAndroidData which contains the manifest to be written to
@@ -401,6 +344,12 @@ public class AndroidResourceProcessor {
Path mainDexProguardOut,
Path publicResourcesOut)
throws IOException, InterruptedException, LoggedErrorException {
+ List<SymbolFileProvider> libraries = new ArrayList<>();
+ for (DependencyAndroidData dataDep : dependencyData) {
+ SymbolFileProvider library = dataDep.asSymbolFileProvider();
+ libraries.add(library);
+ }
+
Path androidManifest = primaryData.getManifest();
Path resourceDir = primaryData.getResourceDir();
Path assetsDir = primaryData.getAssetDir();
@@ -457,10 +406,9 @@ public class AndroidResourceProcessor {
// The R needs to be created for each library in the dependencies,
// but only if the current project is not a library.
- if (sourceOut != null && variantType != VariantConfiguration.Type.LIBRARY) {
- writeDependencyPackageRJavaFiles(
- dependencyData, customPackageForR, androidManifest, sourceOut);
- }
+ writeDependencyPackageRs(variantType, customPackageForR, libraries, androidManifest.toFile(),
+ sourceOut);
+
// Reset the output date stamps.
if (proguardOut != null) {
Files.setLastModifiedTime(proguardOut, FileTime.fromMillis(0L));
@@ -476,155 +424,59 @@ public class AndroidResourceProcessor {
}
}
- /** 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 void writeDependencyPackageRs(VariantConfiguration.Type variantType,
+ String customPackageForR, List<SymbolFileProvider> libraries, File androidManifest,
+ Path sourceOut) throws IOException {
+ if (sourceOut != null && variantType != VariantConfiguration.Type.LIBRARY
+ && !libraries.isEmpty()) {
+ SymbolLoader fullSymbolValues = null;
- private final SymbolLoader symbolLoader;
+ String appPackageName = customPackageForR;
+ if (appPackageName == null) {
+ appPackageName = VariantConfiguration.getManifestPackage(androidManifest);
+ }
- SymbolLoadingTask(SymbolLoader symbolLoader) {
- this.symbolLoader = symbolLoader;
- }
+ // List of all the symbol loaders per package names.
+ Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create();
- @Override
- public Object call() throws Exception {
- symbolLoader.load();
- return null;
- }
- }
-
- public SymbolLoader loadResourceSymbolTable(
- List<SymbolFileProvider> libraries,
- String appPackageName,
- Path primaryRTxt,
- Multimap<String, SymbolLoader> 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.
- int numThreads = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
- 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);
- }
- // Associate the packages with symbol files.
- // Eagerly load fullSymbolValues, in case we only have resources defined for the binary
- // and not for any of the libraries.
- SymbolLoader fullSymbolValues = new SymbolLoader(primaryRTxt.toFile(), stdLogger);
for (SymbolFileProvider lib : libraries) {
- String packageName = packageNames.get(lib);
+ String packageName = VariantConfiguration.getManifestPackage(lib.getManifest());
+
// If the library package matches the app package skip -- the R class will contain
// all the possible resources so it will not need to generate a new R.
if (appPackageName.equals(packageName)) {
continue;
}
+
File rFile = lib.getSymbolFile();
// If the library has no resource, this file won't exist.
if (rFile.isFile()) {
+ // Load the full values if that's not already been done.
+ // Doing it lazily allow us to support the case where there's no
+ // resources anywhere.
+ if (fullSymbolValues == null) {
+ fullSymbolValues = new SymbolLoader(sourceOut.resolve("R.txt").toFile(), stdLogger);
+ fullSymbolValues.load();
+ }
+
SymbolLoader libSymbols = new SymbolLoader(rFile, stdLogger);
+ libSymbols.load();
+
+ // store these symbols by associating them with the package name.
libMap.put(packageName, libSymbols);
}
}
- // Now load the symbol files in parallel.
- List<ListenableFuture<?>> loadJobs = new ArrayList<>();
- for (final SymbolLoader loader : Iterables.concat(
- libMap.values(), ImmutableList.of(fullSymbolValues))) {
- 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;
- }
- }
- private void writeDependencyPackageRJavaFiles(
- List<DependencyAndroidData> dependencyData,
- String customPackageForR,
- Path androidManifest,
- Path sourceOut) throws IOException {
- List<SymbolFileProvider> libraries = new ArrayList<>();
- for (DependencyAndroidData dataDep : dependencyData) {
- SymbolFileProvider library = dataDep.asSymbolFileProvider();
- libraries.add(library);
- }
- String appPackageName = customPackageForR;
- if (appPackageName == null) {
- appPackageName = VariantConfiguration.getManifestPackage(androidManifest.toFile());
- }
- Multimap<String, SymbolLoader> libSymbolMap = ArrayListMultimap.create();
- Path primaryRTxt = sourceOut != null ? sourceOut.resolve("R.txt") : null;
- if (primaryRTxt != null && !libraries.isEmpty()) {
- SymbolLoader fullSymbolValues = loadResourceSymbolTable(libraries,
- appPackageName, primaryRTxt, libSymbolMap);
- writePackageRJavaFiles(libSymbolMap, fullSymbolValues, sourceOut);
- }
- }
-
- public 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);
- }
- writer.write();
- }
- }
-
- public void writePackageRClasses(
- Multimap<String, SymbolLoader> libMap,
- SymbolLoader fullSymbolValues,
- String appPackageName,
- Path classesOut,
- boolean finalFields) throws IOException {
- for (String packageName : libMap.keySet()) {
- Collection<SymbolLoader> symbols = libMap.get(packageName);
- RClassWriter classWriter =
- new RClassWriter(classesOut.toFile(), packageName, fullSymbolValues, finalFields);
- for (SymbolLoader symbolLoader : symbols) {
- classWriter.addSymbolsToWrite(symbolLoader);
+ // 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);
+ }
+ writer.write();
}
- 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).
- RClassWriter classWriter =
- new RClassWriter(classesOut.toFile(), appPackageName, fullSymbolValues, finalFields);
- classWriter.addSymbolsToWrite(fullSymbolValues);
- classWriter.write();
}
public MergedAndroidData processManifest(
@@ -917,79 +769,17 @@ public class AndroidResourceProcessor {
assetSets.add(mainAssets);
}
- @Nullable
- private Path prepareOutputPath(@Nullable Path out) throws IOException {
+ @Nullable private Path prepareOutputPath(@Nullable Path out) throws IOException {
if (out == null) {
return null;
}
return Files.createDirectories(out);
}
- private static class ZipBuilderVisitor extends SimpleFileVisitor<Path> {
-
- // The earliest date representable in a zip file, 1-1-1980 (the DOS epoch).
- private static final long ZIP_EPOCH = 315561600000L;
- // ZIP timestamps have a resolution of 2 seconds.
- // see http://www.info-zip.org/FAQ.html#limits
- private static final long MINIMUM_TIMESTAMP_INCREMENT = 2000L;
-
- private final ZipOutputStream zip;
- protected final Path root;
- private final String directoryPrefix;
- private int storageMethod = ZipEntry.STORED;
-
- ZipBuilderVisitor(ZipOutputStream zip, Path root, String directory) {
- this.zip = zip;
- this.root = root;
- this.directoryPrefix = directory;
- }
-
- public void setCompress(boolean compress) {
- storageMethod = compress ? ZipEntry.DEFLATED : ZipEntry.STORED;
- }
-
- /**
- * Normalize timestamps for deterministic builds. Stamp .class files to be a bit newer
- * than .java files. See:
- * {@link com.google.devtools.build.buildjar.jarhelper.JarHelper#normalizedTimestamp(String)}
- */
- protected long normalizeTime(String filename) {
- if (filename.endsWith(".class")) {
- return ZIP_EPOCH + MINIMUM_TIMESTAMP_INCREMENT;
- } else {
- return ZIP_EPOCH;
- }
- }
-
- protected void addEntry(Path file, byte[] content) throws IOException {
- String prefix = directoryPrefix != null ? (directoryPrefix + "/") : "";
- String relativeName = root.relativize(file).toString();
- ZipEntry entry = new ZipEntry(prefix + relativeName);
- entry.setMethod(storageMethod);
- entry.setTime(normalizeTime(relativeName));
- entry.setSize(content.length);
- CRC32 crc32 = new CRC32();
- crc32.update(content);
- entry.setCrc(crc32.getValue());
-
- zip.putNextEntry(entry);
- zip.write(content);
- zip.closeEntry();
- }
-
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- byte[] content = Files.readAllBytes(file);
- addEntry(file, content);
- return FileVisitResult.CONTINUE;
- }
- }
-
/**
* A FileVisitor that will add all R.java files to be stored in a zip archive.
*/
- private static final class SymbolFileSrcJarBuildingVisitor extends ZipBuilderVisitor {
-
+ private static final class SymbolFileSrcJarBuildingVisitor extends SimpleFileVisitor<Path> {
static final Pattern PACKAGE_PATTERN = Pattern.compile(
"\\s*package ([a-zA-Z_$][a-zA-Z\\d_$]*(?:\\.[a-zA-Z_$][a-zA-Z\\d_$]*)*)");
static final Pattern ID_PATTERN = Pattern.compile(
@@ -997,10 +787,15 @@ public class AndroidResourceProcessor {
static final Pattern INNER_CLASS = Pattern.compile("public static class ([a-z_]*) \\{(.*?)\\}",
Pattern.DOTALL);
+ // The earliest date representable in a zip file, 1-1-1980.
+ private static final long ZIP_EPOCH = 315561600000L;
+ private final ZipOutputStream zip;
+ private final Path root;
private final boolean staticIds;
private SymbolFileSrcJarBuildingVisitor(ZipOutputStream zip, Path root, boolean staticIds) {
- super(zip, root, null);
+ this.zip = zip;
+ this.root = root;
this.staticIds = staticIds;
}
@@ -1037,48 +832,52 @@ public class AndroidResourceProcessor {
content = replaceIdsWithStaticIds(UTF_8.decode(
ByteBuffer.wrap(content)).toString()).getBytes(UTF_8);
}
- addEntry(file, content);
+ ZipEntry entry = new ZipEntry(root.relativize(file).toString());
+
+ entry.setMethod(ZipEntry.STORED);
+ entry.setTime(ZIP_EPOCH);
+ entry.setSize(content.length);
+ CRC32 crc32 = new CRC32();
+ crc32.update(content);
+ entry.setCrc(crc32.getValue());
+ zip.putNextEntry(entry);
+ zip.write(content);
+ zip.closeEntry();
}
return FileVisitResult.CONTINUE;
}
}
- /**
- * A FileVisitor that will add all R class files to be stored in a zip archive.
- */
- private static final class ClassJarBuildingVisitor extends ZipBuilderVisitor {
+ private static final class ZipBuilderVisitor extends SimpleFileVisitor<Path> {
+ // The earliest date representable in a zip file, 1-1-1980.
+ private static final long ZIP_EPOCH = 315561600000L;
+ private final ZipOutputStream zip;
+ private final Path root;
+ private final String directory;
- ClassJarBuildingVisitor(ZipOutputStream zip, Path root) {
- super(zip, root, null);
+ public ZipBuilderVisitor(ZipOutputStream zip, Path root, String directory) {
+ this.zip = zip;
+ this.root = root;
+ this.directory = directory;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Path filename = file.getFileName();
- String name = filename.toString();
- if (name.endsWith(".class")) {
- byte[] content = Files.readAllBytes(file);
- addEntry(file, content);
- }
- return FileVisitResult.CONTINUE;
- }
+ byte[] content = Files.readAllBytes(file);
- private byte[] manifestContent() throws IOException {
- Manifest manifest = new Manifest();
- Attributes attributes = manifest.getMainAttributes();
- attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
- Attributes.Name createdBy = new Attributes.Name("Created-By");
- if (attributes.getValue(createdBy) == null) {
- attributes.put(createdBy, "bazel");
- }
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- manifest.write(out);
- return out.toByteArray();
- }
+ CRC32 crc32 = new CRC32();
+ crc32.update(content);
- void writeManifestContent() throws IOException {
- addEntry(root.resolve(JarFile.MANIFEST_NAME), manifestContent());
+ ZipEntry entry = new ZipEntry(directory + "/" + root.relativize(file));
+ entry.setMethod(ZipEntry.STORED);
+ entry.setTime(ZIP_EPOCH);
+ entry.setSize(content.length);
+ entry.setCrc(crc32.getValue());
+
+ zip.putNextEntry(entry);
+ zip.write(content);
+ zip.closeEntry();
+ return FileVisitResult.CONTINUE;
}
}
-
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD b/src/tools/android/java/com/google/devtools/build/android/BUILD
index fe6b36d0b6..f4a9a27cee 100644
--- a/src/tools/android/java/com/google/devtools/build/android/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/BUILD
@@ -26,14 +26,6 @@ java_binary(
)
java_binary(
- name = "AndroidResourceCompilingAction",
- main_class = "com.google.devtools.build.android.AndroidResourceCompilationAction",
- runtime_deps = [
- ":android_builder_lib",
- ],
-)
-
-java_binary(
name = "AndroidResourceProcessingAction",
main_class = "com.google.devtools.build.android.AndroidResourceProcessingAction",
runtime_deps = [
@@ -67,7 +59,6 @@ java_library(
"//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:package_manifest_java_proto",
"//src/tools/android/java/com/google/devtools/build/android/proto:serialize_format_proto",
- "//src/tools/android/java/com/google/devtools/build/android/resources",
"//third_party:android_common",
"//third_party:apache_commons_compress",
"//third_party:asm",
@@ -84,7 +75,6 @@ filegroup(
"//src/tools/android/java/com/google/devtools/build/android/idlclass:srcs",
"//src/tools/android/java/com/google/devtools/build/android/incrementaldeployment:srcs",
"//src/tools/android/java/com/google/devtools/build/android/proto:srcs",
- "//src/tools/android/java/com/google/devtools/build/android/resources:srcs",
"//src/tools/android/java/com/google/devtools/build/android/ziputils:srcs",
],
visibility = ["//src:__pkg__"],
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD.tools b/src/tools/android/java/com/google/devtools/build/android/BUILD.tools
index 1d5da32118..84db120540 100644
--- a/src/tools/android/java/com/google/devtools/build/android/BUILD.tools
+++ b/src/tools/android/java/com/google/devtools/build/android/BUILD.tools
@@ -6,14 +6,6 @@ java_import(
)
java_binary(
- name = "AndroidResourceCompilationAction",
- main_class = "com.google.devtools.build.android.AndroidResourceCompilationAction",
- runtime_deps = [
- ":classes",
- ],
-)
-
-java_binary(
name = "AndroidResourceProcessingAction",
main_class = "com.google.devtools.build.android.AndroidResourceProcessingAction",
runtime_deps = [
diff --git a/src/tools/android/java/com/google/devtools/build/android/Converters.java b/src/tools/android/java/com/google/devtools/build/android/Converters.java
index 5fae6821a8..54d1150f93 100644
--- a/src/tools/android/java/com/google/devtools/build/android/Converters.java
+++ b/src/tools/android/java/com/google/devtools/build/android/Converters.java
@@ -95,38 +95,6 @@ public final class Converters {
}
/**
- * Converter for a list of {@link DependencySymbolFileProvider}. Relies on
- * {@code DependencySymbolFileProvider#valueOf(String)} to perform conversion and validation.
- */
- public static class DependencySymbolFileProviderListConverter
- implements Converter<List<DependencySymbolFileProvider>> {
-
- @Override
- public List<DependencySymbolFileProvider> convert(String input) throws OptionsParsingException {
- if (input.isEmpty()) {
- return ImmutableList.<DependencySymbolFileProvider>of();
- }
- try {
- ImmutableList.Builder<DependencySymbolFileProvider> builder = ImmutableList.builder();
- for (String item : input.split(",")) {
- builder.add(DependencySymbolFileProvider.valueOf(item));
- }
- return builder.build();
- } catch (IllegalArgumentException e) {
- throw new OptionsParsingException(
- String.format("invalid DependencyAndroidData: %s", e.getMessage()), e);
- }
- }
-
- @Override
- public String getTypeDescription() {
- return String.format("a list of dependency android data in the format: %s[%s]",
- DependencySymbolFileProvider.commandlineFormat("1"),
- DependencySymbolFileProvider.commandlineFormat("2"));
- }
- }
-
- /**
* Converter for {@link FullRevision}. Relies on {@code FullRevision#parseRevision(String)} to
* perform conversion and validation.
*/
diff --git a/src/tools/android/java/com/google/devtools/build/android/DependencySymbolFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/DependencySymbolFileProvider.java
deleted file mode 100644
index 2676001b11..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/DependencySymbolFileProvider.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 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.google.common.base.Preconditions;
-
-import com.android.builder.dependency.SymbolFileProvider;
-
-import java.io.File;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.regex.Pattern;
-
-/**
- * Represents the R.txt symbol file and AndroidManifest (provides Java package) of libraries.
- */
-class DependencySymbolFileProvider implements SymbolFileProvider {
-
- private static final Pattern VALID_REGEX = Pattern.compile(".*:.*");
-
- private final File symbolFile;
- private final File manifest;
-
- public DependencySymbolFileProvider(File symbolFile, File manifest) {
- this.symbolFile = symbolFile;
- this.manifest = manifest;
- }
-
- public static DependencySymbolFileProvider valueOf(String text) {
- return valueOf(text, FileSystems.getDefault());
- }
-
- @Override
- public File getSymbolFile() {
- return symbolFile;
- }
-
- @Override
- public File getManifest() {
- return manifest;
- }
-
- private static DependencySymbolFileProvider valueOf(String text, FileSystem fileSystem) {
- if (!VALID_REGEX.matcher(text).find()) {
- throw new IllegalArgumentException(text + " is not in the format " + commandlineFormat(""));
- }
- String[] parts = text.split(":");
- return new DependencySymbolFileProvider(getFile(parts[0], fileSystem),
- getFile(parts[1], fileSystem));
- }
-
- private static File getFile(String pathString, FileSystem fileSystem) {
- Preconditions.checkArgument(!pathString.trim().isEmpty());
- return exists(fileSystem.getPath(pathString)).toFile();
- }
-
- private static Path exists(Path path) {
- if (!Files.exists(path)) {
- throw new IllegalArgumentException(path + " does not exist");
- }
- return path;
- }
-
- public static String commandlineFormat(String libNum) {
- return String.format("lib%s/R.txt:lib%s/AndroidManifest.xml", libNum, libNum);
- }
-
- @Override
- public String toString() {
- return String.format("%s, %s", symbolFile, manifest);
- }
-
-}
diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
index 7846844273..69317a135b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
@@ -160,10 +160,11 @@ public class ResourceShrinkerAction {
options = optionsParser.getOptions(Options.class);
AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(stdLogger);
- // Setup temporary working directories.
- try (ScopedTemporaryDirectory scopedTmp =
- new ScopedTemporaryDirectory("resource_shrinker_tmp")) {
- Path working = scopedTmp.getPath();
+ try {
+ // Setup temporary working directories.
+ Path working = Files.createTempDirectory("resource_shrinker_tmp");
+ working.toFile().deleteOnExit();
+
final Path resourceFiles = working.resolve("resource_files");
final Path shrunkResources = working.resolve("shrunk_resources");
diff --git a/src/tools/android/java/com/google/devtools/build/android/ScopedTemporaryDirectory.java b/src/tools/android/java/com/google/devtools/build/android/ScopedTemporaryDirectory.java
deleted file mode 100644
index 684e5a5919..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/ScopedTemporaryDirectory.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 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 java.io.Closeable;
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-
-/**
- * Creates a temporary directory that will be deleted once a scope closes. NOTE: If an error occurs
- * during deletion, it will just stop rather than try an continue.
- */
-final class ScopedTemporaryDirectory extends SimpleFileVisitor<Path> implements Closeable {
-
- private final Path path;
-
- public ScopedTemporaryDirectory(String prefix) throws IOException {
- this.path = Files.createTempDirectory(prefix);
- }
-
- public Path getPath() {
- return this.path;
- }
-
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public void close() throws IOException {
- Files.walkFileTree(path, this);
- }
-}
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/BUILD b/src/tools/android/java/com/google/devtools/build/android/resources/BUILD
deleted file mode 100644
index c1d565e561..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/resources/BUILD
+++ /dev/null
@@ -1,24 +0,0 @@
-# Description:
-# Tools for android resource processing
-
-package(default_visibility = [
- "//src/test/java/com/google/devtools/build/android/resources:__pkg__",
- "//src/tools/android/java/com/google/devtools/build/android:__pkg__",
-])
-
-java_library(
- name = "resources",
- srcs = glob(["*.java"]),
- deps = [
- "//third_party:android_common",
- "//third_party:asm",
- "//third_party:asm-commons",
- "//third_party:guava",
- ],
-)
-
-filegroup(
- name = "srcs",
- srcs = glob(["**"]),
- visibility = ["//src/tools/android/java/com/google/devtools/build/android:__pkg__"],
-)
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/RClassWriter.java b/src/tools/android/java/com/google/devtools/build/android/resources/RClassWriter.java
deleted file mode 100644
index 53bb945dc1..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/resources/RClassWriter.java
+++ /dev/null
@@ -1,317 +0,0 @@
-// Copyright 2016 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.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Table;
-import com.google.common.io.Files;
-
-import com.android.SdkConstants;
-import com.android.builder.internal.SymbolLoader;
-import com.android.builder.internal.SymbolLoader.SymbolEntry;
-
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.commons.InstructionAdapter;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Writes out bytecode for an R.class directly, rather than go through an R.java and compile. This
- * avoids re-parsing huge R.java files and other time spent in the java compiler (e.g., plugins like
- * ErrorProne). A difference is that this doesn't generate line number tables and other debugging
- * information. Also, the order of the constant pool tends to be different.
- */
-public class RClassWriter {
-
- private static final int JAVA_VERSION = Opcodes.V1_7;
- private static final String SUPER_CLASS = "java/lang/Object";
- private final File outFolder;
- private final String packageName;
- private final List<SymbolLoader> symbolTables = new ArrayList<>();
- private final SymbolLoader symbolValues;
- private final boolean finalFields;
-
- public RClassWriter(File outFolder,
- String packageName,
- SymbolLoader values,
- boolean finalFields) {
- this.outFolder = outFolder;
- this.packageName = packageName;
- this.symbolValues = values;
- this.finalFields = finalFields;
- }
-
- public void addSymbolsToWrite(SymbolLoader symbols) {
- symbolTables.add(symbols);
- }
-
- private Table<String, String, SymbolEntry> getAllSymbols() throws IOException {
- Table<String, String, SymbolEntry> symbols = HashBasedTable.create();
- for (SymbolLoader symbolLoader : symbolTables) {
- symbols.putAll(getSymbols(symbolLoader));
- }
- return symbols;
- }
-
- private Method symbolsMethod;
-
- private Table<String, String, SymbolEntry> getSymbols(SymbolLoader symbolLoader)
- throws IOException {
- // TODO(bazel-team): upstream a patch to change the visibility instead of hacking it.
- try {
- if (symbolsMethod == null) {
- Method getSymbols = SymbolLoader.class.getDeclaredMethod("getSymbols");
- getSymbols.setAccessible(true);
- symbolsMethod = getSymbols;
- }
- @SuppressWarnings("unchecked")
- Table<String, String, SymbolEntry> result = (Table<String, String, SymbolEntry>)
- symbolsMethod.invoke(symbolLoader);
- return result;
- } catch (ReflectiveOperationException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Builds the bytecode and writes out the R.class file, and R$inner.class files.
- */
- public void write() throws IOException {
- Splitter splitter = Splitter.on('.');
- Iterable<String> folders = splitter.split(packageName);
- File packageDir = outFolder;
- for (String folder : folders) {
- packageDir = new File(packageDir, folder);
- }
- File rClassFile = new File(packageDir, SdkConstants.FN_COMPILED_RESOURCE_CLASS);
- Files.createParentDirs(rClassFile);
- String packageWithSlashes = packageName.replaceAll("\\.", "/");
- String rClassName = packageWithSlashes + "/R";
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- classWriter
- .visit(JAVA_VERSION, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER,
- rClassName, null, SUPER_CLASS, null);
- classWriter.visitSource(SdkConstants.FN_RESOURCE_CLASS, null);
- writeConstructor(classWriter);
-
- Table<String, String, SymbolEntry> symbols = getAllSymbols();
- Table<String, String, SymbolEntry> values = getSymbols(symbolValues);
-
- Set<String> rowSet = symbols.rowKeySet();
- List<String> rowList = new ArrayList<>(rowSet);
- Collections.sort(rowList);
-
- // Build the R.class w/ the inner classes, then later build the individual R$inner.class.
- for (String row : rowList) {
- String innerClassName = rClassName + "$" + row;
- classWriter.visitInnerClass(innerClassName, rClassName, row,
- Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC);
- }
- classWriter.visitEnd();
- Files.write(classWriter.toByteArray(), rClassFile);
-
- // Now generate the R$inner.class files.
- for (String row : rowList) {
- writeInnerClass(symbols, values, packageDir, rClassName, row);
- }
- }
-
- /**
- * Represents an int or int[] field and its initializer (where initialization is done via code in
- * the static clinit function).
- */
- private interface DeferredInitializer {
-
- /**
- * Write the code for the initializer via insts.
- *
- * @return the number of stack slots needed for the code.
- */
- int writeCLInit(String className, InstructionAdapter insts);
- }
-
- private static final class IntArrayDeferredInitializer implements DeferredInitializer {
-
- private final String fieldName;
- private final ImmutableList<Integer> values;
-
- IntArrayDeferredInitializer(String fieldName, ImmutableList<Integer> values) {
- this.fieldName = fieldName;
- this.values = values;
- }
-
- public static DeferredInitializer of(String name, String value) {
- Preconditions.checkArgument(value.startsWith("{ "), "Expected list starting with { ");
- Preconditions.checkArgument(value.endsWith(" }"), "Expected list ending with } ");
- // Check for an empty list, which is "{ }".
- if (value.length() < 4) {
- return new IntArrayDeferredInitializer(name, ImmutableList.<Integer>of());
- }
- ImmutableList.Builder<Integer> intValues = ImmutableList.builder();
- String trimmedValue = value.substring(2, value.length() - 2);
- Iterable<String> valueStrings = Splitter.on(',')
- .trimResults()
- .omitEmptyStrings()
- .split(trimmedValue);
- for (String valueString : valueStrings) {
- intValues.add(Integer.decode(valueString));
- }
- return new IntArrayDeferredInitializer(name, intValues.build());
- }
-
- @Override
- public int writeCLInit(String className, InstructionAdapter insts) {
- insts.iconst(values.size());
- insts.newarray(Type.INT_TYPE);
- int curIndex = 0;
- for (Integer value : values) {
- insts.dup();
- insts.iconst(curIndex);
- insts.iconst(value);
- insts.astore(Type.INT_TYPE);
- ++curIndex;
- }
- insts.putstatic(className, fieldName, "[I");
- // Needs up to 4 stack slots for: the array ref for the putstatic, the dup of the array ref
- // for the store, the index, and the value to store.
- return 4;
- }
- }
-
- private static final class IntDeferredInitializer implements DeferredInitializer {
-
- private final String fieldName;
- private final Integer value;
-
- IntDeferredInitializer(String fieldName, Integer value) {
- this.fieldName = fieldName;
- this.value = value;
- }
-
- public static DeferredInitializer of(String name, String value) {
- return new IntDeferredInitializer(name, Integer.decode(value));
- }
-
- @Override
- public int writeCLInit(String className, InstructionAdapter insts) {
- insts.iconst(value);
- insts.putstatic(className, fieldName, "I");
- // Just needs one stack slot for the iconst.
- return 1;
- }
- }
-
- private void writeInnerClass(
- Table<String, String, SymbolEntry> symbols,
- Table<String, String, SymbolEntry> values,
- File packageDir,
- String fullyQualifiedOuterClass,
- String innerClass) throws IOException {
- ClassWriter innerClassWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- String fullyQualifiedInnerClass = fullyQualifiedOuterClass + "$" + innerClass;
- innerClassWriter
- .visit(JAVA_VERSION, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER,
- fullyQualifiedInnerClass, null, SUPER_CLASS, null);
- innerClassWriter.visitSource("R.java", null);
- writeConstructor(innerClassWriter);
- innerClassWriter.visitInnerClass(
- fullyQualifiedInnerClass, fullyQualifiedOuterClass, innerClass,
- Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC);
-
- Map<String, SymbolEntry> rowMap = symbols.row(innerClass);
- Set<String> symbolSet = rowMap.keySet();
- List<String> symbolList = new ArrayList<>(symbolSet);
- Collections.sort(symbolList);
- List<DeferredInitializer> deferredInitializers = new ArrayList<>();
- int fieldAccessLevel = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
- if (finalFields) {
- fieldAccessLevel |= Opcodes.ACC_FINAL;
- }
- for (String symbolName : symbolList) {
- // get the matching SymbolEntry from the values Table.
- SymbolEntry value = values.get(innerClass, symbolName);
- if (value != null) {
- String desc;
- Object initializer = null;
- if (value.getType().equals("int")) {
- desc = "I";
- if (finalFields) {
- initializer = Integer.decode(value.getValue());
- } else {
- deferredInitializers.add(IntDeferredInitializer.of(value.getName(), value.getValue()));
- }
- } else {
- Preconditions.checkArgument(value.getType().equals("int[]"));
- desc = "[I";
- deferredInitializers
- .add(IntArrayDeferredInitializer.of(value.getName(), value.getValue()));
- }
- innerClassWriter
- .visitField(fieldAccessLevel, value.getName(), desc, null, initializer)
- .visitEnd();
- }
- }
-
- if (!deferredInitializers.isEmpty()) {
- // build the <clinit> method.
- writeStaticClassInit(innerClassWriter, fullyQualifiedInnerClass, deferredInitializers);
- }
-
- innerClassWriter.visitEnd();
- File innerFile = new File(packageDir, "R$" + innerClass + ".class");
- Files.write(innerClassWriter.toByteArray(), innerFile);
- }
-
- private static void writeConstructor(ClassWriter classWriter) {
- MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V",
- null, null);
- constructor.visitCode();
- constructor.visitVarInsn(Opcodes.ALOAD, 0);
- constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, SUPER_CLASS, "<init>", "()V", false);
- constructor.visitInsn(Opcodes.RETURN);
- constructor.visitMaxs(1, 1);
- constructor.visitEnd();
- }
-
- private static void writeStaticClassInit(
- ClassWriter classWriter,
- String className,
- List<DeferredInitializer> deferredInitializers) {
- MethodVisitor visitor = classWriter.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V",
- null, null);
- visitor.visitCode();
- int stackSlotsNeeded = 0;
- InstructionAdapter insts = new InstructionAdapter(visitor);
- for (DeferredInitializer fieldInit : deferredInitializers) {
- stackSlotsNeeded = Math.max(stackSlotsNeeded, fieldInit.writeCLInit(className, insts));
- }
- insts.areturn(Type.VOID_TYPE);
- visitor.visitMaxs(stackSlotsNeeded, 0);
- visitor.visitEnd();
- }
-
-}