1// Copyright 2021 Google Inc. All rights reserved. 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 java 16 17import ( 18 "maps" 19 "slices" 20 21 "github.com/google/blueprint" 22 23 "android/soong/android" 24 "android/soong/dexpreopt" 25) 26 27func init() { 28 registerPlatformBootclasspathBuildComponents(android.InitRegistrationContext) 29} 30 31func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) { 32 ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory) 33} 34 35// The tags used for the dependencies between the platform bootclasspath and any configured boot 36// jars. 37type platformBootclasspathImplLibDepTagType struct { 38 blueprint.BaseDependencyTag 39} 40 41func (p platformBootclasspathImplLibDepTagType) ExcludeFromVisibilityEnforcement() {} 42 43var platformBootclasspathImplLibDepTag platformBootclasspathImplLibDepTagType 44 45var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathImplLibDepTag 46 47type platformBootclasspathModule struct { 48 android.ModuleBase 49 ClasspathFragmentBase 50 51 properties platformBootclasspathProperties 52 53 // The apex:module pairs obtained from the configured modules. 54 configuredModules []android.Module 55 56 // The apex:module pairs obtained from the fragments. 57 fragments []android.Module 58 59 // The map of apex to the fragments they contain. 60 apexNameToFragment map[string]android.Module 61 62 // The map of library modules in the bootclasspath to the fragments that contain them. 63 libraryToApex map[android.Module]string 64 65 // Path to the monolithic hiddenapi-flags.csv file. 66 hiddenAPIFlagsCSV android.OutputPath 67 68 // Path to the monolithic hiddenapi-index.csv file. 69 hiddenAPIIndexCSV android.OutputPath 70 71 // Path to the monolithic hiddenapi-unsupported.csv file. 72 hiddenAPIMetadataCSV android.OutputPath 73} 74 75type platformBootclasspathProperties struct { 76 BootclasspathFragmentsDepsProperties 77 78 HiddenAPIFlagFileProperties 79} 80 81func platformBootclasspathFactory() android.Module { 82 m := &platformBootclasspathModule{} 83 m.AddProperties(&m.properties) 84 initClasspathFragment(m, BOOTCLASSPATH) 85 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 86 return m 87} 88 89func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { 90 entries = append(entries, android.AndroidMkEntries{ 91 Class: "FAKE", 92 // Need at least one output file in order for this to take effect. 93 OutputFile: android.OptionalPathForPath(b.hiddenAPIFlagsCSV), 94 Include: "$(BUILD_PHONY_PACKAGE)", 95 }) 96 entries = append(entries, b.classpathFragmentBase().androidMkEntries()...) 97 return 98} 99 100func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) { 101 // Create a dependency on all_apex_contributions to determine the selected mainline module 102 ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions") 103 104 b.hiddenAPIDepsMutator(ctx) 105 106 if dexpreopt.IsDex2oatNeeded(ctx) { 107 // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The 108 // path is retrieved from the dependency by GetGlobalSoongConfig(ctx). 109 dexpreopt.RegisterToolDeps(ctx) 110 } 111 112 // Add dependencies on all the ART jars. 113 global := dexpreopt.GetGlobalConfig(ctx) 114 addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art") 115 116 var bootImageModuleNames []string 117 118 // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly 119 addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, artBootJar) 120 bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...) 121 122 // Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable 123 // APEXes. 124 platformJars := b.platformJars(ctx) 125 addDependenciesOntoBootImageModules(ctx, platformJars, platformBootJar) 126 bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...) 127 128 // Add dependencies on all the updatable jars, except the ART jars. 129 apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars 130 apexes := []string{} 131 for i := 0; i < apexJars.Len(); i++ { 132 apexes = append(apexes, apexJars.Apex(i)) 133 } 134 addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...) 135 // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly 136 addDependenciesOntoBootImageModules(ctx, apexJars, apexBootJar) 137 bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...) 138 139 // Add dependencies on all the fragments. 140 b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx) 141 142 for _, bootImageModuleName := range bootImageModuleNames { 143 implLibName := implLibraryModuleName(bootImageModuleName) 144 if ctx.OtherModuleExists(implLibName) { 145 ctx.AddFarVariationDependencies(nil, platformBootclasspathImplLibDepTag, implLibName) 146 } 147 } 148} 149 150func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) { 151 if ctx.Config().DisableHiddenApiChecks() { 152 return 153 } 154 155 // Add dependencies onto the stub lib modules. 156 apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config()) 157 hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules) 158} 159 160func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tagType bootclasspathDependencyTagType) { 161 for i := 0; i < modules.Len(); i++ { 162 apex := modules.Apex(i) 163 name := modules.Jar(i) 164 165 addDependencyOntoApexModulePair(ctx, apex, name, tagType) 166 } 167} 168 169func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 170 // Gather all the dependencies from the art, platform, and apex boot jars. 171 artModules, artModulesToApex := gatherApexModulePairDepsWithTag(ctx, artBootJar) 172 platformModules, platformModulesToApex := gatherApexModulePairDepsWithTag(ctx, platformBootJar) 173 apexModules, apexModulesToApex := gatherApexModulePairDepsWithTag(ctx, apexBootJar) 174 175 // Concatenate them all, in order as they would appear on the bootclasspath. 176 allModules := slices.Concat(artModules, platformModules, apexModules) 177 b.configuredModules = allModules 178 b.libraryToApex = maps.Clone(artModulesToApex) 179 maps.Copy(b.libraryToApex, platformModulesToApex) 180 maps.Copy(b.libraryToApex, apexModulesToApex) 181 182 // Do not add implLibModule to allModules as the impl lib is only used to collect the 183 // transitive source files 184 var implLibModule []android.Module 185 ctx.VisitDirectDepsWithTag(platformBootclasspathImplLibDepTag, func(m android.Module) { 186 implLibModule = append(implLibModule, m) 187 }) 188 189 var transitiveSrcFiles android.Paths 190 for _, module := range append(allModules, implLibModule...) { 191 if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { 192 transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...) 193 } 194 } 195 jarArgs := resourcePathsToJarArgs(transitiveSrcFiles) 196 jarArgs = append(jarArgs, "-srcjar") // Move srcfiles to the right package 197 srcjar := android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar") 198 TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles) 199 200 // Gather all the fragments dependencies. 201 b.fragments, b.apexNameToFragment = gatherFragments(ctx) 202 203 // Check the configuration of the boot modules. 204 // ART modules are checked by the art-bootclasspath-fragment. 205 b.checkPlatformModules(ctx, platformModules) 206 b.checkApexModules(ctx, apexModules) 207 208 b.generateClasspathProtoBuildActions(ctx) 209 210 bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments, b.libraryToApex, b.apexNameToFragment) 211 buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule) 212 213 ctx.SetOutputFiles(android.Paths{b.hiddenAPIFlagsCSV}, "hiddenapi-flags.csv") 214 ctx.SetOutputFiles(android.Paths{b.hiddenAPIIndexCSV}, "hiddenapi-index.csv") 215 ctx.SetOutputFiles(android.Paths{b.hiddenAPIMetadataCSV}, "hiddenapi-metadata.csv") 216 ctx.SetOutputFiles(android.Paths{srcjar}, ".srcjar") 217} 218 219// Generate classpaths.proto config 220func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { 221 configuredJars := b.configuredJars(ctx) 222 // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH 223 classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) 224 b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) 225 b.classpathFragmentBase().installClasspathProto(ctx) 226} 227 228func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { 229 // Include all non APEX jars 230 jars := b.platformJars(ctx) 231 232 // Include jars from APEXes that don't populate their classpath proto config. 233 remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars 234 for _, fragment := range b.fragments { 235 info, _ := android.OtherModuleProvider(ctx, fragment, ClasspathFragmentProtoContentInfoProvider) 236 if info.ClasspathFragmentProtoGenerated { 237 remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents) 238 } 239 } 240 for i := 0; i < remainingJars.Len(); i++ { 241 jars = jars.Append(remainingJars.Apex(i), remainingJars.Jar(i)) 242 } 243 244 return jars 245} 246 247func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) android.ConfiguredJarList { 248 global := dexpreopt.GetGlobalConfig(ctx) 249 return global.BootJars.RemoveList(global.ArtApexJars) 250} 251 252// checkPlatformModules ensures that the non-updatable modules supplied are not part of an 253// apex module. 254func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) { 255 // TODO(satayev): change this check to only allow core-icu4j, all apex jars should not be here. 256 for _, m := range modules { 257 apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) 258 fromUpdatableApex := apexInfo.Updatable 259 if fromUpdatableApex { 260 // error: this jar is part of an updatable apex 261 ctx.ModuleErrorf("module %q from updatable apex %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.BaseApexName) 262 } else { 263 // ok: this jar is part of the platform or a non-updatable apex 264 } 265 } 266} 267 268// checkApexModules ensures that the apex modules supplied are not from the platform. 269func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext, modules []android.Module) { 270 for _, m := range modules { 271 apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) 272 fromUpdatableApex := apexInfo.Updatable 273 if fromUpdatableApex { 274 // ok: this jar is part of an updatable apex 275 } else { 276 name := ctx.OtherModuleName(m) 277 if apexInfo.IsForPlatform() { 278 // If AlwaysUsePrebuiltSdks() returns true then it is possible that the updatable list will 279 // include platform variants of a prebuilt module due to workarounds elsewhere. In that case 280 // do not treat this as an error. 281 // TODO(b/179354495): Always treat this as an error when migration to bootclasspath_fragment 282 // modules is complete. 283 if !ctx.Config().AlwaysUsePrebuiltSdks() { 284 // error: this jar is part of the platform 285 if ctx.Config().AllowMissingDependencies() { 286 ctx.AddMissingDependencies([]string{"module_" + name + "_from_platform_is_not_allowed_in_the_apex_boot_jars_list"}) 287 } else { 288 ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name) 289 } 290 } 291 } else { 292 // TODO(b/177892522): Treat this as an error. 293 // Cannot do that at the moment because framework-wifi and framework-tethering are in the 294 // PRODUCT_APEX_BOOT_JARS but not marked as updatable in AOSP. 295 } 296 } 297 } 298} 299 300// generateHiddenAPIBuildActions generates all the hidden API related build rules. 301func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, 302 fragments []android.Module, libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) bootDexJarByModule { 303 createEmptyHiddenApiFiles := func() { 304 paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV} 305 for _, path := range paths { 306 ctx.Build(pctx, android.BuildParams{ 307 Rule: android.Touch, 308 Output: path, 309 }) 310 } 311 } 312 313 // Save the paths to the monolithic files for retrieval via OutputFiles(). 314 b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags 315 b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index 316 b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata 317 318 bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules) 319 320 // Don't run any hiddenapi rules if hidden api checks are disabled. This is a performance 321 // optimization that can be used to reduce the incremental build time but as its name suggests it 322 // can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath. 323 if ctx.Config().DisableHiddenApiChecks() { 324 createEmptyHiddenApiFiles() 325 return bootDexJarByModule 326 } 327 328 // Construct a list of ClasspathElement objects from the modules and fragments. 329 classpathElements := CreateClasspathElements(ctx, modules, fragments, libraryToApex, apexNameToFragment) 330 331 monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements) 332 333 // Extract the classes jars only from those libraries that do not have corresponding fragments as 334 // the fragments will have already provided the flags that are needed. 335 classesJars := monolithicInfo.ClassesJars 336 337 if len(classesJars) == 0 { 338 // This product does not include any monolithic jars. Monolithic hiddenapi flag generation is not required. 339 // However, generate an empty file so that the dist tags in f/b/boot/Android.bp can be resolved, and `m dist` works. 340 createEmptyHiddenApiFiles() 341 return bootDexJarByModule 342 } 343 344 // Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile 345 input := newHiddenAPIFlagInput() 346 347 // Gather stub library information from the dependencies on modules provided by 348 // hiddenAPIComputeMonolithicStubLibModules. 349 input.gatherStubLibInfo(ctx, nil) 350 351 // Use the flag files from this module and all the fragments. 352 input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory 353 354 // Generate the monolithic stub-flags.csv file. 355 stubFlags := hiddenAPISingletonPaths(ctx).stubFlags 356 buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagSubsets) 357 358 // Generate the annotation-flags.csv file from all the module annotations. 359 annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv") 360 buildRuleToGenerateAnnotationFlags(ctx, "intermediate hidden API flags", classesJars, stubFlags, annotationFlags) 361 362 // Generate the monolithic hiddenapi-flags.csv file. 363 // 364 // Use annotation flags generated directly from the classes jars as well as annotation flag files 365 // provided by prebuilts. 366 allAnnotationFlagFiles := android.Paths{annotationFlags} 367 allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...) 368 allFlags := hiddenAPISingletonPaths(ctx).flags 369 buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.FlagSubsets, android.OptionalPath{}) 370 371 // Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations 372 // in the source code. 373 intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "metadata-from-classes.csv") 374 buildRuleToGenerateMetadata(ctx, "intermediate hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV) 375 376 // Generate the monolithic hiddenapi-metadata.csv file. 377 // 378 // Use metadata files generated directly from the classes jars as well as metadata files provided 379 // by prebuilts. 380 // 381 // This has the side effect of ensuring that the output file uses | quotes just in case that is 382 // important for the tools that consume the metadata file. 383 allMetadataFlagFiles := android.Paths{intermediateMetadataCSV} 384 allMetadataFlagFiles = append(allMetadataFlagFiles, monolithicInfo.MetadataPaths...) 385 metadataCSV := hiddenAPISingletonPaths(ctx).metadata 386 b.buildRuleMergeCSV(ctx, "monolithic hidden API metadata", allMetadataFlagFiles, metadataCSV) 387 388 // Generate an intermediate monolithic hiddenapi-index.csv file directly from the CSV files in the 389 // classes jars. 390 intermediateIndexCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "index-from-classes.csv") 391 buildRuleToGenerateIndex(ctx, "intermediate hidden API index", classesJars, intermediateIndexCSV) 392 393 // Generate the monolithic hiddenapi-index.csv file. 394 // 395 // Use index files generated directly from the classes jars as well as index files provided 396 // by prebuilts. 397 allIndexFlagFiles := android.Paths{intermediateIndexCSV} 398 allIndexFlagFiles = append(allIndexFlagFiles, monolithicInfo.IndexPaths...) 399 indexCSV := hiddenAPISingletonPaths(ctx).index 400 b.buildRuleMergeCSV(ctx, "monolithic hidden API index", allIndexFlagFiles, indexCSV) 401 402 return bootDexJarByModule 403} 404 405// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for 406// testing. 407func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, classpathElements ClasspathElements) MonolithicHiddenAPIInfo { 408 // Create a temporary input structure in which to collate information provided directly by this 409 // module, either through properties or direct dependencies. 410 temporaryInput := newHiddenAPIFlagInput() 411 412 // Create paths to the flag files specified in the properties. 413 temporaryInput.extractFlagFilesFromProperties(ctx, &b.properties.HiddenAPIFlagFileProperties) 414 415 // Create the monolithic info, by starting with the flag files specified on this and then merging 416 // in information from all the fragment dependencies of this. 417 monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements) 418 419 // Store the information for testing. 420 android.SetProvider(ctx, MonolithicHiddenAPIInfoProvider, monolithicInfo) 421 return monolithicInfo 422} 423 424func (b *platformBootclasspathModule) buildRuleMergeCSV(ctx android.ModuleContext, desc string, inputPaths android.Paths, outputPath android.WritablePath) { 425 rule := android.NewRuleBuilder(pctx, ctx) 426 rule.Command(). 427 BuiltTool("merge_csv"). 428 Flag("--key_field signature"). 429 FlagWithOutput("--output=", outputPath). 430 Inputs(inputPaths) 431 432 rule.Build(desc, desc) 433} 434