public ImmutableMultimap execute()

in src/com/facebook/buck/android/dalvik/DalvikAwareZipSplitter.java [168:305]


  public ImmutableMultimap<APKModule, Path> execute() throws IOException {
    ClasspathTraverser classpathTraverser = new DefaultClasspathTraverser();
    Set<String> secondaryTail = new HashSet<String>();

    // Start out by writing the primary zip and recording which entries were added to it.
    primaryOut = newZipOutput(outPrimary);
    secondaryDexWriter.reset();

    ImmutableMap.Builder<String, FileLike> entriesBuilder = ImmutableMap.builder();
    List<String> additionalDexStoreEntries = new ArrayList<>();

    // Iterate over all of the inFiles and add all entries that match the requiredInPrimaryZip
    // predicate.
    LOG.verbose("Traversing classpath for primary zip");

    classpathTraverser.traverse(
        new ClasspathTraversal(inFiles, filesystem) {
          @Override
          public void visit(FileLike entry) throws IOException {
            LOG.verbose("Visiting " + entry.getRelativePath());

            String relativePath = entry.getRelativePath();
            if (!relativePath.endsWith(".class")) {
              // We don't need resources in dex jars, so just drop them.
              return;
            }
            String classPath = relativePath.replaceAll("\\.class$", "");

            Objects.requireNonNull(primaryOut);
            Objects.requireNonNull(classPathToDexStore);

            if (requiredInPrimaryZip.test(relativePath)) {
              primaryOut.putEntry(entry);
            } else if (wantedInPrimaryZip.contains(relativePath)
                || (secondaryHeadSet != null && secondaryHeadSet.contains(relativePath))) {
              entriesBuilder.put(relativePath, new BufferedFileLike(entry));
            } else if (secondaryTailSet != null && secondaryTailSet.contains(relativePath)) {
              entriesBuilder.put(relativePath, new BufferedFileLike(entry));
              secondaryTail.add(relativePath);
            } else {
              ImmutableCollection<APKModule> containingModule = classPathToDexStore.get(classPath);
              if (!containingModule.isEmpty()) {
                if (containingModule.size() > 1) {
                  throw new IllegalStateException(
                      String.format(
                          "classpath %s is contained in multiple dex stores: %s",
                          classPath, classPathToDexStore.get(classPath).asList().toString()));
                }
                APKModule dexStore = containingModule.iterator().next();
                if (!dexStore.equals(rootModule)) {
                  MySecondaryDexHelper dexHelper = additionalDexWriters.get(dexStore);
                  Objects.requireNonNull(dexHelper);
                  dexHelper.getOutputToWriteTo(entry).putEntry(entry);
                  additionalDexStoreEntries.add(relativePath);
                }
              }
            }
          }
        });

    // Put as many of the items wanted in the primary dex as we can into the primary dex.
    ImmutableMap<String, FileLike> entries = entriesBuilder.build();
    for (String wanted : wantedInPrimaryZip) {
      FileLike entry = entries.get(wanted);
      if ((entry != null) && !primaryOut.containsEntry(entry) && primaryOut.canPutEntry(entry)) {
        primaryOut.putEntry(entry);
      }
    }

    if (secondaryHeadSet != null) {
      for (String head : secondaryHeadSet) {
        FileLike headEntry = entries.get(head);
        if ((headEntry != null) && !primaryOut.containsEntry(headEntry)) {
          secondaryDexWriter.getOutputToWriteTo(headEntry).putEntry(headEntry);
        }
      }
    }

    LOG.verbose("Traversing classpath for secondary zip");

    // Now that all of the required entries have been added to the primary zip, fill the rest of
    // the zip up with the remaining entries.
    classpathTraverser.traverse(
        new ClasspathTraversal(inFiles, filesystem) {
          @Override
          public void visit(FileLike entry) throws IOException {
            Objects.requireNonNull(primaryOut);
            String relativePath = entry.getRelativePath();

            // skip if it is the primary dex, is part of a modular dex store, or is not a class file
            if (primaryOut.containsEntry(entry)
                || additionalDexStoreEntries.contains(relativePath)) {
              return;
            }

            LOG.verbose("Visiting " + entry.getRelativePath());

            // Even if we have started writing a secondary dex, we still check if there is any
            // leftover
            // room in the primary dex for the current entry in the traversal.
            if (dexSplitStrategy == DexSplitStrategy.MAXIMIZE_PRIMARY_DEX_SIZE
                && primaryOut.canPutEntry(entry)) {
              primaryOut.putEntry(entry);
            } else {
              if (secondaryHeadSet != null && secondaryHeadSet.contains(relativePath)) {
                return;
              }
              if (secondaryTail.contains(relativePath)) {
                return;
              }
              secondaryDexWriter.getOutputToWriteTo(entry).putEntry(entry);
            }
          }
        });
    if (secondaryTailSet != null) {
      for (String tail : secondaryTailSet) {
        FileLike tailEntry = entries.get(tail);
        if ((tailEntry != null)
            && !primaryOut.containsEntry(tailEntry)
            && secondaryTail.contains(tail)) {
          secondaryDexWriter.getOutputToWriteTo(tailEntry).putEntry(tailEntry);
        }
      }
    }
    primaryOut.close();
    secondaryDexWriter.close();

    ImmutableMultimap.Builder<APKModule, Path> outputFilesBuilder = ImmutableMultimap.builder();
    APKModule secondaryDexStore = rootModule;
    outputFilesBuilder.putAll(secondaryDexStore, secondaryDexWriter.getFiles());
    for (Map.Entry<APKModule, MySecondaryDexHelper> entry : additionalDexWriters.entrySet()) {
      if (!entry.getKey().equals(secondaryDexStore)) {
        entry.getValue().close();
        outputFilesBuilder.putAll(entry.getKey(), entry.getValue().getFiles());
      }
    }
    return outputFilesBuilder.build();
  }