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 "fmt" 19 "path/filepath" 20 "regexp" 21 "sort" 22 "strings" 23 24 "github.com/google/blueprint/proptools" 25 26 "android/soong/android" 27 "android/soong/bazel" 28 "android/soong/java/config" 29 "android/soong/remoteexec" 30) 31 32// The values allowed for Droidstubs' Api_levels_sdk_type 33var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"} 34 35func init() { 36 RegisterStubsBuildComponents(android.InitRegistrationContext) 37} 38 39func RegisterStubsBuildComponents(ctx android.RegistrationContext) { 40 ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory) 41 42 ctx.RegisterModuleType("droidstubs", DroidstubsFactory) 43 ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory) 44 45 ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) 46} 47 48// Droidstubs 49type Droidstubs struct { 50 Javadoc 51 52 properties DroidstubsProperties 53 apiFile android.Path 54 removedApiFile android.Path 55 nullabilityWarningsFile android.WritablePath 56 57 checkCurrentApiTimestamp android.WritablePath 58 updateCurrentApiTimestamp android.WritablePath 59 checkLastReleasedApiTimestamp android.WritablePath 60 apiLintTimestamp android.WritablePath 61 apiLintReport android.WritablePath 62 63 checkNullabilityWarningsTimestamp android.WritablePath 64 65 annotationsZip android.WritablePath 66 apiVersionsXml android.WritablePath 67 68 metadataZip android.WritablePath 69 metadataDir android.WritablePath 70} 71 72type DroidstubsProperties struct { 73 // The generated public API filename by Metalava, defaults to <module>_api.txt 74 Api_filename *string 75 76 // the generated removed API filename by Metalava, defaults to <module>_removed.txt 77 Removed_api_filename *string 78 79 Check_api struct { 80 Last_released ApiToCheck 81 82 Current ApiToCheck 83 84 Api_lint struct { 85 Enabled *bool 86 87 // If set, performs api_lint on any new APIs not found in the given signature file 88 New_since *string `android:"path"` 89 90 // If not blank, path to the baseline txt file for approved API lint violations. 91 Baseline_file *string `android:"path"` 92 } 93 } 94 95 // user can specify the version of previous released API file in order to do compatibility check. 96 Previous_api *string `android:"path"` 97 98 // is set to true, Metalava will allow framework SDK to contain annotations. 99 Annotations_enabled *bool 100 101 // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from. 102 Merge_annotations_dirs []string 103 104 // a list of top-level directories containing Java stub files to merge show/hide annotations from. 105 Merge_inclusion_annotations_dirs []string 106 107 // a file containing a list of classes to do nullability validation for. 108 Validate_nullability_from_list *string 109 110 // a file containing expected warnings produced by validation of nullability annotations. 111 Check_nullability_warnings *string 112 113 // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false. 114 Create_doc_stubs *bool 115 116 // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false. 117 // Has no effect if create_doc_stubs: true. 118 Output_javadoc_comments *bool 119 120 // if set to false then do not write out stubs. Defaults to true. 121 // 122 // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately. 123 Generate_stubs *bool 124 125 // if set to true, provides a hint to the build system that this rule uses a lot of memory, 126 // whicih can be used for scheduling purposes 127 High_mem *bool 128 129 // if set to true, Metalava will allow framework SDK to contain API levels annotations. 130 Api_levels_annotations_enabled *bool 131 132 // Apply the api levels database created by this module rather than generating one in this droidstubs. 133 Api_levels_module *string 134 135 // the dirs which Metalava extracts API levels annotations from. 136 Api_levels_annotations_dirs []string 137 138 // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system', 'module-lib' and 'system-server'; defaults to public. 139 Api_levels_sdk_type *string 140 141 // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. 142 Api_levels_jar_filename *string 143 144 // if set to true, collect the values used by the Dev tools and 145 // write them in files packaged with the SDK. Defaults to false. 146 Write_sdk_values *bool 147 148 // path or filegroup to file defining extension an SDK name <-> numerical ID mapping and 149 // what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info 150 Extensions_info_file *string `android:"path"` 151 152 // API surface of this module. If set, the module contributes to an API surface. 153 // For the full list of available API surfaces, refer to soong/android/sdk_version.go 154 Api_surface *string 155} 156 157// Used by xsd_config 158type ApiFilePath interface { 159 ApiFilePath() android.Path 160} 161 162type ApiStubsSrcProvider interface { 163 StubsSrcJar() android.Path 164} 165 166// Provider of information about API stubs, used by java_sdk_library. 167type ApiStubsProvider interface { 168 AnnotationsZip() android.Path 169 ApiFilePath 170 RemovedApiFilePath() android.Path 171 172 ApiStubsSrcProvider 173} 174 175// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be 176// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to 177// a droiddoc module to generate documentation. 178func DroidstubsFactory() android.Module { 179 module := &Droidstubs{} 180 181 module.AddProperties(&module.properties, 182 &module.Javadoc.properties) 183 184 InitDroiddocModule(module, android.HostAndDeviceSupported) 185 186 module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { 187 module.createApiContribution(ctx) 188 }) 189 return module 190} 191 192// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API 193// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be 194// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs 195// module when symbols needed by the source files are provided by java_library_host modules. 196func DroidstubsHostFactory() android.Module { 197 module := &Droidstubs{} 198 199 module.AddProperties(&module.properties, 200 &module.Javadoc.properties) 201 202 InitDroiddocModule(module, android.HostSupported) 203 return module 204} 205 206func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { 207 switch tag { 208 case "": 209 return android.Paths{d.stubsSrcJar}, nil 210 case ".docs.zip": 211 return android.Paths{d.docZip}, nil 212 case ".api.txt", android.DefaultDistTag: 213 // This is the default dist path for dist properties that have no tag property. 214 return android.Paths{d.apiFile}, nil 215 case ".removed-api.txt": 216 return android.Paths{d.removedApiFile}, nil 217 case ".annotations.zip": 218 return android.Paths{d.annotationsZip}, nil 219 case ".api_versions.xml": 220 return android.Paths{d.apiVersionsXml}, nil 221 default: 222 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 223 } 224} 225 226func (d *Droidstubs) AnnotationsZip() android.Path { 227 return d.annotationsZip 228} 229 230func (d *Droidstubs) ApiFilePath() android.Path { 231 return d.apiFile 232} 233 234func (d *Droidstubs) RemovedApiFilePath() android.Path { 235 return d.removedApiFile 236} 237 238func (d *Droidstubs) StubsSrcJar() android.Path { 239 return d.stubsSrcJar 240} 241 242var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"} 243var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"} 244var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"} 245var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"} 246 247func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { 248 d.Javadoc.addDeps(ctx) 249 250 if len(d.properties.Merge_annotations_dirs) != 0 { 251 for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs { 252 ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir) 253 } 254 } 255 256 if len(d.properties.Merge_inclusion_annotations_dirs) != 0 { 257 for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs { 258 ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir) 259 } 260 } 261 262 if len(d.properties.Api_levels_annotations_dirs) != 0 { 263 for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs { 264 ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir) 265 } 266 } 267 268 if d.properties.Api_levels_module != nil { 269 ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module)) 270 } 271} 272 273func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) { 274 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || 275 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || 276 String(d.properties.Api_filename) != "" { 277 filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") 278 uncheckedApiFile := android.PathForModuleOut(ctx, "metalava", filename) 279 cmd.FlagWithOutput("--api ", uncheckedApiFile) 280 d.apiFile = uncheckedApiFile 281 } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" { 282 // If check api is disabled then make the source file available for export. 283 d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) 284 } 285 286 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || 287 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || 288 String(d.properties.Removed_api_filename) != "" { 289 filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") 290 uncheckedRemovedFile := android.PathForModuleOut(ctx, "metalava", filename) 291 cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) 292 d.removedApiFile = uncheckedRemovedFile 293 } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" { 294 // If check api is disabled then make the source removed api file available for export. 295 d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) 296 } 297 298 if Bool(d.properties.Write_sdk_values) { 299 d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata") 300 cmd.FlagWithArg("--sdk-values ", d.metadataDir.String()) 301 } 302 303 if stubsDir.Valid() { 304 if Bool(d.properties.Create_doc_stubs) { 305 cmd.FlagWithArg("--doc-stubs ", stubsDir.String()) 306 } else { 307 cmd.FlagWithArg("--stubs ", stubsDir.String()) 308 if !Bool(d.properties.Output_javadoc_comments) { 309 cmd.Flag("--exclude-documentation-from-stubs") 310 } 311 } 312 } 313} 314 315func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 316 if Bool(d.properties.Annotations_enabled) { 317 cmd.Flag("--include-annotations") 318 319 cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi") 320 321 validatingNullability := 322 strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || 323 String(d.properties.Validate_nullability_from_list) != "" 324 325 migratingNullability := String(d.properties.Previous_api) != "" 326 if migratingNullability { 327 previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api)) 328 cmd.FlagWithInput("--migrate-nullness ", previousApi) 329 } 330 331 if s := String(d.properties.Validate_nullability_from_list); s != "" { 332 cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) 333 } 334 335 if validatingNullability { 336 d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt") 337 cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile) 338 } 339 340 d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip") 341 cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip) 342 343 if len(d.properties.Merge_annotations_dirs) != 0 { 344 d.mergeAnnoDirFlags(ctx, cmd) 345 } 346 347 // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. 348 cmd.FlagWithArg("--hide ", "HiddenTypedefConstant"). 349 FlagWithArg("--hide ", "SuperfluousPrefix"). 350 FlagWithArg("--hide ", "AnnotationExtraction"). 351 // b/222738070 352 FlagWithArg("--hide ", "BannedThrow"). 353 // b/223382732 354 FlagWithArg("--hide ", "ChangedDefault") 355 } 356} 357 358func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 359 ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) { 360 if t, ok := m.(*ExportedDroiddocDir); ok { 361 cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps) 362 } else { 363 ctx.PropertyErrorf("merge_annotations_dirs", 364 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m)) 365 } 366 }) 367} 368 369func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 370 ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) { 371 if t, ok := m.(*ExportedDroiddocDir); ok { 372 cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps) 373 } else { 374 ctx.PropertyErrorf("merge_inclusion_annotations_dirs", 375 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m)) 376 } 377 }) 378} 379 380func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 381 var apiVersions android.Path 382 if proptools.Bool(d.properties.Api_levels_annotations_enabled) { 383 d.apiLevelsGenerationFlags(ctx, cmd) 384 apiVersions = d.apiVersionsXml 385 } else { 386 ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) { 387 if s, ok := m.(*Droidstubs); ok { 388 apiVersions = s.apiVersionsXml 389 } else { 390 ctx.PropertyErrorf("api_levels_module", 391 "module %q is not a droidstubs module", ctx.OtherModuleName(m)) 392 } 393 }) 394 } 395 if apiVersions != nil { 396 cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String()) 397 cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename()) 398 cmd.FlagWithInput("--apply-api-levels ", apiVersions) 399 } 400} 401 402func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 403 if len(d.properties.Api_levels_annotations_dirs) == 0 { 404 ctx.PropertyErrorf("api_levels_annotations_dirs", 405 "has to be non-empty if api levels annotations was enabled!") 406 } 407 408 d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml") 409 cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml) 410 411 filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") 412 413 var dirs []string 414 var extensions_dir string 415 ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { 416 if t, ok := m.(*ExportedDroiddocDir); ok { 417 extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`) 418 419 // Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps; 420 // ideally this should be read from prebuiltApis.properties.Extensions_* 421 for _, dep := range t.deps { 422 if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil { 423 if extensions_dir == "" { 424 extensions_dir = t.dir.String() + "/extensions" 425 } 426 cmd.Implicit(dep) 427 } 428 if dep.Base() == filename { 429 cmd.Implicit(dep) 430 } 431 if filename != "android.jar" && dep.Base() == "android.jar" { 432 // Metalava implicitly searches these patterns: 433 // prebuilts/tools/common/api-versions/android-%/android.jar 434 // prebuilts/sdk/%/public/android.jar 435 // Add android.jar files from the api_levels_annotations_dirs directories to try 436 // to satisfy these patterns. If Metalava can't find a match for an API level 437 // between 1 and 28 in at least one pattern it will fail. 438 cmd.Implicit(dep) 439 } 440 } 441 442 dirs = append(dirs, t.dir.String()) 443 } else { 444 ctx.PropertyErrorf("api_levels_annotations_dirs", 445 "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) 446 } 447 }) 448 449 // Add all relevant --android-jar-pattern patterns for Metalava. 450 // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines 451 // an actual file present on disk (in the order the patterns were passed). For system APIs for 452 // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs 453 // for older releases. Similarly, module-lib falls back to system API. 454 var sdkDirs []string 455 switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") { 456 case "system-server": 457 sdkDirs = []string{"system-server", "module-lib", "system", "public"} 458 case "module-lib": 459 sdkDirs = []string{"module-lib", "system", "public"} 460 case "system": 461 sdkDirs = []string{"system", "public"} 462 case "public": 463 sdkDirs = []string{"public"} 464 default: 465 ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) 466 return 467 } 468 469 for _, sdkDir := range sdkDirs { 470 for _, dir := range dirs { 471 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename)) 472 } 473 } 474 475 if d.properties.Extensions_info_file != nil { 476 if extensions_dir == "" { 477 ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found") 478 } 479 info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file) 480 cmd.Implicit(info_file) 481 cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir) 482 cmd.FlagWithArg("--sdk-extensions-info ", info_file.String()) 483 } 484} 485 486func metalavaUseRbe(ctx android.ModuleContext) bool { 487 return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") 488} 489 490func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, 491 srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand { 492 rule.Command().Text("rm -rf").Flag(homeDir.String()) 493 rule.Command().Text("mkdir -p").Flag(homeDir.String()) 494 495 cmd := rule.Command() 496 cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String()) 497 498 if metalavaUseRbe(ctx) { 499 rule.Remoteable(android.RemoteRuleSupports{RBE: true}) 500 execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy) 501 labels := map[string]string{"type": "tool", "name": "metalava"} 502 // TODO: metalava pool rejects these jobs 503 pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16") 504 rule.Rewrapper(&remoteexec.REParams{ 505 Labels: labels, 506 ExecStrategy: execStrategy, 507 ToolchainInputs: []string{config.JavaCmd(ctx).String()}, 508 Platform: map[string]string{remoteexec.PoolKey: pool}, 509 }) 510 } 511 512 cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")). 513 Flag(config.JavacVmFlags). 514 Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). 515 FlagWithArg("-encoding ", "UTF-8"). 516 FlagWithArg("-source ", javaVersion.String()). 517 FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs). 518 FlagWithInput("@", srcJarList) 519 520 if len(bootclasspath) > 0 { 521 cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":") 522 } 523 524 if len(classpath) > 0 { 525 cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":") 526 } 527 528 cmd.Flag("--no-banner"). 529 Flag("--color"). 530 Flag("--quiet"). 531 Flag("--format=v2"). 532 FlagWithArg("--repeat-errors-max ", "10"). 533 FlagWithArg("--hide ", "UnresolvedImport"). 534 FlagWithArg("--hide ", "InvalidNullabilityOverride"). 535 // b/223382732 536 FlagWithArg("--hide ", "ChangedDefault") 537 538 return cmd 539} 540 541func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { 542 deps := d.Javadoc.collectDeps(ctx) 543 544 javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) 545 546 // Create rule for metalava 547 548 srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars") 549 550 rule := android.NewRuleBuilder(pctx, ctx) 551 552 rule.Sbox(android.PathForModuleOut(ctx, "metalava"), 553 android.PathForModuleOut(ctx, "metalava.sbox.textproto")). 554 SandboxInputs() 555 556 if BoolDefault(d.properties.High_mem, false) { 557 // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel. 558 rule.HighMem() 559 } 560 561 generateStubs := BoolDefault(d.properties.Generate_stubs, true) 562 var stubsDir android.OptionalPath 563 if generateStubs { 564 d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar") 565 stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir")) 566 rule.Command().Text("rm -rf").Text(stubsDir.String()) 567 rule.Command().Text("mkdir -p").Text(stubsDir.String()) 568 } 569 570 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) 571 572 homeDir := android.PathForModuleOut(ctx, "metalava", "home") 573 cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, 574 deps.bootClasspath, deps.classpath, homeDir) 575 cmd.Implicits(d.Javadoc.implicits) 576 577 d.stubsFlags(ctx, cmd, stubsDir) 578 579 d.annotationsFlags(ctx, cmd) 580 d.inclusionAnnotationsFlags(ctx, cmd) 581 d.apiLevelsAnnotationsFlags(ctx, cmd) 582 583 d.expandArgs(ctx, cmd) 584 585 for _, o := range d.Javadoc.properties.Out { 586 cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) 587 } 588 589 // Add options for the other optional tasks: API-lint and check-released. 590 // We generate separate timestamp files for them. 591 592 doApiLint := false 593 doCheckReleased := false 594 595 // Add API lint options. 596 597 if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) { 598 doApiLint = true 599 600 newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since) 601 if newSince.Valid() { 602 cmd.FlagWithInput("--api-lint ", newSince.Path()) 603 } else { 604 cmd.Flag("--api-lint") 605 } 606 d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt") 607 cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint" 608 609 // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released. 610 if d.Name() != "android.car-system-stubs-docs" && 611 d.Name() != "android.car-stubs-docs" { 612 cmd.Flag("--lints-as-errors") 613 cmd.Flag("--warnings-as-errors") // Most lints are actually warnings. 614 } 615 616 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) 617 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt") 618 d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp") 619 620 // Note this string includes a special shell quote $' ... ', which decodes the "\n"s. 621 // 622 // TODO: metalava also has a slightly different message hardcoded. Should we unify this 623 // message and metalava's one? 624 msg := `$'` + // Enclose with $' ... ' 625 `************************************************************\n` + 626 `Your API changes are triggering API Lint warnings or errors.\n` + 627 `To make these errors go away, fix the code according to the\n` + 628 `error and/or warning messages above.\n` + 629 `\n` + 630 `If it is not possible to do so, there are workarounds:\n` + 631 `\n` + 632 `1. You can suppress the errors with @SuppressLint("<id>")\n` + 633 ` where the <id> is given in brackets in the error message above.\n` 634 635 if baselineFile.Valid() { 636 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) 637 cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput) 638 639 msg += fmt.Sprintf(``+ 640 `2. You can update the baseline by executing the following\n`+ 641 ` command:\n`+ 642 ` (cd $ANDROID_BUILD_TOP && cp \\\n`+ 643 ` "%s" \\\n`+ 644 ` "%s")\n`+ 645 ` To submit the revised baseline.txt to the main Android\n`+ 646 ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path()) 647 } else { 648 msg += fmt.Sprintf(``+ 649 `2. You can add a baseline file of existing lint failures\n`+ 650 ` to the build rule of %s.\n`, d.Name()) 651 } 652 // Note the message ends with a ' (single quote), to close the $' ... ' . 653 msg += `************************************************************\n'` 654 655 cmd.FlagWithArg("--error-message:api-lint ", msg) 656 } 657 658 // Add "check released" options. (Detect incompatible API changes from the last public release) 659 660 if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") { 661 doCheckReleased = true 662 663 if len(d.Javadoc.properties.Out) > 0 { 664 ctx.PropertyErrorf("out", "out property may not be combined with check_api") 665 } 666 667 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file)) 668 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file)) 669 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) 670 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt") 671 672 d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp") 673 674 cmd.FlagWithInput("--check-compatibility:api:released ", apiFile) 675 cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile) 676 677 if baselineFile.Valid() { 678 cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) 679 cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput) 680 } 681 682 // Note this string includes quote ($' ... '), which decodes the "\n"s. 683 msg := `$'\n******************************\n` + 684 `You have tried to change the API from what has been previously released in\n` + 685 `an SDK. Please fix the errors listed above.\n` + 686 `******************************\n'` 687 688 cmd.FlagWithArg("--error-message:compatibility:released ", msg) 689 } 690 691 if generateStubs { 692 rule.Command(). 693 BuiltTool("soong_zip"). 694 Flag("-write_if_changed"). 695 Flag("-jar"). 696 FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). 697 FlagWithArg("-C ", stubsDir.String()). 698 FlagWithArg("-D ", stubsDir.String()) 699 } 700 701 if Bool(d.properties.Write_sdk_values) { 702 d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip") 703 rule.Command(). 704 BuiltTool("soong_zip"). 705 Flag("-write_if_changed"). 706 Flag("-d"). 707 FlagWithOutput("-o ", d.metadataZip). 708 FlagWithArg("-C ", d.metadataDir.String()). 709 FlagWithArg("-D ", d.metadataDir.String()) 710 } 711 712 // TODO: We don't really need two separate API files, but this is a reminiscence of how 713 // we used to run metalava separately for API lint and the "last_released" check. Unify them. 714 if doApiLint { 715 rule.Command().Text("touch").Output(d.apiLintTimestamp) 716 } 717 if doCheckReleased { 718 rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) 719 } 720 721 // TODO(b/183630617): rewrapper doesn't support restat rules 722 if !metalavaUseRbe(ctx) { 723 rule.Restat() 724 } 725 726 zipSyncCleanupCmd(rule, srcJarDir) 727 728 rule.Build("metalava", "metalava merged") 729 730 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { 731 732 if len(d.Javadoc.properties.Out) > 0 { 733 ctx.PropertyErrorf("out", "out property may not be combined with check_api") 734 } 735 736 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file)) 737 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file)) 738 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file) 739 740 if baselineFile.Valid() { 741 ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName()) 742 } 743 744 d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp") 745 746 rule := android.NewRuleBuilder(pctx, ctx) 747 748 // Diff command line. 749 // -F matches the closest "opening" line, such as "package android {" 750 // and " public class Intent {". 751 diff := `diff -u -F '{ *$'` 752 753 rule.Command().Text("( true") 754 rule.Command(). 755 Text(diff). 756 Input(apiFile).Input(d.apiFile) 757 758 rule.Command(). 759 Text(diff). 760 Input(removedApiFile).Input(d.removedApiFile) 761 762 msg := fmt.Sprintf(`\n******************************\n`+ 763 `You have tried to change the API from what has been previously approved.\n\n`+ 764 `To make these errors go away, you have two choices:\n`+ 765 ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+ 766 ` to the new methods, etc. shown in the above diff.\n\n`+ 767 ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+ 768 ` m %s-update-current-api\n\n`+ 769 ` To submit the revised current.txt to the main Android repository,\n`+ 770 ` you will need approval.\n`+ 771 `******************************\n`, ctx.ModuleName()) 772 773 rule.Command(). 774 Text("touch").Output(d.checkCurrentApiTimestamp). 775 Text(") || ("). 776 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 777 Text("; exit 38"). 778 Text(")") 779 780 rule.Build("metalavaCurrentApiCheck", "check current API") 781 782 d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp") 783 784 // update API rule 785 rule = android.NewRuleBuilder(pctx, ctx) 786 787 rule.Command().Text("( true") 788 789 rule.Command(). 790 Text("cp").Flag("-f"). 791 Input(d.apiFile).Flag(apiFile.String()) 792 793 rule.Command(). 794 Text("cp").Flag("-f"). 795 Input(d.removedApiFile).Flag(removedApiFile.String()) 796 797 msg = "failed to update public API" 798 799 rule.Command(). 800 Text("touch").Output(d.updateCurrentApiTimestamp). 801 Text(") || ("). 802 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 803 Text("; exit 38"). 804 Text(")") 805 806 rule.Build("metalavaCurrentApiUpdate", "update current API") 807 } 808 809 if String(d.properties.Check_nullability_warnings) != "" { 810 if d.nullabilityWarningsFile == nil { 811 ctx.PropertyErrorf("check_nullability_warnings", 812 "Cannot specify check_nullability_warnings unless validating nullability") 813 } 814 815 checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings)) 816 817 d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp") 818 819 msg := fmt.Sprintf(`\n******************************\n`+ 820 `The warnings encountered during nullability annotation validation did\n`+ 821 `not match the checked in file of expected warnings. The diffs are shown\n`+ 822 `above. You have two options:\n`+ 823 ` 1. Resolve the differences by editing the nullability annotations.\n`+ 824 ` 2. Update the file of expected warnings by running:\n`+ 825 ` cp %s %s\n`+ 826 ` and submitting the updated file as part of your change.`, 827 d.nullabilityWarningsFile, checkNullabilityWarnings) 828 829 rule := android.NewRuleBuilder(pctx, ctx) 830 831 rule.Command(). 832 Text("("). 833 Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile). 834 Text("&&"). 835 Text("touch").Output(d.checkNullabilityWarningsTimestamp). 836 Text(") || ("). 837 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 838 Text("; exit 38"). 839 Text(")") 840 841 rule.Build("nullabilityWarningsCheck", "nullability warnings check") 842 } 843} 844 845var _ android.ApiProvider = (*Droidstubs)(nil) 846 847type bazelJavaApiContributionAttributes struct { 848 Api bazel.LabelAttribute 849 Api_surface *string 850} 851 852func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) { 853 props := bazel.BazelTargetModuleProperties{ 854 Rule_class: "java_api_contribution", 855 Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl", 856 } 857 apiFile := d.properties.Check_api.Current.Api_file 858 // Do not generate a target if check_api is not set 859 if apiFile == nil { 860 return 861 } 862 attrs := &bazelJavaApiContributionAttributes{ 863 Api: *bazel.MakeLabelAttribute( 864 android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label, 865 ), 866 Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())), 867 } 868 ctx.CreateBazelTargetModule(props, android.CommonAttributes{ 869 Name: android.ApiContributionTargetName(ctx.ModuleName()), 870 }, attrs) 871} 872 873func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { 874 api_file := d.properties.Check_api.Current.Api_file 875 api_surface := d.properties.Api_surface 876 877 props := struct { 878 Name *string 879 Api_surface *string 880 Api_file *string 881 Visibility []string 882 }{} 883 884 props.Name = proptools.StringPtr(d.Name() + ".api.contribution") 885 props.Api_surface = api_surface 886 props.Api_file = api_file 887 props.Visibility = []string{"//visibility:override", "//visibility:public"} 888 889 ctx.CreateModule(ApiContributionFactory, &props) 890} 891 892// TODO (b/262014796): Export the API contributions of CorePlatformApi 893// A map to populate the api surface of a droidstub from a substring appearing in its name 894// This map assumes that droidstubs (either checked-in or created by java_sdk_library) 895// use a strict naming convention 896var ( 897 droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{ 898 //public is commented out since the core libraries use public in their java_sdk_library names 899 "intracore": android.SdkIntraCore, 900 "intra.core": android.SdkIntraCore, 901 "system_server": android.SdkSystemServer, 902 "system-server": android.SdkSystemServer, 903 "system": android.SdkSystem, 904 "module_lib": android.SdkModule, 905 "module-lib": android.SdkModule, 906 "platform.api": android.SdkCorePlatform, 907 "test": android.SdkTest, 908 "toolchain": android.SdkToolchain, 909 } 910) 911 912// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target 913// The api_surface is populated using the naming convention of the droidstubs module. 914func bazelApiSurfaceName(name string) string { 915 // Sort the keys so that longer strings appear first 916 // Otherwise substrings like system will match both system and system_server 917 sortedKeys := make([]string, 0) 918 for key := range droidstubsModuleNamingToSdkKind { 919 sortedKeys = append(sortedKeys, key) 920 } 921 sort.Slice(sortedKeys, func(i, j int) bool { 922 return len(sortedKeys[i]) > len(sortedKeys[j]) 923 }) 924 for _, sortedKey := range sortedKeys { 925 if strings.Contains(name, sortedKey) { 926 sdkKind := droidstubsModuleNamingToSdkKind[sortedKey] 927 return sdkKind.String() + "api" 928 } 929 } 930 // Default is publicapi 931 return android.SdkPublic.String() + "api" 932} 933 934func StubsDefaultsFactory() android.Module { 935 module := &DocDefaults{} 936 937 module.AddProperties( 938 &JavadocProperties{}, 939 &DroidstubsProperties{}, 940 ) 941 942 android.InitDefaultsModule(module) 943 944 return module 945} 946 947var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil) 948 949type PrebuiltStubsSourcesProperties struct { 950 Srcs []string `android:"path"` 951} 952 953type PrebuiltStubsSources struct { 954 android.ModuleBase 955 android.DefaultableModuleBase 956 prebuilt android.Prebuilt 957 958 properties PrebuiltStubsSourcesProperties 959 960 stubsSrcJar android.Path 961} 962 963func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { 964 switch tag { 965 case "": 966 return android.Paths{p.stubsSrcJar}, nil 967 default: 968 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 969 } 970} 971 972func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { 973 return d.stubsSrcJar 974} 975 976func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { 977 if len(p.properties.Srcs) != 1 { 978 ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs)) 979 return 980 } 981 982 src := p.properties.Srcs[0] 983 if filepath.Ext(src) == ".srcjar" { 984 // This is a srcjar. We can use it directly. 985 p.stubsSrcJar = android.PathForModuleSrc(ctx, src) 986 } else { 987 outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") 988 989 // This is a directory. Glob the contents just in case the directory does not exist. 990 srcGlob := src + "/**/*" 991 srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) 992 993 // Although PathForModuleSrc can return nil if either the path doesn't exist or 994 // the path components are invalid it won't in this case because no components 995 // are specified and the module directory must exist in order to get this far. 996 srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src) 997 998 rule := android.NewRuleBuilder(pctx, ctx) 999 rule.Command(). 1000 BuiltTool("soong_zip"). 1001 Flag("-write_if_changed"). 1002 Flag("-jar"). 1003 FlagWithOutput("-o ", outPath). 1004 FlagWithArg("-C ", srcDir.String()). 1005 FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths) 1006 rule.Restat() 1007 rule.Build("zip src", "Create srcjar from prebuilt source") 1008 p.stubsSrcJar = outPath 1009 } 1010} 1011 1012func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { 1013 return &p.prebuilt 1014} 1015 1016func (p *PrebuiltStubsSources) Name() string { 1017 return p.prebuilt.Name(p.ModuleBase.Name()) 1018} 1019 1020// prebuilt_stubs_sources imports a set of java source files as if they were 1021// generated by droidstubs. 1022// 1023// By default, a prebuilt_stubs_sources has a single variant that expects a 1024// set of `.java` files generated by droidstubs. 1025// 1026// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one 1027// for host modules. 1028// 1029// Intended only for use by sdk snapshots. 1030func PrebuiltStubsSourcesFactory() android.Module { 1031 module := &PrebuiltStubsSources{} 1032 1033 module.AddProperties(&module.properties) 1034 1035 android.InitPrebuiltModule(module, &module.properties.Srcs) 1036 InitDroiddocModule(module, android.HostAndDeviceSupported) 1037 return module 1038} 1039