1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package apex 16 17import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "android/soong/android" 23 "android/soong/java" 24 "github.com/google/blueprint" 25 "github.com/google/blueprint/proptools" 26) 27 28// Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires 29// apexes. 30 31var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers( 32 java.PrepareForTestWithDexpreopt, 33 PrepareForTestWithApexBuildComponents, 34) 35 36func TestPlatformBootclasspath_Fragments(t *testing.T) { 37 result := android.GroupFixturePreparers( 38 prepareForTestWithPlatformBootclasspath, 39 prepareForTestWithMyapex, 40 java.PrepareForTestWithJavaSdkLibraryFiles, 41 java.FixtureWithLastReleaseApis("foo"), 42 java.FixtureConfigureBootJars("myapex:bar"), 43 android.FixtureWithRootAndroidBp(` 44 platform_bootclasspath { 45 name: "platform-bootclasspath", 46 fragments: [ 47 { 48 apex: "myapex", 49 module:"bar-fragment", 50 }, 51 ], 52 hidden_api: { 53 unsupported: [ 54 "unsupported.txt", 55 ], 56 removed: [ 57 "removed.txt", 58 ], 59 max_target_r_low_priority: [ 60 "max-target-r-low-priority.txt", 61 ], 62 max_target_q: [ 63 "max-target-q.txt", 64 ], 65 max_target_p: [ 66 "max-target-p.txt", 67 ], 68 max_target_o_low_priority: [ 69 "max-target-o-low-priority.txt", 70 ], 71 blocked: [ 72 "blocked.txt", 73 ], 74 unsupported_packages: [ 75 "unsupported-packages.txt", 76 ], 77 }, 78 } 79 80 apex { 81 name: "myapex", 82 key: "myapex.key", 83 bootclasspath_fragments: [ 84 "bar-fragment", 85 ], 86 updatable: false, 87 } 88 89 apex_key { 90 name: "myapex.key", 91 public_key: "testkey.avbpubkey", 92 private_key: "testkey.pem", 93 } 94 95 bootclasspath_fragment { 96 name: "bar-fragment", 97 contents: ["bar"], 98 apex_available: ["myapex"], 99 api: { 100 stub_libs: ["foo"], 101 }, 102 hidden_api: { 103 unsupported: [ 104 "bar-unsupported.txt", 105 ], 106 removed: [ 107 "bar-removed.txt", 108 ], 109 max_target_r_low_priority: [ 110 "bar-max-target-r-low-priority.txt", 111 ], 112 max_target_q: [ 113 "bar-max-target-q.txt", 114 ], 115 max_target_p: [ 116 "bar-max-target-p.txt", 117 ], 118 max_target_o_low_priority: [ 119 "bar-max-target-o-low-priority.txt", 120 ], 121 blocked: [ 122 "bar-blocked.txt", 123 ], 124 unsupported_packages: [ 125 "bar-unsupported-packages.txt", 126 ], 127 }, 128 } 129 130 java_library { 131 name: "bar", 132 apex_available: ["myapex"], 133 srcs: ["a.java"], 134 system_modules: "none", 135 sdk_version: "none", 136 compile_dex: true, 137 permitted_packages: ["bar"], 138 } 139 140 java_sdk_library { 141 name: "foo", 142 srcs: ["a.java"], 143 public: { 144 enabled: true, 145 }, 146 compile_dex: true, 147 } 148 `), 149 ).RunTest(t) 150 151 pbcp := result.Module("platform-bootclasspath", "android_common") 152 info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo) 153 154 for _, category := range java.HiddenAPIFlagFileCategories { 155 name := category.PropertyName 156 message := fmt.Sprintf("category %s", name) 157 filename := strings.ReplaceAll(name, "_", "-") 158 expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)} 159 android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category]) 160 } 161 162 android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths) 163 android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths) 164 android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths) 165 android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths) 166 android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths) 167} 168 169func TestPlatformBootclasspathDependencies(t *testing.T) { 170 result := android.GroupFixturePreparers( 171 prepareForTestWithPlatformBootclasspath, 172 prepareForTestWithArtApex, 173 prepareForTestWithMyapex, 174 // Configure some libraries in the art and framework boot images. 175 java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo"), 176 java.FixtureConfigureUpdatableBootJars("myapex:bar"), 177 java.PrepareForTestWithJavaSdkLibraryFiles, 178 java.FixtureWithLastReleaseApis("foo"), 179 ).RunTestWithBp(t, ` 180 apex { 181 name: "com.android.art", 182 key: "com.android.art.key", 183 bootclasspath_fragments: [ 184 "art-bootclasspath-fragment", 185 ], 186 updatable: false, 187 } 188 189 apex_key { 190 name: "com.android.art.key", 191 public_key: "com.android.art.avbpubkey", 192 private_key: "com.android.art.pem", 193 } 194 195 bootclasspath_fragment { 196 name: "art-bootclasspath-fragment", 197 apex_available: [ 198 "com.android.art", 199 ], 200 contents: [ 201 "baz", 202 "quuz", 203 ], 204 } 205 206 java_library { 207 name: "baz", 208 apex_available: [ 209 "com.android.art", 210 ], 211 srcs: ["b.java"], 212 installable: true, 213 } 214 215 // Add a java_import that is not preferred and so won't have an appropriate apex variant created 216 // for it to make sure that the platform_bootclasspath doesn't try and add a dependency onto it. 217 java_import { 218 name: "baz", 219 apex_available: [ 220 "com.android.art", 221 ], 222 jars: ["b.jar"], 223 } 224 225 java_library { 226 name: "quuz", 227 apex_available: [ 228 "com.android.art", 229 ], 230 srcs: ["b.java"], 231 installable: true, 232 } 233 234 apex { 235 name: "myapex", 236 key: "myapex.key", 237 bootclasspath_fragments: [ 238 "my-bootclasspath-fragment", 239 ], 240 updatable: false, 241 } 242 243 bootclasspath_fragment { 244 name: "my-bootclasspath-fragment", 245 contents: ["bar"], 246 apex_available: ["myapex"], 247 } 248 249 apex_key { 250 name: "myapex.key", 251 public_key: "testkey.avbpubkey", 252 private_key: "testkey.pem", 253 } 254 255 java_sdk_library { 256 name: "foo", 257 srcs: ["b.java"], 258 } 259 260 java_library { 261 name: "bar", 262 srcs: ["b.java"], 263 installable: true, 264 apex_available: ["myapex"], 265 permitted_packages: ["bar"], 266 } 267 268 platform_bootclasspath { 269 name: "myplatform-bootclasspath", 270 271 fragments: [ 272 { 273 apex: "com.android.art", 274 module: "art-bootclasspath-fragment", 275 }, 276 { 277 apex: "myapex", 278 module: "my-bootclasspath-fragment", 279 }, 280 ], 281 } 282`, 283 ) 284 285 java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{ 286 // The configured contents of BootJars. 287 "com.android.art:baz", 288 "com.android.art:quuz", 289 "platform:foo", 290 291 // The configured contents of UpdatableBootJars. 292 "myapex:bar", 293 }) 294 295 java.CheckPlatformBootclasspathFragments(t, result, "myplatform-bootclasspath", []string{ 296 "com.android.art:art-bootclasspath-fragment", 297 "myapex:my-bootclasspath-fragment", 298 }) 299 300 // Make sure that the myplatform-bootclasspath has the correct dependencies. 301 CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ 302 // The following are stubs. 303 `platform:android_stubs_current`, 304 `platform:android_system_stubs_current`, 305 `platform:android_test_stubs_current`, 306 `platform:legacy.core.platform.api.stubs`, 307 308 // Needed for generating the boot image. 309 `platform:dex2oatd`, 310 311 // The configured contents of BootJars. 312 `com.android.art:baz`, 313 `com.android.art:quuz`, 314 `platform:foo`, 315 316 // The configured contents of UpdatableBootJars. 317 `myapex:bar`, 318 319 // The fragments. 320 `com.android.art:art-bootclasspath-fragment`, 321 `myapex:my-bootclasspath-fragment`, 322 }) 323} 324 325// TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when 326// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what 327// currently exists in some places in the Android build but it is not the intended structure. It is 328// in fact an invalid structure that should cause build failures. However, fixing that structure 329// will take too long so in the meantime this tests the workarounds to avoid build breakages. 330// 331// The main issues with this structure are: 332// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import. 333// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the 334// prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import. 335// 336// Together these cause the following symptoms: 337// 1. The "foo" java_sdk_library_import does not have a dex implementation jar. 338// 2. The "foo" java_sdk_library_import does not have a myapex variant. 339// 340// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed. 341func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { 342 result := android.GroupFixturePreparers( 343 prepareForTestWithPlatformBootclasspath, 344 prepareForTestWithMyapex, 345 // Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because 346 // of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide 347 // a boot dex jar. The second is a normal library that is unaffected. The order matters because 348 // if the dependency on myapex:foo is filtered out because of either of those conditions then 349 // the dependencies resolved by the platform_bootclasspath will not match the configured list 350 // and so will fail the test. 351 java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"), 352 java.PrepareForTestWithJavaSdkLibraryFiles, 353 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 354 variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) 355 }), 356 java.FixtureWithPrebuiltApis(map[string][]string{ 357 "current": {}, 358 "30": {"foo"}, 359 }), 360 ).RunTestWithBp(t, ` 361 apex { 362 name: "myapex", 363 key: "myapex.key", 364 bootclasspath_fragments: [ 365 "mybootclasspath-fragment", 366 ], 367 updatable: false, 368 } 369 370 apex_key { 371 name: "myapex.key", 372 public_key: "testkey.avbpubkey", 373 private_key: "testkey.pem", 374 } 375 376 java_library { 377 name: "bar", 378 srcs: ["b.java"], 379 installable: true, 380 apex_available: ["myapex"], 381 permitted_packages: ["bar"], 382 } 383 384 java_sdk_library { 385 name: "foo", 386 srcs: ["b.java"], 387 shared_library: false, 388 public: { 389 enabled: true, 390 }, 391 apex_available: ["myapex"], 392 permitted_packages: ["foo"], 393 } 394 395 // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred 396 // because AlwaysUsePrebuiltSdks() is true. 397 java_sdk_library_import { 398 name: "foo", 399 prefer: false, 400 shared_library: false, 401 permitted_packages: ["foo"], 402 public: { 403 jars: ["sdk_library/public/foo-stubs.jar"], 404 stub_srcs: ["sdk_library/public/foo_stub_sources"], 405 current_api: "sdk_library/public/foo.txt", 406 removed_api: "sdk_library/public/foo-removed.txt", 407 sdk_version: "current", 408 }, 409 apex_available: ["myapex"], 410 } 411 412 // This always depends on the source foo module, its dependencies are not affected by the 413 // AlwaysUsePrebuiltSdks(). 414 bootclasspath_fragment { 415 name: "mybootclasspath-fragment", 416 apex_available: [ 417 "myapex", 418 ], 419 contents: [ 420 "foo", "bar", 421 ], 422 } 423 424 platform_bootclasspath { 425 name: "myplatform-bootclasspath", 426 fragments: [ 427 { 428 apex: "myapex", 429 module:"mybootclasspath-fragment", 430 }, 431 ], 432 } 433`, 434 ) 435 436 java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{ 437 // The configured contents of BootJars. 438 "platform:prebuilt_foo", // Note: This is the platform not myapex variant. 439 "myapex:bar", 440 }) 441 442 // Make sure that the myplatform-bootclasspath has the correct dependencies. 443 CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ 444 // The following are stubs. 445 "platform:prebuilt_sdk_public_current_android", 446 "platform:prebuilt_sdk_system_current_android", 447 "platform:prebuilt_sdk_test_current_android", 448 449 // Not a prebuilt as no prebuilt existed when it was added. 450 "platform:legacy.core.platform.api.stubs", 451 452 // Needed for generating the boot image. 453 "platform:dex2oatd", 454 455 // The platform_bootclasspath intentionally adds dependencies on both source and prebuilt 456 // modules when available as it does not know which one will be preferred. 457 // 458 // The source module has an APEX variant but the prebuilt does not. 459 "myapex:foo", 460 "platform:prebuilt_foo", 461 462 // Only a source module exists. 463 "myapex:bar", 464 465 // The fragments. 466 "myapex:mybootclasspath-fragment", 467 }) 468} 469 470// CheckModuleDependencies checks the dependencies of the selected module against the expected list. 471// 472// The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the 473// name of the apex, or platform is it is not part of an apex and <module> is the module name. 474func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) { 475 t.Helper() 476 module := ctx.ModuleForTests(name, variant).Module() 477 modules := []android.Module{} 478 ctx.VisitDirectDeps(module, func(m blueprint.Module) { 479 modules = append(modules, m.(android.Module)) 480 }) 481 482 pairs := java.ApexNamePairsFromModules(ctx, modules) 483 android.AssertDeepEquals(t, "module dependencies", expected, pairs) 484} 485