diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 55e0b1178b05b1..3e8c2fd7ae074c 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -69,6 +69,7 @@ import com.google.devtools.build.lib.rules.android.AndroidDeviceBrokerInfo; import com.google.devtools.build.lib.rules.android.AndroidDeviceRule; import com.google.devtools.build.lib.rules.android.AndroidDeviceScriptFixtureRule; +import com.google.devtools.build.lib.rules.android.AndroidDexInfo; import com.google.devtools.build.lib.rules.android.AndroidFeatureFlagSetProvider; import com.google.devtools.build.lib.rules.android.AndroidHostServiceFixtureRule; import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider; @@ -423,7 +424,8 @@ public void init(ConfiguredRuleClassProvider.Builder builder) { ProguardMappingProvider.PROVIDER, AndroidBinaryDataInfo.PROVIDER, AndroidBinaryNativeLibsInfo.PROVIDER, - BaselineProfileProvider.PROVIDER); + BaselineProfileProvider.PROVIDER, + AndroidDexInfo.PROVIDER); builder.addStarlarkBootstrap(bootstrap); try { diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java index a6612d83fca5dd..1a621beac7ee27 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java @@ -59,6 +59,7 @@ import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate; import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate.OutputPathMapper; +import com.google.devtools.build.lib.analysis.actions.SymlinkAction; import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; @@ -601,11 +602,16 @@ public static RuleConfiguredTargetBuilder createAndroidBinary( proguardOutput, postProcessingOutputMap); + AndroidDexInfo androidDexInfo = + ruleContext.getPrerequisite("application_resources", AndroidDexInfo.PROVIDER); + // Compute the final DEX files by appending Java 8 legacy .dex if used. - Artifact finalClassesDex; + final Artifact finalClassesDex; Java8LegacyDexOutput java8LegacyDexOutput; ImmutableList finalShardDexZips = dexingOutput.shardDexZips; - if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs() + if (androidDexInfo != null) { + finalClassesDex = androidDexInfo.getFinalClassesDexZip(); + } else if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs() && dexPostprocessingOutput.classesDexZip().getFilename().endsWith(".zip")) { if (binaryJar.equals(jarToDex)) { // No shrinking: use canned Java 8 legacy .dex file @@ -690,7 +696,11 @@ public static RuleConfiguredTargetBuilder createAndroidBinary( ApkActionsBuilder.create("apk") .setClassesDex(finalClassesDex) .addInputZip(resourceApk.getArtifact()) - .setJavaResourceZip(dexingOutput.javaResourceJar, resourceExtractor) + .setJavaResourceZip( + androidDexInfo == null + ? dexingOutput.javaResourceJar + : androidDexInfo.getJavaResourceJar(), + resourceExtractor) .addInputZips(nativeLibsAar.toList()) .setNativeLibs(nativeLibs) .setUnsignedApk(unsignedApk) @@ -979,7 +989,10 @@ public static NestedSet getLibraryResourceJars(RuleContext ruleContext return libraryResourceJarsBuilder.build(); } - /** Generates an uncompressed _deploy.jar of all the runtime jars. */ + /** + * Generates an uncompressed _deploy.jar of all the runtime jars, or creates a link to the deploy + * jar created by this android_binary's android_binary_internal target if it is provided. + */ public static Artifact createDeployJar( RuleContext ruleContext, JavaSemantics javaSemantics, @@ -988,15 +1001,32 @@ public static Artifact createDeployJar( boolean checkDesugarDeps, Function derivedJarFunction) throws InterruptedException { + Artifact deployJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_DEPLOY_JAR); - new DeployArchiveBuilder(javaSemantics, ruleContext) - .setOutputJar(deployJar) - .setAttributes(attributes) - .addRuntimeJars(common.getRuntimeJars()) - .setDerivedJarFunction(derivedJarFunction) - .setCheckDesugarDeps(checkDesugarDeps) - .build(); + + AndroidDexInfo androidDexInfo = + ruleContext.getPrerequisite("application_resources", AndroidDexInfo.PROVIDER); + + if (androidDexInfo != null && androidDexInfo.getDeployJar() != null) { + // Symlink to the deploy jar created by this android_binary's android_binary_internal target + // to satisfy the deploy jar implicit output of android_binary. + ruleContext.registerAction( + SymlinkAction.toArtifact( + ruleContext.getActionOwner(), + androidDexInfo.getDeployJar(), // target + deployJar, // symlink + "Symlinking Android deploy jar")); + } else { + new DeployArchiveBuilder(javaSemantics, ruleContext) + .setOutputJar(deployJar) + .setAttributes(attributes) + .addRuntimeJars(common.getRuntimeJars()) + .setDerivedJarFunction(derivedJarFunction) + .setCheckDesugarDeps(checkDesugarDeps) + .build(); + } + return deployJar; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java new file mode 100644 index 00000000000000..8f04e94b7c0a37 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java @@ -0,0 +1,85 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// 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.google.devtools.build.lib.rules.android; + +import static com.google.devtools.build.lib.rules.android.AndroidStarlarkData.fromNoneable; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.packages.BuiltinProvider; +import com.google.devtools.build.lib.packages.NativeInfo; +import com.google.devtools.build.lib.starlarkbuildapi.android.AndroidDexInfoApi; +import javax.annotation.Nullable; +import net.starlark.java.eval.EvalException; + +/** A provider for Android Dex artifacts */ +@Immutable +public class AndroidDexInfo extends NativeInfo implements AndroidDexInfoApi { + + public static final String PROVIDER_NAME = "AndroidDexInfo"; + public static final Provider PROVIDER = new Provider(); + + private final Artifact deployJar; + private final Artifact finalClassesDexZip; + private final Artifact javaResourceJar; + + public AndroidDexInfo(Artifact deployJar, Artifact finalClassesDexZip, Artifact javaResourceJar) { + this.deployJar = deployJar; + this.finalClassesDexZip = finalClassesDexZip; + this.javaResourceJar = javaResourceJar; + } + + @Override + public Provider getProvider() { + return PROVIDER; + } + + @Override + public Artifact getDeployJar() { + return deployJar; + } + + @Override + public Artifact getFinalClassesDexZip() { + return finalClassesDexZip; + } + + @Override + @Nullable + public Artifact getJavaResourceJar() { + return javaResourceJar; + } + + /** Provider for {@link AndroidDexInfo}. */ + public static class Provider extends BuiltinProvider + implements AndroidDexInfoApi.Provider { + + private Provider() { + super(PROVIDER_NAME, AndroidDexInfo.class); + } + + public String getName() { + return PROVIDER_NAME; + } + + @Override + public AndroidDexInfo createInfo( + Artifact deployJar, Artifact finalClassesDexZip, Object javaResourceJar) + throws EvalException { + + return new AndroidDexInfo( + deployJar, finalClassesDexZip, fromNoneable(javaResourceJar, Artifact.class)); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidBootstrap.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidBootstrap.java index 69edd3acc58e40..5e2860fd41cf71 100644 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidBootstrap.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidBootstrap.java @@ -64,7 +64,8 @@ public AndroidBootstrap( ProguardMappingProviderApi.Provider proguardMappingProviderApiProvider, AndroidBinaryDataInfoApi.Provider androidBinaryDataInfoProvider, AndroidBinaryNativeLibsInfoApi.Provider androidBinaryInternalNativeLibsInfoApiProvider, - BaselineProfileProviderApi.Provider baselineProfileProvider) { + BaselineProfileProviderApi.Provider baselineProfileProvider, + AndroidDexInfoApi.Provider androidDexInfoApiProvider) { this.androidCommon = androidCommon; ImmutableMap.Builder builder = ImmutableMap.builder(); @@ -93,6 +94,7 @@ public AndroidBootstrap( builder.put(ProguardMappingProviderApi.NAME, proguardMappingProviderApiProvider); builder.put(AndroidBinaryDataInfoApi.NAME, androidBinaryDataInfoProvider); builder.put(BaselineProfileProviderApi.NAME, baselineProfileProvider); + builder.put(AndroidDexInfoApi.NAME, androidDexInfoApiProvider); providers = builder.build(); } diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java new file mode 100644 index 00000000000000..7e8e5bb0244f7e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java @@ -0,0 +1,105 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// 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.google.devtools.build.lib.starlarkbuildapi.android; + +import com.google.devtools.build.docgen.annot.StarlarkConstructor; +import com.google.devtools.build.lib.starlarkbuildapi.FileApi; +import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi; +import com.google.devtools.build.lib.starlarkbuildapi.core.StructApi; +import javax.annotation.Nullable; +import net.starlark.java.annot.Param; +import net.starlark.java.annot.ParamType; +import net.starlark.java.annot.StarlarkBuiltin; +import net.starlark.java.annot.StarlarkMethod; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.NoneType; + +/** An Info that can provides dex artifacts. */ +@StarlarkBuiltin( + name = "AndroidDexInfo", + doc = + "Do not use this module. It is intended for migration purposes only. If you depend on it, " + + "you will be broken when it is removed.", + documented = false) +public interface AndroidDexInfoApi extends StructApi { + + /** Name of this info object. */ + String NAME = "AndroidDexInfo"; + + @StarlarkMethod( + name = "deploy_jar", + doc = "The deploy jar.", + documented = false, + structField = true) + FileT getDeployJar(); + + @StarlarkMethod( + name = "final_classes_dex_zip", + doc = "The zip file containing the final dex classes.", + documented = false, + structField = true) + FileT getFinalClassesDexZip(); + + @Nullable + @StarlarkMethod( + name = "java_resource_jar", + doc = "The final Java resource jar.", + documented = false, + structField = true, + allowReturnNones = true) + FileT getJavaResourceJar(); + + /** Provider for {@link AndroidDexInfoApi}. */ + @StarlarkBuiltin( + name = "Provider", + doc = + "Do not use this module. It is intended for migration purposes only. If you depend on " + + "it, you will be broken when it is removed.", + documented = false) + interface Provider extends ProviderApi { + + @StarlarkMethod( + name = NAME, + doc = "The AndroidDexInfo constructor.", + documented = false, + parameters = { + @Param( + name = "deploy_jar", + allowedTypes = { + @ParamType(type = FileApi.class), + }, + named = true, + doc = "The \"_deploy\" jar suitable for deployment."), + @Param( + name = "final_classes_dex_zip", + allowedTypes = { + @ParamType(type = FileApi.class), + }, + named = true, + doc = "The zip file containing the final dex classes."), + @Param( + name = "java_resource_jar", + allowedTypes = { + @ParamType(type = FileApi.class), + @ParamType(type = NoneType.class), + }, + named = true, + doc = "The final Java resource jar."), + }, + selfCall = true) + @StarlarkConstructor + AndroidDexInfoApi createInfo( + FileT deployJar, FileT finalClassesDexZip, Object javaResourceJar) throws EvalException; + } +} diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java index 3e735a6d3397e8..00d2cb12342659 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java @@ -189,6 +189,11 @@ protected Artifact getFinalUnsignedApk(ConfiguredTarget target) { target.getProvider(FileProvider.class).getFilesToBuild(), "_unsigned.apk"); } + protected Artifact getDeployJar(ConfiguredTarget target) { + return getFirstArtifactEndingWith( + target.getProvider(FileProvider.class).getFilesToBuild(), "_deploy.jar"); + } + protected Artifact getResourceApk(ConfiguredTarget target) { Artifact resourceApk = getFirstArtifactEndingWith(