public AndroidNativeLibsGraphEnhancementResult enhance()

in src/com/facebook/buck/android/AndroidNativeLibsPackageableGraphEnhancer.java [170:412]


  public AndroidNativeLibsGraphEnhancementResult enhance(
      AndroidPackageableCollection packageableCollection) {
    ImmutableMultimap<APKModule, NativeLinkableGroup> nativeLinkableGroups =
        packageableCollection.getNativeLinkables();
    ImmutableMultimap<APKModule, NativeLinkableGroup> nativeLinkableGroupsAssets =
        packageableCollection.getNativeLinkablesAssets();

    // TODO(cjhopman): The linkables handling is much more complex and we probably should split it
    // out into its own function.
    boolean hasLinkables =
        !(nativeLinkableGroups.isEmpty() && nativeLinkableGroupsAssets.isEmpty());
    boolean hasNativeLibDirs =
        !(packageableCollection.getNativeLibsDirectories().isEmpty()
            && packageableCollection.getNativeLibAssetsDirectories().isEmpty()
            && packageableCollection.getNativeLibsDirectoriesForSystemLoader().isEmpty());
    boolean hasNativeCode = hasLinkables || hasNativeLibDirs;

    if (!hasNativeCode) {
      return ImmutableAndroidNativeLibsGraphEnhancementResult.of(
          Optional.empty(),
          Optional.of(ImmutableSortedSet.of()),
          Optional.empty(),
          Optional.empty(),
          Optional.empty());
    }

    ImmutableMap<TargetCpuType, NdkCxxPlatform> nativePlatforms = ImmutableMap.of();

    if (hasLinkables) {
      NdkCxxPlatformsProvider ndkCxxPlatformsProvider =
          toolchainProvider.getByName(
              NdkCxxPlatformsProvider.DEFAULT_NAME,
              originalBuildTarget.getTargetConfiguration(),
              NdkCxxPlatformsProvider.class);

      nativePlatforms = ndkCxxPlatformsProvider.getResolvedNdkCxxPlatforms(graphBuilder);

      if (nativePlatforms.isEmpty()) {
        throw new HumanReadableException(
            "No native platforms detected. Probably Android NDK is not configured properly.");
      }
    }

    Iterable<TargetCpuType> filteredLinkablePlatforms =
        hasLinkables ? getFilteredPlatforms(nativePlatforms, cpuFilters) : ImmutableList.of();

    ImmutableMap.Builder<TargetCpuType, NativeLinkableEnhancementResult> nativeLinkablesBuilder =
        ImmutableMap.builder();

    for (TargetCpuType cpuType : filteredLinkablePlatforms) {
      nativeLinkablesBuilder.put(
          cpuType,
          expandLinkableGroups(
              nativePlatforms.get(cpuType).getCxxPlatform(),
              nativeLinkableGroups,
              nativeLinkableGroupsAssets));
    }

    ImmutableMap<TargetCpuType, NativeLinkableEnhancementResult> nativeLinkables =
        nativeLinkablesBuilder.build();

    ImmutableSortedMap<String, String> sonameMapping = null;
    ImmutableSortedMap<String, ImmutableSortedSet<String>> sharedObjectTargets = null;
    if (nativeLibraryMergeMap.isPresent()
        && !nativeLibraryMergeMap.get().isEmpty()
        && !nativePlatforms.isEmpty()) {
      NativeLibraryMergeEnhancer.NativeLibraryMergeEnhancementResult enhancement =
          NativeLibraryMergeEnhancer.enhance(
              cellPathResolver,
              cxxBuckConfig,
              graphBuilder,
              originalBuildTarget,
              projectFilesystem,
              nativePlatforms,
              nativeLibraryMergeMap.get(),
              nativeLibraryMergeGlue,
              nativeLibraryMergeLocalizedSymbols,
              nativeLinkables);
      nativeLinkables = enhancement.getMergedLinkables();
      sonameMapping = enhancement.getSonameMapping();
      sharedObjectTargets = enhancement.getSharedObjectTargets();
    }

    // Iterate over all the {@link AndroidNativeLinkable}s from the collector and grab the shared
    // libraries for all the {@link TargetCpuType}s that we care about.  We deposit them into a map
    // of CPU type and SONAME to the shared library path, which the {@link CopyNativeLibraries}
    // rule will use to compose the destination name.
    ImmutableMap.Builder<APKModule, CopyNativeLibraries> moduleMappedCopyNativeLibriesBuilder =
        ImmutableMap.builder();

    boolean hasCopyNativeLibraries = false;

    // Make sure we process the root module last so that we know if any of the module contain
    // libraries that depend on a non-system runtime and add it to the root module if needed.
    ImmutableSet<APKModule> apkModules =
        FluentIterable.from(apkModuleGraph.getAPKModules())
            .filter(input -> !input.isRootModule())
            .append(apkModuleGraph.getRootAPKModule())
            .toSet();

    ImmutableMap.Builder<AndroidLinkableMetadata, SourcePath> nativeLinkableLibsBuilder =
        ImmutableMap.builder();
    ImmutableMap.Builder<AndroidLinkableMetadata, SourcePath> nativeLinkableLibsAssetsBuilder =
        ImmutableMap.builder();

    BiConsumer<AndroidLinkableMetadata, BuildTarget> duplicateReporter =
        new BiConsumer<AndroidLinkableMetadata, BuildTarget>() {
          Map<AndroidLinkableMetadata, BuildTarget> nativeLinkableLibsMap = new HashMap<>();

          @Override
          public void accept(AndroidLinkableMetadata metadata, BuildTarget buildTarget) {
            BuildTarget existing = nativeLinkableLibsMap.putIfAbsent(metadata, buildTarget);
            if (existing != null && existing != buildTarget) {
              throw new HumanReadableException(
                  "Two libraries in the dependencies have the same output filename: %s:\n"
                      + "Those libraries are  %s and %s",
                  metadata.getSoName(), buildTarget, existing);
            }
          }
        };

    for (TargetCpuType targetCpuType : filteredLinkablePlatforms) {
      getNativeLinkableMetadata(
          nativeLinkables.get(targetCpuType).getNativeLinkables(),
          nativeLinkableLibsBuilder,
          duplicateReporter,
          targetCpuType);
      getNativeLinkableMetadata(
          nativeLinkables.get(targetCpuType).getNativeLinkableAssets(),
          nativeLinkableLibsAssetsBuilder,
          duplicateReporter,
          targetCpuType);
    }

    // Adds a cxxruntime linkable to the nativeLinkableLibsBuilder for every platform that needs it.
    ImmutableMap<AndroidLinkableMetadata, SourcePath> nativeLinkableLibsAssets =
        nativeLinkableLibsAssetsBuilder.build();
    addCxxRuntimeLinkables(nativePlatforms, nativeLinkableLibsBuilder, nativeLinkableLibsAssets);

    ImmutableMap<AndroidLinkableMetadata, SourcePath> nativeLinkableLibs =
        nativeLinkableLibsBuilder.build();

    if (relinkerMode == RelinkerMode.ENABLED
        && (!nativeLinkableLibs.isEmpty() || !nativeLinkableLibsAssets.isEmpty())) {
      NativeRelinker relinker =
          new NativeRelinker(
              originalBuildTarget,
              projectFilesystem,
              cellPathResolver,
              graphBuilder.getSourcePathResolver(),
              graphBuilder,
              cxxBuckConfig,
              nativePlatforms,
              nativeLinkableLibs,
              nativeLinkableLibsAssets,
              relinkerWhitelist);

      nativeLinkableLibs = relinker.getRelinkedLibs();
      nativeLinkableLibsAssets = relinker.getRelinkedLibsAssets();
      for (BuildRule rule : relinker.getRules()) {
        graphBuilder.addToIndex(rule);
      }
    }

    ImmutableMap<StripLinkable, CopyNativeLibraries.StrippedObjectDescription> strippedLibsMap =
        generateStripRules(nativePlatforms, nativeLinkableLibs);
    ImmutableMap<StripLinkable, CopyNativeLibraries.StrippedObjectDescription>
        strippedLibsAssetsMap = generateStripRules(nativePlatforms, nativeLinkableLibsAssets);

    ImmutableSortedSet<SourcePath> unstrippedLibraries =
        RichStream.from(nativeLinkableLibs.values())
            .concat(nativeLinkableLibsAssets.values().stream())
            .toImmutableSortedSet(Ordering.natural());

    Optional<CopyNativeLibraries> nativeLibrariesForPrimaryApk = Optional.empty();

    for (APKModule module : apkModules) {
      ImmutableMap<StripLinkable, CopyNativeLibraries.StrippedObjectDescription>
          filteredStrippedLibsMap =
              ImmutableMap.copyOf(
                  FluentIterable.from(strippedLibsMap.entrySet())
                      .filter(entry -> module.equals(entry.getValue().getApkModule())));

      ImmutableMap<StripLinkable, CopyNativeLibraries.StrippedObjectDescription>
          filteredStrippedLibsAssetsMap =
              ImmutableMap.copyOf(
                  FluentIterable.from(strippedLibsAssetsMap.entrySet())
                      .filter(entry -> module.equals(entry.getValue().getApkModule())));

      ImmutableCollection<SourcePath> nativeLibsDirectories =
          packageableCollection.getNativeLibsDirectories().get(module);

      ImmutableCollection<SourcePath> nativeLibsAssetsDirectories =
          packageableCollection.getNativeLibAssetsDirectories().get(module);

      // Handle native libs which need to go into the primary APK and skip exopackage/optimization
      ImmutableCollection<SourcePath> nativeLibsDirectoriesForPrimaryDexModule =
          module.isRootModule()
              ? packageableCollection.getNativeLibsDirectoriesForSystemLoader()
              : ImmutableList.of();
      if (module.isRootModule() && !nativeLibsDirectoriesForPrimaryDexModule.isEmpty()) {
        nativeLibrariesForPrimaryApk =
            Optional.of(
                createCopyNativeLibraries(
                    module,
                    ImmutableMap.of(),
                    ImmutableMap.of(),
                    nativeLibsDirectoriesForPrimaryDexModule,
                    ImmutableList.of(),
                    "_for_primary_apk"));
      }

      if (filteredStrippedLibsMap.isEmpty()
          && filteredStrippedLibsAssetsMap.isEmpty()
          && nativeLibsDirectories.isEmpty()
          && nativeLibsAssetsDirectories.isEmpty()) {
        continue;
      }

      moduleMappedCopyNativeLibriesBuilder.put(
          module,
          createCopyNativeLibraries(
              module,
              filteredStrippedLibsMap,
              filteredStrippedLibsAssetsMap,
              nativeLibsDirectories,
              nativeLibsAssetsDirectories,
              ""));
      hasCopyNativeLibraries = true;
    }

    Optional<ImmutableMap<APKModule, CopyNativeLibraries>> copyNativeLibraries =
        hasCopyNativeLibraries
            ? Optional.of(moduleMappedCopyNativeLibriesBuilder.build())
            : Optional.empty();

    return ImmutableAndroidNativeLibsGraphEnhancementResult.of(
        copyNativeLibraries,
        Optional.of(unstrippedLibraries),
        Optional.ofNullable(sonameMapping),
        Optional.ofNullable(sharedObjectTargets),
        nativeLibrariesForPrimaryApk);
  }