public static RuleConfiguredTargetBuilder createAndroidBinary()

in src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java [404:827]


  public static RuleConfiguredTargetBuilder createAndroidBinary(
      RuleContext ruleContext,
      AndroidDataContext dataContext,
      NestedSetBuilder<Artifact> filesBuilder,
      Artifact binaryJar,
      Function<Artifact, Artifact> derivedJarFunction,
      boolean isBinaryJarFiltered,
      AndroidCommon androidCommon,
      JavaSemantics javaSemantics,
      AndroidSemantics androidSemantics,
      NativeLibs nativeLibs,
      ResourceApk resourceApk,
      @Nullable MobileInstallResourceApks mobileInstallResourceApks,
      JavaTargetAttributes resourceClasses,
      ImmutableList<Artifact> apksUnderTest,
      ImmutableList<Artifact> additionalMergedManifests,
      Artifact proguardMapping,
      @Nullable Artifact oneVersionEnforcementArtifact)
      throws InterruptedException, RuleErrorException {

    List<ProguardSpecProvider> proguardDeps = new ArrayList<>();
    Iterables.addAll(
        proguardDeps, ruleContext.getPrerequisites("deps", ProguardSpecProvider.PROVIDER));
    Iterables.addAll(
        proguardDeps,
        ruleContext.getPrerequisites("application_resources", ProguardSpecProvider.PROVIDER));
    if (ruleContext.getConfiguration().isCodeCoverageEnabled()
        && ruleContext.attributes().has("$jacoco_runtime", BuildType.LABEL)) {
      proguardDeps.add(
          ruleContext.getPrerequisite("$jacoco_runtime", ProguardSpecProvider.PROVIDER));
    }
    ImmutableList<Artifact> proguardSpecs =
        getProguardSpecs(
            dataContext,
            androidSemantics,
            resourceApk.getResourceProguardConfig(),
            resourceApk.getManifest(),
            ruleContext.attributes().has(ProguardHelper.PROGUARD_SPECS, BuildType.LABEL_LIST)
                ? ruleContext.getPrerequisiteArtifacts(ProguardHelper.PROGUARD_SPECS).list()
                : ImmutableList.of(),
            ruleContext.getPrerequisiteArtifacts(":extra_proguard_specs").list(),
            proguardDeps);
    boolean hasProguardSpecs = !proguardSpecs.isEmpty();

    // TODO(bazel-team): Verify that proguard spec files don't contain -printmapping directions
    // which this -printmapping command line flag will override.
    Artifact proguardOutputMap = null;
    boolean generateProguardMap =
        ProguardHelper.genProguardMapping(ruleContext.attributes())
            || dataContext.isResourceShrinkingEnabled();
    boolean postprocessingRewritesMap = androidSemantics.postprocessClassesRewritesMap(ruleContext);
    boolean desugarJava8LibsGeneratesMap =
        AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs();
    if (generateProguardMap) {
      // Determine the output of the Proguard map from shrinking the app. This depends on the
      // additional steps which can process the map before the final Proguard map artifact is
      // generated.
      if (!hasProguardSpecs && !postprocessingRewritesMap) {
        // When no shrinking happens a generating rule for the output map artifact is still needed.
        proguardOutputMap = androidSemantics.getProguardOutputMap(ruleContext);
      } else if (postprocessingRewritesMap) {
        // Proguard map from shrinking goes to postprocessing.
        proguardOutputMap =
            ProguardHelper.getProguardTempArtifact(ruleContext, "proguard_output_for_rex.map");
      } else if (desugarJava8LibsGeneratesMap) {
        // Proguard map from shrinking will be merged with desugared library proguard map.
        proguardOutputMap =
            getDxArtifact(ruleContext, "_proguard_output_for_desugared_library.map");
      } else {
        // Proguard map from shrinking is the final output.
        proguardOutputMap = androidSemantics.getProguardOutputMap(ruleContext);
      }
    }

    ProguardOutput proguardOutput =
        applyProguard(
            ruleContext,
            androidCommon,
            javaSemantics,
            binaryJar,
            proguardSpecs,
            proguardMapping,
            proguardOutputMap);

    // Determine the outputs for the proguard map transition through post-processing and adding of
    // desugared library map.
    Artifact postProcessingOutputMap = null;
    Artifact finalProguardOutputMap = null;
    if (proguardOutput.hasMapping()) {
      // Determine the Proguard map artifacts for the additional steps (if any) if shrinking of
      // the app is enabled.
      if (postprocessingRewritesMap && desugarJava8LibsGeneratesMap) {
        // Proguard map from preprocessing will be merged with Proguard map for desugared
        // library.
        postProcessingOutputMap =
            getDxArtifact(ruleContext, "_proguard_output_for_desugared_library.map");
        finalProguardOutputMap = androidSemantics.getProguardOutputMap(ruleContext);
      } else if (postprocessingRewritesMap) {
        // No desugared library, Proguard map from preprocessing is the final Proguard map.
        postProcessingOutputMap = androidSemantics.getProguardOutputMap(ruleContext);
        finalProguardOutputMap = postProcessingOutputMap;
      } else if (desugarJava8LibsGeneratesMap) {
        // No postprocessing, Proguard map from merging with the desugared library map is the
        // final Proguard map.
        postProcessingOutputMap = proguardOutputMap;
        finalProguardOutputMap = androidSemantics.getProguardOutputMap(ruleContext);
      } else {
        // No postprocessing, no desugared library, the final Proguard map is the Proguard map
        // from shrinking
        postProcessingOutputMap = proguardOutputMap;
        finalProguardOutputMap = proguardOutputMap;
      }
    }

    if (dataContext.useResourceShrinking(hasProguardSpecs)) {
      resourceApk =
          shrinkResources(
              ruleContext,
              androidSemantics.makeContextForNative(ruleContext),
              resourceApk,
              proguardOutput,
              filesBuilder);
    }

    resourceApk = maybeOptimizeResources(dataContext, resourceApk, hasProguardSpecs);

    Artifact jarToDex = proguardOutput.getOutputJar();
    DexingOutput dexingOutput =
        dex(
            ruleContext,
            androidSemantics,
            binaryJar,
            jarToDex,
            isBinaryJarFiltered,
            androidCommon,
            resourceApk.getMainDexProguardConfig(),
            resourceClasses,
            derivedJarFunction,
            proguardOutputMap);

    // Collect all native shared libraries across split transitions. Some AARs contain shared
    // libraries across multiple architectures, e.g. x86 and armeabi-v7a, and need to be packed
    // into the APK.
    NestedSetBuilder<Artifact> transitiveNativeLibs = NestedSetBuilder.naiveLinkOrder();
    for (Map.Entry<
            com.google.common.base.Optional<String>,
            ? extends List<? extends TransitiveInfoCollection>>
        entry : ruleContext.getSplitPrerequisites("deps").entrySet()) {
      for (AndroidNativeLibsInfo provider :
          AnalysisUtils.getProviders(entry.getValue(), AndroidNativeLibsInfo.PROVIDER)) {
        transitiveNativeLibs.addTransitive(provider.getNativeLibs());
      }
    }
    NestedSet<Artifact> nativeLibsAar = transitiveNativeLibs.build();

    DexPostprocessingOutput dexPostprocessingOutput =
        androidSemantics.postprocessClassesDexZip(
            ruleContext,
            filesBuilder,
            dexingOutput.classesDexZip,
            proguardOutput,
            postProcessingOutputMap);

    // Compute the final DEX files by appending Java 8 legacy .dex if used.
    Artifact finalClassesDex;
    Java8LegacyDexOutput java8LegacyDexOutput;
    ImmutableList<Artifact> finalShardDexZips = dexingOutput.shardDexZips;
    if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs()
        && dexPostprocessingOutput.classesDexZip().getFilename().endsWith(".zip")) {
      if (binaryJar.equals(jarToDex)) {
        // No shrinking: use canned Java 8 legacy .dex file
        java8LegacyDexOutput = Java8LegacyDexOutput.getCanned(ruleContext);
      } else {
        // Shrinking is used: build custom Java 8 legacy .dex file
        java8LegacyDexOutput = buildJava8LegacyDex(ruleContext, jarToDex);

        // Merge the mapping files from shrinking the program and Java 8 legacy .dex file.
        if (finalProguardOutputMap != null) {
          ruleContext.registerAction(
              createSpawnActionBuilder(ruleContext)
                  .useDefaultShellEnvironment()
                  .setExecutable(ruleContext.getExecutablePrerequisite("$merge_proguard_maps"))
                  .addInput(dexPostprocessingOutput.proguardMap())
                  .addInput(java8LegacyDexOutput.getMap())
                  .addOutput(finalProguardOutputMap)
                  .addCommandLine(
                      CustomCommandLine.builder()
                          .addExecPath("--pg-map", dexPostprocessingOutput.proguardMap())
                          .addExecPath("--pg-map", java8LegacyDexOutput.getMap())
                          .addExecPath("--pg-map-output", finalProguardOutputMap)
                          .build())
                  .setMnemonic("MergeProguardMaps")
                  .setProgressMessage(
                      "Merging app and desugared library Proguard maps for %{label}")
                  .build(ruleContext));
        }
      }

      // Append legacy .dex library to app's .dex files
      finalClassesDex = getDxArtifact(ruleContext, "_final_classes.dex.zip");
      ruleContext.registerAction(
          createSpawnActionBuilder(ruleContext)
              .useDefaultShellEnvironment()
              .setMnemonic("AppendJava8LegacyDex")
              .setProgressMessage("Adding Java 8 legacy library for %s", ruleContext.getLabel())
              .setExecutable(ruleContext.getExecutablePrerequisite("$merge_dexzips"))
              .addInput(dexPostprocessingOutput.classesDexZip())
              .addInput(java8LegacyDexOutput.getDex())
              .addOutput(finalClassesDex)
              // Order matters here: we want java8LegacyDex to be the highest-numbered classesN.dex
              .addCommandLine(
                  CustomCommandLine.builder()
                      .addExecPath("--input_zip", dexPostprocessingOutput.classesDexZip())
                      .addExecPath("--input_zip", java8LegacyDexOutput.getDex())
                      .addExecPath("--output_zip", finalClassesDex)
                      .build())
              .build(ruleContext));
      finalShardDexZips =
          ImmutableList.<Artifact>builder()
              .addAll(finalShardDexZips)
              .add(java8LegacyDexOutput.getDex())
              .build();

    } else {
      finalClassesDex = dexPostprocessingOutput.classesDexZip();
    }

    if (hasProguardSpecs) {
      proguardOutput.addAllToSet(filesBuilder, finalProguardOutputMap);
    }

    Artifact unsignedApk =
        ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_UNSIGNED_APK);
    Artifact zipAlignedApk =
        ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_APK);
    Artifact v4Signature =
        (dataContext.getAndroidConfig().apkSigningMethodV4() != null
                && dataContext.getAndroidConfig().apkSigningMethodV4())
            ? ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_V4_SIGNATURE)
            : null;
    ImmutableList<Artifact> signingKeys = AndroidCommon.getApkDebugSigningKeys(ruleContext);
    Artifact signingLineage = ruleContext.getPrerequisiteArtifact("debug_signing_lineage_file");
    String keyRotationMinSdk = ruleContext.attributes().get("key_rotation_min_sdk", Type.STRING);
    FilesToRunProvider resourceExtractor =
        ruleContext.getExecutablePrerequisite("$resource_extractor");

    ApkActionsBuilder.create("apk")
        .setClassesDex(finalClassesDex)
        .addInputZip(resourceApk.getArtifact())
        .setJavaResourceZip(dexingOutput.javaResourceJar, resourceExtractor)
        .addInputZips(nativeLibsAar.toList())
        .setNativeLibs(nativeLibs)
        .setUnsignedApk(unsignedApk)
        .setSignedApk(zipAlignedApk)
        .setSigningKeys(signingKeys)
        .setSigningLineageFile(signingLineage)
        .setSigningKeyRotationMinSdk(keyRotationMinSdk)
        .setV4Signature(v4Signature)
        .setZipalignApk(true)
        .setDeterministicSigning(androidSemantics.deterministicSigning())
        .registerActions(ruleContext);

    filesBuilder.add(binaryJar);
    filesBuilder.add(unsignedApk);
    filesBuilder.add(zipAlignedApk);
    if (v4Signature != null) {
      filesBuilder.add(v4Signature);
    }
    NestedSet<Artifact> filesToBuild = filesBuilder.build();

    Artifact deployInfo = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.DEPLOY_INFO);
    ImmutableList.Builder<Artifact> listBuilder =
        ImmutableList.<Artifact>builder().add(zipAlignedApk).addAll(apksUnderTest);
    if (v4Signature != null) {
      listBuilder.add(v4Signature);
    }
    AndroidDeployInfoAction.createDeployInfoAction(
        ruleContext,
        deployInfo,
        resourceApk.getManifest(),
        additionalMergedManifests,
        listBuilder.build());

    RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);

    // If this is an instrumentation APK, create the provider for android_instrumentation_test.
    if (isInstrumentation(ruleContext)) {
      ApkInfo targetApkProvider = ruleContext.getPrerequisite("instruments", ApkInfo.PROVIDER);

      AndroidInstrumentationInfo instrumentationProvider =
          new AndroidInstrumentationInfo(targetApkProvider);

      builder.addNativeDeclaredProvider(instrumentationProvider);

      // At this point, the Android manifests of both target and instrumentation APKs are finalized.
      FilesToRunProvider checker =
          ruleContext.getExecutablePrerequisite("$instrumentation_test_check");
      Artifact targetManifest = targetApkProvider.getMergedManifest();
      Artifact instrumentationManifest = resourceApk.getManifest();
      Artifact checkOutput =
          ruleContext.getImplicitOutputArtifact(
              AndroidRuleClasses.INSTRUMENTATION_TEST_CHECK_RESULTS);

      SpawnAction.Builder checkAction =
          createSpawnActionBuilder(ruleContext)
              .setExecutable(checker)
              .addInput(targetManifest)
              .addInput(instrumentationManifest)
              .addOutput(checkOutput)
              .setProgressMessage(
                  "Validating the merged manifests of the target and instrumentation APKs")
              .setMnemonic("AndroidManifestInstrumentationCheck");

      CustomCommandLine commandLine =
          CustomCommandLine.builder()
              .addExecPath("--instrumentation_manifest", instrumentationManifest)
              .addExecPath("--target_manifest", targetManifest)
              .addExecPath("--output", checkOutput)
              .build();

      builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, checkOutput);
      checkAction.addCommandLine(commandLine);
      ruleContext.registerAction(checkAction.build(ruleContext));
    }

    androidCommon.addTransitiveInfoProviders(
        builder,
        /* aar= */ null,
        resourceApk,
        zipAlignedApk,
        apksUnderTest,
        nativeLibs,
        androidCommon.isNeverLink(),
        /* isLibrary = */ false);

    if (dexPostprocessingOutput.proguardMap() != null) {
      builder.addNativeDeclaredProvider(
          new ProguardMappingProvider(dexPostprocessingOutput.proguardMap()));
    }

    if (oneVersionEnforcementArtifact != null) {
      builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, oneVersionEnforcementArtifact);
    }

    if (mobileInstallResourceApks != null) {
      AndroidBinaryMobileInstall.addMobileInstall(
          ruleContext,
          builder,
          dexingOutput.javaResourceJar,
          finalShardDexZips,
          javaSemantics,
          nativeLibs,
          resourceApk,
          mobileInstallResourceApks,
          resourceExtractor,
          nativeLibsAar,
          signingKeys,
          signingLineage,
          additionalMergedManifests);
    }

    // First propagate validations from most rule attributes as usual; then handle "deps" separately
    // to propagate validations from each config split but avoid known-redundant Android Lint
    // validations (b/168038145, b/180746622).
    // TODO(b/180746622): remove custom filtering once semantically identical actions with
    //   different configurations are deduped (while still propagating actions from all splits)
    RuleConfiguredTargetBuilder.collectTransitiveValidationOutputGroups(
        ruleContext,
        attr -> !"deps".equals(attr),
        validations -> builder.addOutputGroup(OutputGroupInfo.VALIDATION_TRANSITIVE, validations));
    boolean filterSplitValidations = false; // propagate validations from first split unfiltered
    for (List<? extends TransitiveInfoCollection> deps :
        ruleContext.getSplitPrerequisites("deps").values()) {
      for (OutputGroupInfo provider :
          AnalysisUtils.getProviders(deps, OutputGroupInfo.STARLARK_CONSTRUCTOR)) {
        NestedSet<Artifact> validations = provider.getOutputGroup(OutputGroupInfo.VALIDATION);
        if (filterSplitValidations) {
          // Filter out Android Lint validations by name: we know these validations are expensive
          // and duplicative between splits, so arbitrarily only propagate them from the first split
          // (b/180746622). While it's cheesy to rely on naming patterns, more semantic filtering
          // requires a lot of work (e.g., using an aspect that observes actions by mnemonic).
          NestedSetBuilder<Artifact> filtered = NestedSetBuilder.stableOrder();
          validations.toList().stream()
              .filter(Artifact::hasKnownGeneratingAction)
              .filter(a -> !a.getFilename().endsWith("android_lint_output.xml"))
              .forEach(filtered::add);
          validations = filtered.build();
        }
        if (!validations.isEmpty()) {
          builder.addOutputGroup(OutputGroupInfo.VALIDATION_TRANSITIVE, validations);
        }
      }
      // Filter out Android Lint Validations from any subsequent split,
      // because they're redundant with those in the first split.
      filterSplitValidations = true;
    }

    return builder
        .setFilesToBuild(filesToBuild)
        .addProvider(
            RunfilesProvider.class,
            RunfilesProvider.simple(
                new Runfiles.Builder(
                        ruleContext.getWorkspaceName(),
                        ruleContext.getConfiguration().legacyExternalRunfiles())
                    .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
                    .addTransitiveArtifacts(filesToBuild)
                    .build()))
        .addNativeDeclaredProvider(
            new ApkInfo(
                zipAlignedApk,
                unsignedApk,
                binaryJar,
                getCoverageInstrumentationJarForApk(ruleContext),
                resourceApk.getManifest(),
                signingKeys,
                signingLineage,
                keyRotationMinSdk))
        .addNativeDeclaredProvider(new AndroidPreDexJarProvider(jarToDex))
        .addNativeDeclaredProvider(
            AndroidFeatureFlagSetProvider.create(
                AndroidFeatureFlagSetProvider.getAndValidateFlagMapFromRuleContext(ruleContext)))
        .addOutputGroup("android_deploy_info", deployInfo);
  }