in src/com/facebook/buck/android/NonPreDexedDexBuildable.java [282:491]
public ImmutableList<? extends Step> getBuildSteps(
BuildContext buildContext, BuildableContext buildableContext) {
ImmutableList.Builder<Step> steps = ImmutableList.builder();
ImmutableSet<Path> classpathEntriesToDex =
classpathEntriesToDexSourcePaths.stream()
.map(
input ->
getProjectFilesystem()
.relativize(buildContext.getSourcePathResolver().getAbsolutePath(input))
.getPath())
.collect(ImmutableSet.toImmutableSet());
steps.addAll(
MakeCleanDirectoryStep.of(
BuildCellRelativePath.fromCellRelativePath(
buildContext.getBuildCellRootPath(), getProjectFilesystem(), getRootGenPath())));
steps.addAll(
MakeCleanDirectoryStep.of(
BuildCellRelativePath.fromCellRelativePath(
buildContext.getBuildCellRootPath(),
getProjectFilesystem(),
getSecondaryDexRoot())));
ImmutableMultimap<APKModule, Path> additionalDexStoreToJarPathMap =
moduleMappedClasspathEntriesToDex.get().entrySet().stream()
.flatMap(
entry ->
entry.getValue().stream()
.map(
v ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
buildContext.getSourcePathResolver().getAbsolutePath(v))))
.collect(
ImmutableListMultimap.toImmutableListMultimap(e -> e.getKey(), e -> e.getValue()));
// Execute preprocess_java_classes_binary, if appropriate.
if (preprocessJavaClassesBash.isPresent()) {
// Symlink everything in dexTransitiveDependencies.classpathEntriesToDex to the input
// directory.
Path preprocessJavaClassesInDir = getBinPath("java_classes_preprocess_in");
Path preprocessJavaClassesOutDir = getBinPath("java_classes_preprocess_out");
Path ESCAPED_PARENT = getProjectFilesystem().getPath("_.._");
ImmutableList.Builder<Pair<Path, Path>> pathToTargetBuilder = ImmutableList.builder();
ImmutableSet.Builder<Path> outDirPaths = ImmutableSet.builder();
for (Path entry : classpathEntriesToDex) {
// The entries are relative to the current cell root, and may contain '..' to
// reference entries in other roots. To construct the path in InDir, escape '..'
// with a normal directory name, so that the path does not escape InDir.
Path relPath =
RichStream.from(entry)
.map(fragment -> fragment.toString().equals("..") ? ESCAPED_PARENT : fragment)
.reduce(Path::resolve)
.orElse(getProjectFilesystem().getPath(""));
pathToTargetBuilder.add(new Pair<>(preprocessJavaClassesInDir.resolve(relPath), entry));
outDirPaths.add(preprocessJavaClassesOutDir.resolve(relPath));
}
// cell relative path of where the symlink should go, to where the symlink should map to.
ImmutableList<Pair<Path, Path>> pathToTarget = pathToTargetBuilder.build();
// Expect parallel outputs in the output directory and update classpathEntriesToDex
// to reflect that.
classpathEntriesToDex = outDirPaths.build();
steps.addAll(
MakeCleanDirectoryStep.of(
BuildCellRelativePath.fromCellRelativePath(
buildContext.getBuildCellRootPath(),
getProjectFilesystem(),
preprocessJavaClassesInDir)));
steps.addAll(
MakeCleanDirectoryStep.of(
BuildCellRelativePath.fromCellRelativePath(
buildContext.getBuildCellRootPath(),
getProjectFilesystem(),
preprocessJavaClassesOutDir)));
steps.add(
new AbstractExecutionStep("symlinking for preprocessing") {
@Override
public StepExecutionResult execute(ExecutionContext context) throws IOException {
for (Pair<Path, Path> entry : pathToTarget) {
Path symlinkPath = getProjectFilesystem().resolve(entry.getFirst());
Path symlinkTarget = getProjectFilesystem().resolve(entry.getSecond());
java.nio.file.Files.createDirectories(symlinkPath.getParent());
getProjectFilesystem().createSymLink(symlinkPath, symlinkTarget, false);
}
return StepExecutionResults.SUCCESS;
}
});
AbstractGenruleStep.CommandString commandString =
new AbstractGenruleStep.CommandString(
/* cmd */ Optional.empty(),
/* bash */ Arg.flattenToSpaceSeparatedString(
preprocessJavaClassesBash, buildContext.getSourcePathResolver()),
/* cmdExe */ Optional.empty());
steps.add(
new AbstractGenruleStep(
getProjectFilesystem(),
commandString,
getProjectFilesystem().getRootPath().resolve(preprocessJavaClassesInDir)) {
@Override
protected void addEnvironmentVariables(
ExecutionContext context,
ImmutableMap.Builder<String, String> environmentVariablesBuilder) {
environmentVariablesBuilder.put(
"IN_JARS_DIR",
getProjectFilesystem().resolve(preprocessJavaClassesInDir).toString());
environmentVariablesBuilder.put(
"OUT_JARS_DIR",
getProjectFilesystem().resolve(preprocessJavaClassesOutDir).toString());
String bootclasspath =
Joiner.on(':')
.join(
Iterables.transform(
androidPlatformTarget.getBootclasspathEntries(),
getProjectFilesystem()::resolve));
environmentVariablesBuilder.put("ANDROID_BOOTCLASSPATH", bootclasspath);
}
});
}
// Execute proguard if desired (transforms input classpaths).
if (shouldProguard) {
classpathEntriesToDex =
addProguardCommands(
classpathEntriesToDex,
proguardConfigs.stream()
.map(buildContext.getSourcePathResolver()::getAbsolutePath)
.collect(ImmutableSet.toImmutableSet()),
skipProguard,
steps,
buildableContext,
buildContext);
}
// 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 primaryDexPath
// can only contain .dex files from this build rule.
// Create dex artifacts. If split-dex is used, the assets/ directory should contain entries
// that look something like the following:
//
// assets/secondary-program-dex-jars/metadata.txt
// assets/secondary-program-dex-jars/secondary-1.dex.jar
// assets/secondary-program-dex-jars/secondary-2.dex.jar
// assets/secondary-program-dex-jars/secondary-3.dex.jar
//
// The contents of the metadata.txt file should look like:
// secondary-1.dex.jar fffe66877038db3af2cbd0fe2d9231ed5912e317 secondary.dex01.Canary
// secondary-2.dex.jar b218a3ea56c530fed6501d9f9ed918d1210cc658 secondary.dex02.Canary
// secondary-3.dex.jar 40f11878a8f7a278a3f12401c643da0d4a135e1a secondary.dex03.Canary
//
// The scratch directories that contain the metadata.txt and secondary-N.dex.jar files must be
// listed in secondaryDexDirectoriesBuilder so that their contents will be compressed
// appropriately for Froyo.
ImmutableSet.Builder<Path> secondaryDexDirectoriesBuilder = ImmutableSet.builder();
Supplier<ImmutableMap<String, HashCode>> classNamesToHashesSupplier =
addAccumulateClassNamesStep(classpathEntriesToDex, steps);
Path primaryDexPath = getNonPredexedPrimaryDexPath();
steps.add(
MkdirStep.of(
BuildCellRelativePath.fromCellRelativePath(
buildContext.getBuildCellRootPath(),
getProjectFilesystem(),
primaryDexPath.getParent())));
addDexingSteps(
classpathEntriesToDex,
classNamesToHashesSupplier,
path -> {
Path secondaryDexRoot = getSecondaryDexRoot();
Preconditions.checkState(
path.startsWith(secondaryDexRoot),
"Secondary dex directory %s is not a subdirectory of the secondary dex root %s.",
path,
secondaryDexRoot);
secondaryDexDirectoriesBuilder.add(secondaryDexRoot.relativize(path));
},
steps,
primaryDexPath,
dexReorderToolFile,
dexReorderDataDumpFile,
additionalDexStoreToJarPathMap,
buildContext);
steps.add(
new AbstractExecutionStep("writing_secondary_dex_listing") {
@Override
public StepExecutionResult execute(ExecutionContext context) throws IOException {
getProjectFilesystem().mkdirs(getSecondaryDexListing().getParent());
getProjectFilesystem()
.writeLinesToPath(
secondaryDexDirectoriesBuilder.build().stream().map(t -> t.toString())
::iterator,
getSecondaryDexListing());
return StepExecutionResults.SUCCESS;
}
});
buildableContext.recordArtifact(getRootGenPath());
buildableContext.recordArtifact(getSecondaryDexRoot());
return steps.build();
}