Native changes required to support linking against resource apks.

Note that this only wires up for validation in the native pipeline. The main implementation here is in the Starlark pipeline, but there is still one existing validation step in native (which we will need to clean up separately).

PiperOrigin-RevId: 517694868
Change-Id: Ia1a65214f8fd84814d3f1aead8879b5cedb6a2a5
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
index 7e2e2a3..fd2f6d2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
@@ -425,6 +425,7 @@
     return process(
         dataContext,
         manifest,
+        ImmutableList.of(),
         ResourceDependencies.fromRuleDeps(ruleContext, neverlink),
         dataBindingContext);
   }
@@ -432,12 +433,13 @@
   ValidatedAndroidResources process(
       AndroidDataContext dataContext,
       StampedAndroidManifest manifest,
+      List<Artifact> resApkDeps,
       ResourceDependencies resourceDeps,
       DataBindingContext dataBindingContext)
       throws InterruptedException {
     return parse(dataContext, manifest, dataBindingContext)
         .merge(dataContext, resourceDeps)
-        .validate(dataContext);
+        .validate(dataContext, resApkDeps);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidStarlarkData.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidStarlarkData.java
index cf2ec4e..230ab27 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidStarlarkData.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidStarlarkData.java
@@ -149,6 +149,7 @@
       AndroidManifestInfo manifest,
       Sequence<?> resources, // <ConfiguredTarget>
       Sequence<?> deps, // <AndroidResourcesInfo>
+      Sequence<?> resApkDeps, // <File>
       boolean neverlink,
       boolean enableDataBinding)
       throws EvalException, InterruptedException {
@@ -161,6 +162,7 @@
           .process(
               ctx,
               manifest.asStampedManifest(),
+              Sequence.cast(resApkDeps, Artifact.class, "resource_apks"),
               ResourceDependencies.fromProviders(
                   Sequence.cast(deps, AndroidResourcesInfo.class, "deps"), neverlink),
               DataBinding.contextFrom(
@@ -180,7 +182,8 @@
       boolean enableDataBinding)
       throws EvalException, InterruptedException {
     ValidatedAndroidResources validated =
-        mergeRes(ctx, manifest, resources, deps, neverlink, enableDataBinding);
+        mergeRes(
+            ctx, manifest, resources, deps, StarlarkList.empty(), neverlink, enableDataBinding);
     JavaInfo javaInfo =
         getJavaInfoForRClassJar(validated.getClassJar(), validated.getJavaSourceJar());
     return Dict.<Provider, NativeInfo>builder()
@@ -258,9 +261,10 @@
             .process(
                 ctx,
                 AndroidManifest.forAarImport(androidManifestArtifact),
+                ImmutableList.of(),
                 ResourceDependencies.fromProviders(
                     getProviders(depsTargets, AndroidResourcesInfo.PROVIDER),
-                    /* neverlink = */ false),
+                    /* neverlink= */ false),
                 DataBinding.getDisabledDataBindingContext(ctx));
 
     MergedAndroidAssets mergedAssets =
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidResources.java
index 8e5df74..aa04760 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidResources.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidResources.java
@@ -14,9 +14,11 @@
 package com.google.devtools.build.lib.rules.android;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleErrorConsumer;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import javax.annotation.Nullable;
@@ -169,7 +171,18 @@
    */
   public ValidatedAndroidResources validate(AndroidDataContext dataContext)
       throws InterruptedException {
-    return ValidatedAndroidResources.validateFrom(dataContext, this);
+    return ValidatedAndroidResources.validateFrom(dataContext, this, ImmutableList.of());
+  }
+
+  /**
+   * Validates and packages this rule's resources.
+   *
+   * <p>See {@link ValidatedAndroidResources#validateFrom(AndroidDataContext,
+   * MergedAndroidResources)}. This method is a convenience method for calling that one.
+   */
+  public ValidatedAndroidResources validate(
+      AndroidDataContext dataContext, List<Artifact> resApkDeps) throws InterruptedException {
+    return ValidatedAndroidResources.validateFrom(dataContext, this, resApkDeps);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ValidatedAndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/ValidatedAndroidResources.java
index 05a9ec1..7d11049 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ValidatedAndroidResources.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ValidatedAndroidResources.java
@@ -13,10 +13,13 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.android;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleErrorConsumer;
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.starlarkbuildapi.android.ValidatedAndroidDataApi;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import javax.annotation.Nullable;
@@ -58,7 +61,9 @@
    * </ul>
    */
   public static ValidatedAndroidResources validateFrom(
-      AndroidDataContext dataContext, MergedAndroidResources merged) throws InterruptedException {
+      AndroidDataContext dataContext, MergedAndroidResources merged, List<Artifact> resApkDeps)
+      throws InterruptedException {
+
     Artifact rTxtOut = dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT);
     Artifact sourceJarOut =
         dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_JAVA_SOURCE_JAR);
@@ -69,9 +74,13 @@
         .addInput("--libraries", dataContext.getSdk().getAndroidJar())
         .addInput("--compiled", merged.getCompiledSymbols())
         .addInput("--manifest", merged.getManifest())
+        .addInputs(resApkDeps)
         // Sets an alternative java package for the generated R.java
         // this allows android rules to generate resources outside of the java{,tests} tree.
         .maybeAddFlag("--packageForR", merged.getJavaPackage())
+        .addVectoredFlag(
+            "--additionalApksToLinkAgainst",
+            resApkDeps.stream().map(Artifact::getRootRelativePathString).collect(toImmutableList()))
         .addTransitiveVectoredInput(
             "--compiledDep", merged.getResourceDependencies().getTransitiveCompiledSymbols())
         .addOutput("--sourceJarOut", sourceJarOut)
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDataProcessingApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDataProcessingApi.java
index 55212da..c38831c 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDataProcessingApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDataProcessingApi.java
@@ -289,6 +289,17 @@
                 "Targets containing raw resources from dependencies. These resources will be merged"
                     + " together with each other and this target's resources."),
         @Param(
+            name = "validation_res_apks",
+            positional = false,
+            defaultValue = "[]",
+            allowedTypes = {
+              @ParamType(type = Sequence.class, generic1 = FileProviderApi.class),
+            },
+            named = true,
+            doc =
+                "Resource APK deps to be used for validation only. Not fully supported in the"
+                    + " native resource pipeline."),
+        @Param(
             name = "neverlink",
             positional = false,
             defaultValue = "False",
@@ -321,6 +332,7 @@
       AndroidManifestInfoT manifest,
       Sequence<?> resources, // <TransitiveInfoCollectionT>
       Sequence<?> deps, // <AndroidResourcesInfoT>
+      Sequence<?> resApkDeps, // <FileT>
       boolean neverlink,
       boolean enableDataBinding)
       throws EvalException, InterruptedException;
diff --git a/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java b/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
index d463586..3fe4355 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ValidateAndLinkResourcesAction.java
@@ -14,11 +14,15 @@
 // Copyright 2017 The Bazel Authors. All rights reserved.
 package com.google.devtools.build.android;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
 import com.android.aapt.Resources.Reference;
 import com.android.aapt.Resources.XmlNode;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.build.android.aapt2.Aapt2ConfigOptions;
 import com.google.devtools.build.android.aapt2.CompiledResources;
 import com.google.devtools.build.android.aapt2.ResourceLinker;
@@ -38,10 +42,15 @@
 import java.util.Optional;
 
 /** Performs resource validation and static linking for compiled android resources. */
-public class ValidateAndLinkResourcesAction {
+public final class ValidateAndLinkResourcesAction {
 
   /** Action configuration options. */
   public static class Options extends OptionsBase {
+    /**
+     * TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
+     *
+     * @deprecated Use --resources.
+     */
     @Option(
         name = "compiled",
         documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
@@ -51,7 +60,6 @@
         category = "input",
         help = "Compiled resources to link.",
         deprecationWarning = "Use --resources.")
-    // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
     @Deprecated
     public Path compiled;
 
@@ -66,6 +74,11 @@
         help = "Compiled resource dependencies to link.")
     public List<Path> compiledDeps;
 
+    /**
+     * TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
+     *
+     * @deprecated Use --resources.
+     */
     @Option(
         name = "manifest",
         documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
@@ -75,7 +88,6 @@
         category = "input",
         help = "Manifest for the library.",
         deprecationWarning = "Use --resources.")
-    // TODO(b/64570523): Still used by blaze. Will be removed as part of the command line cleanup.
     @Deprecated
     public Path manifest;
 
@@ -149,6 +161,16 @@
         category = "output",
         help = "Generated java classes from the resources.")
     public Path sourceJarOut;
+
+    @Option(
+        name = "additionalApksToLinkAgainst",
+        defaultValue = "null",
+        category = "input",
+        converter = PathListConverter.class,
+        documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+        effectTags = {OptionEffectTag.UNKNOWN},
+        help = "List of APKs used during linking.")
+    public List<Path> additionalApksToLinkAgainst;
   }
 
   public static void main(String[] args) throws Exception {
@@ -183,16 +205,25 @@
                           .writeDummyManifestForAapt(
                               scopedTmp.getPath().resolve("manifest-aapt-dummy"),
                               options.packageForR));
-      List<CompiledResources> deps =
-          options.compiledDeps.stream()
-              .map(CompiledResources::from)
-              .collect(ImmutableList.toImmutableList());
+      ImmutableList<CompiledResources> includes =
+          options.compiledDeps.stream().map(CompiledResources::from).collect(toImmutableList());
       profiler.recordEndOf("manifest").startTask("validate");
 
       // TODO(b/146663858): distinguish direct/transitive deps for "strict deps".
       // TODO(b/128711690): validate AndroidManifest.xml
       checkVisibilityOfResourceReferences(
-          /*androidManifest=*/ XmlNode.getDefaultInstance(), resources, deps);
+          /* androidManifest= */ XmlNode.getDefaultInstance(), resources, includes);
+
+      ImmutableList.Builder<StaticLibrary> dependencies = ImmutableList.builder();
+      dependencies.addAll(
+          Optional.ofNullable(options.deprecatedLibraries).orElse(options.libraries));
+
+      if (options.additionalApksToLinkAgainst != null) {
+        dependencies.addAll(
+            options.additionalApksToLinkAgainst.stream()
+                .map(StaticLibrary::from)
+                .collect(toImmutableList()));
+      }
 
       profiler.recordEndOf("validate").startTask("link");
       ResourceLinker.create(aapt2Options.aapt2, executorService, scopedTmp.getPath())
