void addDexingSteps()

in src/com/facebook/buck/android/NonPreDexedDexBuildable.java [630:903]


  void addDexingSteps(
      Set<Path> classpathEntriesToDex,
      Supplier<ImmutableMap<String, HashCode>> classNamesToHashesSupplier,
      Consumer<Path> secondaryDexDirectoriesConsumer,
      ImmutableList.Builder<Step> steps,
      Path primaryDexPath,
      Optional<SourcePath> dexReorderToolFile,
      Optional<SourcePath> dexReorderDataDumpFile,
      ImmutableMultimap<APKModule, Path> additionalDexStoreToJarPathMap,
      BuildContext buildContext) {
    SourcePathResolverAdapter resolver = buildContext.getSourcePathResolver();
    Supplier<Set<Path>> primaryInputsToDex;
    Optional<Path> secondaryDexDir;
    Optional<Supplier<Multimap<Path, Path>>> secondaryOutputToInputs;
    Path secondaryDexParentDir = getSecondaryDexRoot().resolve("__secondary_dex__/");
    Path additionalDexParentDir = getSecondaryDexRoot().resolve("__additional_dex__/");
    Path additionalDexAssetsDir = additionalDexParentDir.resolve("assets");
    Optional<ImmutableSet<Path>> additionalDexDirs;

    if (shouldSplitDex) {
      Optional<Path> proguardFullConfigFile = Optional.empty();
      Optional<Path> proguardMappingFile = Optional.empty();
      if (shouldProguard) {
        proguardFullConfigFile = Optional.of(getProguardConfigDir().resolve("configuration.txt"));
        proguardMappingFile = Optional.of(getProguardConfigDir().resolve("mapping.txt"));
      }
      // DexLibLoader expects that metadata.txt and secondary jar files are under this dir
      // in assets.

      // Intermediate directory holding the primary split-zip jar.
      Path splitZipDir = getBinPath("__split_zip__");

      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  buildContext.getBuildCellRootPath(), getProjectFilesystem(), splitZipDir)));
      Path primaryJarPath = splitZipDir.resolve("primary.jar");

      Path secondaryJarMetaDirParent = getSecondaryDexRoot().resolve("secondary_meta");
      Path secondaryJarMetaDir =
          secondaryJarMetaDirParent.resolve(AndroidBinary.SECONDARY_DEX_SUBDIR);

      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  buildContext.getBuildCellRootPath(),
                  getProjectFilesystem(),
                  secondaryJarMetaDir)));
      Path secondaryJarMeta = secondaryJarMetaDir.resolve("metadata.txt");

      // Intermediate directory holding _ONLY_ the secondary split-zip jar files.  This is
      // important because SmartDexingCommand will try to dx every entry in this directory.  It
      // does this because it's impossible to know what outputs split-zip will generate until it
      // runs.
      Path secondaryZipDir = getBinPath("__secondary_zip__");

      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  buildContext.getBuildCellRootPath(), getProjectFilesystem(), secondaryZipDir)));

      // Intermediate directory holding the directories holding _ONLY_ the additional split-zip
      // jar files that are intended for that dex store.
      Path additionalDexStoresZipDir = getBinPath("__dex_stores_zip__");

      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  buildContext.getBuildCellRootPath(),
                  getProjectFilesystem(),
                  additionalDexStoresZipDir)));
      for (APKModule dexStore : additionalDexStoreToJarPathMap.keySet()) {

        steps.addAll(
            MakeCleanDirectoryStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    buildContext.getBuildCellRootPath(),
                    getProjectFilesystem(),
                    additionalDexStoresZipDir.resolve(dexStore.getName()))));

        steps.addAll(
            MakeCleanDirectoryStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    buildContext.getBuildCellRootPath(),
                    getProjectFilesystem(),
                    secondaryJarMetaDirParent.resolve("assets").resolve(dexStore.getName()))));
      }

      // Run the split-zip command which is responsible for dividing the large set of input
      // classpaths into a more compact set of jar files such that no one jar file when dexed will
      // yield a dex artifact too large for dexopt or the dx method limit to handle.
      Path zipSplitReportDir = getBinPath("__split_zip_report__");

      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  buildContext.getBuildCellRootPath(), getProjectFilesystem(), zipSplitReportDir)));
      SplitZipStep splitZipCommand =
          new SplitZipStep(
              getProjectFilesystem(),
              classpathEntriesToDex,
              secondaryJarMeta,
              primaryJarPath,
              secondaryZipDir,
              "secondary-%d.jar",
              secondaryJarMetaDirParent,
              additionalDexStoresZipDir,
              proguardFullConfigFile,
              proguardMappingFile,
              skipProguard,
              dexSplitMode,
              dexSplitMode.getPrimaryDexScenarioFile().map(resolver::getAbsolutePath),
              dexSplitMode.getPrimaryDexClassesFile().map(resolver::getAbsolutePath),
              dexSplitMode.getSecondaryDexHeadClassesFile().map(resolver::getAbsolutePath),
              dexSplitMode.getSecondaryDexTailClassesFile().map(resolver::getAbsolutePath),
              additionalDexStoreToJarPathMap,
              apkModuleMap,
              rootAPKModule,
              zipSplitReportDir);
      steps.add(splitZipCommand);

      // Add the secondary dex directory that has yet to be created, but will be by the
      // smart dexing command.  Smart dex will handle "cleaning" this directory properly.
      if (reorderClassesIntraDex) {
        secondaryDexDir =
            Optional.of(secondaryDexParentDir.resolve(SMART_DEX_SECONDARY_DEX_SUBDIR));
        Path intraDexReorderSecondaryDexDir =
            secondaryDexParentDir.resolve(AndroidBinary.SECONDARY_DEX_SUBDIR);

        steps.addAll(
            MakeCleanDirectoryStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    buildContext.getBuildCellRootPath(),
                    getProjectFilesystem(),
                    secondaryDexDir.get())));

        steps.addAll(
            MakeCleanDirectoryStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    buildContext.getBuildCellRootPath(),
                    getProjectFilesystem(),
                    intraDexReorderSecondaryDexDir)));
      } else {
        secondaryDexDir =
            Optional.of(secondaryDexParentDir.resolve(AndroidBinary.SECONDARY_DEX_SUBDIR));
        steps.add(
            MkdirStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    buildContext.getBuildCellRootPath(),
                    getProjectFilesystem(),
                    secondaryDexDir.get())));
      }

      if (additionalDexStoreToJarPathMap.isEmpty()) {
        additionalDexDirs = Optional.empty();
      } else {
        ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
        for (APKModule dexStore : additionalDexStoreToJarPathMap.keySet()) {
          Path dexStorePath = additionalDexAssetsDir.resolve(dexStore.getName());
          builder.add(dexStorePath);

          steps.addAll(
              MakeCleanDirectoryStep.of(
                  BuildCellRelativePath.fromCellRelativePath(
                      buildContext.getBuildCellRootPath(), getProjectFilesystem(), dexStorePath)));
        }
        additionalDexDirs = Optional.of(builder.build());
      }

      if (dexSplitMode.getDexStore() == DexStore.RAW) {
        secondaryDexDirectoriesConsumer.accept(secondaryDexDir.get());
      } else {
        secondaryDexDirectoriesConsumer.accept(secondaryJarMetaDirParent);
        secondaryDexDirectoriesConsumer.accept(secondaryDexParentDir);
      }
      if (additionalDexDirs.isPresent()) {
        secondaryDexDirectoriesConsumer.accept(additionalDexParentDir);
      }

      // Adjust smart-dex inputs for the split-zip case.
      primaryInputsToDex = Suppliers.ofInstance(ImmutableSet.of(primaryJarPath));
      Supplier<Multimap<Path, Path>> secondaryOutputToInputsMap =
          splitZipCommand.getOutputToInputsMapSupplier(
              secondaryDexDir.get(), additionalDexAssetsDir);
      secondaryOutputToInputs = Optional.of(secondaryOutputToInputsMap);
    } else {
      // Simple case where our inputs are the natural classpath directories and we don't have
      // to worry about secondary jar/dex files.
      primaryInputsToDex = Suppliers.ofInstance(classpathEntriesToDex);
      secondaryDexDir = Optional.empty();
      secondaryOutputToInputs = Optional.empty();
    }

    HashInputJarsToDexStep hashInputJarsToDexStep =
        new HashInputJarsToDexStep(
            getProjectFilesystem(),
            primaryInputsToDex,
            secondaryOutputToInputs,
            classNamesToHashesSupplier);
    steps.add(hashInputJarsToDexStep);

    // Stores checksum information from each invocation to intelligently decide when dx needs
    // to be re-run.
    Path successDir = getBinPath("__smart_dex__/.success");
    steps.add(
        MkdirStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                buildContext.getBuildCellRootPath(), getProjectFilesystem(), successDir)));

    // Add the smart dexing tool that is capable of avoiding the external dx invocation(s) if
    // it can be shown that the inputs have not changed.  It also parallelizes dx invocations
    // where applicable.
    //
    // Note that by not specifying the number of threads this command will use it will select an
    // optimal default regardless of the value of --num-threads.  This decision was made with the
    // assumption that --num-threads specifies the threading of build rule execution and does not
    // directly apply to the internal threading/parallelization details of various build commands
    // being executed.  For example, aapt is internally threaded by default when preprocessing
    // images.
    EnumSet<DxStep.Option> dxOptions =
        shouldProguard
            ? EnumSet.of(DxStep.Option.NO_LOCALS)
            : EnumSet.of(DxStep.Option.NO_OPTIMIZE);
    Path selectedPrimaryDexPath = primaryDexPath;
    if (reorderClassesIntraDex) {
      String primaryDexFileName = primaryDexPath.getFileName().toString();
      String smartDexPrimaryDexFileName = "smart-dex-" + primaryDexFileName;
      selectedPrimaryDexPath =
          Paths.get(
              primaryDexPath.toString().replace(primaryDexFileName, smartDexPrimaryDexFileName));
    }
    SmartDexingStep smartDexingCommand =
        new SmartDexingStep(
            androidPlatformTarget,
            buildContext,
            getProjectFilesystem(),
            Optional.of(selectedPrimaryDexPath),
            Optional.of(primaryInputsToDex),
            Optional.empty(),
            secondaryDexDir,
            secondaryOutputToInputs,
            hashInputJarsToDexStep,
            successDir,
            dxOptions,
            dxExecutorService,
            xzCompressionLevel,
            dxMaxHeapSize,
            dexTool,
            desugarInterfaceMethods,
            true,
            Optional.of(
                additionalJarsForProguardAndDesugar.stream()
                    .map(input -> buildContext.getSourcePathResolver().getAbsolutePath(input))
                    .collect(ImmutableSet.toImmutableSet())),
            getBuildTarget(),
            minSdkVersion);
    steps.add(smartDexingCommand);

    if (reorderClassesIntraDex) {
      IntraDexReorderStep intraDexReorderStep =
          new IntraDexReorderStep(
              buildContext,
              getProjectFilesystem(),
              resolver.getAbsolutePath(dexReorderToolFile.get()),
              resolver.getAbsolutePath(dexReorderDataDumpFile.get()),
              getBuildTarget(),
              selectedPrimaryDexPath,
              primaryDexPath,
              secondaryOutputToInputs,
              SMART_DEX_SECONDARY_DEX_SUBDIR,
              AndroidBinary.SECONDARY_DEX_SUBDIR);
      steps.add(intraDexReorderStep);
    }
  }