diff options
Diffstat (limited to 'src/tools/android/java')
5 files changed, 275 insertions, 41 deletions
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 cc78940086..322180b1a2 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 @@ -13,8 +13,6 @@ // limitations under the License. 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; @@ -38,7 +36,6 @@ import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.TriState; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.concurrent.TimeUnit; @@ -283,12 +280,7 @@ public class AndroidResourceProcessingAction { } if (options.packageType == Type.LIBRARY) { - Files.createDirectories(dummyManifest.getParent()); - Files.write(dummyManifest, String.format( - "<?xml version=\"1.0\" encoding=\"utf-8\"?>" - + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"" - + " package=\"%s\">" - + "</manifest>", options.packageForR).getBytes(UTF_8)); + resourceProcessor.writeDummyManifestForAapt(dummyManifest, options.packageForR); processedData = new MergedAndroidData( processedData.getResourceDir(), processedData.getAssetDir(), 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 6e2f340623..53b7f8a9d6 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 @@ -418,12 +418,12 @@ public class AndroidResourceProcessor { Collection<String> splits, MergedAndroidData primaryData, List<DependencyAndroidData> dependencyData, - Path sourceOut, - Path packageOut, - Path proguardOut, - Path mainDexProguardOut, - Path publicResourcesOut, - Path dataBindingInfoOut) + @Nullable Path sourceOut, + @Nullable Path packageOut, + @Nullable Path proguardOut, + @Nullable Path mainDexProguardOut, + @Nullable Path publicResourcesOut, + @Nullable Path dataBindingInfoOut) throws IOException, InterruptedException, LoggedErrorException, UnrecognizedSplitsException { Path androidManifest = primaryData.getManifest(); final Path resourceDir = processDataBindings(primaryData.getResourceDir(), dataBindingInfoOut, @@ -433,7 +433,69 @@ public class AndroidResourceProcessor { if (publicResourcesOut != null) { prepareOutputPath(publicResourcesOut.getParent()); } + runAapt(aapt, + androidJar, + buildToolsVersion, + variantType, + debug, + customPackageForR, + aaptOptions, + resourceConfigs, + splits, + androidManifest, + resourceDir, + assetsDir, + sourceOut, + packageOut, + proguardOut, + mainDexProguardOut, + publicResourcesOut); + // 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); + } + // Reset the output date stamps. + if (proguardOut != null) { + Files.setLastModifiedTime(proguardOut, FileTime.fromMillis(0L)); + } + if (mainDexProguardOut != null) { + Files.setLastModifiedTime(mainDexProguardOut, FileTime.fromMillis(0L)); + } + if (packageOut != null) { + Files.setLastModifiedTime(packageOut, FileTime.fromMillis(0L)); + if (!splits.isEmpty()) { + Iterable<Path> splitFilenames = findAndRenameSplitPackages(packageOut, splits); + for (Path splitFilename : splitFilenames) { + Files.setLastModifiedTime(splitFilename, FileTime.fromMillis(0L)); + } + } + } + if (publicResourcesOut != null && Files.exists(publicResourcesOut)) { + Files.setLastModifiedTime(publicResourcesOut, FileTime.fromMillis(0L)); + } + } + public void runAapt( + Path aapt, + Path androidJar, + @Nullable FullRevision buildToolsVersion, + VariantConfiguration.Type variantType, + boolean debug, + String customPackageForR, + AaptOptions aaptOptions, + Collection<String> resourceConfigs, + Collection<String> splits, + Path androidManifest, + Path resourceDir, + Path assetsDir, + Path sourceOut, + @Nullable Path packageOut, + @Nullable Path proguardOut, + @Nullable Path mainDexProguardOut, + @Nullable Path publicResourcesOut) + throws InterruptedException, LoggedErrorException, IOException { AaptCommandBuilder commandBuilder = new AaptCommandBuilder(aapt) .forBuildToolsVersion(buildToolsVersion) @@ -485,32 +547,6 @@ public class AndroidResourceProcessor { 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. - if (sourceOut != null && variantType != VariantConfiguration.Type.LIBRARY) { - writeDependencyPackageRJavaFiles( - dependencyData, customPackageForR, androidManifest, sourceOut); - } - // Reset the output date stamps. - if (proguardOut != null) { - Files.setLastModifiedTime(proguardOut, FileTime.fromMillis(0L)); - } - if (mainDexProguardOut != null) { - Files.setLastModifiedTime(mainDexProguardOut, FileTime.fromMillis(0L)); - } - if (packageOut != null) { - Files.setLastModifiedTime(packageOut, FileTime.fromMillis(0L)); - if (!splits.isEmpty()) { - Iterable<Path> splitFilenames = findAndRenameSplitPackages(packageOut, splits); - for (Path splitFilename : splitFilenames) { - Files.setLastModifiedTime(splitFilename, FileTime.fromMillis(0L)); - } - } - } - if (publicResourcesOut != null && Files.exists(publicResourcesOut)) { - Files.setLastModifiedTime(publicResourcesOut, FileTime.fromMillis(0L)); - } } /** Adds 10 lines of source to each syntax error. Very useful for debugging. */ @@ -956,6 +992,15 @@ public class AndroidResourceProcessor { Files.write(manifestOut, xmlDocument.prettyPrint().getBytes(UTF_8)); } + public void writeDummyManifestForAapt(Path dummyManifest, String packageForR) throws IOException { + Files.createDirectories(dummyManifest.getParent()); + Files.write(dummyManifest, String.format( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"" + + " package=\"%s\">" + + "</manifest>", packageForR).getBytes(UTF_8)); + } + /** * Overwrite the package attribute of {@code <manifest>} in an AndroidManifest.xml file. * diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java new file mode 100644 index 0000000000..df8c9489a8 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java @@ -0,0 +1,181 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android; + +import com.android.builder.core.VariantConfiguration; +import com.android.builder.core.VariantConfiguration.Type; +import com.android.utils.StdLogger; +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions; +import com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions; +import com.google.devtools.build.android.Converters.ExistingPathConverter; +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 java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Validates merged resources for an android_library via AAPT. Takes as input, the merged resources + * from {@link AndroidResourceMergingAction}. + */ +public class AndroidResourceValidatorAction { + + private static final StdLogger stdLogger = new StdLogger(StdLogger.Level.WARNING); + + private static final Logger logger = + Logger.getLogger(AndroidResourceValidatorAction.class.getName()); + + /** Flag specifications for this action. */ + public static final class Options extends OptionsBase { + + @Option( + name = "mergedResources", + defaultValue = "null", + converter = ExistingPathConverter.class, + category = "input", + help = "Path to the read merged resources archive." + ) + public Path mergedResources; + + @Option( + name = "manifest", + defaultValue = "null", + converter = ExistingPathConverter.class, + category = "input", + help = "Path for the AndroidManifest.xml." + ) + public Path manifest; + + @Option( + name = "packageForR", + defaultValue = "null", + category = "config", + help = "Custom java package to generate the R symbols files." + ) + public String packageForR; + + @Option( + name = "srcJarOutput", + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "Path for the generated java source jar." + ) + public Path srcJarOutput; + + @Option( + name = "rOutput", + defaultValue = "null", + converter = PathConverter.class, + category = "output", + help = "Path to where the R.txt should be written." + ) + public Path rOutput; + } + + public static void main(String[] args) throws Exception { + final Stopwatch timer = Stopwatch.createStarted(); + OptionsParser optionsParser = + OptionsParser.newOptionsParser(Options.class, AaptConfigOptions.class); + optionsParser.parseAndExitUponError(args); + AaptConfigOptions aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class); + Options options = optionsParser.getOptions(Options.class); + + final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(stdLogger); + VariantConfiguration.Type packageType = Type.LIBRARY; + + Preconditions.checkNotNull(options.rOutput); + Preconditions.checkNotNull(options.srcJarOutput); + try (ScopedTemporaryDirectory scopedTmp = + new ScopedTemporaryDirectory("resource_validator_tmp")) { + Path tmp = scopedTmp.getPath(); + Path expandedOut = tmp.resolve("tmp-expanded"); + Path resources = expandedOut.resolve("res"); + Path assets = expandedOut.resolve("assets"); + Path generatedSources = tmp.resolve("generated_resources"); + Path dummyManifest = tmp.resolve("manifest-aapt-dummy/AndroidManifest.xml"); + + unpackZip(options.mergedResources, expandedOut); + logger.fine(String.format("unpacked zip at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + // We need to make the manifest aapt safe (w.r.t., placeholders). For now, just stub it out. + resourceProcessor.writeDummyManifestForAapt(dummyManifest, options.packageForR); + + resourceProcessor.runAapt( + aaptConfigOptions.aapt, + aaptConfigOptions.androidJar, + aaptConfigOptions.buildToolsVersion, + packageType, + aaptConfigOptions.debug, + options.packageForR, + new FlagAaptOptions(aaptConfigOptions), + aaptConfigOptions.resourceConfigs, + ImmutableList.<String>of(), + dummyManifest, + resources, + assets, + generatedSources, + null, /* packageOut */ + null, /* proguardOut */ + null, /* mainDexProguardOut */ + null /* publicResourcesOut */); + logger.fine(String.format("aapt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + + resourceProcessor.copyRToOutput( + generatedSources, options.rOutput, VariantConfiguration.Type.LIBRARY == packageType); + + resourceProcessor.createSrcJar( + generatedSources, options.srcJarOutput, VariantConfiguration.Type.LIBRARY == packageType); + } catch (Exception e) { + logger.log(java.util.logging.Level.SEVERE, "Unexpected", e); + throw e; + } finally { + resourceProcessor.shutdown(); + } + logger.fine(String.format("Resources merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); + } + + private static void unpackZip(Path mergedResources, Path expandedOut) throws IOException { + byte[] buffer = new byte[4096]; + try (ZipInputStream zis = + new ZipInputStream(new BufferedInputStream(Files.newInputStream(mergedResources)))) { + ZipEntry z = zis.getNextEntry(); + while (z != null) { + String entryName = z.getName(); + Path outputPath = expandedOut.resolve(entryName); + Files.createDirectories(outputPath.getParent()); + try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(outputPath))) { + int count = zis.read(buffer); + while (count != -1) { + out.write(buffer, 0, count); + count = zis.read(buffer); + } + } + z = zis.getNextEntry(); + } + } + } +} 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 6dc6b4b2ce..d8df5b4103 100644 --- a/src/tools/android/java/com/google/devtools/build/android/BUILD +++ b/src/tools/android/java/com/google/devtools/build/android/BUILD @@ -50,6 +50,14 @@ java_binary( ) java_binary( + name = "AndroidResourceValidatorAction", + main_class = "com.google.devtools.build.android.AndroidResourceValidatorAction", + runtime_deps = [ + ":android_builder_lib", + ], +) + +java_binary( name = "ManifestMergerAction", main_class = "com.google.devtools.build.android.ManifestMergerAction", runtime_deps = [ 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 3a08124f58..444bf4f699 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 @@ -38,6 +38,14 @@ java_binary( ) java_binary( + name = "AndroidResourceValidatorAction", + main_class = "com.google.devtools.build.android.AndroidResourceValidatorAction", + runtime_deps = [ + ":classes", + ], +) + +java_binary( name = "ManifestMergerAction", main_class = "com.google.devtools.build.android.ManifestMergerAction", runtime_deps = [ |