Halt resource_files.zip generation for Robolectric builds to enable skipping aapt2 convert.

resource_files.zip is unused by Robolectric builds. We can forego its creation.

By using a lack of resource_files.zip as a marker, ResourceLinker can skip the costly 'aapt2 convert' step.

PiperOrigin-RevId: 949770100
Change-Id: I76b857dd252ffeec53b6d107fa18481929be4e06
diff --git a/rules/android_local_test/impl.bzl b/rules/android_local_test/impl.bzl
index fe61058..67e57de 100644
--- a/rules/android_local_test/impl.bzl
+++ b/rules/android_local_test/impl.bzl
@@ -118,6 +118,7 @@
         shrink_resources = attrs.tristate.no,
         build_java_with_final_resources = True,
         generate_out_symbols = False,
+        generate_resource_files_zip = False,
         crunch_png = False,
         aapt = get_android_toolchain(ctx).aapt2.files_to_run,
         android_jar = get_android_sdk(ctx).android_jar,
@@ -209,18 +210,14 @@
     )
 
 def _process_deploy_jar(ctx, java_package, jvm_ctx, proto_ctx, resources_ctx, **_unused_sub_ctxs):
-    res_file_path = resources_ctx.validation_result.short_path
     subs = {
         "%android_merged_manifest%": resources_ctx.processed_manifest.short_path,
-        "%android_merged_resources%": "jar:file:" + res_file_path + "!/res",
-        "%android_merged_assets%": "jar:file:" + res_file_path + "!/assets",
         # The native resources_ctx has the package field, whereas the starlark resources_ctx uses the java_package
         "%android_custom_package%": getattr(resources_ctx, "package", java_package or ""),
         "%android_resource_apk%": resources_ctx.resources_apk.short_path,
     }
     res_runfiles = [
         resources_ctx.resources_apk,
-        resources_ctx.validation_result,
         resources_ctx.processed_manifest,
     ]
 
diff --git a/rules/resources.bzl b/rules/resources.bzl
index ee1c615..3072d1c 100644
--- a/rules/resources.bzl
+++ b/rules/resources.bzl
@@ -496,6 +496,7 @@
         generate_minsdk_proguard_config = False,
         build_java_with_final_resources = False,
         generate_out_symbols = True,
+        generate_resource_files_zip = True,
         feature_flags = "",
         crunch_png = True,
         aapt = None,
@@ -566,6 +567,7 @@
         generally only desirable for test targets that aren't potentially
         running compile-time optimizations.
       generate_out_symbols: boolean. Whether to generate the merged symbols binary file.
+      generate_resource_files_zip: boolean. Whether to generate resource_files.zip.
       feature_flags: string. The string value for --feature-flags to pass to aapt2.
       crunch_png: boolean. Determines whether `aapt2 compile` should crunch PNG files.
       aapt: FilesToRunProvider. The aapt executable or FilesToRunProvider.
