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 "strings" 22 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/java/config" 27 "android/soong/remoteexec" 28) 29 30// The values allowed for Droidstubs' Api_levels_sdk_type 31var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"} 32 33type StubsType int 34 35const ( 36 Everything StubsType = iota 37 Runtime 38 Exportable 39 Unavailable 40) 41 42func (s StubsType) String() string { 43 switch s { 44 case Everything: 45 return "everything" 46 case Runtime: 47 return "runtime" 48 case Exportable: 49 return "exportable" 50 default: 51 return "" 52 } 53} 54 55func StringToStubsType(s string) StubsType { 56 switch strings.ToLower(s) { 57 case Everything.String(): 58 return Everything 59 case Runtime.String(): 60 return Runtime 61 case Exportable.String(): 62 return Exportable 63 default: 64 return Unavailable 65 } 66} 67 68func init() { 69 RegisterStubsBuildComponents(android.InitRegistrationContext) 70} 71 72func RegisterStubsBuildComponents(ctx android.RegistrationContext) { 73 ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory) 74 75 ctx.RegisterModuleType("droidstubs", DroidstubsFactory) 76 ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory) 77 78 ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) 79} 80 81type stubsArtifacts struct { 82 nullabilityWarningsFile android.WritablePath 83 annotationsZip android.WritablePath 84 apiVersionsXml android.WritablePath 85 metadataZip android.WritablePath 86 metadataDir android.WritablePath 87} 88 89// Droidstubs 90type Droidstubs struct { 91 Javadoc 92 embeddableInModuleAndImport 93 94 properties DroidstubsProperties 95 apiFile android.Path 96 removedApiFile android.Path 97 98 checkCurrentApiTimestamp android.WritablePath 99 updateCurrentApiTimestamp android.WritablePath 100 checkLastReleasedApiTimestamp android.WritablePath 101 apiLintTimestamp android.WritablePath 102 apiLintReport android.WritablePath 103 104 checkNullabilityWarningsTimestamp android.WritablePath 105 106 everythingArtifacts stubsArtifacts 107 exportableArtifacts stubsArtifacts 108 109 exportableApiFile android.WritablePath 110 exportableRemovedApiFile android.WritablePath 111} 112 113type DroidstubsProperties struct { 114 // The generated public API filename by Metalava, defaults to <module>_api.txt 115 Api_filename *string 116 117 // the generated removed API filename by Metalava, defaults to <module>_removed.txt 118 Removed_api_filename *string 119 120 Check_api struct { 121 Last_released ApiToCheck 122 123 Current ApiToCheck 124 125 Api_lint struct { 126 Enabled *bool 127 128 // If set, performs api_lint on any new APIs not found in the given signature file 129 New_since *string `android:"path"` 130 131 // If not blank, path to the baseline txt file for approved API lint violations. 132 Baseline_file *string `android:"path"` 133 } 134 } 135 136 // user can specify the version of previous released API file in order to do compatibility check. 137 Previous_api *string `android:"path"` 138 139 // is set to true, Metalava will allow framework SDK to contain annotations. 140 Annotations_enabled *bool 141 142 // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from. 143 Merge_annotations_dirs []string 144 145 // a list of top-level directories containing Java stub files to merge show/hide annotations from. 146 Merge_inclusion_annotations_dirs []string 147 148 // a file containing a list of classes to do nullability validation for. 149 Validate_nullability_from_list *string 150 151 // a file containing expected warnings produced by validation of nullability annotations. 152 Check_nullability_warnings *string 153 154 // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false. 155 Create_doc_stubs *bool 156 157 // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false. 158 // Has no effect if create_doc_stubs: true. 159 Output_javadoc_comments *bool 160 161 // if set to false then do not write out stubs. Defaults to true. 162 // 163 // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately. 164 Generate_stubs *bool 165 166 // if set to true, provides a hint to the build system that this rule uses a lot of memory, 167 // which can be used for scheduling purposes 168 High_mem *bool 169 170 // if set to true, Metalava will allow framework SDK to contain API levels annotations. 171 Api_levels_annotations_enabled *bool 172 173 // Apply the api levels database created by this module rather than generating one in this droidstubs. 174 Api_levels_module *string 175 176 // the dirs which Metalava extracts API levels annotations from. 177 Api_levels_annotations_dirs []string 178 179 // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system', 'module-lib' and 'system-server'; defaults to public. 180 Api_levels_sdk_type *string 181 182 // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. 183 Api_levels_jar_filename *string 184 185 // if set to true, collect the values used by the Dev tools and 186 // write them in files packaged with the SDK. Defaults to false. 187 Write_sdk_values *bool 188 189 // path or filegroup to file defining extension an SDK name <-> numerical ID mapping and 190 // what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info 191 Extensions_info_file *string `android:"path"` 192 193 // API surface of this module. If set, the module contributes to an API surface. 194 // For the full list of available API surfaces, refer to soong/android/sdk_version.go 195 Api_surface *string 196 197 // a list of aconfig_declarations module names that the stubs generated in this module 198 // depend on. 199 Aconfig_declarations []string 200} 201 202// Used by xsd_config 203type ApiFilePath interface { 204 ApiFilePath(StubsType) (android.Path, error) 205} 206 207type ApiStubsSrcProvider interface { 208 StubsSrcJar(StubsType) (android.Path, error) 209} 210 211// Provider of information about API stubs, used by java_sdk_library. 212type ApiStubsProvider interface { 213 AnnotationsZip(StubsType) (android.Path, error) 214 ApiFilePath 215 RemovedApiFilePath(StubsType) (android.Path, error) 216 217 ApiStubsSrcProvider 218} 219 220type currentApiTimestampProvider interface { 221 CurrentApiTimestamp() android.Path 222} 223 224type annotationFlagsParams struct { 225 migratingNullability bool 226 validatingNullability bool 227 nullabilityWarningsFile android.WritablePath 228 annotationsZip android.WritablePath 229} 230type stubsCommandParams struct { 231 srcJarDir android.ModuleOutPath 232 stubsDir android.OptionalPath 233 stubsSrcJar android.WritablePath 234 metadataZip android.WritablePath 235 metadataDir android.WritablePath 236 apiVersionsXml android.WritablePath 237 nullabilityWarningsFile android.WritablePath 238 annotationsZip android.WritablePath 239 stubConfig stubsCommandConfigParams 240} 241type stubsCommandConfigParams struct { 242 stubsType StubsType 243 javaVersion javaVersion 244 deps deps 245 checkApi bool 246 generateStubs bool 247 doApiLint bool 248 doCheckReleased bool 249 writeSdkValues bool 250 migratingNullability bool 251 validatingNullability bool 252} 253 254// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be 255// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to 256// a droiddoc module to generate documentation. 257func DroidstubsFactory() android.Module { 258 module := &Droidstubs{} 259 260 module.AddProperties(&module.properties, 261 &module.Javadoc.properties) 262 module.initModuleAndImport(module) 263 264 InitDroiddocModule(module, android.HostAndDeviceSupported) 265 266 module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { 267 module.createApiContribution(ctx) 268 }) 269 return module 270} 271 272// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API 273// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be 274// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs 275// module when symbols needed by the source files are provided by java_library_host modules. 276func DroidstubsHostFactory() android.Module { 277 module := &Droidstubs{} 278 279 module.AddProperties(&module.properties, 280 &module.Javadoc.properties) 281 282 InitDroiddocModule(module, android.HostSupported) 283 return module 284} 285 286func getStubsTypeAndTag(tag string) (StubsType, string, error) { 287 if len(tag) == 0 { 288 return Everything, "", nil 289 } 290 if tag[0] != '.' { 291 return Unavailable, "", fmt.Errorf("tag must begin with \".\"") 292 } 293 294 stubsType := Everything 295 // Check if the tag has a stubs type prefix (e.g. ".exportable") 296 for st := Everything; st <= Exportable; st++ { 297 if strings.HasPrefix(tag, "."+st.String()) { 298 stubsType = st 299 } 300 } 301 302 return stubsType, strings.TrimPrefix(tag, "."+stubsType.String()), nil 303} 304 305// Droidstubs' tag supports specifying with the stubs type. 306// While supporting the pre-existing tags, it also supports tags with 307// the stubs type prefix. Some examples are shown below: 308// {.annotations.zip} - pre-existing behavior. Returns the path to the 309// annotation zip. 310// {.exportable} - Returns the path to the exportable stubs src jar. 311// {.exportable.annotations.zip} - Returns the path to the exportable 312// annotations zip file. 313// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions 314// xml file. For unsupported combinations, the default everything output file 315// is returned. 316func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { 317 stubsType, prefixRemovedTag, err := getStubsTypeAndTag(tag) 318 if err != nil { 319 return nil, err 320 } 321 switch prefixRemovedTag { 322 case "": 323 stubsSrcJar, err := d.StubsSrcJar(stubsType) 324 return android.Paths{stubsSrcJar}, err 325 case ".docs.zip": 326 docZip, err := d.DocZip(stubsType) 327 return android.Paths{docZip}, err 328 case ".api.txt", android.DefaultDistTag: 329 // This is the default dist path for dist properties that have no tag property. 330 apiFilePath, err := d.ApiFilePath(stubsType) 331 return android.Paths{apiFilePath}, err 332 case ".removed-api.txt": 333 removedApiFilePath, err := d.RemovedApiFilePath(stubsType) 334 return android.Paths{removedApiFilePath}, err 335 case ".annotations.zip": 336 annotationsZip, err := d.AnnotationsZip(stubsType) 337 return android.Paths{annotationsZip}, err 338 case ".api_versions.xml": 339 apiVersionsXmlFilePath, err := d.ApiVersionsXmlFilePath(stubsType) 340 return android.Paths{apiVersionsXmlFilePath}, err 341 default: 342 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 343 } 344} 345 346func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) { 347 switch stubsType { 348 case Everything: 349 ret, err = d.everythingArtifacts.annotationsZip, nil 350 case Exportable: 351 ret, err = d.exportableArtifacts.annotationsZip, nil 352 default: 353 ret, err = nil, fmt.Errorf("annotations zip not supported for the stub type %s", stubsType.String()) 354 } 355 return ret, err 356} 357 358func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err error) { 359 switch stubsType { 360 case Everything: 361 ret, err = d.apiFile, nil 362 case Exportable: 363 ret, err = d.exportableApiFile, nil 364 default: 365 ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String()) 366 } 367 if ret == nil && err == nil { 368 err = fmt.Errorf("api file is null for the stub type %s", stubsType.String()) 369 } 370 return ret, err 371} 372 373func (d *Droidstubs) ApiVersionsXmlFilePath(stubsType StubsType) (ret android.Path, err error) { 374 switch stubsType { 375 case Everything: 376 ret, err = d.everythingArtifacts.apiVersionsXml, nil 377 case Exportable: 378 ret, err = d.exportableArtifacts.apiVersionsXml, nil 379 default: 380 ret, err = nil, fmt.Errorf("api versions xml file path not supported for the stub type %s", stubsType.String()) 381 } 382 if ret == nil && err == nil { 383 err = fmt.Errorf("api versions xml file is null for the stub type %s", stubsType.String()) 384 } 385 return ret, err 386} 387 388func (d *Droidstubs) DocZip(stubsType StubsType) (ret android.Path, err error) { 389 switch stubsType { 390 case Everything: 391 ret, err = d.docZip, nil 392 default: 393 ret, err = nil, fmt.Errorf("docs zip not supported for the stub type %s", stubsType.String()) 394 } 395 if ret == nil && err == nil { 396 err = fmt.Errorf("docs zip is null for the stub type %s", stubsType.String()) 397 } 398 return ret, err 399} 400 401func (d *Droidstubs) RemovedApiFilePath(stubsType StubsType) (ret android.Path, err error) { 402 switch stubsType { 403 case Everything: 404 ret, err = d.removedApiFile, nil 405 case Exportable: 406 ret, err = d.exportableRemovedApiFile, nil 407 default: 408 ret, err = nil, fmt.Errorf("removed api file path not supported for the stub type %s", stubsType.String()) 409 } 410 if ret == nil && err == nil { 411 err = fmt.Errorf("removed api file is null for the stub type %s", stubsType.String()) 412 } 413 return ret, err 414} 415 416func (d *Droidstubs) StubsSrcJar(stubsType StubsType) (ret android.Path, err error) { 417 switch stubsType { 418 case Everything: 419 ret, err = d.stubsSrcJar, nil 420 case Exportable: 421 ret, err = d.exportableStubsSrcJar, nil 422 default: 423 ret, err = nil, fmt.Errorf("stubs srcjar not supported for the stub type %s", stubsType.String()) 424 } 425 if ret == nil && err == nil { 426 err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String()) 427 } 428 return ret, err 429} 430 431func (d *Droidstubs) CurrentApiTimestamp() android.Path { 432 return d.checkCurrentApiTimestamp 433} 434 435var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"} 436var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"} 437var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"} 438var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"} 439var metalavaCurrentApiTimestampTag = dependencyTag{name: "metalava-current-api-timestamp-tag"} 440 441func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { 442 d.Javadoc.addDeps(ctx) 443 444 if len(d.properties.Merge_annotations_dirs) != 0 { 445 for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs { 446 ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir) 447 } 448 } 449 450 if len(d.properties.Merge_inclusion_annotations_dirs) != 0 { 451 for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs { 452 ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir) 453 } 454 } 455 456 if len(d.properties.Api_levels_annotations_dirs) != 0 { 457 for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs { 458 ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir) 459 } 460 } 461 462 if len(d.properties.Aconfig_declarations) != 0 { 463 for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations { 464 ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName) 465 } 466 } 467 468 if d.properties.Api_levels_module != nil { 469 ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module)) 470 } 471} 472 473func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, metadataDir android.WritablePath) { 474 cmd.FlagWithArg("--sdk-values ", metadataDir.String()) 475} 476 477func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) { 478 479 apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") 480 uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName) 481 cmd.FlagWithOutput("--api ", uncheckedApiFile) 482 if checkApi || String(d.properties.Api_filename) != "" { 483 if stubsType == Everything { 484 d.apiFile = uncheckedApiFile 485 } else if stubsType == Exportable { 486 d.exportableApiFile = uncheckedApiFile 487 } 488 } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" { 489 if stubsType == Everything { 490 // If check api is disabled then make the source file available for export. 491 d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) 492 } else if stubsType == Exportable { 493 d.exportableApiFile = uncheckedApiFile 494 } 495 } 496 497 removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") 498 uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName) 499 cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) 500 if checkApi || String(d.properties.Removed_api_filename) != "" { 501 if stubsType == Everything { 502 d.removedApiFile = uncheckedRemovedFile 503 } else if stubsType == Exportable { 504 d.exportableRemovedApiFile = uncheckedRemovedFile 505 } 506 } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" { 507 if stubsType == Everything { 508 // If check api is disabled then make the source removed api file available for export. 509 d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) 510 } else if stubsType == Exportable { 511 d.exportableRemovedApiFile = uncheckedRemovedFile 512 } 513 } 514 515 if stubsDir.Valid() { 516 if Bool(d.properties.Create_doc_stubs) { 517 cmd.FlagWithArg("--doc-stubs ", stubsDir.String()) 518 } else { 519 cmd.FlagWithArg("--stubs ", stubsDir.String()) 520 if !Bool(d.properties.Output_javadoc_comments) { 521 cmd.Flag("--exclude-documentation-from-stubs") 522 } 523 } 524 } 525} 526 527func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) { 528 if Bool(d.properties.Annotations_enabled) { 529 cmd.Flag(config.MetalavaAnnotationsFlags) 530 531 if params.migratingNullability { 532 previousApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Previous_api)}) 533 cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles) 534 } 535 536 if s := String(d.properties.Validate_nullability_from_list); s != "" { 537 cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) 538 } 539 540 if params.validatingNullability { 541 cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile) 542 } 543 544 cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip) 545 546 if len(d.properties.Merge_annotations_dirs) != 0 { 547 d.mergeAnnoDirFlags(ctx, cmd) 548 } 549 550 cmd.Flag(config.MetalavaAnnotationsWarningsFlags) 551 } 552} 553 554func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 555 ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) { 556 if t, ok := m.(*ExportedDroiddocDir); ok { 557 cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps) 558 } else { 559 ctx.PropertyErrorf("merge_annotations_dirs", 560 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m)) 561 } 562 }) 563} 564 565func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { 566 ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) { 567 if t, ok := m.(*ExportedDroiddocDir); ok { 568 cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps) 569 } else { 570 ctx.PropertyErrorf("merge_inclusion_annotations_dirs", 571 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m)) 572 } 573 }) 574} 575 576func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { 577 var apiVersions android.Path 578 if proptools.Bool(d.properties.Api_levels_annotations_enabled) { 579 d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml) 580 apiVersions = apiVersionsXml 581 } else { 582 ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) { 583 if s, ok := m.(*Droidstubs); ok { 584 if stubsType == Everything { 585 apiVersions = s.everythingArtifacts.apiVersionsXml 586 } else if stubsType == Exportable { 587 apiVersions = s.exportableArtifacts.apiVersionsXml 588 } else { 589 ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String()) 590 } 591 } else { 592 ctx.PropertyErrorf("api_levels_module", 593 "module %q is not a droidstubs module", ctx.OtherModuleName(m)) 594 } 595 }) 596 } 597 if apiVersions != nil { 598 cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String()) 599 cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename()) 600 cmd.FlagWithInput("--apply-api-levels ", apiVersions) 601 } 602} 603 604// AndroidPlusUpdatableJar is the name of some extra jars added into `module-lib` and 605// `system-server` directories that contain all the APIs provided by the platform and updatable 606// modules because the `android.jar` files do not. See b/337836752. 607const AndroidPlusUpdatableJar = "android-plus-updatable.jar" 608 609func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { 610 if len(d.properties.Api_levels_annotations_dirs) == 0 { 611 ctx.PropertyErrorf("api_levels_annotations_dirs", 612 "has to be non-empty if api levels annotations was enabled!") 613 } 614 615 cmd.FlagWithOutput("--generate-api-levels ", apiVersionsXml) 616 617 filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") 618 619 // TODO: Avoid the duplication of API surfaces, reuse apiScope. 620 // Add all relevant --android-jar-pattern patterns for Metalava. 621 // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines 622 // an actual file present on disk (in the order the patterns were passed). For system APIs for 623 // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs 624 // for older releases. Similarly, module-lib falls back to system API. 625 var sdkDirs []string 626 apiLevelsSdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") 627 switch apiLevelsSdkType { 628 case "system-server": 629 sdkDirs = []string{"system-server", "module-lib", "system", "public"} 630 case "module-lib": 631 sdkDirs = []string{"module-lib", "system", "public"} 632 case "system": 633 sdkDirs = []string{"system", "public"} 634 case "public": 635 sdkDirs = []string{"public"} 636 default: 637 ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) 638 return 639 } 640 641 // Construct a pattern to match the appropriate extensions that should be included in the 642 // generated api-versions.xml file. 643 // 644 // Use the first item in the sdkDirs array as that is the sdk type for the target API levels 645 // being generated but has the advantage over `Api_levels_sdk_type` as it has been validated. 646 // The exception is for system-server which needs to include module-lib and system-server. That 647 // is because while system-server extends module-lib the system-server extension directory only 648 // contains service-* modules which provide system-server APIs it does not list the modules which 649 // only provide a module-lib, so they have to be included separately. 650 extensionSurfacesPattern := sdkDirs[0] 651 if apiLevelsSdkType == "system-server" { 652 // Take the first two items in sdkDirs, which are system-server and module-lib, and construct 653 // a pattern that will match either. 654 extensionSurfacesPattern = strings.Join(sdkDirs[0:2], "|") 655 } 656 extensionsPattern := fmt.Sprintf(`/extensions/[0-9]+/(%s)/.*\.jar`, extensionSurfacesPattern) 657 658 var dirs []string 659 var extensions_dir string 660 ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { 661 if t, ok := m.(*ExportedDroiddocDir); ok { 662 extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern) 663 664 // Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps; 665 // ideally this should be read from prebuiltApis.properties.Extensions_* 666 for _, dep := range t.deps { 667 // Check to see if it matches an extension first. 668 depBase := dep.Base() 669 if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil { 670 if extensions_dir == "" { 671 extensions_dir = t.dir.String() + "/extensions" 672 } 673 cmd.Implicit(dep) 674 } else if depBase == filename { 675 // Check to see if it matches a dessert release for an SDK, e.g. Android, Car, Wear, etc.. 676 cmd.Implicit(dep) 677 } else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil { 678 // The output api-versions.xml has been requested to include information on SDK 679 // extensions. That means it also needs to include 680 // so 681 // The module-lib and system-server directories should use `android-plus-updatable.jar` 682 // instead of `android.jar`. See AndroidPlusUpdatableJar for more information. 683 cmd.Implicit(dep) 684 } else if filename != "android.jar" && depBase == "android.jar" { 685 // Metalava implicitly searches these patterns: 686 // prebuilts/tools/common/api-versions/android-%/android.jar 687 // prebuilts/sdk/%/public/android.jar 688 // Add android.jar files from the api_levels_annotations_dirs directories to try 689 // to satisfy these patterns. If Metalava can't find a match for an API level 690 // between 1 and 28 in at least one pattern it will fail. 691 cmd.Implicit(dep) 692 } 693 } 694 695 dirs = append(dirs, t.dir.String()) 696 } else { 697 ctx.PropertyErrorf("api_levels_annotations_dirs", 698 "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) 699 } 700 }) 701 702 // Generate the list of --android-jar-pattern options. The order matters so the first one which 703 // matches will be the one that is used for a specific api level.. 704 for _, sdkDir := range sdkDirs { 705 for _, dir := range dirs { 706 addPattern := func(jarFilename string) { 707 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename)) 708 } 709 710 if sdkDir == "module-lib" || sdkDir == "system-server" { 711 // The module-lib and system-server android.jars do not include the updatable modules (as 712 // doing so in the source would introduce dependency cycles and the prebuilts have to 713 // match the sources). So, instead an additional `android-plus-updatable.jar` will be used 714 // that does include the updatable modules and this pattern will match that. This pattern 715 // is added in addition to the following pattern to decouple this change from the change 716 // to add the `android-plus-updatable.jar`. 717 addPattern(AndroidPlusUpdatableJar) 718 } 719 720 addPattern(filename) 721 } 722 } 723 724 if d.properties.Extensions_info_file != nil { 725 if extensions_dir == "" { 726 ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found") 727 } 728 info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file) 729 cmd.Implicit(info_file) 730 cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir) 731 cmd.FlagWithArg("--sdk-extensions-info ", info_file.String()) 732 } 733} 734 735func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType) { 736 if len(d.Javadoc.properties.Out) > 0 { 737 ctx.PropertyErrorf("out", "out property may not be combined with check_api") 738 } 739 740 apiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Api_file)}) 741 removedApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Removed_api_file)}) 742 743 cmd.FlagForEachInput("--check-compatibility:api:released ", apiFiles) 744 cmd.FlagForEachInput("--check-compatibility:removed:released ", removedApiFiles) 745 746 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) 747 if baselineFile.Valid() { 748 cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) 749 } 750} 751 752func metalavaUseRbe(ctx android.ModuleContext) bool { 753 return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") 754} 755 756func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths, 757 srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand { 758 rule.Command().Text("rm -rf").Flag(homeDir.String()) 759 rule.Command().Text("mkdir -p").Flag(homeDir.String()) 760 761 cmd := rule.Command() 762 cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String()) 763 764 if metalavaUseRbe(ctx) { 765 rule.Remoteable(android.RemoteRuleSupports{RBE: true}) 766 execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy) 767 compare := ctx.Config().IsEnvTrue("RBE_METALAVA_COMPARE") 768 remoteUpdateCache := !ctx.Config().IsEnvFalse("RBE_METALAVA_REMOTE_UPDATE_CACHE") 769 labels := map[string]string{"type": "tool", "name": "metalava"} 770 // TODO: metalava pool rejects these jobs 771 pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16") 772 rule.Rewrapper(&remoteexec.REParams{ 773 Labels: labels, 774 ExecStrategy: execStrategy, 775 ToolchainInputs: []string{config.JavaCmd(ctx).String()}, 776 Platform: map[string]string{remoteexec.PoolKey: pool}, 777 Compare: compare, 778 NumLocalRuns: 1, 779 NumRemoteRuns: 1, 780 NoRemoteUpdateCache: !remoteUpdateCache, 781 }) 782 } 783 784 cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")). 785 Flag(config.JavacVmFlags). 786 Flag(config.MetalavaAddOpens). 787 FlagWithArg("--java-source ", params.javaVersion.String()). 788 FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, fmt.Sprintf("%s.metalava.rsp", params.stubsType.String())), srcs). 789 FlagWithInput("@", srcJarList) 790 791 // Metalava does not differentiate between bootclasspath and classpath and has not done so for 792 // years, so it is unlikely to change any time soon. 793 combinedPaths := append(([]android.Path)(nil), params.deps.bootClasspath.Paths()...) 794 combinedPaths = append(combinedPaths, params.deps.classpath.Paths()...) 795 if len(combinedPaths) > 0 { 796 cmd.FlagWithInputList("--classpath ", combinedPaths, ":") 797 } 798 799 cmd.Flag(config.MetalavaFlags) 800 801 return cmd 802} 803 804// Pass flagged apis related flags to metalava. When aconfig_declarations property is not 805// defined for a module, simply revert all flagged apis annotations. If aconfig_declarations 806// property is defined, apply transformations and only revert the flagged apis that are not 807// enabled via release configurations and are not specified in aconfig_declarations 808func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) { 809 810 if len(aconfigFlagsPaths) == 0 { 811 cmd.Flag("--revert-annotation android.annotation.FlaggedApi") 812 return 813 } 814 815 releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) 816 revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) 817 818 var filterArgs string 819 switch stubsType { 820 // No flagged apis specific flags need to be passed to metalava when generating 821 // everything stubs 822 case Everything: 823 return 824 825 case Runtime: 826 filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" 827 828 case Exportable: 829 // When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with 830 // the flagged apis that have read_write permissions are exposed on top of the enabled 831 // and read_only apis. This is to support local override of flag values at runtime. 832 if ctx.Config().ReleaseExportRuntimeApis() { 833 filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" 834 } else { 835 filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'" 836 } 837 } 838 839 ctx.Build(pctx, android.BuildParams{ 840 Rule: gatherReleasedFlaggedApisRule, 841 Inputs: aconfigFlagsPaths, 842 Output: releasedFlaggedApisFile, 843 Description: fmt.Sprintf("%s gather aconfig flags", stubsType), 844 Args: map[string]string{ 845 "flags_path": android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "), 846 "filter_args": filterArgs, 847 }, 848 }) 849 850 ctx.Build(pctx, android.BuildParams{ 851 Rule: generateMetalavaRevertAnnotationsRule, 852 Input: releasedFlaggedApisFile, 853 Output: revertAnnotationsFile, 854 Description: fmt.Sprintf("%s revert annotations", stubsType), 855 }) 856 857 cmd.FlagWithInput("@", revertAnnotationsFile) 858} 859 860func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, 861 params stubsCommandParams) *android.RuleBuilderCommand { 862 if BoolDefault(d.properties.High_mem, false) { 863 // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel. 864 rule.HighMem() 865 } 866 867 if params.stubConfig.generateStubs { 868 rule.Command().Text("rm -rf").Text(params.stubsDir.String()) 869 rule.Command().Text("mkdir -p").Text(params.stubsDir.String()) 870 } 871 872 srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars) 873 874 homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home") 875 cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig) 876 cmd.Implicits(d.Javadoc.implicits) 877 878 d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi) 879 880 if params.stubConfig.writeSdkValues { 881 d.sdkValuesFlags(ctx, cmd, params.metadataDir) 882 } 883 884 annotationParams := annotationFlagsParams{ 885 migratingNullability: params.stubConfig.migratingNullability, 886 validatingNullability: params.stubConfig.validatingNullability, 887 nullabilityWarningsFile: params.nullabilityWarningsFile, 888 annotationsZip: params.annotationsZip, 889 } 890 891 d.annotationsFlags(ctx, cmd, annotationParams) 892 d.inclusionAnnotationsFlags(ctx, cmd) 893 d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml) 894 895 if params.stubConfig.doCheckReleased { 896 d.apiCompatibilityFlags(ctx, cmd, params.stubConfig.stubsType) 897 } 898 899 d.expandArgs(ctx, cmd) 900 901 for _, o := range d.Javadoc.properties.Out { 902 cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) 903 } 904 905 return cmd 906} 907 908// Sandbox rule for generating the everything stubs and other artifacts 909func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { 910 srcJarDir := android.PathForModuleOut(ctx, Everything.String(), "srcjars") 911 rule := android.NewRuleBuilder(pctx, ctx) 912 rule.Sbox(android.PathForModuleOut(ctx, Everything.String()), 913 android.PathForModuleOut(ctx, "metalava.sbox.textproto")). 914 SandboxInputs() 915 916 var stubsDir android.OptionalPath 917 if params.generateStubs { 918 stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, Everything.String(), "stubsDir")) 919 d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-"+"stubs.srcjar") 920 } 921 922 if params.writeSdkValues { 923 d.everythingArtifacts.metadataDir = android.PathForModuleOut(ctx, Everything.String(), "metadata") 924 d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip") 925 } 926 927 if Bool(d.properties.Annotations_enabled) { 928 if params.validatingNullability { 929 d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt") 930 } 931 d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip") 932 } 933 if Bool(d.properties.Api_levels_annotations_enabled) { 934 d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml") 935 } 936 937 commonCmdParams := stubsCommandParams{ 938 srcJarDir: srcJarDir, 939 stubsDir: stubsDir, 940 stubsSrcJar: d.Javadoc.stubsSrcJar, 941 metadataDir: d.everythingArtifacts.metadataDir, 942 apiVersionsXml: d.everythingArtifacts.apiVersionsXml, 943 nullabilityWarningsFile: d.everythingArtifacts.nullabilityWarningsFile, 944 annotationsZip: d.everythingArtifacts.annotationsZip, 945 stubConfig: params, 946 } 947 948 cmd := d.commonMetalavaStubCmd(ctx, rule, commonCmdParams) 949 950 d.everythingOptionalCmd(ctx, cmd, params.doApiLint, params.doCheckReleased) 951 952 if params.generateStubs { 953 rule.Command(). 954 BuiltTool("soong_zip"). 955 Flag("-write_if_changed"). 956 Flag("-jar"). 957 FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). 958 FlagWithArg("-C ", stubsDir.String()). 959 FlagWithArg("-D ", stubsDir.String()) 960 } 961 962 if params.writeSdkValues { 963 rule.Command(). 964 BuiltTool("soong_zip"). 965 Flag("-write_if_changed"). 966 Flag("-d"). 967 FlagWithOutput("-o ", d.everythingArtifacts.metadataZip). 968 FlagWithArg("-C ", d.everythingArtifacts.metadataDir.String()). 969 FlagWithArg("-D ", d.everythingArtifacts.metadataDir.String()) 970 } 971 972 // TODO: We don't really need two separate API files, but this is a reminiscence of how 973 // we used to run metalava separately for API lint and the "last_released" check. Unify them. 974 if params.doApiLint { 975 rule.Command().Text("touch").Output(d.apiLintTimestamp) 976 } 977 if params.doCheckReleased { 978 rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) 979 } 980 981 // TODO(b/183630617): rewrapper doesn't support restat rules 982 if !metalavaUseRbe(ctx) { 983 rule.Restat() 984 } 985 986 zipSyncCleanupCmd(rule, srcJarDir) 987 988 rule.Build("metalava", "metalava merged") 989} 990 991// Sandbox rule for generating the everything artifacts that are not run by 992// default but only run based on the module configurations 993func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) { 994 995 // Add API lint options. 996 treatDocumentationIssuesAsErrors := false 997 if doApiLint { 998 var newSince android.Paths 999 if d.properties.Check_api.Api_lint.New_since != nil { 1000 newSince = android.PathsForModuleSrc(ctx, []string{proptools.String(d.properties.Check_api.Api_lint.New_since)}) 1001 } 1002 cmd.Flag("--api-lint") 1003 cmd.FlagForEachInput("--api-lint-previous-api ", newSince) 1004 d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt") 1005 cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint" 1006 1007 // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released. 1008 if d.Name() != "android.car-system-stubs-docs" && 1009 d.Name() != "android.car-stubs-docs" { 1010 treatDocumentationIssuesAsErrors = true 1011 cmd.Flag("--warnings-as-errors") // Most lints are actually warnings. 1012 } 1013 1014 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) 1015 updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "api_lint_baseline.txt") 1016 d.apiLintTimestamp = android.PathForModuleOut(ctx, Everything.String(), "api_lint.timestamp") 1017 1018 // Note this string includes a special shell quote $' ... ', which decodes the "\n"s. 1019 // 1020 // TODO: metalava also has a slightly different message hardcoded. Should we unify this 1021 // message and metalava's one? 1022 msg := `$'` + // Enclose with $' ... ' 1023 `************************************************************\n` + 1024 `Your API changes are triggering API Lint warnings or errors.\n` + 1025 `To make these errors go away, fix the code according to the\n` + 1026 `error and/or warning messages above.\n` + 1027 `\n` + 1028 `If it is not possible to do so, there are workarounds:\n` + 1029 `\n` + 1030 `1. You can suppress the errors with @SuppressLint("<id>")\n` + 1031 ` where the <id> is given in brackets in the error message above.\n` 1032 1033 if baselineFile.Valid() { 1034 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) 1035 cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput) 1036 1037 msg += fmt.Sprintf(``+ 1038 `2. You can update the baseline by executing the following\n`+ 1039 ` command:\n`+ 1040 ` (cd $ANDROID_BUILD_TOP && cp \\\n`+ 1041 ` "%s" \\\n`+ 1042 ` "%s")\n`+ 1043 ` To submit the revised baseline.txt to the main Android\n`+ 1044 ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path()) 1045 } else { 1046 msg += fmt.Sprintf(``+ 1047 `2. You can add a baseline file of existing lint failures\n`+ 1048 ` to the build rule of %s.\n`, d.Name()) 1049 } 1050 // Note the message ends with a ' (single quote), to close the $' ... ' . 1051 msg += `************************************************************\n'` 1052 1053 cmd.FlagWithArg("--error-message:api-lint ", msg) 1054 } 1055 1056 if !treatDocumentationIssuesAsErrors { 1057 treatDocumentationIssuesAsWarningErrorWhenNew(cmd) 1058 } 1059 1060 // Add "check released" options. (Detect incompatible API changes from the last public release) 1061 if doCheckReleased { 1062 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) 1063 d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp") 1064 if baselineFile.Valid() { 1065 updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt") 1066 cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput) 1067 } 1068 // Note this string includes quote ($' ... '), which decodes the "\n"s. 1069 msg := `$'\n******************************\n` + 1070 `You have tried to change the API from what has been previously released in\n` + 1071 `an SDK. Please fix the errors listed above.\n` + 1072 `******************************\n'` 1073 1074 cmd.FlagWithArg("--error-message:compatibility:released ", msg) 1075 } 1076 1077 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { 1078 // Pass the current API file into metalava so it can use it as the basis for determining how to 1079 // generate the output signature files (both api and removed). 1080 currentApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file)) 1081 cmd.FlagWithInput("--use-same-format-as ", currentApiFile) 1082 } 1083} 1084 1085// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be 1086// hidden as they are very noisy and provide little value. 1087var HIDDEN_DOCUMENTATION_ISSUES = []string{ 1088 "Deprecated", 1089 "IntDef", 1090 "Nullable", 1091} 1092 1093func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) { 1094 // Treat documentation issues as warnings, but error when new. 1095 cmd.Flag("--error-when-new-category").Flag("Documentation") 1096 1097 // Hide some documentation issues that generated a lot of noise for little benefit. 1098 cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES) 1099} 1100 1101// Sandbox rule for generating exportable stubs and other artifacts 1102func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { 1103 optionalCmdParams := stubsCommandParams{ 1104 stubConfig: params, 1105 } 1106 1107 if params.generateStubs { 1108 d.Javadoc.exportableStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar") 1109 optionalCmdParams.stubsSrcJar = d.Javadoc.exportableStubsSrcJar 1110 } 1111 1112 if params.writeSdkValues { 1113 d.exportableArtifacts.metadataZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-metadata.zip") 1114 d.exportableArtifacts.metadataDir = android.PathForModuleOut(ctx, params.stubsType.String(), "metadata") 1115 optionalCmdParams.metadataZip = d.exportableArtifacts.metadataZip 1116 optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir 1117 } 1118 1119 if Bool(d.properties.Annotations_enabled) { 1120 if params.validatingNullability { 1121 d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt") 1122 optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile 1123 } 1124 d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip") 1125 optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip 1126 } 1127 if Bool(d.properties.Api_levels_annotations_enabled) { 1128 d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml") 1129 optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml 1130 } 1131 1132 if params.checkApi || String(d.properties.Api_filename) != "" { 1133 filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") 1134 d.exportableApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) 1135 } 1136 1137 if params.checkApi || String(d.properties.Removed_api_filename) != "" { 1138 filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_api.txt") 1139 d.exportableRemovedApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) 1140 } 1141 1142 d.optionalStubCmd(ctx, optionalCmdParams) 1143} 1144 1145func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) { 1146 1147 params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars") 1148 rule := android.NewRuleBuilder(pctx, ctx) 1149 rule.Sbox(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String()), 1150 android.PathForModuleOut(ctx, fmt.Sprintf("metalava_%s.sbox.textproto", params.stubConfig.stubsType.String()))). 1151 SandboxInputs() 1152 1153 if params.stubConfig.generateStubs { 1154 params.stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "stubsDir")) 1155 } 1156 1157 cmd := d.commonMetalavaStubCmd(ctx, rule, params) 1158 1159 generateRevertAnnotationArgs(ctx, cmd, params.stubConfig.stubsType, params.stubConfig.deps.aconfigProtoFiles) 1160 1161 if params.stubConfig.doApiLint { 1162 // Pass the lint baseline file as an input to resolve the lint errors. 1163 // The exportable stubs generation does not update the lint baseline file. 1164 // Lint baseline file update is handled by the everything stubs 1165 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) 1166 if baselineFile.Valid() { 1167 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) 1168 } 1169 } 1170 1171 // Treat documentation issues as warnings, but error when new. 1172 treatDocumentationIssuesAsWarningErrorWhenNew(cmd) 1173 1174 if params.stubConfig.generateStubs { 1175 rule.Command(). 1176 BuiltTool("soong_zip"). 1177 Flag("-write_if_changed"). 1178 Flag("-jar"). 1179 FlagWithOutput("-o ", params.stubsSrcJar). 1180 FlagWithArg("-C ", params.stubsDir.String()). 1181 FlagWithArg("-D ", params.stubsDir.String()) 1182 } 1183 1184 if params.stubConfig.writeSdkValues { 1185 rule.Command(). 1186 BuiltTool("soong_zip"). 1187 Flag("-write_if_changed"). 1188 Flag("-d"). 1189 FlagWithOutput("-o ", params.metadataZip). 1190 FlagWithArg("-C ", params.metadataDir.String()). 1191 FlagWithArg("-D ", params.metadataDir.String()) 1192 } 1193 1194 // TODO(b/183630617): rewrapper doesn't support restat rules 1195 if !metalavaUseRbe(ctx) { 1196 rule.Restat() 1197 } 1198 1199 zipSyncCleanupCmd(rule, params.srcJarDir) 1200 1201 rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged") 1202} 1203 1204func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { 1205 deps := d.Javadoc.collectDeps(ctx) 1206 1207 javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) 1208 generateStubs := BoolDefault(d.properties.Generate_stubs, true) 1209 1210 // Add options for the other optional tasks: API-lint and check-released. 1211 // We generate separate timestamp files for them. 1212 doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) 1213 doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") 1214 1215 writeSdkValues := Bool(d.properties.Write_sdk_values) 1216 1217 annotationsEnabled := Bool(d.properties.Annotations_enabled) 1218 1219 migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != "" 1220 validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || 1221 String(d.properties.Validate_nullability_from_list) != "") 1222 1223 checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || 1224 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") 1225 1226 stubCmdParams := stubsCommandConfigParams{ 1227 javaVersion: javaVersion, 1228 deps: deps, 1229 checkApi: checkApi, 1230 generateStubs: generateStubs, 1231 doApiLint: doApiLint, 1232 doCheckReleased: doCheckReleased, 1233 writeSdkValues: writeSdkValues, 1234 migratingNullability: migratingNullability, 1235 validatingNullability: validatingNullability, 1236 } 1237 stubCmdParams.stubsType = Everything 1238 // Create default (i.e. "everything" stubs) rule for metalava 1239 d.everythingStubCmd(ctx, stubCmdParams) 1240 1241 // The module generates "exportable" (and "runtime" eventually) stubs regardless of whether 1242 // aconfig_declarations property is defined or not. If the property is not defined, the module simply 1243 // strips all flagged apis to generate the "exportable" stubs 1244 stubCmdParams.stubsType = Exportable 1245 d.exportableStubCmd(ctx, stubCmdParams) 1246 1247 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { 1248 1249 if len(d.Javadoc.properties.Out) > 0 { 1250 ctx.PropertyErrorf("out", "out property may not be combined with check_api") 1251 } 1252 1253 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file)) 1254 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file)) 1255 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file) 1256 1257 if baselineFile.Valid() { 1258 ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName()) 1259 } 1260 1261 d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_current_api.timestamp") 1262 1263 rule := android.NewRuleBuilder(pctx, ctx) 1264 1265 // Diff command line. 1266 // -F matches the closest "opening" line, such as "package android {" 1267 // and " public class Intent {". 1268 diff := `diff -u -F '{ *$'` 1269 1270 rule.Command().Text("( true") 1271 rule.Command(). 1272 Text(diff). 1273 Input(apiFile).Input(d.apiFile) 1274 1275 rule.Command(). 1276 Text(diff). 1277 Input(removedApiFile).Input(d.removedApiFile) 1278 1279 msg := fmt.Sprintf(`\n******************************\n`+ 1280 `You have tried to change the API from what has been previously approved.\n\n`+ 1281 `To make these errors go away, you have two choices:\n`+ 1282 ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+ 1283 ` to the new methods, etc. shown in the above diff.\n\n`+ 1284 ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+ 1285 ` m %s-update-current-api\n\n`+ 1286 ` To submit the revised current.txt to the main Android repository,\n`+ 1287 ` you will need approval.\n`+ 1288 `If your build failed due to stub validation, you can resolve the errors with\n`+ 1289 `either of the two choices above and try re-building the target.\n`+ 1290 `If the mismatch between the stubs and the current.txt is intended,\n`+ 1291 `you can try re-building the target by executing the following command:\n`+ 1292 `m DISABLE_STUB_VALIDATION=true <your build target>.\n`+ 1293 `Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+ 1294 `******************************\n`, ctx.ModuleName()) 1295 1296 rule.Command(). 1297 Text("touch").Output(d.checkCurrentApiTimestamp). 1298 Text(") || ("). 1299 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 1300 Text("; exit 38"). 1301 Text(")") 1302 1303 rule.Build("metalavaCurrentApiCheck", "check current API") 1304 1305 d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp") 1306 1307 // update API rule 1308 rule = android.NewRuleBuilder(pctx, ctx) 1309 1310 rule.Command().Text("( true") 1311 1312 rule.Command(). 1313 Text("cp").Flag("-f"). 1314 Input(d.apiFile).Flag(apiFile.String()) 1315 1316 rule.Command(). 1317 Text("cp").Flag("-f"). 1318 Input(d.removedApiFile).Flag(removedApiFile.String()) 1319 1320 msg = "failed to update public API" 1321 1322 rule.Command(). 1323 Text("touch").Output(d.updateCurrentApiTimestamp). 1324 Text(") || ("). 1325 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 1326 Text("; exit 38"). 1327 Text(")") 1328 1329 rule.Build("metalavaCurrentApiUpdate", "update current API") 1330 } 1331 1332 if String(d.properties.Check_nullability_warnings) != "" { 1333 if d.everythingArtifacts.nullabilityWarningsFile == nil { 1334 ctx.PropertyErrorf("check_nullability_warnings", 1335 "Cannot specify check_nullability_warnings unless validating nullability") 1336 } 1337 1338 checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings)) 1339 1340 d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp") 1341 1342 msg := fmt.Sprintf(`\n******************************\n`+ 1343 `The warnings encountered during nullability annotation validation did\n`+ 1344 `not match the checked in file of expected warnings. The diffs are shown\n`+ 1345 `above. You have two options:\n`+ 1346 ` 1. Resolve the differences by editing the nullability annotations.\n`+ 1347 ` 2. Update the file of expected warnings by running:\n`+ 1348 ` cp %s %s\n`+ 1349 ` and submitting the updated file as part of your change.`, 1350 d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings) 1351 1352 rule := android.NewRuleBuilder(pctx, ctx) 1353 1354 rule.Command(). 1355 Text("("). 1356 Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile). 1357 Text("&&"). 1358 Text("touch").Output(d.checkNullabilityWarningsTimestamp). 1359 Text(") || ("). 1360 Text("echo").Flag("-e").Flag(`"` + msg + `"`). 1361 Text("; exit 38"). 1362 Text(")") 1363 1364 rule.Build("nullabilityWarningsCheck", "nullability warnings check") 1365 } 1366} 1367 1368func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { 1369 api_file := d.properties.Check_api.Current.Api_file 1370 api_surface := d.properties.Api_surface 1371 1372 props := struct { 1373 Name *string 1374 Api_surface *string 1375 Api_file *string 1376 Visibility []string 1377 }{} 1378 1379 props.Name = proptools.StringPtr(d.Name() + ".api.contribution") 1380 props.Api_surface = api_surface 1381 props.Api_file = api_file 1382 props.Visibility = []string{"//visibility:override", "//visibility:public"} 1383 1384 ctx.CreateModule(ApiContributionFactory, &props) 1385} 1386 1387// TODO (b/262014796): Export the API contributions of CorePlatformApi 1388// A map to populate the api surface of a droidstub from a substring appearing in its name 1389// This map assumes that droidstubs (either checked-in or created by java_sdk_library) 1390// use a strict naming convention 1391var ( 1392 droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{ 1393 // public is commented out since the core libraries use public in their java_sdk_library names 1394 "intracore": android.SdkIntraCore, 1395 "intra.core": android.SdkIntraCore, 1396 "system_server": android.SdkSystemServer, 1397 "system-server": android.SdkSystemServer, 1398 "system": android.SdkSystem, 1399 "module_lib": android.SdkModule, 1400 "module-lib": android.SdkModule, 1401 "platform.api": android.SdkCorePlatform, 1402 "test": android.SdkTest, 1403 "toolchain": android.SdkToolchain, 1404 } 1405) 1406 1407func StubsDefaultsFactory() android.Module { 1408 module := &DocDefaults{} 1409 1410 module.AddProperties( 1411 &JavadocProperties{}, 1412 &DroidstubsProperties{}, 1413 ) 1414 1415 android.InitDefaultsModule(module) 1416 1417 return module 1418} 1419 1420var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil) 1421 1422type PrebuiltStubsSourcesProperties struct { 1423 Srcs []string `android:"path"` 1424 1425 // Name of the source soong module that gets shadowed by this prebuilt 1426 // If unspecified, follows the naming convention that the source module of 1427 // the prebuilt is Name() without "prebuilt_" prefix 1428 Source_module_name *string 1429 1430 // Non-nil if this prebuilt stub srcs module was dynamically created by a java_sdk_library_import 1431 // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file 1432 // (without any prebuilt_ prefix) 1433 Created_by_java_sdk_library_name *string `blueprint:"mutated"` 1434} 1435 1436func (j *PrebuiltStubsSources) BaseModuleName() string { 1437 return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name()) 1438} 1439 1440func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string { 1441 return j.properties.Created_by_java_sdk_library_name 1442} 1443 1444type PrebuiltStubsSources struct { 1445 android.ModuleBase 1446 android.DefaultableModuleBase 1447 embeddableInModuleAndImport 1448 1449 prebuilt android.Prebuilt 1450 1451 properties PrebuiltStubsSourcesProperties 1452 1453 stubsSrcJar android.Path 1454} 1455 1456func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { 1457 switch tag { 1458 // prebuilt droidstubs does not output "exportable" stubs. 1459 // Output the "everything" stubs srcjar file if the tag is ".exportable". 1460 case "", ".exportable": 1461 return android.Paths{p.stubsSrcJar}, nil 1462 default: 1463 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 1464 } 1465} 1466 1467func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) { 1468 return d.stubsSrcJar, nil 1469} 1470 1471func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { 1472 if len(p.properties.Srcs) != 1 { 1473 ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs)) 1474 return 1475 } 1476 1477 src := p.properties.Srcs[0] 1478 if filepath.Ext(src) == ".srcjar" { 1479 // This is a srcjar. We can use it directly. 1480 p.stubsSrcJar = android.PathForModuleSrc(ctx, src) 1481 } else { 1482 outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") 1483 1484 // This is a directory. Glob the contents just in case the directory does not exist. 1485 srcGlob := src + "/**/*" 1486 srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) 1487 1488 // Although PathForModuleSrc can return nil if either the path doesn't exist or 1489 // the path components are invalid it won't in this case because no components 1490 // are specified and the module directory must exist in order to get this far. 1491 srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src) 1492 1493 rule := android.NewRuleBuilder(pctx, ctx) 1494 rule.Command(). 1495 BuiltTool("soong_zip"). 1496 Flag("-write_if_changed"). 1497 Flag("-jar"). 1498 FlagWithOutput("-o ", outPath). 1499 FlagWithArg("-C ", srcDir.String()). 1500 FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths) 1501 rule.Restat() 1502 rule.Build("zip src", "Create srcjar from prebuilt source") 1503 p.stubsSrcJar = outPath 1504 } 1505} 1506 1507func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { 1508 return &p.prebuilt 1509} 1510 1511func (p *PrebuiltStubsSources) Name() string { 1512 return p.prebuilt.Name(p.ModuleBase.Name()) 1513} 1514 1515// prebuilt_stubs_sources imports a set of java source files as if they were 1516// generated by droidstubs. 1517// 1518// By default, a prebuilt_stubs_sources has a single variant that expects a 1519// set of `.java` files generated by droidstubs. 1520// 1521// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one 1522// for host modules. 1523// 1524// Intended only for use by sdk snapshots. 1525func PrebuiltStubsSourcesFactory() android.Module { 1526 module := &PrebuiltStubsSources{} 1527 1528 module.AddProperties(&module.properties) 1529 module.initModuleAndImport(module) 1530 1531 android.InitPrebuiltModule(module, &module.properties.Srcs) 1532 InitDroiddocModule(module, android.HostAndDeviceSupported) 1533 return module 1534} 1535