From 32c6c15c8b9bc4e203529f60bedbc5cd8a496a36 Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 21 Jun 2016 01:41:17 +0000 Subject: Rollback of commit 1f1f207573c7b9c3e2d3ca1ffb0780a8fd592214. *** Reason for rollback *** Doesn't handle aapt that doesn't generate R.txt properly. -- MOS_MIGRATED_REVID=125405481 --- .../devtools/build/android/AarGeneratorAction.java | 20 +- .../android/AndroidResourceCompilationAction.java | 160 --------- .../android/AndroidResourceProcessingAction.java | 9 +- .../build/android/AndroidResourceProcessor.java | 377 +++++---------------- .../java/com/google/devtools/build/android/BUILD | 10 - .../com/google/devtools/build/android/BUILD.tools | 8 - .../google/devtools/build/android/Converters.java | 32 -- .../android/DependencySymbolFileProvider.java | 86 ----- .../build/android/ResourceShrinkerAction.java | 9 +- .../build/android/ScopedTemporaryDirectory.java | 56 --- .../google/devtools/build/android/resources/BUILD | 24 -- .../build/android/resources/RClassWriter.java | 317 ----------------- 12 files changed, 110 insertions(+), 998 deletions(-) delete mode 100644 src/tools/android/java/com/google/devtools/build/android/AndroidResourceCompilationAction.java delete mode 100644 src/tools/android/java/com/google/devtools/build/android/DependencySymbolFileProvider.java delete mode 100644 src/tools/android/java/com/google/devtools/build/android/ScopedTemporaryDirectory.java delete mode 100644 src/tools/android/java/com/google/devtools/build/android/resources/BUILD delete mode 100644 src/tools/android/java/com/google/devtools/build/android/resources/RClassWriter.java (limited to 'src/tools/android/java/com/google/devtools') 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. - * - *
- * 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
- * 
- */ -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 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 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 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 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 SYSTEM_PROPERTY_NAMES = Maps.toMap( Arrays.asList(SystemProperty.values()), new Function() { - @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)); @@ -321,25 +283,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. * @@ -401,6 +344,12 @@ public class AndroidResourceProcessor { Path mainDexProguardOut, Path publicResourcesOut) throws IOException, InterruptedException, LoggedErrorException { + List 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 { - - 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 { + private void writeDependencyPackageRs(VariantConfiguration.Type variantType, + String customPackageForR, List 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 libMap = ArrayListMultimap.create(); - @Override - public Object call() throws Exception { - symbolLoader.load(); - return null; - } - } - - public SymbolLoader loadResourceSymbolTable( - List libraries, - String appPackageName, - Path primaryRTxt, - Multimap 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> packageJobs = new HashMap<>(); - for (final SymbolFileProvider lib : libraries) { - packageJobs.put(lib, executorService.submit(new PackageParsingTask(lib.getManifest()))); - } - Map packageNames = new HashMap<>(); - try { - for (Map.Entry> 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> 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 dependencyData, - String customPackageForR, - Path androidManifest, - Path sourceOut) throws IOException { - List 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 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 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 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 libMap, - SymbolLoader fullSymbolValues, - String appPackageName, - Path classesOut, - boolean finalFields) throws IOException { - for (String packageName : libMap.keySet()) { - Collection 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 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 { - - // 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 { 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 { + // 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 @@ -25,14 +25,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", @@ -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 @@ -5,14 +5,6 @@ java_import( jars = [":classes_deploy.jar"], ) -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", 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 @@ -94,38 +94,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> { - - @Override - public List convert(String input) throws OptionsParsingException { - if (input.isEmpty()) { - return ImmutableList.of(); - } - try { - ImmutableList.Builder 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 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 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 getAllSymbols() throws IOException { - Table symbols = HashBasedTable.create(); - for (SymbolLoader symbolLoader : symbolTables) { - symbols.putAll(getSymbols(symbolLoader)); - } - return symbols; - } - - private Method symbolsMethod; - - private Table 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 result = (Table) - 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 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 symbols = getAllSymbols(); - Table values = getSymbols(symbolValues); - - Set rowSet = symbols.rowKeySet(); - List 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 values; - - IntArrayDeferredInitializer(String fieldName, ImmutableList 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.of()); - } - ImmutableList.Builder intValues = ImmutableList.builder(); - String trimmedValue = value.substring(2, value.length() - 2); - Iterable 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 symbols, - Table 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 rowMap = symbols.row(innerClass); - Set symbolSet = rowMap.keySet(); - List symbolList = new ArrayList<>(symbolSet); - Collections.sort(symbolList); - List 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 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, "", "()V", - null, null); - constructor.visitCode(); - constructor.visitVarInsn(Opcodes.ALOAD, 0); - constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, SUPER_CLASS, "", "()V", false); - constructor.visitInsn(Opcodes.RETURN); - constructor.visitMaxs(1, 1); - constructor.visitEnd(); - } - - private static void writeStaticClassInit( - ClassWriter classWriter, - String className, - List deferredInitializers) { - MethodVisitor visitor = classWriter.visitMethod(Opcodes.ACC_STATIC, "", "()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(); - } - -} -- cgit v1.2.3