in src/com/facebook/buck/android/AndroidBinaryRule.java [687:822]
private void addDxAndApkBuilderSteps(BuildContext context,
ImmutableList.Builder<Step> steps,
final AndroidTransitiveDependencies transitiveDependencies,
final AndroidDexTransitiveDependencies dexTransitiveDependencies,
ImmutableSet<String> resDirectories,
ImmutableSet<String> nativeLibraryDirectories,
String resourceApkPath,
String unsignedApkPath) {
// Execute preprocess_java_classes_binary, if appropriate.
ImmutableSet<String> classpathEntriesToDex;
if (preprocessJavaClassesBash.isPresent()) {
// Symlink everything in dexTransitiveDependencies.classpathEntriesToDex to the input
// directory. Expect parallel outputs in the output directory and update classpathEntriesToDex
// to reflect that.
final String preprocessJavaClassesInDir = getBinPath("java_classes_preprocess_in_%s");
final String preprocessJavaClassesOutDir = getBinPath("java_classes_preprocess_out_%s");
steps.add(new MakeCleanDirectoryStep(preprocessJavaClassesInDir));
steps.add(new MakeCleanDirectoryStep(preprocessJavaClassesOutDir));
steps.add(new SymlinkFilesIntoDirectoryStep(
Paths.get("."),
dexTransitiveDependencies.classpathEntriesToDex,
Paths.get(preprocessJavaClassesInDir)
));
classpathEntriesToDex = FluentIterable.from(dexTransitiveDependencies.classpathEntriesToDex)
.transform(new Function<String, String>() {
@Override
public String apply(String classpathEntry) {
return Paths.get(preprocessJavaClassesOutDir, classpathEntry).toString();
}
})
.toSet();
AbstractGenruleStep.CommandString commandString = new AbstractGenruleStep.CommandString(
/* cmd */ Optional.<String>absent(),
/* bash */ preprocessJavaClassesBash,
/* cmdExe */ Optional.<String>absent());
steps.add(new AbstractGenruleStep(this, commandString, preprocessJavaClassesDeps) {
@Override
protected void addEnvironmentVariables(
ExecutionContext context,
ImmutableMap.Builder<String, String> environmentVariablesBuilder) {
environmentVariablesBuilder.put("IN_JARS_DIR", preprocessJavaClassesInDir);
environmentVariablesBuilder.put("OUT_JARS_DIR", preprocessJavaClassesOutDir);
}
});
} else {
classpathEntriesToDex = dexTransitiveDependencies.classpathEntriesToDex;
}
// Execute proguard if desired (transforms input classpaths).
if (packageType.isBuildWithObfuscation()) {
classpathEntriesToDex = addProguardCommands(
context,
classpathEntriesToDex,
transitiveDependencies.proguardConfigs,
steps,
resDirectories);
}
// Create the final DEX (or set of DEX files in the case of split dex).
// The APK building command needs to take a directory of raw files, so we create a directory
// that can only contain .dex files from this build rule.
String dexDir = getBinPath(".dex/%s");
steps.add(new MkdirStep(dexDir));
String dexFile = String.format("%s/classes.dex", dexDir);
// Create dex artifacts.
ImmutableSet.Builder<String> secondaryDexDirectoriesBuilder = ImmutableSet.builder();
if (preDexDeps.isEmpty()) {
addDexingSteps(
classpathEntriesToDex,
secondaryDexDirectoriesBuilder,
steps,
dexFile,
context.getSourcePathResolver());
} else {
Iterable<Path> filesToDex = FluentIterable.from(preDexDeps)
.transform(
new Function<IntermediateDexRule, Path>() {
@Override
@Nullable
public Path apply(IntermediateDexRule preDexDep) {
DexProducedFromJavaLibraryThatContainsClassFiles preDex = preDexDep
.getBuildable();
if (preDex.hasOutput()) {
return preDex.getPathToDex();
} else {
return null;
}
}
})
.filter(Predicates.notNull());
// If this APK has Android resources, then the generated R.class files also need to be dexed.
if (dexTransitiveDependencies.pathToCompiledRDotJavaFiles.isPresent()) {
Path pathToCompiledRDotJavaFilesDirectory =
dexTransitiveDependencies.pathToCompiledRDotJavaFiles.get();
filesToDex = Iterables.concat(filesToDex,
Collections.singleton(pathToCompiledRDotJavaFilesDirectory));
}
// This will combine the pre-dexed files and the R.class files into a single classes.dex file.
steps.add(new DxStep(dexFile,
filesToDex,
/* options */ EnumSet.of(DxStep.Option.USE_CUSTOM_DX_IF_AVAILABLE)));
}
ImmutableSet<String> secondaryDexDirectories = secondaryDexDirectoriesBuilder.build();
// Due to limitations of Froyo, we need to ensure that all secondary zip files are STORED in
// the final APK, not DEFLATED. The only way to ensure this with ApkBuilder is to zip up the
// the files properly and then add the zip files to the apk.
ImmutableSet.Builder<String> secondaryDexZips = ImmutableSet.builder();
for (String secondaryDexDirectory : secondaryDexDirectories) {
// String the trailing slash from the directory name and add the zip extension.
String zipFile = secondaryDexDirectory.replaceAll("/$", "") + ".zip";
secondaryDexZips.add(zipFile);
steps.add(new ZipDirectoryWithMaxDeflateStep(secondaryDexDirectory,
zipFile,
FROYO_DEFLATE_LIMIT_BYTES));
}
ApkBuilderStep apkBuilderCommand = new ApkBuilderStep(
resourceApkPath,
unsignedApkPath,
dexFile,
ImmutableSet.<String>of(),
nativeLibraryDirectories,
secondaryDexZips.build(),
false);
steps.add(apkBuilderCommand);
}