@@ -184,6 +184,13 @@ private static void validateRuleContext(RuleContext ruleContext, AndroidDataCont
184184 ruleContext .attributeError ("multidex" , "Multidex must be enabled" );
185185 }
186186
187+ if (ruleContext .attributes ().isAttributeValueExplicitlySpecified ("min_sdk_version" )
188+ && Allowlist .hasAllowlist (ruleContext , "allow_min_sdk_version" )
189+ && !Allowlist .isAvailable (ruleContext , "allow_min_sdk_version" )) {
190+ ruleContext .attributeError (
191+ "min_sdk_version" , "Target is not permitted to set min_sdk_version" );
192+ }
193+
187194 if (AndroidCommon .getAndroidConfig (ruleContext ).desugarJava8Libs ()
188195 && getMultidexMode (ruleContext ) == MultidexMode .OFF ) {
189196 // Multidex is required so we can include legacy libs as a separate .dex file.
@@ -263,30 +270,6 @@ private static RuleConfiguredTargetBuilder init(
263270 : null );
264271 }
265272
266- Artifact manifestValidation = null ;
267- if (Allowlist .hasAllowlist (ruleContext , "android_multidex_native_min_sdk_allowlist" )
268- && !Allowlist .isAvailable (ruleContext , "android_multidex_native_min_sdk_allowlist" )
269- && getMultidexMode (ruleContext ) == MultidexMode .NATIVE
270- && ruleContext .isAttrDefined ("$validate_manifest" , LABEL )) {
271- manifestValidation =
272- ruleContext .getPackageRelativeArtifact (
273- ruleContext .getLabel ().getName () + "_manifest_validation_output" ,
274- ruleContext .getBinOrGenfilesDirectory ());
275- ruleContext .registerAction (
276- createSpawnActionBuilder (ruleContext )
277- .setExecutable (ruleContext .getExecutablePrerequisite ("$validate_manifest" ))
278- .setProgressMessage ("Validating %{input}" )
279- .setMnemonic ("ValidateManifest" )
280- .addInput (manifest .getManifest ())
281- .addOutput (manifestValidation )
282- .addCommandLine (
283- CustomCommandLine .builder ()
284- .addExecPath ("--manifest" , manifest .getManifest ())
285- .addExecPath ("--output" , manifestValidation )
286- .build ())
287- .build (ruleContext ));
288- }
289-
290273 boolean shrinkResourceCycles =
291274 shouldShrinkResourceCycles (
292275 dataContext .getAndroidConfig (), ruleContext , dataContext .isResourceShrinkingEnabled ());
@@ -415,6 +398,38 @@ && getMultidexMode(ruleContext) == MultidexMode.NATIVE
415398 AndroidBinaryMobileInstall .createMobileInstallResourceApks (
416399 ruleContext , dataContext , manifest );
417400
401+ Artifact manifestValidation = null ;
402+ boolean shouldValidateMultidex =
403+ (Allowlist .hasAllowlist (ruleContext , "android_multidex_native_min_sdk_allowlist" )
404+ && !Allowlist .isAvailable (ruleContext , "android_multidex_native_min_sdk_allowlist" )
405+ && getMultidexMode (ruleContext ) == MultidexMode .NATIVE );
406+ boolean shouldValidateMinSdk = getMinSdkVersion (ruleContext ) > 0 ;
407+ if (ruleContext .isAttrDefined ("$validate_manifest" , LABEL )
408+ && (shouldValidateMultidex || shouldValidateMinSdk )) {
409+ manifestValidation =
410+ ruleContext .getPackageRelativeArtifact (
411+ ruleContext .getLabel ().getName () + "_manifest_validation_output" ,
412+ ruleContext .getBinOrGenfilesDirectory ());
413+ ruleContext .registerAction (
414+ createSpawnActionBuilder (ruleContext )
415+ .setExecutable (ruleContext .getExecutablePrerequisite ("$validate_manifest" ))
416+ .setProgressMessage ("Validating %{input}" )
417+ .setMnemonic ("ValidateManifest" )
418+ .addInput (manifest .getManifest ())
419+ .addOutput (manifestValidation )
420+ .addCommandLine (
421+ CustomCommandLine .builder ()
422+ .addExecPath ("--manifest" , manifest .getManifest ())
423+ .addExecPath ("--output" , manifestValidation )
424+ .addFormatted (
425+ "--validate_multidex=%s" , Boolean .toString (shouldValidateMultidex ))
426+ .add (
427+ "--expected_min_sdk_version" ,
428+ Integer .toString (getMinSdkVersion (ruleContext )))
429+ .build ())
430+ .build (ruleContext ));
431+ }
432+
418433 return createAndroidBinary (
419434 ruleContext ,
420435 dataContext ,
@@ -1267,6 +1282,8 @@ private static DexingOutput dex(
12671282 + "\" not supported by this version of the Android SDK" );
12681283 }
12691284
1285+ int minSdkVersion = getMinSdkVersion (ruleContext );
1286+
12701287 int dexShards = ruleContext .attributes ().get ("dex_shards" , Type .INTEGER ).toIntUnchecked ();
12711288 if (dexShards > 1 ) {
12721289 if (multidexMode == MultidexMode .OFF ) {
@@ -1306,6 +1323,7 @@ private static DexingOutput dex(
13061323 common ,
13071324 inclusionFilterJar ,
13081325 dexopts ,
1326+ minSdkVersion ,
13091327 androidSemantics ,
13101328 attributes ,
13111329 derivedJarFunction ,
@@ -1320,6 +1338,7 @@ private static DexingOutput dex(
13201338 proguardedJar ,
13211339 classesDex ,
13221340 dexopts ,
1341+ minSdkVersion ,
13231342 /*multidex=*/ false ,
13241343 /*mainDexList=*/ null );
13251344 }
@@ -1356,6 +1375,7 @@ private static DexingOutput dex(
13561375 common ,
13571376 inclusionFilterJar ,
13581377 dexopts ,
1378+ minSdkVersion ,
13591379 androidSemantics ,
13601380 attributes ,
13611381 derivedJarFunction ,
@@ -1381,7 +1401,13 @@ private static DexingOutput dex(
13811401 dexopts );
13821402 } else {
13831403 AndroidCommon .createDexAction (
1384- ruleContext , shard , shardDex , dexopts , /*multidex=*/ true , /*mainDexList=*/ null );
1404+ ruleContext ,
1405+ shard ,
1406+ shardDex ,
1407+ dexopts ,
1408+ minSdkVersion ,
1409+ /*multidex=*/ true ,
1410+ /*mainDexList=*/ null );
13851411 }
13861412 }
13871413 ImmutableList <Artifact > shardDexes = shardDexesBuilder .build ();
@@ -1417,6 +1443,7 @@ private static DexingOutput dex(
14171443 common ,
14181444 inclusionFilterJar ,
14191445 dexopts ,
1446+ minSdkVersion ,
14201447 androidSemantics ,
14211448 attributes ,
14221449 derivedJarFunction ,
@@ -1436,6 +1463,7 @@ private static DexingOutput dex(
14361463 proguardedJar ,
14371464 classesDexIntermediate ,
14381465 dexopts ,
1466+ minSdkVersion ,
14391467 /*multidex=*/ true ,
14401468 mainDexList );
14411469 createCleanDexZipAction (ruleContext , classesDexIntermediate , classesDex );
@@ -1455,6 +1483,7 @@ private static void createIncrementalDexingActions(
14551483 AndroidCommon common ,
14561484 @ Nullable Artifact inclusionFilterJar ,
14571485 List <String > dexopts ,
1486+ int minSdkVersion ,
14581487 AndroidSemantics androidSemantics ,
14591488 JavaTargetAttributes attributes ,
14601489 Function <Artifact , Artifact > derivedJarFunction ,
@@ -1471,7 +1500,12 @@ private static void createIncrementalDexingActions(
14711500 ruleContext ,
14721501 collectRuntimeJars (common , attributes ),
14731502 collectDexArchives (
1474- ruleContext , common , dexopts , androidSemantics , derivedJarFunction ));
1503+ ruleContext ,
1504+ common ,
1505+ dexopts ,
1506+ minSdkVersion ,
1507+ androidSemantics ,
1508+ derivedJarFunction ));
14751509 } else {
14761510 if (proguardedJar != null
14771511 && AndroidCommon .getAndroidConfig (ruleContext ).incrementalDexingShardsAfterProguard ()
@@ -1493,6 +1527,7 @@ private static void createIncrementalDexingActions(
14931527 "$dexbuilder_after_proguard" ,
14941528 proguardedJar ,
14951529 DexArchiveAspect .topLevelDexbuilderDexopts (dexopts ),
1530+ minSdkVersion ,
14961531 dexArchives .get (0 ));
14971532 } else {
14981533 createShuffleJarActions (
@@ -1503,6 +1538,7 @@ private static void createIncrementalDexingActions(
15031538 common ,
15041539 inclusionFilterJar ,
15051540 dexopts ,
1541+ minSdkVersion ,
15061542 androidSemantics ,
15071543 attributes ,
15081544 derivedJarFunction ,
@@ -1771,6 +1807,7 @@ public static Function<Artifact, Artifact> collectDesugaredJars(
17711807 jar ,
17721808 attributes .getBootClassPath ().bootclasspath (),
17731809 attributes .getCompileTimeClassPath (),
1810+ getMinSdkVersion (ruleContext ),
17741811 ruleContext .getDerivedArtifact (
17751812 jarPath .replaceName (jarPath .getBaseName () + "_desugared.jar" ), jar .getRoot ()));
17761813 result .addDesugaredJar (jar , desugared );
@@ -1797,6 +1834,7 @@ private static Map<Artifact, Artifact> collectDexArchives(
17971834 RuleContext ruleContext ,
17981835 AndroidCommon common ,
17991836 List <String > dexopts ,
1837+ int minSdkVersion ,
18001838 AndroidSemantics semantics ,
18011839 Function <Artifact , Artifact > derivedJarFunction ) {
18021840 DexArchiveProvider .Builder result = new DexArchiveProvider .Builder ();
@@ -1820,6 +1858,7 @@ private static Map<Artifact, Artifact> collectDexArchives(
18201858 "$dexbuilder" ,
18211859 derivedJarFunction .apply (jar ),
18221860 incrementalDexopts ,
1861+ minSdkVersion ,
18231862 ruleContext .getDerivedArtifact (
18241863 jarPath .replaceName (jarPath .getBaseName () + ".dex.zip" ), jar .getRoot ()));
18251864 result .addDexArchive (incrementalDexopts , dexArchive , jar );
@@ -1835,6 +1874,7 @@ private static Artifact createShuffleJarActions(
18351874 AndroidCommon common ,
18361875 @ Nullable Artifact inclusionFilterJar ,
18371876 List <String > dexopts ,
1877+ int minSdkVersion ,
18381878 AndroidSemantics semantics ,
18391879 JavaTargetAttributes attributes ,
18401880 Function <Artifact , Artifact > derivedJarFunction ,
@@ -1889,7 +1929,8 @@ private static Artifact createShuffleJarActions(
18891929 // there should be very few or no Jar files that still end up in shards. The dexing
18901930 // step below will have to deal with those in addition to merging .dex files together.
18911931 Map <Artifact , Artifact > dexArchives =
1892- collectDexArchives (ruleContext , common , dexopts , semantics , derivedJarFunction );
1932+ collectDexArchives (
1933+ ruleContext , common , dexopts , minSdkVersion , semantics , derivedJarFunction );
18931934 classpath = toDexedClasspath (ruleContext , classpath , dexArchives );
18941935 shardCommandLine .add ("--split_dexed_classes" );
18951936 } else {
@@ -1916,6 +1957,7 @@ private static Artifact createShuffleJarActions(
19161957 "$dexbuilder_after_proguard" ,
19171958 shuffleOutputs .get (i ),
19181959 DexArchiveAspect .topLevelDexbuilderDexopts (dexopts ),
1960+ minSdkVersion ,
19191961 shards .get (i ));
19201962 }
19211963 }
@@ -2178,6 +2220,13 @@ public static MultidexMode getMultidexMode(RuleContext ruleContext) {
21782220 }
21792221 }
21802222
2223+ private static int getMinSdkVersion (RuleContext ruleContext ) {
2224+ if (ruleContext .getRule ().isAttrDefined ("min_sdk_version" , Type .INTEGER )) {
2225+ return ruleContext .attributes ().get ("min_sdk_version" , Type .INTEGER ).toIntUnchecked ();
2226+ }
2227+ return 0 ;
2228+ }
2229+
21812230 /**
21822231 * List of Android SDKs that contain runtimes that do not support the native multidexing
21832232 * introduced in Android L. If someone tries to build an android_binary that has multidex=native
0 commit comments