@@ -736,7 +738,7 @@
     )
     resource_files_zip = ctx.actions.declare_file(
         "_migrated/" + ctx.label.name + "_files/resource_files.zip",
-    )
+    ) if generate_resource_files_zip else None
     out_symbols = ctx.actions.declare_file("_migrated/" + ctx.label.name + "_symbols/merged.bin") if generate_out_symbols else None
     _busybox.package(
         ctx,
diff --git a/rules/robolectric_properties_template.txt b/rules/robolectric_properties_template.txt
index d05a28c..564bbf2 100644
--- a/rules/robolectric_properties_template.txt
+++ b/rules/robolectric_properties_template.txt
@@ -1,5 +1,3 @@
 android_merged_manifest=%android_merged_manifest%
-android_merged_resources=%android_merged_resources%
-android_merged_assets=%android_merged_assets%
 android_custom_package=%android_custom_package%
 android_resource_apk=%android_resource_apk%
diff --git a/src/tools/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java b/src/tools/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
index 22f8243..3a52308 100644
--- a/src/tools/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
+++ b/src/tools/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java
@@ -413,6 +413,7 @@
               .includeProguardLocationReferences(options.includeProguardLocationReferences)
               .featureFlags(aaptConfigOptions.featureFlags)
               .optimizeThroughput(aaptConfigOptions.optimizeThroughput)
+              .hasResourcesOutput(options.resourcesOutput != null)
               .link(compiled);
       profiler.recordEndOf("link").startTask("validate");
 
diff --git a/src/tools/java/com/google/devtools/build/android/ResourcesZip.java b/src/tools/java/com/google/devtools/build/android/ResourcesZip.java
index cd3410a..82c6a03 100644
--- a/src/tools/java/com/google/devtools/build/android/ResourcesZip.java
+++ b/src/tools/java/com/google/devtools/build/android/ResourcesZip.java
@@ -241,6 +241,9 @@
 
   @VisibleForTesting
   ImmutableListMultimap<String, String> parseToolAttributes() throws IOException {
+    if (attributes == null || !Files.exists(attributes)) {
+      return ImmutableListMultimap.of();
+    }
     return ToolAttributes.parseFrom(
             Files.readAllBytes(attributes), ExtensionRegistry.getEmptyRegistry())
         .getAttributesMap()
diff --git a/src/tools/java/com/google/devtools/build/android/aapt2/PackagedResources.java b/src/tools/java/com/google/devtools/build/android/aapt2/PackagedResources.java
index 7a388e0..6f830f7 100644
--- a/src/tools/java/com/google/devtools/build/android/aapt2/PackagedResources.java
+++ b/src/tools/java/com/google/devtools/build/android/aapt2/PackagedResources.java
@@ -16,6 +16,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.devtools.build.android.ResourcesZip;
 import java.nio.file.Path;
+import javax.annotation.Nullable;
 
 /** Represents the packaged, flattened resources. */
 @AutoValue
@@ -23,6 +24,7 @@
 
   public abstract Path apk();
 
+  @Nullable
   public abstract Path proto();
 
   public abstract Path rTxt();
@@ -33,22 +35,25 @@
 
   public abstract Path javaSourceDirectory();
 
+  @Nullable
   abstract Path resourceIds();
 
+  @Nullable
   public abstract Path attributes();
 
+  @Nullable
   public abstract Path packages();
 
   public static PackagedResources of(
       Path outPath,
-      Path protoPath,
+      @Nullable Path protoPath,
       Path rTxt,
       Path proguardConfig,
       Path mainDexProguard,
       Path javaSourceDirectory,
-      Path resourceIds,
-      Path attributes,
-      Path packages) {
+      @Nullable Path resourceIds,
+      @Nullable Path attributes,
+      @Nullable Path packages) {
     return new AutoValue_PackagedResources(
         outPath,
         protoPath,
diff --git a/src/tools/java/com/google/devtools/build/android/aapt2/ResourceLinker.java b/src/tools/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
index 5126f30..92cdde5 100644
--- a/src/tools/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
+++ b/src/tools/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
@@ -88,6 +88,7 @@
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
+import javax.annotation.Nullable;
 
 /** Performs linking of {@link CompiledResources} using aapt2. */
 public class ResourceLinker {
@@ -168,6 +169,7 @@
   private List<StaticLibrary> resourceApks = ImmutableList.of();
   private String featureFlags = "";
   private boolean optimizeThroughput = false;
+  private boolean hasResourcesOutput = true;
 
   private ResourceLinker(
       Path aapt2, ListeningExecutorService executorService, Path workingDirectory) {
@@ -274,6 +276,12 @@
     return this;
   }
 
+  @CanIgnoreReturnValue
+  public ResourceLinker hasResourcesOutput(boolean hasResourcesOutput) {
+    this.hasResourcesOutput = hasResourcesOutput;
+    return this;
+  }
+
   /**
    * Statically links the {@link CompiledResources} with the dependencies to produce a {@link
    * StaticLibrary}.
@@ -438,20 +446,21 @@
     return fileName.substring(0, lastIndex).concat(".").concat(newExtension);
   }
 
-  private ProtoApk linkProtoApk(
+  private Path linkApk(
       CompiledResources compiled,
       Path rTxt,
       Path proguardConfig,
       Path mainDexProguard,
       Path javaSourceDirectory,
-      Path resourceIds)
+      @Nullable Path resourceIds,
+      boolean asProto)
       throws IOException {
     Predicate<DirectoryEntry> flatFileShouldKeep =
         generatePseudoLocale && resourceConfigs.stream().anyMatch(PSEUDO_LOCALE_FILTERS::contains)
             ? USE_GENERATED
             : USE_DEFAULT;
     profiler.startTask("fulllink");
-    final Path linked = workingDirectory.resolve("bin." + PROTO_EXTENSION);
+    final Path linked = workingDirectory.resolve(asProto ? ("bin." + PROTO_EXTENSION) : "bin.apk");
     logger.fine(
         new AaptCommandBuilder(aapt2)
             .forBuildToolsVersion(buildToolsVersion)
@@ -470,8 +479,8 @@
             .add("--auto-add-overlay")
             .when(OVERRIDE_STYLES_INSTEAD_OF_OVERLAYING)
             .thenAdd("--override-styles-instead-of-overlaying")
-            // Always link to proto, as resource shrinking needs the extra information.
-            .add("--proto-format")
+            .when(asProto)
+            .thenAdd("--proto-format")
             .when(debug)
             .thenAdd("--debug-mode")
             .add("--custom-package", customPackage)
@@ -501,7 +510,8 @@
             .when(!resourceConfigs.isEmpty())
             .thenAdd("-c", Joiner.on(',').join(resourceConfigs))
             .add("--output-text-symbols", rTxt)
-            .add("--emit-ids", resourceIds)
+            .when(resourceIds != null)
+            .thenAdd("--emit-ids", resourceIds)
             .add("--java", javaSourceDirectory)
             .add("--proguard", proguardConfig)
             .add("--proguard-main-dex", mainDexProguard)
@@ -512,7 +522,8 @@
             .add("--feature-flags", featureFlags)
             .execute(String.format("Linking %s", compiled.getManifest())));
     profiler.recordEndOf("fulllink");
-    return ProtoApk.readFrom(optimize(compiled, linked));
+    Path optimized = optimize(compiled, linked, asProto);
+    return asProto ? optimized : copyAndFixCompression(optimized, workingDirectory);
   }
 
   /** Modes for overriding compression of a given file. */
@@ -658,13 +669,14 @@
     return attributes;
   }
 
-  private Path optimize(CompiledResources compiled, Path protoApk) throws IOException {
+  private Path optimize(CompiledResources compiled, Path apk, boolean isProto) throws IOException {
     if (densities.size() < 2) {
-      return protoApk;
+      return apk;
     }
 
     profiler.startTask("optimize");
-    final Path optimized = workingDirectory.resolve("optimized." + PROTO_EXTENSION);
+    final Path optimized =
+        workingDirectory.resolve(isProto ? ("optimized." + PROTO_EXTENSION) : "optimized.apk");
     logger.fine(
         new AaptCommandBuilder(aapt2)
             .forBuildToolsVersion(buildToolsVersion)
@@ -680,7 +692,7 @@
             .when(densities.size() >= 2)
             .thenAdd("--target-densities", densities.stream().collect(Collectors.joining(",")))
             .add("-o", optimized)
-            .add(protoApk.toString())
+            .add(apk.toString())
             .execute(String.format("Optimizing %s", compiled.getManifest())));
     profiler.recordEndOf("optimize");
     return optimized;
@@ -693,23 +705,48 @@
       Path proguardConfig = workingDirectory.resolve("proguard.cfg");
       Path mainDexProguard = workingDirectory.resolve("proguard.maindex.cfg");
       Path javaSourceDirectory = Files.createDirectories(workingDirectory.resolve("java"));
-      Path resourceIds = workingDirectory.resolve("ids.txt");
-      try (ProtoApk protoApk =
-          linkProtoApk(
-              compiled, rTxt, proguardConfig, mainDexProguard, javaSourceDirectory, resourceIds)) {
-        return PackagedResources.of(
-            outputAsProto ? protoApk.asApkPath() : convertProtoApkToBinary(protoApk),
-            protoApk.asApkPath(),
-            rTxt,
-            proguardConfig,
-            mainDexProguard,
-            javaSourceDirectory,
-            resourceIds,
-            extractAttributes(compiled),
-            extractPackages(compiled));
+      Path resourceIds = hasResourcesOutput ? workingDirectory.resolve("ids.txt") : null;
+      // conditionalKeepRules=true implies resource shrinking, which can only occur if
+      // resource_files.zip exists.
+      Preconditions.checkState(
+          hasResourcesOutput || (!conditionalKeepRules && !outputAsProto),
+          "Cannot construct proto apk when resource_files.zip is unavailable.");
+      boolean linkAsProto = hasResourcesOutput || outputAsProto || conditionalKeepRules;
+      Path apkPath =
+          linkApk(
+              compiled,
+              rTxt,
+              proguardConfig,
+              mainDexProguard,
+              javaSourceDirectory,
+              resourceIds,
+              linkAsProto);
+      if (linkAsProto) {
+        try (ProtoApk protoApk = ProtoApk.readFrom(apkPath)) {
+          return PackagedResources.of(
+              outputAsProto ? protoApk.asApkPath() : convertProtoApkToBinary(protoApk),
+              protoApk.asApkPath(),
+              rTxt,
+              proguardConfig,
+              mainDexProguard,
+              javaSourceDirectory,
+              resourceIds,
+              extractAttributes(compiled),
+              extractPackages(compiled));
+        }
       }
 
-    } catch (IOException e) {
+      return PackagedResources.of(
+          apkPath,
+          null,
+          rTxt,
+          proguardConfig,
+          mainDexProguard,
+          javaSourceDirectory,
+          resourceIds,
+          null,
+          null);
+    } catch (IOException | IllegalStateException e) {
       throw new LinkError(e);
     }
   }