/* * Copyright 2013-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.android; import com.facebook.buck.android.AndroidBinary.ExopackageMode; import com.facebook.buck.android.AndroidBinary.PackageType; import com.facebook.buck.android.AndroidBinary.RelinkerMode; import com.facebook.buck.android.FilterResourcesStep.ResourceFilter; import com.facebook.buck.android.NdkCxxPlatforms.TargetCpuType; import com.facebook.buck.android.ResourcesFilter.ResourceCompressionMode; import com.facebook.buck.android.aapt.RDotTxtEntry.RType; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.jvm.java.DefaultJavaLibrary; import com.facebook.buck.jvm.java.JavaBuckConfig; import com.facebook.buck.jvm.java.JavaLibrary; import com.facebook.buck.jvm.java.Javac; import com.facebook.buck.jvm.java.JavacOptions; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildRules; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.coercer.BuildConfigFields; import com.facebook.buck.rules.coercer.ManifestEntries; import com.facebook.buck.util.MoreCollectors; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListeningExecutorService; import java.nio.file.Path; import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; public class AndroidBinaryGraphEnhancer { public static final Flavor DEX_FLAVOR = InternalFlavor.of("dex"); public static final Flavor DEX_MERGE_FLAVOR = InternalFlavor.of("dex_merge"); private static final Flavor CALCULATE_ABI_FLAVOR = InternalFlavor.of("calculate_exopackage_abi"); private static final Flavor TRIM_UBER_R_DOT_JAVA_FLAVOR = InternalFlavor.of("trim_uber_r_dot_java"); private static final Flavor COMPILE_UBER_R_DOT_JAVA_FLAVOR = InternalFlavor.of("compile_uber_r_dot_java"); private static final Flavor DEX_UBER_R_DOT_JAVA_FLAVOR = InternalFlavor.of("dex_uber_r_dot_java"); private static final Flavor GENERATE_NATIVE_LIB_MERGE_MAP_GENERATED_CODE_FLAVOR = InternalFlavor.of("generate_native_lib_merge_map_generated_code"); private static final Flavor COMPILE_NATIVE_LIB_MERGE_MAP_GENERATED_CODE_FLAVOR = InternalFlavor.of("compile_native_lib_merge_map_generated_code"); public static final Flavor NATIVE_LIBRARY_PROGUARD_FLAVOR = InternalFlavor.of("generate_proguard_config_from_native_libs"); private final BuildTarget originalBuildTarget; private final ImmutableSortedSet<BuildRule> originalDeps; private final BuildRuleParams buildRuleParams; private final boolean trimResourceIds; private final Optional<String> keepResourcePattern; private final Optional<BuildTarget> nativeLibraryMergeCodeGenerator; private final BuildRuleResolver ruleResolver; private final SourcePathRuleFinder ruleFinder; private final PackageType packageType; private final boolean shouldPreDex; private final Path primaryDexPath; private final DexSplitMode dexSplitMode; private final ImmutableSet<BuildTarget> buildTargetsToExcludeFromDex; private final ImmutableSet<BuildTarget> resourcesToExclude; private final JavaBuckConfig javaBuckConfig; private final Javac javac; private final JavacOptions javacOptions; private final EnumSet<ExopackageMode> exopackageModes; private final BuildConfigFields buildConfigValues; private final Optional<SourcePath> buildConfigValuesFile; private final Optional<Integer> xzCompressionLevel; private final AndroidNativeLibsPackageableGraphEnhancer nativeLibsEnhancer; private final APKModuleGraph apkModuleGraph; private final Optional<BuildTarget> nativeLibraryProguardConfigGenerator; private final ListeningExecutorService dxExecutorService; private final DxConfig dxConfig; private final AndroidBinaryResourcesGraphEnhancer androidBinaryResourcesGraphEnhancer; AndroidBinaryGraphEnhancer( BuildRuleParams originalParams, BuildRuleResolver ruleResolver, AndroidBinary.AaptMode aaptMode, ResourceCompressionMode resourceCompressionMode, ResourceFilter resourcesFilter, EnumSet<RType> bannedDuplicateResourceTypes, Optional<String> resourceUnionPackage, ImmutableSet<String> locales, SourcePath manifest, PackageType packageType, ImmutableSet<TargetCpuType> cpuFilters, boolean shouldBuildStringSourceMap, boolean shouldPreDex, Path primaryDexPath, DexSplitMode dexSplitMode, ImmutableSet<BuildTarget> buildTargetsToExcludeFromDex, ImmutableSet<BuildTarget> resourcesToExclude, boolean skipCrunchPngs, boolean includesVectorDrawables, JavaBuckConfig javaBuckConfig, Javac javac, JavacOptions javacOptions, EnumSet<ExopackageMode> exopackageModes, BuildConfigFields buildConfigValues, Optional<SourcePath> buildConfigValuesFile, Optional<Integer> xzCompressionLevel, boolean trimResourceIds, Optional<String> keepResourcePattern, ImmutableMap<TargetCpuType, NdkCxxPlatform> nativePlatforms, Optional<Map<String, List<Pattern>>> nativeLibraryMergeMap, Optional<BuildTarget> nativeLibraryMergeGlue, Optional<BuildTarget> nativeLibraryMergeCodeGenerator, Optional<ImmutableSortedSet<String>> nativeLibraryMergeLocalizedSymbols, Optional<BuildTarget> nativeLibraryProguardConfigGenerator, RelinkerMode relinkerMode, ListeningExecutorService dxExecutorService, ManifestEntries manifestEntries, CxxBuckConfig cxxBuckConfig, APKModuleGraph apkModuleGraph, DxConfig dxConfig, Optional<Arg> postFilterResourcesCmd) { this.buildRuleParams = originalParams; this.originalBuildTarget = originalParams.getBuildTarget(); this.originalDeps = originalParams.getBuildDeps(); this.ruleResolver = ruleResolver; this.ruleFinder = new SourcePathRuleFinder(ruleResolver); this.packageType = packageType; this.shouldPreDex = shouldPreDex; this.primaryDexPath = primaryDexPath; this.dexSplitMode = dexSplitMode; this.buildTargetsToExcludeFromDex = buildTargetsToExcludeFromDex; this.resourcesToExclude = resourcesToExclude; this.javaBuckConfig = javaBuckConfig; this.javac = javac; this.javacOptions = javacOptions; this.exopackageModes = exopackageModes; this.buildConfigValues = buildConfigValues; this.buildConfigValuesFile = buildConfigValuesFile; this.dxExecutorService = dxExecutorService; this.xzCompressionLevel = xzCompressionLevel; this.trimResourceIds = trimResourceIds; this.keepResourcePattern = keepResourcePattern; this.nativeLibraryMergeCodeGenerator = nativeLibraryMergeCodeGenerator; this.nativeLibraryProguardConfigGenerator = nativeLibraryProguardConfigGenerator; this.nativeLibsEnhancer = new AndroidNativeLibsPackageableGraphEnhancer( ruleResolver, originalParams, nativePlatforms, cpuFilters, cxxBuckConfig, nativeLibraryMergeMap, nativeLibraryMergeGlue, nativeLibraryMergeLocalizedSymbols, relinkerMode, apkModuleGraph); this.androidBinaryResourcesGraphEnhancer = new AndroidBinaryResourcesGraphEnhancer( buildRuleParams, ruleResolver, originalBuildTarget, ExopackageMode.enabledForResources(exopackageModes), manifest, aaptMode, resourcesFilter, resourceCompressionMode, locales, resourceUnionPackage, shouldBuildStringSourceMap, skipCrunchPngs, includesVectorDrawables, bannedDuplicateResourceTypes, manifestEntries, postFilterResourcesCmd); this.apkModuleGraph = apkModuleGraph; this.dxConfig = dxConfig; } AndroidGraphEnhancementResult createAdditionalBuildables() throws NoSuchBuildTargetException { ImmutableSortedSet.Builder<BuildRule> enhancedDeps = ImmutableSortedSet.naturalOrder(); enhancedDeps.addAll(originalDeps); ImmutableList.Builder<BuildRule> additionalJavaLibrariesBuilder = ImmutableList.builder(); AndroidPackageableCollector collector = new AndroidPackageableCollector( originalBuildTarget, buildTargetsToExcludeFromDex, resourcesToExclude, apkModuleGraph); collector.addPackageables(AndroidPackageableCollector.getPackageableRules(originalDeps)); AndroidPackageableCollection packageableCollection = collector.build(); ImmutableList.Builder<SourcePath> proguardConfigsBuilder = ImmutableList.builder(); proguardConfigsBuilder.addAll(packageableCollection.getProguardConfigs()); AndroidNativeLibsGraphEnhancementResult nativeLibsEnhancementResult = nativeLibsEnhancer.enhance(packageableCollection); Optional<ImmutableMap<APKModule, CopyNativeLibraries>> copyNativeLibraries = nativeLibsEnhancementResult.getCopyNativeLibraries(); if (copyNativeLibraries.isPresent()) { ruleResolver.addAllToIndex(copyNativeLibraries.get().values()); enhancedDeps.addAll(copyNativeLibraries.get().values()); } if (nativeLibraryProguardConfigGenerator.isPresent() && packageType.isBuildWithObfuscation()) { NativeLibraryProguardGenerator nativeLibraryProguardGenerator = createNativeLibraryProguardGenerator( copyNativeLibraries .get() .values() .stream() .map(CopyNativeLibraries::getSourcePathToAllLibsDir) .collect(MoreCollectors.toImmutableList())); ruleResolver.addToIndex(nativeLibraryProguardGenerator); enhancedDeps.add(nativeLibraryProguardGenerator); proguardConfigsBuilder.add(nativeLibraryProguardGenerator.getSourcePathToOutput()); } Optional<ImmutableSortedMap<String, String>> sonameMergeMap = nativeLibsEnhancementResult.getSonameMergeMap(); if (sonameMergeMap.isPresent() && nativeLibraryMergeCodeGenerator.isPresent()) { BuildRule generatorRule = ruleResolver.getRule(nativeLibraryMergeCodeGenerator.get()); GenerateCodeForMergedLibraryMap generateCodeForMergedLibraryMap = new GenerateCodeForMergedLibraryMap( buildRuleParams .withAppendedFlavor(GENERATE_NATIVE_LIB_MERGE_MAP_GENERATED_CODE_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of(generatorRule)), Suppliers.ofInstance(ImmutableSortedSet.of())), sonameMergeMap.get(), generatorRule); ruleResolver.addToIndex(generateCodeForMergedLibraryMap); BuildRuleParams paramsForCompileGenCode = buildRuleParams .withAppendedFlavor(COMPILE_NATIVE_LIB_MERGE_MAP_GENERATED_CODE_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of(generateCodeForMergedLibraryMap)), Suppliers.ofInstance(ImmutableSortedSet.of())); DefaultJavaLibrary compileMergedNativeLibMapGenCode = DefaultJavaLibrary.builder(paramsForCompileGenCode, ruleResolver, javaBuckConfig) // Kind of a hack: override language level to 7 to allow string switch. // This can be removed once no one who uses this feature sets the level // to 6 in their .buckconfig. .setJavacOptions(javacOptions.withSourceLevel("7").withTargetLevel("7")) .setSrcs( ImmutableSortedSet.of(generateCodeForMergedLibraryMap.getSourcePathToOutput())) .setGeneratedSourceFolder(javacOptions.getGeneratedSourceFolderName()) .build(); ruleResolver.addToIndex(compileMergedNativeLibMapGenCode); additionalJavaLibrariesBuilder.add(compileMergedNativeLibMapGenCode); enhancedDeps.add(compileMergedNativeLibMapGenCode); } AndroidBinaryResourcesGraphEnhancementResult resourcesEnhancementResult = androidBinaryResourcesGraphEnhancer.enhance(packageableCollection); enhancedDeps.addAll(resourcesEnhancementResult.getEnhancedDeps()); // BuildConfig deps should not be added for instrumented APKs because BuildConfig.class has // already been added to the APK under test. if (packageType != PackageType.INSTRUMENTED) { ImmutableSortedSet<JavaLibrary> buildConfigDepsRules = addBuildConfigDeps( buildRuleParams, packageType, exopackageModes, buildConfigValues, buildConfigValuesFile, ruleResolver, javac, javacOptions, packageableCollection); enhancedDeps.addAll(buildConfigDepsRules); additionalJavaLibrariesBuilder.addAll(buildConfigDepsRules); } ImmutableList<BuildRule> additionalJavaLibraries = additionalJavaLibrariesBuilder.build(); ImmutableMultimap<APKModule, DexProducedFromJavaLibrary> preDexedLibraries = ImmutableMultimap.of(); if (shouldPreDex) { preDexedLibraries = createPreDexRulesForLibraries( // TODO(dreiss): Put R.java here. additionalJavaLibraries, packageableCollection); } // Create rule to trim uber R.java sources. Collection<DexProducedFromJavaLibrary> preDexedLibrariesForResourceIdFiltering = trimResourceIds ? preDexedLibraries.values() : ImmutableList.of(); BuildRuleParams paramsForTrimUberRDotJava = buildRuleParams .withAppendedFlavor(TRIM_UBER_R_DOT_JAVA_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll( ruleFinder.filterBuildRuleInputs( resourcesEnhancementResult.getRDotJavaDir().orElse(null))) .addAll(preDexedLibrariesForResourceIdFiltering) .build()), Suppliers.ofInstance(ImmutableSortedSet.of())); TrimUberRDotJava trimUberRDotJava = new TrimUberRDotJava( paramsForTrimUberRDotJava, resourcesEnhancementResult.getRDotJavaDir(), preDexedLibrariesForResourceIdFiltering, keepResourcePattern); ruleResolver.addToIndex(trimUberRDotJava); // Create rule to compile uber R.java sources. BuildRuleParams paramsForCompileUberRDotJava = buildRuleParams .withAppendedFlavor(COMPILE_UBER_R_DOT_JAVA_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of(trimUberRDotJava)), Suppliers.ofInstance(ImmutableSortedSet.of())); JavaLibrary compileUberRDotJava = DefaultJavaLibrary.builder(paramsForCompileUberRDotJava, ruleResolver, javaBuckConfig) .setJavacOptions(javacOptions.withSourceLevel("7").withTargetLevel("7")) .setSrcs(ImmutableSortedSet.of(trimUberRDotJava.getSourcePathToOutput())) .setGeneratedSourceFolder(javacOptions.getGeneratedSourceFolderName()) .build(); ruleResolver.addToIndex(compileUberRDotJava); // Create rule to dex uber R.java sources. BuildRuleParams paramsForDexUberRDotJava = buildRuleParams .withAppendedFlavor(DEX_UBER_R_DOT_JAVA_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of(compileUberRDotJava)), Suppliers.ofInstance(ImmutableSortedSet.of())); DexProducedFromJavaLibrary dexUberRDotJava = new DexProducedFromJavaLibrary(paramsForDexUberRDotJava, compileUberRDotJava); ruleResolver.addToIndex(dexUberRDotJava); Optional<PreDexMerge> preDexMerge = Optional.empty(); if (shouldPreDex) { preDexMerge = Optional.of(createPreDexMergeRule(preDexedLibraries, dexUberRDotJava)); enhancedDeps.add(preDexMerge.get()); } else { enhancedDeps.addAll(getTargetsAsRules(packageableCollection.getJavaLibrariesToDex())); // If not pre-dexing, AndroidBinary needs to ProGuard and/or dex the compiled R.java. enhancedDeps.add(compileUberRDotJava); } // Add dependencies on all the build rules generating third-party JARs. This is mainly to // correctly capture deps when a prebuilt_jar forwards the output from another build rule. enhancedDeps.addAll( ruleFinder.filterBuildRuleInputs(packageableCollection.getPathsToThirdPartyJars())); Optional<ComputeExopackageDepsAbi> computeExopackageDepsAbi = Optional.empty(); if (!exopackageModes.isEmpty()) { BuildRuleParams paramsForComputeExopackageAbi = buildRuleParams .withAppendedFlavor(CALCULATE_ABI_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(enhancedDeps.build()), Suppliers.ofInstance(ImmutableSortedSet.of())); computeExopackageDepsAbi = Optional.of( new ComputeExopackageDepsAbi( paramsForComputeExopackageAbi, exopackageModes, packageableCollection, copyNativeLibraries, preDexMerge)); ruleResolver.addToIndex(computeExopackageDepsAbi.get()); enhancedDeps.add(computeExopackageDepsAbi.get()); } return AndroidGraphEnhancementResult.builder() .setPackageableCollection(packageableCollection) .setPrimaryResourcesApkPath(resourcesEnhancementResult.getPrimaryResourcesApkPath()) .setPrimaryApkAssetZips(resourcesEnhancementResult.getPrimaryApkAssetZips()) .setExoResources(resourcesEnhancementResult.getExoResources()) .setAndroidManifestPath(resourcesEnhancementResult.getAndroidManifestXml()) .setSourcePathToAaptGeneratedProguardConfigFile( resourcesEnhancementResult.getAaptGeneratedProguardConfigFile()) .setProguardConfigs(proguardConfigsBuilder.build()) .setCompiledUberRDotJava(compileUberRDotJava) .setCopyNativeLibraries(copyNativeLibraries) .setPackageStringAssets(resourcesEnhancementResult.getPackageStringAssets()) .setPreDexMerge(preDexMerge) .setComputeExopackageDepsAbi(computeExopackageDepsAbi) .setClasspathEntriesToDex( ImmutableSet.<SourcePath>builder() .addAll(packageableCollection.getClasspathEntriesToDex()) .addAll( additionalJavaLibraries .stream() .map(BuildRule::getSourcePathToOutput) .collect(MoreCollectors.toImmutableList())) .build()) .setFinalDeps(enhancedDeps.build()) .setAPKModuleGraph(apkModuleGraph) .build(); } private NativeLibraryProguardGenerator createNativeLibraryProguardGenerator( ImmutableList<SourcePath> nativeLibsDirs) throws NoSuchBuildTargetException { BuildRuleParams paramsForNativeLibraryProguardGenerator = buildRuleParams .withAppendedFlavor(NATIVE_LIBRARY_PROGUARD_FLAVOR) .copyReplacingDeclaredAndExtraDeps(ImmutableSortedSet::of, ImmutableSortedSet::of); return new NativeLibraryProguardGenerator( paramsForNativeLibraryProguardGenerator, ruleFinder, nativeLibsDirs, ruleResolver.requireRule(nativeLibraryProguardConfigGenerator.get())); } /** * If the user specified any android_build_config() rules, then we must add some build rules to * generate the production {@code BuildConfig.class} files and ensure that they are included in * the list of {@link AndroidPackageableCollection#getClasspathEntriesToDex}. */ public static ImmutableSortedSet<JavaLibrary> addBuildConfigDeps( BuildRuleParams originalParams, PackageType packageType, EnumSet<ExopackageMode> exopackageModes, BuildConfigFields buildConfigValues, Optional<SourcePath> buildConfigValuesFile, BuildRuleResolver ruleResolver, Javac javac, JavacOptions javacOptions, AndroidPackageableCollection packageableCollection) throws NoSuchBuildTargetException { ImmutableSortedSet.Builder<JavaLibrary> result = ImmutableSortedSet.naturalOrder(); BuildConfigFields buildConfigConstants = BuildConfigFields.fromFields( ImmutableList.of( BuildConfigFields.Field.of( "boolean", BuildConfigs.DEBUG_CONSTANT, String.valueOf(packageType != AndroidBinary.PackageType.RELEASE)), BuildConfigFields.Field.of( "boolean", BuildConfigs.IS_EXO_CONSTANT, String.valueOf(!exopackageModes.isEmpty())), BuildConfigFields.Field.of( "int", BuildConfigs.EXOPACKAGE_FLAGS, String.valueOf(ExopackageMode.toBitmask(exopackageModes))))); for (Map.Entry<String, BuildConfigFields> entry : packageableCollection.getBuildConfigs().entrySet()) { // Merge the user-defined constants with the APK-specific overrides. BuildConfigFields totalBuildConfigValues = BuildConfigFields.empty() .putAll(entry.getValue()) .putAll(buildConfigValues) .putAll(buildConfigConstants); // Each enhanced dep needs a unique build target, so we parameterize the build target by the // Java package. String javaPackage = entry.getKey(); Flavor flavor = InternalFlavor.of("buildconfig_" + javaPackage.replace('.', '_')); BuildTarget buildTargetWithFlavors = BuildTarget.builder(originalParams.getBuildTarget()).addFlavors(flavor).build(); BuildRuleParams buildConfigParams = new BuildRuleParams( buildTargetWithFlavors, /* declaredDeps */ Suppliers.ofInstance(ImmutableSortedSet.of()), /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.of()), ImmutableSortedSet.of(), originalParams.getProjectFilesystem()); JavaLibrary buildConfigJavaLibrary = AndroidBuildConfigDescription.createBuildRule( buildConfigParams, javaPackage, totalBuildConfigValues, buildConfigValuesFile, /* useConstantExpressions */ true, javac, javacOptions, ruleResolver); ruleResolver.addToIndex(buildConfigJavaLibrary); Preconditions.checkNotNull( buildConfigJavaLibrary.getSourcePathToOutput(), "%s must have an output file.", buildConfigJavaLibrary); result.add(buildConfigJavaLibrary); } return result.build(); } /** * Creates/finds the set of build rules that correspond to pre-dex'd artifacts that should be * merged to create the final classes.dex for the APK. * * <p>This method may modify {@code ruleResolver}, inserting new rules into its index. */ @VisibleForTesting PreDexMerge createPreDexMergeRule( ImmutableMultimap<APKModule, DexProducedFromJavaLibrary> allPreDexDeps, DexProducedFromJavaLibrary dexForUberRDotJava) { BuildRuleParams paramsForPreDexMerge = buildRuleParams .withAppendedFlavor(DEX_MERGE_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll( getDexMergeDeps( dexForUberRDotJava, ImmutableSet.copyOf(allPreDexDeps.values()))) .build()), Suppliers.ofInstance(ImmutableSortedSet.of())); PreDexMerge preDexMerge = new PreDexMerge( paramsForPreDexMerge, primaryDexPath, dexSplitMode, apkModuleGraph, allPreDexDeps, dexForUberRDotJava, dxExecutorService, xzCompressionLevel, dxConfig.getDxMaxHeapSize()); ruleResolver.addToIndex(preDexMerge); return preDexMerge; } @VisibleForTesting ImmutableMultimap<APKModule, DexProducedFromJavaLibrary> createPreDexRulesForLibraries( Iterable<BuildRule> additionalJavaLibrariesToDex, AndroidPackageableCollection packageableCollection) { Iterable<BuildTarget> additionalJavaLibraryTargets = FluentIterable.from(additionalJavaLibrariesToDex).transform(BuildRule::getBuildTarget); ImmutableMultimap.Builder<APKModule, DexProducedFromJavaLibrary> preDexDeps = ImmutableMultimap.builder(); for (BuildTarget buildTarget : Iterables.concat( packageableCollection.getJavaLibrariesToDex(), additionalJavaLibraryTargets)) { Preconditions.checkState( !buildTargetsToExcludeFromDex.contains(buildTarget), "JavaLibrary should have been excluded from target to dex: %s", buildTarget); BuildRule libraryRule = ruleResolver.getRule(buildTarget); Preconditions.checkState(libraryRule instanceof JavaLibrary); JavaLibrary javaLibrary = (JavaLibrary) libraryRule; // If the rule has no output file (which happens when a java_library has no srcs or // resources, but export_deps is true), then there will not be anything to dx. if (javaLibrary.getSourcePathToOutput() == null) { continue; } // See whether the corresponding IntermediateDexRule has already been added to the // ruleResolver. BuildTarget originalTarget = javaLibrary.getBuildTarget(); BuildTarget preDexTarget = BuildTarget.builder(originalTarget).addFlavors(DEX_FLAVOR).build(); Optional<BuildRule> preDexRule = ruleResolver.getRuleOptional(preDexTarget); if (preDexRule.isPresent()) { preDexDeps.put( apkModuleGraph.findModuleForTarget(buildTarget), (DexProducedFromJavaLibrary) preDexRule.get()); continue; } // Create the IntermediateDexRule and add it to both the ruleResolver and preDexDeps. BuildRuleParams paramsForPreDex = buildRuleParams .withBuildTarget(preDexTarget) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.of(ruleResolver.getRule(javaLibrary.getBuildTarget()))), Suppliers.ofInstance(ImmutableSortedSet.of())); DexProducedFromJavaLibrary preDex = new DexProducedFromJavaLibrary(paramsForPreDex, javaLibrary); ruleResolver.addToIndex(preDex); preDexDeps.put(apkModuleGraph.findModuleForTarget(buildTarget), preDex); } return preDexDeps.build(); } private ImmutableSortedSet<BuildRule> getDexMergeDeps( DexProducedFromJavaLibrary dexForUberRDotJava, ImmutableSet<DexProducedFromJavaLibrary> preDexDeps) { ImmutableSet.Builder<BuildTarget> targets = ImmutableSet.builder(); targets.add(dexForUberRDotJava.getBuildTarget()); for (DexProducedFromJavaLibrary preDex : preDexDeps) { targets.add(preDex.getBuildTarget()); } return getTargetsAsRules(targets.build()); } private ImmutableSortedSet<BuildRule> getTargetsAsRules(Collection<BuildTarget> buildTargets) { return BuildRules.toBuildRulesFor(originalBuildTarget, ruleResolver, buildTargets); } }