/* * Copyright 2012-present Facebook, Inc. * * 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.facebook.buck.android; import com.android.sdklib.build.ApkBuilder; import com.facebook.buck.rules.coercer.ManifestEntries; import com.facebook.buck.shell.ShellStep; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.util.MoreIterables; import com.facebook.buck.util.MoreStrings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedSet; import java.nio.file.Path; /** * Runs the Android Asset Packaging Tool ({@code aapt}), which creates an {@code .apk} file. * Frequently, the {@code pathsToRawFilesDirs} excludes {@code classes.dex}, as {@code classes.dex} * will be added separately to the final APK via {@link ApkBuilder}. */ public class AaptStep extends ShellStep { // aapt, unless specified a pattern, ignores certain files and directories. We follow the same // logic as the default pattern found at http://goo.gl/OTTK88 and line 61. private static final String DEFAULT_IGNORE_ASSETS_PATTERN = "!.gitkeep:!.svn:!.git:" + "!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"; // Ignore *.orig files generated by mercurial. public static final String IGNORE_ASSETS_PATTERN = DEFAULT_IGNORE_ASSETS_PATTERN + ":*.orig"; /** * Determines whether the default AAPT ignore pattern in {@link * com.facebook.buck.android.AaptStep#DEFAULT_IGNORE_ASSETS_PATTERN} would silently ignore a file. * * @param path The path of the file we are interested in. * @return Whether the file would be silently ignored. */ public static boolean isSilentlyIgnored(Path path) { String fileName = path.getFileName().toString(); return ".gitkeep".equalsIgnoreCase(fileName) || ".svn".equalsIgnoreCase(fileName) || ".git".equalsIgnoreCase(fileName) || ".ds_store".equalsIgnoreCase(fileName) || MoreStrings.endsWithIgnoreCase(fileName, ".scc") || "cvs".equalsIgnoreCase(fileName) || "thumbs.db".equalsIgnoreCase(fileName) || "picasa.ini".equalsIgnoreCase(fileName) || fileName.endsWith("~"); } private final Path androidManifest; private final ImmutableList<Path> resDirectories; private final ImmutableSortedSet<Path> assetsDirectories; private final Path pathToOutputApkFile; private final Path pathToRDotTxtDir; private final Path pathToGeneratedProguardConfig; private final boolean isCrunchPngFiles; private final boolean includesVectorDrawables; private final ManifestEntries manifestEntries; public AaptStep( Path workingDirectory, Path androidManifest, ImmutableList<Path> resDirectories, ImmutableSortedSet<Path> assetsDirectories, Path pathToOutputApkFile, Path pathToRDotTxtDir, Path pathToGeneratedProguardConfig, boolean isCrunchPngFiles, boolean includesVectorDrawables, ManifestEntries manifestEntries) { super(workingDirectory); this.androidManifest = androidManifest; this.resDirectories = resDirectories; this.assetsDirectories = assetsDirectories; this.pathToOutputApkFile = pathToOutputApkFile; this.pathToRDotTxtDir = pathToRDotTxtDir; this.pathToGeneratedProguardConfig = pathToGeneratedProguardConfig; this.isCrunchPngFiles = isCrunchPngFiles; this.includesVectorDrawables = includesVectorDrawables; this.manifestEntries = manifestEntries; } @Override protected ImmutableList<String> getShellCommandInternal(ExecutionContext context) { ImmutableList.Builder<String> builder = ImmutableList.builder(); AndroidPlatformTarget androidPlatformTarget = context.getAndroidPlatformTarget(); builder.add(androidPlatformTarget.getAaptExecutable().toString()); builder.add("package"); // verbose flag, if appropriate. if (context.getVerbosity().shouldUseVerbosityFlagIfAvailable()) { builder.add("-v"); } // Force overwrite of existing files. builder.add("-f"); builder.add("-G", pathToGeneratedProguardConfig.toString()); // --no-crunch, if appropriate. if (!isCrunchPngFiles) { builder.add("--no-crunch"); } // Include all of the res/ directories. builder.add("--auto-add-overlay"); for (Path res : MoreIterables.dedupKeepLast(resDirectories)) { builder.add("-S", res.toString()); } // Include the assets/ directory, if any. for (Path assetDir : assetsDirectories) { builder.add("-A", assetDir.toString()); } builder.add("--output-text-symbols").add(pathToRDotTxtDir.toString()); builder.add("-J").add(pathToRDotTxtDir.toString()); builder.add("-M").add(androidManifest.toString()); builder.add("-I", androidPlatformTarget.getAndroidJar().toString()); builder.add("-F", pathToOutputApkFile.toString()); builder.add("--ignore-assets", IGNORE_ASSETS_PATTERN); if (manifestEntries.getMinSdkVersion().isPresent()) { builder.add("--min-sdk-version", manifestEntries.getMinSdkVersion().get().toString()); } if (manifestEntries.getTargetSdkVersion().isPresent()) { builder.add("--target-sdk-version", manifestEntries.getTargetSdkVersion().get().toString()); } if (manifestEntries.getVersionCode().isPresent()) { builder.add("--version-code", manifestEntries.getVersionCode().get().toString()); } if (manifestEntries.getVersionName().isPresent()) { builder.add("--version-name", manifestEntries.getVersionName().get()); } if (manifestEntries.getDebugMode().orElse(false)) { builder.add("--debug-mode"); } if (manifestEntries.hasAny()) { // Force AAPT to error if the command line version clashes with the hardcoded manifest builder.add("--error-on-failed-insert"); } if (includesVectorDrawables) { builder.add("--no-version-vectors"); } return builder.build(); } @Override public String getShortName() { return "aapt_package"; } }