/*
* 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";
}
}