@@ -200,8 +231,8 @@
           // NB: these names are really confusing.
           //   .dependencies is meant for linking in android.jar
           //   .include is meant for regular dependencies
-          .dependencies(Optional.ofNullable(options.deprecatedLibraries).orElse(options.libraries))
-          .include(deps)
+          .dependencies(dependencies.build())
+          .include(includes)
           .buildVersion(aapt2Options.buildToolsVersion)
           .outputAsProto(aapt2Options.resourceTableAsProto)
           .linkStatically(resources)
@@ -228,21 +259,21 @@
             .flatMap(
                 cr ->
                     AndroidCompiledDataDeserializer.create(
-                        /*includeFileContentsForValidation=*/ false)
+                        /* includeFileContentsForValidation= */ false)
                         .read(DependencyInfo.UNKNOWN, cr.getZip())
                         .entrySet()
                         .stream())
             .filter(entry -> entry.getValue().getVisibility() == Visibility.PRIVATE)
             .map(entry -> ((FullyQualifiedName) entry.getKey()).asQualifiedReference())
-            .collect(ImmutableSet.toImmutableSet());
+            .collect(toImmutableSet());
 
     StringBuilder errorMessage = new StringBuilder();
     {
-      List<String> referencedPrivateResources =
+      ImmutableList<String> referencedPrivateResources =
           ProtoXmlUtils.getAllResourceReferences(androidManifest).stream()
               .map(Reference::getName)
               .filter(privateResourceNames::contains)
-              .collect(ImmutableList.toImmutableList());
+              .collect(toImmutableList());
       if (!referencedPrivateResources.isEmpty()) {
         errorMessage
             .append("AndroidManifest.xml references external private resources ")
@@ -255,11 +286,11 @@
         AndroidCompiledDataDeserializer.create(/*includeFileContentsForValidation=*/ true)
             .read(DependencyInfo.UNKNOWN, compiled.getZip())
             .entrySet()) {
-      List<String> referencedPrivateResources =
+      ImmutableList<String> referencedPrivateResources =
           resource.getValue().getReferencedResources().stream()
               .map(Reference::getName)
               .filter(privateResourceNames::contains)
-              .collect(ImmutableList.toImmutableList());
+              .collect(toImmutableList());
 
       if (!referencedPrivateResources.isEmpty()) {
         errorMessage
@@ -276,4 +307,6 @@
       throw new UserException(errorMessage.toString());
     }
   }
+
+  private ValidateAndLinkResourcesAction() {}
 }