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