public ImmutableList getBuildSteps()

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();
  }