diff options
Diffstat (limited to 'src/tools/android/java/com')
3 files changed, 258 insertions, 251 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..dcf0c56ab1 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 @@ -13,11 +13,13 @@ // limitations under the License. package com.google.devtools.build.android; +import com.android.builder.core.VariantConfiguration; +import com.android.ide.common.res2.MergingException; +import com.android.utils.StdLogger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableList; -import com.google.common.hash.Hashing; import com.google.devtools.build.android.Converters.DependencyAndroidDataListConverter; import com.google.devtools.build.android.Converters.ExistingPathConverter; import com.google.devtools.build.android.Converters.PathConverter; @@ -25,15 +27,9 @@ import com.google.devtools.build.android.Converters.UnvalidatedAndroidDataConver import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; - -import com.android.ide.common.res2.MergingException; -import com.android.utils.StdLogger; - import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -50,7 +46,8 @@ import java.util.zip.ZipOutputStream; /** * Action to generate an AAR archive for an Android library. * - * <p><pre> + * <p> + * <pre> * Example Usage: * java/com/google/build/android/AarGeneratorAction\ * --primaryData path/to/resources:path/to/assets:path/to/manifest\ @@ -124,7 +121,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); @@ -132,11 +129,8 @@ public class AarGeneratorAction { checkFlags(options); - FileSystem fileSystem = FileSystems.getDefault(); - Path working = fileSystem.getPath("").toAbsolutePath(); - - AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor( - new StdLogger(com.android.utils.StdLogger.Level.VERBOSE)); + AndroidResourceProcessor resourceProcessor = + new AndroidResourceProcessor(new StdLogger(com.android.utils.StdLogger.Level.VERBOSE)); try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("aar_gen_tmp")) { Path tmp = scopedTmp.getPath(); @@ -144,21 +138,17 @@ public class AarGeneratorAction { Files.createDirectories(resourcesOut); Path assetsOut = tmp.resolve("merged_assets"); Files.createDirectories(assetsOut); - Path expandedOut = tmp.resolve("tmp-expanded"); - Path deduplicatedOut = tmp.resolve("tmp-deduplicated"); - logger.fine(String.format("Setup finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS))); - - ImmutableList<DirectoryModifier> modifiers = ImmutableList.of( - new PackedResourceTarExpander(expandedOut, working), - new FileDeDuplicator(Hashing.murmur3_128(), deduplicatedOut, working)); - MergedAndroidData mergedData = resourceProcessor.mergeData(options.mainData, - options.dependencyData, - resourcesOut, - assetsOut, - modifiers, - null, - options.strictMerge); + MergedAndroidData mergedData = + resourceProcessor.mergeData( + options.mainData, + options.dependencyData, + ImmutableList.<DependencyAndroidData>of(), + resourcesOut, + assetsOut, + null, + VariantConfiguration.Type.LIBRARY, + null); logger.fine(String.format("Merging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS))); writeAar(options.aarOutput, mergedData, options.manifest, options.rtxt, options.classes); @@ -184,15 +174,18 @@ public class AarGeneratorAction { nullFlags.add("classes"); } if (!nullFlags.isEmpty()) { - throw new IllegalArgumentException(String.format("%s must be specified. Building an .aar " - + "without %s is unsupported.", - Joiner.on(", ").join(nullFlags), Joiner.on(", ").join(nullFlags))); + throw new IllegalArgumentException( + String.format( + "%s must be specified. Building an .aar without %s is unsupported.", + Joiner.on(", ").join(nullFlags), + Joiner.on(", ").join(nullFlags))); } } @VisibleForTesting - static void writeAar(Path aar, final MergedAndroidData data, Path manifest, Path rtxt, - Path classes) throws IOException { + static void writeAar( + Path aar, final MergedAndroidData data, Path manifest, Path rtxt, Path classes) + throws IOException { try (final ZipOutputStream zipOut = new ZipOutputStream( new BufferedOutputStream(Files.newOutputStream(aar)))) { ZipEntry manifestEntry = new ZipEntry("AndroidManifest.xml"); @@ -207,8 +200,8 @@ public class AarGeneratorAction { zipOut.write(Files.readAllBytes(classes)); zipOut.closeEntry(); - Files.walkFileTree(data.getResourceDir(), - new ZipDirectoryWriter(zipOut, data.getResourceDir(), "res")); + Files.walkFileTree( + data.getResourceDir(), new ZipDirectoryWriter(zipOut, data.getResourceDir(), "res")); ZipEntry r = new ZipEntry("R.txt"); r.setTime(EPOCH); @@ -217,24 +210,24 @@ public class AarGeneratorAction { zipOut.closeEntry(); if (Files.exists(data.getAssetDir()) && data.getAssetDir().toFile().list().length > 0) { - Files.walkFileTree(data.getAssetDir(), - new ZipDirectoryWriter(zipOut, data.getAssetDir(), "assets")); + Files.walkFileTree( + data.getAssetDir(), new ZipDirectoryWriter(zipOut, data.getAssetDir(), "assets")); } } aar.toFile().setLastModified(EPOCH); } - + private static class ZipDirectoryWriter extends SimpleFileVisitor<Path> { private final ZipOutputStream zipOut; private final Path root; private final String dirName; - + public ZipDirectoryWriter(ZipOutputStream zipOut, Path root, String dirName) { this.zipOut = zipOut; this.root = root; this.dirName = dirName; } - + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { ZipEntry entry = new ZipEntry(new File(dirName, root.relativize(file).toString()).toString()); @@ -244,12 +237,12 @@ public class AarGeneratorAction { zipOut.closeEntry(); return FileVisitResult.CONTINUE; } - + @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - ZipEntry entry = new ZipEntry(new File(dirName, root.relativize(dir).toString()) - .toString() + "/"); + ZipEntry entry = + new ZipEntry(new File(dirName, root.relativize(dir).toString()).toString() + "/"); entry.setTime(EPOCH); zipOut.putNextEntry(entry); zipOut.closeEntry(); 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 17c4822cbe..14f43f9b25 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 @@ -15,10 +15,16 @@ package com.google.devtools.build.android; import static java.nio.charset.StandardCharsets.UTF_8; +import com.android.builder.core.VariantConfiguration; +import com.android.builder.core.VariantConfiguration.Type; +import com.android.ide.common.internal.AaptCruncher; +import com.android.ide.common.internal.CommandLineRunner; +import com.android.ide.common.internal.LoggedErrorException; +import com.android.ide.common.internal.PngCruncher; +import com.android.ide.common.res2.MergingException; +import com.android.utils.StdLogger; import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.google.common.hash.Hashing; import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions; import com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions; import com.google.devtools.build.android.Converters.DependencyAndroidDataListConverter; @@ -31,26 +37,13 @@ import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.TriState; - -import com.android.builder.core.VariantConfiguration; -import com.android.builder.core.VariantConfiguration.Type; -import com.android.ide.common.internal.AaptCruncher; -import com.android.ide.common.internal.CommandLineRunner; -import com.android.ide.common.internal.LoggedErrorException; -import com.android.ide.common.internal.PngCruncher; -import com.android.ide.common.res2.MergingException; -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; import java.util.logging.Logger; - /** * Provides an entry point for the resource processing using the AOSP build tools. * @@ -76,7 +69,7 @@ public class AndroidResourceProcessingAction { private static final StdLogger STD_LOGGER = new StdLogger(com.android.utils.StdLogger.Level.WARNING); - private static final Logger LOGGER = + private static final Logger logger = Logger.getLogger(AndroidResourceProcessingAction.class.getName()); /** Flag specifications for this action. */ @@ -217,15 +210,10 @@ public class AndroidResourceProcessingAction { aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class); options = optionsParser.getOptions(Options.class); - FileSystem fileSystem = FileSystems.getDefault(); - 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(); - final Path expandedOut = tmp.resolve("tmp-expanded"); - final Path deduplicatedOut = tmp.resolve("tmp-deduplicated"); final Path mergedAssets = tmp.resolve("merged_assets"); final Path mergedResources = tmp.resolve("merged_resources"); final Path filteredResources = tmp.resolve("resources-filtered"); @@ -234,19 +222,14 @@ public class AndroidResourceProcessingAction { final Path dummyManifest = tmp.resolve("manifest-aapt-dummy/AndroidManifest.xml"); Path generatedSources = null; - if (options.srcJarOutput != null || options.rOutput != null + if (options.srcJarOutput != null + || options.rOutput != null || options.symbolsTxtOut != null) { generatedSources = tmp.resolve("generated_resources"); } - LOGGER.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + logger.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); - final ImmutableList<DirectoryModifier> modifiers = ImmutableList.of( - new PackedResourceTarExpander(expandedOut, working), - new FileDeDuplicator(Hashing.murmur3_128(), deduplicatedOut, working)); - - // Resources can appear in both the direct dependencies and transitive -- use a set to - // ensure depeduplication. List<DependencyAndroidData> data = ImmutableSet.<DependencyAndroidData>builder() .addAll(options.directData) @@ -254,32 +237,38 @@ public class AndroidResourceProcessingAction { .build() .asList(); - final MergedAndroidData mergedData = resourceProcessor.mergeData( - options.primaryData, - data, - mergedResources, - mergedAssets, - modifiers, - selectPngCruncher(), - true); - - LOGGER.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); - - final DensityFilteredAndroidData filteredData = mergedData.filter( - new DensitySpecificResourceFilter(options.densities, filteredResources, mergedResources), - new DensitySpecificManifestProcessor(options.densities, densityManifest)); - - LOGGER.fine(String.format("Density filtering finished at %sms", - timer.elapsed(TimeUnit.MILLISECONDS))); - - MergedAndroidData processedData = resourceProcessor.processManifest( - options.packageType, - options.packageForR, - options.applicationId, - options.versionCode, - options.versionName, - filteredData, - processedManifest); + final MergedAndroidData mergedData = + resourceProcessor.mergeData( + options.primaryData, + options.directData, + options.transitiveData, + mergedResources, + mergedAssets, + selectPngCruncher(), + options.packageType, + options.symbolsTxtOut); + + logger.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + final DensityFilteredAndroidData filteredData = + mergedData.filter( + new DensitySpecificResourceFilter( + options.densities, filteredResources, mergedResources), + new DensitySpecificManifestProcessor(options.densities, densityManifest)); + + logger.fine( + String.format( + "Density filtering finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + MergedAndroidData processedData = + resourceProcessor.processManifest( + options.packageType, + options.packageForR, + options.applicationId, + options.versionCode, + options.versionName, + filteredData, + processedManifest); // Write manifestOutput now before the dummy manifest is created. if (options.manifestOutput != null) { @@ -318,42 +307,44 @@ public class AndroidResourceProcessingAction { options.resourcesOutput != null ? processedData.getResourceDir().resolve("values").resolve("public.xml") : null); - LOGGER.fine(String.format("aapt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + logger.fine(String.format("aapt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); if (options.srcJarOutput != null) { - resourceProcessor.createSrcJar(generatedSources, options.srcJarOutput, + resourceProcessor.createSrcJar( + generatedSources, + options.srcJarOutput, VariantConfiguration.Type.LIBRARY == options.packageType); } if (options.rOutput != null) { - resourceProcessor.copyRToOutput(generatedSources, options.rOutput, - VariantConfiguration.Type.LIBRARY == options.packageType); - } - if (options.symbolsTxtOut != null) { - resourceProcessor.copyRToOutput(generatedSources, options.symbolsTxtOut, + resourceProcessor.copyRToOutput( + generatedSources, + options.rOutput, VariantConfiguration.Type.LIBRARY == options.packageType); } if (options.resourcesOutput != null) { - resourceProcessor.createResourcesZip(processedData.getResourceDir(), - processedData.getAssetDir(), options.resourcesOutput); + resourceProcessor.createResourcesZip( + processedData.getResourceDir(), + processedData.getAssetDir(), + options.resourcesOutput); } - LOGGER.fine(String.format("Packaging finished at %sms", - timer.elapsed(TimeUnit.MILLISECONDS))); + logger.fine( + String.format("Packaging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } catch (MergingException e) { - LOGGER.log(java.util.logging.Level.SEVERE, "Error during merging resources", e); + logger.log(java.util.logging.Level.SEVERE, "Error during merging resources", e); throw e; } catch (IOException | InterruptedException | LoggedErrorException | UnrecognizedSplitsException e) { - LOGGER.log(java.util.logging.Level.SEVERE, "Error during processing resources", e); + logger.log(java.util.logging.Level.SEVERE, "Error during processing resources", e); throw e; } catch (Exception e) { - LOGGER.log(java.util.logging.Level.SEVERE, "Unexpected", e); + logger.log(java.util.logging.Level.SEVERE, "Unexpected", e); throw e; } finally { resourceProcessor.shutdown(); } - LOGGER.fine(String.format("Resources processed in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + logger.fine(String.format("Resources processed in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } private static boolean usePngCruncher() { 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 644b8290df..55ef25e759 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 @@ -15,28 +15,6 @@ package com.google.devtools.build.android; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -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.SplitConfigurationFilter.UnrecognizedSplitsException; -import com.google.devtools.build.android.resources.RClassGenerator; -import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; -import com.google.devtools.common.options.Option; -import com.google.devtools.common.options.OptionsBase; -import com.google.devtools.common.options.TriState; - import com.android.annotations.Nullable; import com.android.builder.core.VariantConfiguration; import com.android.builder.dependency.SymbolFileProvider; @@ -47,13 +25,7 @@ import com.android.ide.common.internal.CommandLineRunner; import com.android.ide.common.internal.ExecutorSingleton; import com.android.ide.common.internal.LoggedErrorException; import com.android.ide.common.internal.PngCruncher; -import com.android.ide.common.res2.AssetMerger; -import com.android.ide.common.res2.AssetSet; -import com.android.ide.common.res2.MergedAssetWriter; -import com.android.ide.common.res2.MergedResourceWriter; import com.android.ide.common.res2.MergingException; -import com.android.ide.common.res2.ResourceMerger; -import com.android.ide.common.res2.ResourceSet; import com.android.manifmerger.ManifestMerger2; import com.android.manifmerger.ManifestMerger2.Invoker; import com.android.manifmerger.ManifestMerger2.Invoker.Feature; @@ -66,15 +38,35 @@ import com.android.manifmerger.XmlDocument; import com.android.sdklib.repository.FullRevision; import com.android.utils.Pair; import com.android.utils.StdLogger; - -import org.xml.sax.SAXException; - +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Stopwatch; +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.SplitConfigurationFilter.UnrecognizedSplitsException; +import com.google.devtools.build.android.resources.RClassGenerator; +import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.TriState; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -94,15 +86,16 @@ import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLEventFactory; @@ -114,11 +107,14 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import org.xml.sax.SAXException; /** * Provides a wrapper around the AOSP build tools for resource processing. */ public class AndroidResourceProcessor { + private static final Logger logger = Logger.getLogger(AndroidResourceProcessor.class.getName()); + /** * Options class containing flags for Aapt setup. */ @@ -303,8 +299,10 @@ public class AndroidResourceProcessor { final Path source = generatedSourceRoot.resolve("R.txt"); if (Files.exists(source)) { if (staticIds) { - String contents = HEX_REGEX.matcher(Joiner.on("\n").join( - Files.readAllLines(source, UTF_8))).replaceAll("0x1"); + String contents = + HEX_REGEX + .matcher(Joiner.on("\n").join(Files.readAllLines(source, UTF_8))) + .replaceAll("0x1"); Files.write(rOutput, contents.getBytes(UTF_8)); } else { Files.copy(source, rOutput); @@ -363,7 +361,7 @@ public class AndroidResourceProcessor { * Copies the AndroidManifest.xml to the specified output location. * * @param androidData The MergedAndroidData which contains the manifest to be written to - * manifestOut. + * manifestOut. * @param manifestOut The Path to write the AndroidManifest.xml. */ public void copyManifestToOutput(MergedAndroidData androidData, Path manifestOut) { @@ -419,8 +417,8 @@ public class AndroidResourceProcessor { Path publicResourcesOut) throws IOException, InterruptedException, LoggedErrorException, UnrecognizedSplitsException { Path androidManifest = primaryData.getManifest(); - Path resourceDir = primaryData.getResourceDir(); - Path assetsDir = primaryData.getAssetDir(); + final Path resourceDir = primaryData.getResourceDir(); + final Path assetsDir = primaryData.getAssetDir(); if (publicResourcesOut != null) { prepareOutputPath(publicResourcesOut.getParent()); } @@ -469,8 +467,13 @@ public class AndroidResourceProcessor { .add("-c", Joiner.on(',').join(resourceConfigs)) // Split APKs if any splits were specified. .whenVersionIsAtLeast(new FullRevision(23)).thenAddRepeated("--split", splits); - - new CommandLineRunner(stdLogger).runCmdLine(commandBuilder.build(), null); + try { + new CommandLineRunner(stdLogger).runCmdLine(commandBuilder.build(), null); + } catch (LoggedErrorException e) { + // Add context and throw the error to resume processing. + throw new LoggedErrorException( + e.getCmdLineError(), getOutputWithSourceContext(aapt, e.getOutput()), e.getCmdLine()); + } // The R needs to be created for each library in the dependencies, // but only if the current project is not a library. @@ -499,6 +502,46 @@ public class AndroidResourceProcessor { } } + /** Adds 10 lines of source to each syntax error. Very useful for debugging. */ + private List<String> getOutputWithSourceContext(Path aapt, List<String> lines) + throws IOException { + List<String> outputWithSourceContext = new ArrayList<>(); + for (String line : lines) { + if (line.contains("Duplicate file") || line.contains("Original")) { + String[] parts = line.split(":"); + String fileName = parts[0].trim(); + outputWithSourceContext.add("\n" + fileName + ":\n\t"); + outputWithSourceContext.add( + Joiner.on("\n\t") + .join( + Files.readAllLines( + aapt.getFileSystem().getPath(fileName), StandardCharsets.UTF_8))); + } else if (line.contains("error")) { + String[] parts = line.split(":"); + String fileName = parts[0].trim(); + try { + int lineNumber = Integer.valueOf(parts[1].trim()); + StringBuilder expandedError = + new StringBuilder("\nError at " + lineNumber + " : " + line); + List<String> errorSource = + Files.readAllLines(aapt.getFileSystem().getPath(fileName), StandardCharsets.UTF_8); + for (int i = Math.max(lineNumber - 5, 0); + i < Math.min(lineNumber + 5, errorSource.size()); + i++) { + expandedError.append("\n").append(i).append("\t: ").append(errorSource.get(i)); + } + outputWithSourceContext.add(expandedError.toString()); + } catch (IOException | NumberFormatException formatError) { + outputWithSourceContext.add("error parsing line" + line); + stdLogger.error(formatError, "error during reading source %s", fileName); + } + } else { + outputWithSourceContext.add(line); + } + } + return outputWithSourceContext; + } + /** Task to parse java package from AndroidManifest.xml */ private static final class PackageParsingTask implements Callable<String> { @@ -696,13 +739,16 @@ public class AndroidResourceProcessor { int versionCode, String versionName, MergedAndroidData primaryData, - Path processedManifest) throws IOException { + Path processedManifest) + throws IOException { - ManifestMerger2.MergeType mergeType = variantType == VariantConfiguration.Type.DEFAULT - ? ManifestMerger2.MergeType.APPLICATION : ManifestMerger2.MergeType.LIBRARY; + ManifestMerger2.MergeType mergeType = + variantType == VariantConfiguration.Type.DEFAULT + ? ManifestMerger2.MergeType.APPLICATION + : ManifestMerger2.MergeType.LIBRARY; - String newManifestPackage = variantType == VariantConfiguration.Type.DEFAULT - ? applicationId : customPackageForR; + String newManifestPackage = + variantType == VariantConfiguration.Type.DEFAULT ? applicationId : customPackageForR; if (versionCode != -1 || versionName != null || newManifestPackage != null) { Files.createDirectories(processedManifest.getParent()); @@ -743,12 +789,14 @@ public class AndroidResourceProcessor { default: throw new RuntimeException("Unhandled result type : " + mergingReport.getResult()); } - } catch ( - IOException | SAXException | ParserConfigurationException | MergeFailureException e) { + } catch (IOException + | SAXException + | ParserConfigurationException + | MergeFailureException e) { throw new RuntimeException(e); } - return new MergedAndroidData(primaryData.getResourceDir(), primaryData.getAssetDir(), - processedManifest); + return new MergedAndroidData( + primaryData.getResourceDir(), primaryData.getAssetDir(), processedManifest); } return primaryData; } @@ -772,7 +820,8 @@ public class AndroidResourceProcessor { Map<Path, String> mergeeManifests, MergeType mergeType, Map<String, String> values, - Path output) throws IOException { + Path output) + throws IOException { if (mergeeManifests.isEmpty() && values.isEmpty()) { return manifest; } @@ -829,21 +878,19 @@ public class AndroidResourceProcessor { default: throw new RuntimeException("Unhandled result type : " + mergingReport.getResult()); } - } catch ( - SAXException | ParserConfigurationException | MergeFailureException e) { + } catch (SAXException | ParserConfigurationException | MergeFailureException e) { throw new RuntimeException(e); } return output; } - private void writeMergedManifest(MergingReport mergingReport, - Path manifestOut) throws IOException, SAXException, ParserConfigurationException { + private void writeMergedManifest(MergingReport mergingReport, Path manifestOut) + throws IOException, SAXException, ParserConfigurationException { XmlDocument xmlDocument = mergingReport.getMergedDocument().get(); String annotatedDocument = mergingReport.getActions().blame(xmlDocument); stdLogger.verbose(annotatedDocument); - Files.write( - manifestOut, xmlDocument.prettyPrint().getBytes(UTF_8)); + Files.write(manifestOut, xmlDocument.prettyPrint().getBytes(UTF_8)); } /** @@ -855,7 +902,7 @@ public class AndroidResourceProcessor { * @return The output manifest if generated or the input manifest if no overwriting is required. */ /* TODO(apell): switch from custom xml parsing to Gradle merger with NO_PLACEHOLDER_REPLACEMENT - * set when android common is updated to version 2.5.0. + * set when android common is updated to version 2.5.0. */ public Path writeManifestPackage(Path manifest, String customPackage, Path output) { if (Strings.isNullOrEmpty(customPackage)) { @@ -863,10 +910,12 @@ public class AndroidResourceProcessor { } try { Files.createDirectories(output.getParent()); - XMLEventReader reader = XMLInputFactory.newInstance() - .createXMLEventReader(Files.newInputStream(manifest), UTF_8.name()); - XMLEventWriter writer = XMLOutputFactory.newInstance() - .createXMLEventWriter(Files.newOutputStream(output), UTF_8.name()); + XMLEventReader reader = + XMLInputFactory.newInstance() + .createXMLEventReader(Files.newInputStream(manifest), UTF_8.name()); + XMLEventWriter writer = + XMLOutputFactory.newInstance() + .createXMLEventWriter(Files.newOutputStream(output), UTF_8.name()); XMLEventFactory eventFactory = XMLEventFactory.newInstance(); while (reader.hasNext()) { XMLEvent event = reader.nextEvent(); @@ -884,8 +933,9 @@ public class AndroidResourceProcessor { newAttributes.add(attr); } } - writer.add(eventFactory.createStartElement( - element.getName(), newAttributes.build().iterator(), element.getNamespaces())); + writer.add( + eventFactory.createStartElement( + element.getName(), newAttributes.build().iterator(), element.getNamespaces())); } else { writer.add(event); } @@ -897,90 +947,59 @@ public class AndroidResourceProcessor { return output; } - + /** * Merges all secondary resources with the primary resources. */ public MergedAndroidData mergeData( final UnvalidatedAndroidData primary, - final List<DependencyAndroidData> secondary, + final List<DependencyAndroidData> direct, + final List<DependencyAndroidData> transitive, final Path resourcesOut, final Path assetsOut, - final ImmutableList<DirectoryModifier> modifiers, @Nullable final PngCruncher cruncher, - final boolean strict) throws MergingException { - - List<ResourceSet> resourceSets = new ArrayList<>(); - List<AssetSet> assetSets = new ArrayList<>(); - - if (strict) { - androidDataToStrictMergeSet(primary, secondary, modifiers, resourceSets, assetSets); - } else { - androidDataToRelaxedMergeSet(primary, secondary, modifiers, resourceSets, assetSets); - } - ResourceMerger merger = new ResourceMerger(); - for (ResourceSet set : resourceSets) { - set.loadFromFiles(stdLogger); - merger.addDataSet(set); - } - - AssetMerger assetMerger = new AssetMerger(); - for (AssetSet set : assetSets) { - set.loadFromFiles(stdLogger); - assetMerger.addDataSet(set); + final VariantConfiguration.Type type, + @Nullable final Path symbolsOut) + throws MergingException { + Stopwatch timer = Stopwatch.createStarted(); + final ListeningExecutorService executorService = + MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15)); + try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) { + AndroidDataMerger merger = AndroidDataMerger.createWithPathDeduplictor(executorService); + UnwrittenMergedAndroidData merged = + merger.merge(transitive, direct, primary, type != VariantConfiguration.Type.LIBRARY); + logger.fine(String.format("merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + timer.reset().start(); + if (symbolsOut != null) { + AndroidDataSerializer serializer = AndroidDataSerializer.create(); + merged.serializeTo(serializer); + serializer.flushTo(symbolsOut); + logger.fine( + String.format( + "serialize merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + timer.reset().start(); + } + AndroidDataWriter writer = + AndroidDataWriter.createWith( + resourcesOut.getParent(), resourcesOut, assetsOut, cruncher, executorService); + return merged.write(writer); + } catch (IOException e) { + throw new MergingException(e); + } finally { + logger.fine( + String.format("write merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } - - MergedResourceWriter resourceWriter = new MergedResourceWriter(resourcesOut.toFile(), cruncher); - MergedAssetWriter assetWriter = new MergedAssetWriter(assetsOut.toFile()); - - merger.mergeData(resourceWriter, false); - assetMerger.mergeData(assetWriter, false); - - return new MergedAndroidData(resourcesOut, assetsOut, primary.getManifest()); } /** * Shutdown AOSP utilized thread-pool. */ public void shutdown() { + FullyQualifiedName.logCacheUsage(logger); // AOSP code never shuts down its singleton executor and leaves the process hanging. ExecutorSingleton.getExecutor().shutdownNow(); } - private void androidDataToRelaxedMergeSet(UnvalidatedAndroidData primary, - List<DependencyAndroidData> secondary, ImmutableList<DirectoryModifier> modifiers, - List<ResourceSet> resourceSets, List<AssetSet> assetSets) { - - for (DependencyAndroidData dependency : secondary) { - DependencyAndroidData modifiedDependency = dependency.modify(modifiers); - modifiedDependency.addAsResourceSets(resourceSets); - modifiedDependency.addAsAssetSets(assetSets); - } - UnvalidatedAndroidData modifiedPrimary = primary.modify(modifiers); - modifiedPrimary.addAsResourceSets(resourceSets); - modifiedPrimary.addAsAssetSets(assetSets); - - } - - private void androidDataToStrictMergeSet(UnvalidatedAndroidData primary, - List<DependencyAndroidData> secondary, ImmutableList<DirectoryModifier> modifiers, - List<ResourceSet> resourceSets, List<AssetSet> assetSets) { - UnvalidatedAndroidData modifiedPrimary = primary.modify(modifiers); - ResourceSet mainResources = modifiedPrimary.addToResourceSet(new ResourceSet("main")); - AssetSet mainAssets = modifiedPrimary.addToAssets(new AssetSet("main")); - ResourceSet dependentResources = new ResourceSet("deps"); - AssetSet dependentAssets = new AssetSet("deps"); - for (DependencyAndroidData dependency : secondary) { - DependencyAndroidData modifiedDependency = dependency.modify(modifiers); - modifiedDependency.addToResourceSet(dependentResources); - modifiedDependency.addToAssets(dependentAssets); - } - resourceSets.add(dependentResources); - resourceSets.add(mainResources); - assetSets.add(dependentAssets); - assetSets.add(mainAssets); - } - @Nullable private Path prepareOutputPath(@Nullable Path out) throws IOException { if (out == null) { @@ -1054,12 +1073,12 @@ public class AndroidResourceProcessor { */ private static final class SymbolFileSrcJarBuildingVisitor extends ZipBuilderVisitor { - 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( - "public static int ([\\w\\.]+)=0x[0-9A-fa-f]+;"); - static final Pattern INNER_CLASS = Pattern.compile("public static class ([a-z_]*) \\{(.*?)\\}", - Pattern.DOTALL); + 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("public static int ([\\w\\.]+)=0x[0-9A-fa-f]+;"); + static final Pattern INNER_CLASS = + Pattern.compile("public static class ([a-z_]*) \\{(.*?)\\}", Pattern.DOTALL); private final boolean staticIds; @@ -1082,11 +1101,14 @@ public class AndroidResourceProcessor { StringBuffer resourceIds = new StringBuffer(); while (idMatcher.find()) { String javaId = idMatcher.group(1); - idMatcher.appendReplacement(resourceIds, String.format("public static int %s=0x%08X;", - javaId, Objects.hash(pkg, resourceType, javaId))); + idMatcher.appendReplacement( + resourceIds, + String.format( + "public static int %s=0x%08X;", javaId, Objects.hash(pkg, resourceType, javaId))); } idMatcher.appendTail(resourceIds); - innerClassMatcher.appendReplacement(out, + innerClassMatcher.appendReplacement( + out, String.format("public static class %s {%s}", resourceType, resourceIds.toString())); } innerClassMatcher.appendTail(out); @@ -1098,8 +1120,9 @@ public class AndroidResourceProcessor { if (file.getFileName().endsWith("R.java")) { byte[] content = Files.readAllBytes(file); if (staticIds) { - content = replaceIdsWithStaticIds(UTF_8.decode( - ByteBuffer.wrap(content)).toString()).getBytes(UTF_8); + content = + replaceIdsWithStaticIds(UTF_8.decode(ByteBuffer.wrap(content)).toString()) + .getBytes(UTF_8); } addEntry(file, content); } |