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