diff options
Diffstat (limited to 'java/droidstubs.go')
| -rw-r--r-- | java/droidstubs.go | 1042 |
1 files changed, 756 insertions, 286 deletions
diff --git a/java/droidstubs.go b/java/droidstubs.go index 8a521aabb..a8e0a22e5 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -18,13 +18,11 @@ import ( "fmt" "path/filepath" "regexp" - "sort" "strings" "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/java/config" "android/soong/remoteexec" ) @@ -32,6 +30,41 @@ import ( // The values allowed for Droidstubs' Api_levels_sdk_type var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"} +type StubsType int + +const ( + Everything StubsType = iota + Runtime + Exportable + Unavailable +) + +func (s StubsType) String() string { + switch s { + case Everything: + return "everything" + case Runtime: + return "runtime" + case Exportable: + return "exportable" + default: + return "" + } +} + +func StringToStubsType(s string) StubsType { + switch strings.ToLower(s) { + case Everything.String(): + return Everything + case Runtime.String(): + return Runtime + case Exportable.String(): + return Exportable + default: + return Unavailable + } +} + func init() { RegisterStubsBuildComponents(android.InitRegistrationContext) } @@ -45,14 +78,22 @@ func RegisterStubsBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) } +type stubsArtifacts struct { + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath + apiVersionsXml android.WritablePath + metadataZip android.WritablePath + metadataDir android.WritablePath +} + // Droidstubs type Droidstubs struct { Javadoc + embeddableInModuleAndImport - properties DroidstubsProperties - apiFile android.Path - removedApiFile android.Path - nullabilityWarningsFile android.WritablePath + properties DroidstubsProperties + apiFile android.Path + removedApiFile android.Path checkCurrentApiTimestamp android.WritablePath updateCurrentApiTimestamp android.WritablePath @@ -62,11 +103,11 @@ type Droidstubs struct { checkNullabilityWarningsTimestamp android.WritablePath - annotationsZip android.WritablePath - apiVersionsXml android.WritablePath + everythingArtifacts stubsArtifacts + exportableArtifacts stubsArtifacts - metadataZip android.WritablePath - metadataDir android.WritablePath + exportableApiFile android.WritablePath + exportableRemovedApiFile android.WritablePath } type DroidstubsProperties struct { @@ -123,7 +164,7 @@ type DroidstubsProperties struct { Generate_stubs *bool // if set to true, provides a hint to the build system that this rule uses a lot of memory, - // whicih can be used for scheduling purposes + // which can be used for scheduling purposes High_mem *bool // if set to true, Metalava will allow framework SDK to contain API levels annotations. @@ -152,26 +193,64 @@ type DroidstubsProperties struct { // API surface of this module. If set, the module contributes to an API surface. // For the full list of available API surfaces, refer to soong/android/sdk_version.go Api_surface *string + + // a list of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string } // Used by xsd_config type ApiFilePath interface { - ApiFilePath() android.Path + ApiFilePath(StubsType) (android.Path, error) } type ApiStubsSrcProvider interface { - StubsSrcJar() android.Path + StubsSrcJar(StubsType) (android.Path, error) } // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { - AnnotationsZip() android.Path + AnnotationsZip(StubsType) (android.Path, error) ApiFilePath - RemovedApiFilePath() android.Path + RemovedApiFilePath(StubsType) (android.Path, error) ApiStubsSrcProvider } +type currentApiTimestampProvider interface { + CurrentApiTimestamp() android.Path +} + +type annotationFlagsParams struct { + migratingNullability bool + validatingNullability bool + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath +} +type stubsCommandParams struct { + srcJarDir android.ModuleOutPath + stubsDir android.OptionalPath + stubsSrcJar android.WritablePath + metadataZip android.WritablePath + metadataDir android.WritablePath + apiVersionsXml android.WritablePath + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath + stubConfig stubsCommandConfigParams +} +type stubsCommandConfigParams struct { + stubsType StubsType + javaVersion javaVersion + deps deps + checkApi bool + generateStubs bool + doApiLint bool + doCheckReleased bool + writeSdkValues bool + migratingNullability bool + validatingNullability bool +} + // droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be // documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to // a droiddoc module to generate documentation. @@ -180,6 +259,7 @@ func DroidstubsFactory() android.Module { module.AddProperties(&module.properties, &module.Javadoc.properties) + module.initModuleAndImport(module) InitDroiddocModule(module, android.HostAndDeviceSupported) @@ -203,46 +283,100 @@ func DroidstubsHostFactory() android.Module { return module } -func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{d.stubsSrcJar}, nil - case ".docs.zip": - return android.Paths{d.docZip}, nil - case ".api.txt", android.DefaultDistTag: - // This is the default dist path for dist properties that have no tag property. - return android.Paths{d.apiFile}, nil - case ".removed-api.txt": - return android.Paths{d.removedApiFile}, nil - case ".annotations.zip": - return android.Paths{d.annotationsZip}, nil - case ".api_versions.xml": - return android.Paths{d.apiVersionsXml}, nil +func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.everythingArtifacts.annotationsZip, nil + case Exportable: + ret, err = d.exportableArtifacts.annotationsZip, nil default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) + ret, err = nil, fmt.Errorf("annotations zip not supported for the stub type %s", stubsType.String()) } + return ret, err } -func (d *Droidstubs) AnnotationsZip() android.Path { - return d.annotationsZip +func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.apiFile, nil + case Exportable: + ret, err = d.exportableApiFile, nil + default: + ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("api file is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) ApiFilePath() android.Path { - return d.apiFile +func (d *Droidstubs) ApiVersionsXmlFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.everythingArtifacts.apiVersionsXml, nil + case Exportable: + ret, err = d.exportableArtifacts.apiVersionsXml, nil + default: + ret, err = nil, fmt.Errorf("api versions xml file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("api versions xml file is null for the stub type %s", stubsType.String()) + } + return ret, err +} + +func (d *Droidstubs) DocZip(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.docZip, nil + default: + ret, err = nil, fmt.Errorf("docs zip not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("docs zip is null for the stub type %s", stubsType.String()) + } + return ret, err +} + +func (d *Droidstubs) RemovedApiFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.removedApiFile, nil + case Exportable: + ret, err = d.exportableRemovedApiFile, nil + default: + ret, err = nil, fmt.Errorf("removed api file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("removed api file is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) RemovedApiFilePath() android.Path { - return d.removedApiFile +func (d *Droidstubs) StubsSrcJar(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.stubsSrcJar, nil + case Exportable: + ret, err = d.exportableStubsSrcJar, nil + default: + ret, err = nil, fmt.Errorf("stubs srcjar not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) StubsSrcJar() android.Path { - return d.stubsSrcJar +func (d *Droidstubs) CurrentApiTimestamp() android.Path { + return d.checkCurrentApiTimestamp } var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"} var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"} var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"} var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"} +var metalavaCurrentApiTimestampTag = dependencyTag{name: "metalava-current-api-timestamp-tag"} func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { d.Javadoc.addDeps(ctx) @@ -265,39 +399,57 @@ func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { } } + if len(d.properties.Aconfig_declarations) != 0 { + for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName) + } + } + if d.properties.Api_levels_module != nil { ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module)) } } -func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) { - if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || - apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || - String(d.properties.Api_filename) != "" { - filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") - uncheckedApiFile := android.PathForModuleOut(ctx, "metalava", filename) - cmd.FlagWithOutput("--api ", uncheckedApiFile) - d.apiFile = uncheckedApiFile +func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, metadataDir android.WritablePath) { + cmd.FlagWithArg("--sdk-values ", metadataDir.String()) +} + +func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) { + + apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") + uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName) + cmd.FlagWithOutput("--api ", uncheckedApiFile) + if checkApi || String(d.properties.Api_filename) != "" { + if stubsType == Everything { + d.apiFile = uncheckedApiFile + } else if stubsType == Exportable { + d.exportableApiFile = uncheckedApiFile + } } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" { - // If check api is disabled then make the source file available for export. - d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + if stubsType == Everything { + // If check api is disabled then make the source file available for export. + d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + } else if stubsType == Exportable { + d.exportableApiFile = uncheckedApiFile + } } - if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || - apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || - String(d.properties.Removed_api_filename) != "" { - filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") - uncheckedRemovedFile := android.PathForModuleOut(ctx, "metalava", filename) - cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) - d.removedApiFile = uncheckedRemovedFile + removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") + uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName) + cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) + if checkApi || String(d.properties.Removed_api_filename) != "" { + if stubsType == Everything { + d.removedApiFile = uncheckedRemovedFile + } else if stubsType == Exportable { + d.exportableRemovedApiFile = uncheckedRemovedFile + } } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" { - // If check api is disabled then make the source removed api file available for export. - d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) - } - - if Bool(d.properties.Write_sdk_values) { - d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata") - cmd.FlagWithArg("--sdk-values ", d.metadataDir.String()) + if stubsType == Everything { + // If check api is disabled then make the source removed api file available for export. + d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) + } else if stubsType == Exportable { + d.exportableRemovedApiFile = uncheckedRemovedFile + } } if stubsDir.Valid() { @@ -312,46 +464,30 @@ func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuil } } -func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) { if Bool(d.properties.Annotations_enabled) { - cmd.Flag("--include-annotations") - - cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi") + cmd.Flag(config.MetalavaAnnotationsFlags) - validatingNullability := - strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || - String(d.properties.Validate_nullability_from_list) != "" - - migratingNullability := String(d.properties.Previous_api) != "" - if migratingNullability { - previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api)) - cmd.FlagWithInput("--migrate-nullness ", previousApi) + if params.migratingNullability { + previousApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Previous_api)}) + cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles) } if s := String(d.properties.Validate_nullability_from_list); s != "" { cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) } - if validatingNullability { - d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt") - cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile) + if params.validatingNullability { + cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile) } - d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip") - cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip) + cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip) if len(d.properties.Merge_annotations_dirs) != 0 { d.mergeAnnoDirFlags(ctx, cmd) } - // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. - cmd.FlagWithArg("--hide ", "HiddenTypedefConstant"). - FlagWithArg("--hide ", "SuperfluousPrefix"). - FlagWithArg("--hide ", "AnnotationExtraction"). - // b/222738070 - FlagWithArg("--hide ", "BannedThrow"). - // b/223382732 - FlagWithArg("--hide ", "ChangedDefault") + cmd.Flag(config.MetalavaAnnotationsWarningsFlags) } } @@ -377,15 +513,21 @@ func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *a }) } -func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { var apiVersions android.Path if proptools.Bool(d.properties.Api_levels_annotations_enabled) { - d.apiLevelsGenerationFlags(ctx, cmd) - apiVersions = d.apiVersionsXml + d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml) + apiVersions = apiVersionsXml } else { ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) { if s, ok := m.(*Droidstubs); ok { - apiVersions = s.apiVersionsXml + if stubsType == Everything { + apiVersions = s.everythingArtifacts.apiVersionsXml + } else if stubsType == Exportable { + apiVersions = s.exportableArtifacts.apiVersionsXml + } else { + ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String()) + } } else { ctx.PropertyErrorf("api_levels_module", "module %q is not a droidstubs module", ctx.OtherModuleName(m)) @@ -399,36 +541,87 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a } } -func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +// AndroidPlusUpdatableJar is the name of some extra jars added into `module-lib` and +// `system-server` directories that contain all the APIs provided by the platform and updatable +// modules because the `android.jar` files do not. See b/337836752. +const AndroidPlusUpdatableJar = "android-plus-updatable.jar" + +func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { if len(d.properties.Api_levels_annotations_dirs) == 0 { ctx.PropertyErrorf("api_levels_annotations_dirs", "has to be non-empty if api levels annotations was enabled!") } - d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml") - cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml) + cmd.FlagWithOutput("--generate-api-levels ", apiVersionsXml) filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") + // TODO: Avoid the duplication of API surfaces, reuse apiScope. + // Add all relevant --android-jar-pattern patterns for Metalava. + // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines + // an actual file present on disk (in the order the patterns were passed). For system APIs for + // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs + // for older releases. Similarly, module-lib falls back to system API. + var sdkDirs []string + apiLevelsSdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") + switch apiLevelsSdkType { + case "system-server": + sdkDirs = []string{"system-server", "module-lib", "system", "public"} + case "module-lib": + sdkDirs = []string{"module-lib", "system", "public"} + case "system": + sdkDirs = []string{"system", "public"} + case "public": + sdkDirs = []string{"public"} + default: + ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) + return + } + + // Construct a pattern to match the appropriate extensions that should be included in the + // generated api-versions.xml file. + // + // Use the first item in the sdkDirs array as that is the sdk type for the target API levels + // being generated but has the advantage over `Api_levels_sdk_type` as it has been validated. + // The exception is for system-server which needs to include module-lib and system-server. That + // is because while system-server extends module-lib the system-server extension directory only + // contains service-* modules which provide system-server APIs it does not list the modules which + // only provide a module-lib, so they have to be included separately. + extensionSurfacesPattern := sdkDirs[0] + if apiLevelsSdkType == "system-server" { + // Take the first two items in sdkDirs, which are system-server and module-lib, and construct + // a pattern that will match either. + extensionSurfacesPattern = strings.Join(sdkDirs[0:2], "|") + } + extensionsPattern := fmt.Sprintf(`/extensions/[0-9]+/(%s)/.*\.jar`, extensionSurfacesPattern) + var dirs []string var extensions_dir string ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { if t, ok := m.(*ExportedDroiddocDir); ok { - extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`) + extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern) // Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps; // ideally this should be read from prebuiltApis.properties.Extensions_* for _, dep := range t.deps { + // Check to see if it matches an extension first. + depBase := dep.Base() if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil { if extensions_dir == "" { extensions_dir = t.dir.String() + "/extensions" } cmd.Implicit(dep) - } - if dep.Base() == filename { + } else if depBase == filename { + // Check to see if it matches a dessert release for an SDK, e.g. Android, Car, Wear, etc.. cmd.Implicit(dep) - } - if filename != "android.jar" && dep.Base() == "android.jar" { + } else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil { + // The output api-versions.xml has been requested to include information on SDK + // extensions. That means it also needs to include + // so + // The module-lib and system-server directories should use `android-plus-updatable.jar` + // instead of `android.jar`. See AndroidPlusUpdatableJar for more information. + cmd.Implicit(dep) + } else if filename != "android.jar" && depBase == "android.jar" { // Metalava implicitly searches these patterns: // prebuilts/tools/common/api-versions/android-%/android.jar // prebuilts/sdk/%/public/android.jar @@ -446,29 +639,25 @@ func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *an } }) - // Add all relevant --android-jar-pattern patterns for Metalava. - // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines - // an actual file present on disk (in the order the patterns were passed). For system APIs for - // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs - // for older releases. Similarly, module-lib falls back to system API. - var sdkDirs []string - switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") { - case "system-server": - sdkDirs = []string{"system-server", "module-lib", "system", "public"} - case "module-lib": - sdkDirs = []string{"module-lib", "system", "public"} - case "system": - sdkDirs = []string{"system", "public"} - case "public": - sdkDirs = []string{"public"} - default: - ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) - return - } - + // Generate the list of --android-jar-pattern options. The order matters so the first one which + // matches will be the one that is used for a specific api level.. for _, sdkDir := range sdkDirs { for _, dir := range dirs { - cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename)) + addPattern := func(jarFilename string) { + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename)) + } + + if sdkDir == "module-lib" || sdkDir == "system-server" { + // The module-lib and system-server android.jars do not include the updatable modules (as + // doing so in the source would introduce dependency cycles and the prebuilts have to + // match the sources). So, instead an additional `android-plus-updatable.jar` will be used + // that does include the updatable modules and this pattern will match that. This pattern + // is added in addition to the following pattern to decouple this change from the change + // to add the `android-plus-updatable.jar`. + addPattern(AndroidPlusUpdatableJar) + } + + addPattern(filename) } } @@ -483,12 +672,29 @@ func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *an } } +func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType) { + if len(d.Javadoc.properties.Out) > 0 { + ctx.PropertyErrorf("out", "out property may not be combined with check_api") + } + + apiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Api_file)}) + removedApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Removed_api_file)}) + + cmd.FlagForEachInput("--check-compatibility:api:released ", apiFiles) + cmd.FlagForEachInput("--check-compatibility:removed:released ", removedApiFiles) + + baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) + if baselineFile.Valid() { + cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) + } +} + func metalavaUseRbe(ctx android.ModuleContext) bool { return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") } -func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, - srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand { +func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths, + srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -498,87 +704,137 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi if metalavaUseRbe(ctx) { rule.Remoteable(android.RemoteRuleSupports{RBE: true}) execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy) + compare := ctx.Config().IsEnvTrue("RBE_METALAVA_COMPARE") + remoteUpdateCache := !ctx.Config().IsEnvFalse("RBE_METALAVA_REMOTE_UPDATE_CACHE") labels := map[string]string{"type": "tool", "name": "metalava"} // TODO: metalava pool rejects these jobs pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16") rule.Rewrapper(&remoteexec.REParams{ - Labels: labels, - ExecStrategy: execStrategy, - ToolchainInputs: []string{config.JavaCmd(ctx).String()}, - Platform: map[string]string{remoteexec.PoolKey: pool}, + Labels: labels, + ExecStrategy: execStrategy, + ToolchainInputs: []string{config.JavaCmd(ctx).String()}, + Platform: map[string]string{remoteexec.PoolKey: pool}, + Compare: compare, + NumLocalRuns: 1, + NumRemoteRuns: 1, + NoRemoteUpdateCache: !remoteUpdateCache, }) } cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")). Flag(config.JavacVmFlags). - Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). - FlagWithArg("-encoding ", "UTF-8"). - FlagWithArg("-source ", javaVersion.String()). - FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs). + Flag(config.MetalavaAddOpens). + FlagWithArg("--java-source ", params.javaVersion.String()). + FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, fmt.Sprintf("%s.metalava.rsp", params.stubsType.String())), srcs). FlagWithInput("@", srcJarList) - if len(bootclasspath) > 0 { - cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":") - } - - if len(classpath) > 0 { - cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":") + // Metalava does not differentiate between bootclasspath and classpath and has not done so for + // years, so it is unlikely to change any time soon. + combinedPaths := append(([]android.Path)(nil), params.deps.bootClasspath.Paths()...) + combinedPaths = append(combinedPaths, params.deps.classpath.Paths()...) + if len(combinedPaths) > 0 { + cmd.FlagWithInputList("--classpath ", combinedPaths, ":") } - cmd.Flag("--no-banner"). - Flag("--color"). - Flag("--quiet"). - Flag("--format=v2"). - FlagWithArg("--repeat-errors-max ", "10"). - FlagWithArg("--hide ", "UnresolvedImport"). - FlagWithArg("--hide ", "InvalidNullabilityOverride"). - // b/223382732 - FlagWithArg("--hide ", "ChangedDefault") + cmd.Flag(config.MetalavaFlags) return cmd } -func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { - deps := d.Javadoc.collectDeps(ctx) +// Pass flagged apis related flags to metalava. When aconfig_declarations property is not +// defined for a module, simply revert all flagged apis annotations. If aconfig_declarations +// property is defined, apply transformations and only revert the flagged apis that are not +// enabled via release configurations and are not specified in aconfig_declarations +func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) { - javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) + if len(aconfigFlagsPaths) == 0 { + cmd.Flag("--revert-annotation android.annotation.FlaggedApi") + return + } - // Create rule for metalava + releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) + revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) - srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars") + var filterArgs string + switch stubsType { + // No flagged apis specific flags need to be passed to metalava when generating + // everything stubs + case Everything: + return - rule := android.NewRuleBuilder(pctx, ctx) + case Runtime: + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" - rule.Sbox(android.PathForModuleOut(ctx, "metalava"), - android.PathForModuleOut(ctx, "metalava.sbox.textproto")). - SandboxInputs() + case Exportable: + // When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with + // the flagged apis that have read_write permissions are exposed on top of the enabled + // and read_only apis. This is to support local override of flag values at runtime. + if ctx.Config().ReleaseExportRuntimeApis() { + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" + } else { + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'" + } + } + + ctx.Build(pctx, android.BuildParams{ + Rule: gatherReleasedFlaggedApisRule, + Inputs: aconfigFlagsPaths, + Output: releasedFlaggedApisFile, + Description: fmt.Sprintf("%s gather aconfig flags", stubsType), + Args: map[string]string{ + "flags_path": android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "), + "filter_args": filterArgs, + }, + }) + + ctx.Build(pctx, android.BuildParams{ + Rule: generateMetalavaRevertAnnotationsRule, + Input: releasedFlaggedApisFile, + Output: revertAnnotationsFile, + Description: fmt.Sprintf("%s revert annotations", stubsType), + }) + cmd.FlagWithInput("@", revertAnnotationsFile) +} + +func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, + params stubsCommandParams) *android.RuleBuilderCommand { if BoolDefault(d.properties.High_mem, false) { // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel. rule.HighMem() } - generateStubs := BoolDefault(d.properties.Generate_stubs, true) - var stubsDir android.OptionalPath - if generateStubs { - d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar") - stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir")) - rule.Command().Text("rm -rf").Text(stubsDir.String()) - rule.Command().Text("mkdir -p").Text(stubsDir.String()) + if params.stubConfig.generateStubs { + rule.Command().Text("rm -rf").Text(params.stubsDir.String()) + rule.Command().Text("mkdir -p").Text(params.stubsDir.String()) } - srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) + srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars) - homeDir := android.PathForModuleOut(ctx, "metalava", "home") - cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, homeDir) + homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home") + cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig) cmd.Implicits(d.Javadoc.implicits) - d.stubsFlags(ctx, cmd, stubsDir) + d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi) + + if params.stubConfig.writeSdkValues { + d.sdkValuesFlags(ctx, cmd, params.metadataDir) + } + + annotationParams := annotationFlagsParams{ + migratingNullability: params.stubConfig.migratingNullability, + validatingNullability: params.stubConfig.validatingNullability, + nullabilityWarningsFile: params.nullabilityWarningsFile, + annotationsZip: params.annotationsZip, + } - d.annotationsFlags(ctx, cmd) + d.annotationsFlags(ctx, cmd, annotationParams) d.inclusionAnnotationsFlags(ctx, cmd) - d.apiLevelsAnnotationsFlags(ctx, cmd) + d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml) + + if params.stubConfig.doCheckReleased { + d.apiCompatibilityFlags(ctx, cmd, params.stubConfig.stubsType) + } d.expandArgs(ctx, cmd) @@ -586,36 +842,118 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) } - // Add options for the other optional tasks: API-lint and check-released. - // We generate separate timestamp files for them. + return cmd +} - doApiLint := false - doCheckReleased := false +// Sandbox rule for generating the everything stubs and other artifacts +func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { + srcJarDir := android.PathForModuleOut(ctx, Everything.String(), "srcjars") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Sbox(android.PathForModuleOut(ctx, Everything.String()), + android.PathForModuleOut(ctx, "metalava.sbox.textproto")). + SandboxInputs() - // Add API lint options. + var stubsDir android.OptionalPath + if params.generateStubs { + stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, Everything.String(), "stubsDir")) + d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-"+"stubs.srcjar") + } - if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) { - doApiLint = true + if params.writeSdkValues { + d.everythingArtifacts.metadataDir = android.PathForModuleOut(ctx, Everything.String(), "metadata") + d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip") + } - newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since) - if newSince.Valid() { - cmd.FlagWithInput("--api-lint ", newSince.Path()) - } else { - cmd.Flag("--api-lint") + if Bool(d.properties.Annotations_enabled) { + if params.validatingNullability { + d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt") + } + d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip") + } + if Bool(d.properties.Api_levels_annotations_enabled) { + d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml") + } + + commonCmdParams := stubsCommandParams{ + srcJarDir: srcJarDir, + stubsDir: stubsDir, + stubsSrcJar: d.Javadoc.stubsSrcJar, + metadataDir: d.everythingArtifacts.metadataDir, + apiVersionsXml: d.everythingArtifacts.apiVersionsXml, + nullabilityWarningsFile: d.everythingArtifacts.nullabilityWarningsFile, + annotationsZip: d.everythingArtifacts.annotationsZip, + stubConfig: params, + } + + cmd := d.commonMetalavaStubCmd(ctx, rule, commonCmdParams) + + d.everythingOptionalCmd(ctx, cmd, params.doApiLint, params.doCheckReleased) + + if params.generateStubs { + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) + } + + if params.writeSdkValues { + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-d"). + FlagWithOutput("-o ", d.everythingArtifacts.metadataZip). + FlagWithArg("-C ", d.everythingArtifacts.metadataDir.String()). + FlagWithArg("-D ", d.everythingArtifacts.metadataDir.String()) + } + + // TODO: We don't really need two separate API files, but this is a reminiscence of how + // we used to run metalava separately for API lint and the "last_released" check. Unify them. + if params.doApiLint { + rule.Command().Text("touch").Output(d.apiLintTimestamp) + } + if params.doCheckReleased { + rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) + } + + // TODO(b/183630617): rewrapper doesn't support restat rules + if !metalavaUseRbe(ctx) { + rule.Restat() + } + + zipSyncCleanupCmd(rule, srcJarDir) + + rule.Build("metalava", "metalava merged") +} + +// Sandbox rule for generating the everything artifacts that are not run by +// default but only run based on the module configurations +func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) { + + // Add API lint options. + treatDocumentationIssuesAsErrors := false + if doApiLint { + var newSince android.Paths + if d.properties.Check_api.Api_lint.New_since != nil { + newSince = android.PathsForModuleSrc(ctx, []string{proptools.String(d.properties.Check_api.Api_lint.New_since)}) } - d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt") + cmd.Flag("--api-lint") + cmd.FlagForEachInput("--api-lint-previous-api ", newSince) + d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt") cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint" // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released. if d.Name() != "android.car-system-stubs-docs" && d.Name() != "android.car-stubs-docs" { - cmd.Flag("--lints-as-errors") + treatDocumentationIssuesAsErrors = true cmd.Flag("--warnings-as-errors") // Most lints are actually warnings. } baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) - updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt") - d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp") + updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "api_lint_baseline.txt") + d.apiLintTimestamp = android.PathForModuleOut(ctx, Everything.String(), "api_lint.timestamp") // Note this string includes a special shell quote $' ... ', which decodes the "\n"s. // @@ -655,30 +993,18 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.FlagWithArg("--error-message:api-lint ", msg) } - // Add "check released" options. (Detect incompatible API changes from the last public release) - - if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") { - doCheckReleased = true - - if len(d.Javadoc.properties.Out) > 0 { - ctx.PropertyErrorf("out", "out property may not be combined with check_api") - } + if !treatDocumentationIssuesAsErrors { + treatDocumentationIssuesAsWarningErrorWhenNew(cmd) + } - apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file)) - removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file)) + // Add "check released" options. (Detect incompatible API changes from the last public release) + if doCheckReleased { baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) - updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt") - - d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp") - - cmd.FlagWithInput("--check-compatibility:api:released ", apiFile) - cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile) - + d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp") if baselineFile.Valid() { - cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) + updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt") cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput) } - // Note this string includes quote ($' ... '), which decodes the "\n"s. msg := `$'\n******************************\n` + `You have tried to change the API from what has been previously released in\n` + @@ -688,34 +1014,121 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.FlagWithArg("--error-message:compatibility:released ", msg) } - if generateStubs { + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { + // Pass the current API file into metalava so it can use it as the basis for determining how to + // generate the output signature files (both api and removed). + currentApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file)) + cmd.FlagWithInput("--use-same-format-as ", currentApiFile) + } +} + +// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be +// hidden as they are very noisy and provide little value. +var HIDDEN_DOCUMENTATION_ISSUES = []string{ + "Deprecated", + "IntDef", + "Nullable", +} + +func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) { + // Treat documentation issues as warnings, but error when new. + cmd.Flag("--error-when-new-category").Flag("Documentation") + + // Hide some documentation issues that generated a lot of noise for little benefit. + cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES) +} + +// Sandbox rule for generating exportable stubs and other artifacts +func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { + optionalCmdParams := stubsCommandParams{ + stubConfig: params, + } + + if params.generateStubs { + d.Javadoc.exportableStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar") + optionalCmdParams.stubsSrcJar = d.Javadoc.exportableStubsSrcJar + } + + if params.writeSdkValues { + d.exportableArtifacts.metadataZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-metadata.zip") + d.exportableArtifacts.metadataDir = android.PathForModuleOut(ctx, params.stubsType.String(), "metadata") + optionalCmdParams.metadataZip = d.exportableArtifacts.metadataZip + optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir + } + + if Bool(d.properties.Annotations_enabled) { + if params.validatingNullability { + d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt") + optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile + } + d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip") + optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip + } + if Bool(d.properties.Api_levels_annotations_enabled) { + d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml") + optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml + } + + if params.checkApi || String(d.properties.Api_filename) != "" { + filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") + d.exportableApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) + } + + if params.checkApi || String(d.properties.Removed_api_filename) != "" { + filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_api.txt") + d.exportableRemovedApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) + } + + d.optionalStubCmd(ctx, optionalCmdParams) +} + +func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) { + + params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Sbox(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String()), + android.PathForModuleOut(ctx, fmt.Sprintf("metalava_%s.sbox.textproto", params.stubConfig.stubsType.String()))). + SandboxInputs() + + if params.stubConfig.generateStubs { + params.stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "stubsDir")) + } + + cmd := d.commonMetalavaStubCmd(ctx, rule, params) + + generateRevertAnnotationArgs(ctx, cmd, params.stubConfig.stubsType, params.stubConfig.deps.aconfigProtoFiles) + + if params.stubConfig.doApiLint { + // Pass the lint baseline file as an input to resolve the lint errors. + // The exportable stubs generation does not update the lint baseline file. + // Lint baseline file update is handled by the everything stubs + baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) + if baselineFile.Valid() { + cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) + } + } + + // Treat documentation issues as warnings, but error when new. + treatDocumentationIssuesAsWarningErrorWhenNew(cmd) + + if params.stubConfig.generateStubs { rule.Command(). BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-jar"). - FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) + FlagWithOutput("-o ", params.stubsSrcJar). + FlagWithArg("-C ", params.stubsDir.String()). + FlagWithArg("-D ", params.stubsDir.String()) } - if Bool(d.properties.Write_sdk_values) { - d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip") + if params.stubConfig.writeSdkValues { rule.Command(). BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-d"). - FlagWithOutput("-o ", d.metadataZip). - FlagWithArg("-C ", d.metadataDir.String()). - FlagWithArg("-D ", d.metadataDir.String()) - } - - // TODO: We don't really need two separate API files, but this is a reminiscence of how - // we used to run metalava separately for API lint and the "last_released" check. Unify them. - if doApiLint { - rule.Command().Text("touch").Output(d.apiLintTimestamp) - } - if doCheckReleased { - rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) + FlagWithOutput("-o ", params.metadataZip). + FlagWithArg("-C ", params.metadataDir.String()). + FlagWithArg("-D ", params.metadataDir.String()) } // TODO(b/183630617): rewrapper doesn't support restat rules @@ -723,9 +1136,53 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Restat() } - zipSyncCleanupCmd(rule, srcJarDir) + zipSyncCleanupCmd(rule, params.srcJarDir) - rule.Build("metalava", "metalava merged") + rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged") +} + +func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { + deps := d.Javadoc.collectDeps(ctx) + + javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) + generateStubs := BoolDefault(d.properties.Generate_stubs, true) + + // Add options for the other optional tasks: API-lint and check-released. + // We generate separate timestamp files for them. + doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) + doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") + + writeSdkValues := Bool(d.properties.Write_sdk_values) + + annotationsEnabled := Bool(d.properties.Annotations_enabled) + + migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != "" + validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || + String(d.properties.Validate_nullability_from_list) != "") + + checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") + + stubCmdParams := stubsCommandConfigParams{ + javaVersion: javaVersion, + deps: deps, + checkApi: checkApi, + generateStubs: generateStubs, + doApiLint: doApiLint, + doCheckReleased: doCheckReleased, + writeSdkValues: writeSdkValues, + migratingNullability: migratingNullability, + validatingNullability: validatingNullability, + } + stubCmdParams.stubsType = Everything + // Create default (i.e. "everything" stubs) rule for metalava + d.everythingStubCmd(ctx, stubCmdParams) + + // The module generates "exportable" (and "runtime" eventually) stubs regardless of whether + // aconfig_declarations property is defined or not. If the property is not defined, the module simply + // strips all flagged apis to generate the "exportable" stubs + stubCmdParams.stubsType = Exportable + d.exportableStubCmd(ctx, stubCmdParams) if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { @@ -741,7 +1198,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName()) } - d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp") + d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_current_api.timestamp") rule := android.NewRuleBuilder(pctx, ctx) @@ -768,6 +1225,12 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` m %s-update-current-api\n\n`+ ` To submit the revised current.txt to the main Android repository,\n`+ ` you will need approval.\n`+ + `If your build failed due to stub validation, you can resolve the errors with\n`+ + `either of the two choices above and try re-building the target.\n`+ + `If the mismatch between the stubs and the current.txt is intended,\n`+ + `you can try re-building the target by executing the following command:\n`+ + `m DISABLE_STUB_VALIDATION=true <your build target>.\n`+ + `Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+ `******************************\n`, ctx.ModuleName()) rule.Command(). @@ -779,7 +1242,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("metalavaCurrentApiCheck", "check current API") - d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp") + d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp") // update API rule rule = android.NewRuleBuilder(pctx, ctx) @@ -807,14 +1270,14 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { } if String(d.properties.Check_nullability_warnings) != "" { - if d.nullabilityWarningsFile == nil { + if d.everythingArtifacts.nullabilityWarningsFile == nil { ctx.PropertyErrorf("check_nullability_warnings", "Cannot specify check_nullability_warnings unless validating nullability") } checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings)) - d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp") + d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp") msg := fmt.Sprintf(`\n******************************\n`+ `The warnings encountered during nullability annotation validation did\n`+ @@ -824,13 +1287,13 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` 2. Update the file of expected warnings by running:\n`+ ` cp %s %s\n`+ ` and submitting the updated file as part of your change.`, - d.nullabilityWarningsFile, checkNullabilityWarnings) + d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings) rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Text("("). - Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile). + Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile). Text("&&"). Text("touch").Output(d.checkNullabilityWarningsTimestamp). Text(") || ("). @@ -840,34 +1303,46 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("nullabilityWarningsCheck", "nullability warnings check") } -} -var _ android.ApiProvider = (*Droidstubs)(nil) - -type bazelJavaApiContributionAttributes struct { - Api bazel.LabelAttribute - Api_surface *string + d.setOutputFiles(ctx) } -func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) { - props := bazel.BazelTargetModuleProperties{ - Rule_class: "java_api_contribution", - Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl", +// This method sets the outputFiles property, which is used to set the +// OutputFilesProvider later. +// Droidstubs' tag supports specifying with the stubs type. +// While supporting the pre-existing tags, it also supports tags with +// the stubs type prefix. Some examples are shown below: +// {.annotations.zip} - pre-existing behavior. Returns the path to the +// annotation zip. +// {.exportable} - Returns the path to the exportable stubs src jar. +// {.exportable.annotations.zip} - Returns the path to the exportable +// annotations zip file. +// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions +// xml file. For unsupported combinations, the default everything output file +// is returned. +func (d *Droidstubs) setOutputFiles(ctx android.ModuleContext) { + tagToOutputFileFunc := map[string]func(StubsType) (android.Path, error){ + "": d.StubsSrcJar, + ".docs.zip": d.DocZip, + ".api.txt": d.ApiFilePath, + android.DefaultDistTag: d.ApiFilePath, + ".removed-api.txt": d.RemovedApiFilePath, + ".annotations.zip": d.AnnotationsZip, + ".api_versions.xml": d.ApiVersionsXmlFilePath, } - apiFile := d.properties.Check_api.Current.Api_file - // Do not generate a target if check_api is not set - if apiFile == nil { - return + stubsTypeToPrefix := map[StubsType]string{ + Everything: "", + Exportable: ".exportable", } - attrs := &bazelJavaApiContributionAttributes{ - Api: *bazel.MakeLabelAttribute( - android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label, - ), - Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())), + for _, tag := range android.SortedKeys(tagToOutputFileFunc) { + for _, stubType := range android.SortedKeys(stubsTypeToPrefix) { + tagWithPrefix := stubsTypeToPrefix[stubType] + tag + outputFile, err := tagToOutputFileFunc[tag](stubType) + if err == nil { + ctx.SetOutputFiles(android.Paths{outputFile}, tagWithPrefix) + } + } } - ctx.CreateBazelTargetModule(props, android.CommonAttributes{ - Name: android.ApiContributionTargetName(ctx.ModuleName()), - }, attrs) } func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { @@ -895,7 +1370,7 @@ func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { // use a strict naming convention var ( droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{ - //public is commented out since the core libraries use public in their java_sdk_library names + // public is commented out since the core libraries use public in their java_sdk_library names "intracore": android.SdkIntraCore, "intra.core": android.SdkIntraCore, "system_server": android.SdkSystemServer, @@ -909,28 +1384,6 @@ var ( } ) -// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target -// The api_surface is populated using the naming convention of the droidstubs module. -func bazelApiSurfaceName(name string) string { - // Sort the keys so that longer strings appear first - // Otherwise substrings like system will match both system and system_server - sortedKeys := make([]string, 0) - for key := range droidstubsModuleNamingToSdkKind { - sortedKeys = append(sortedKeys, key) - } - sort.Slice(sortedKeys, func(i, j int) bool { - return len(sortedKeys[i]) > len(sortedKeys[j]) - }) - for _, sortedKey := range sortedKeys { - if strings.Contains(name, sortedKey) { - sdkKind := droidstubsModuleNamingToSdkKind[sortedKey] - return sdkKind.String() + "api" - } - } - // Default is publicapi - return android.SdkPublic.String() + "api" -} - func StubsDefaultsFactory() android.Module { module := &DocDefaults{} @@ -948,11 +1401,31 @@ var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil) type PrebuiltStubsSourcesProperties struct { Srcs []string `android:"path"` + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string + + // Non-nil if this prebuilt stub srcs module was dynamically created by a java_sdk_library_import + // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file + // (without any prebuilt_ prefix) + Created_by_java_sdk_library_name *string `blueprint:"mutated"` +} + +func (j *PrebuiltStubsSources) BaseModuleName() string { + return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name()) +} + +func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string { + return j.properties.Created_by_java_sdk_library_name } type PrebuiltStubsSources struct { android.ModuleBase android.DefaultableModuleBase + embeddableInModuleAndImport + prebuilt android.Prebuilt properties PrebuiltStubsSourcesProperties @@ -960,17 +1433,8 @@ type PrebuiltStubsSources struct { stubsSrcJar android.Path } -func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{p.stubsSrcJar}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - -func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { - return d.stubsSrcJar +func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) { + return d.stubsSrcJar, nil } func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -1007,6 +1471,11 @@ func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleCon rule.Build("zip src", "Create srcjar from prebuilt source") p.stubsSrcJar = outPath } + + ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "") + // prebuilt droidstubs does not output "exportable" stubs. + // Output the "everything" stubs srcjar file if the tag is ".exportable". + ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, ".exportable") } func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { @@ -1031,6 +1500,7 @@ func PrebuiltStubsSourcesFactory() android.Module { module := &PrebuiltStubsSources{} module.AddProperties(&module.properties) + module.initModuleAndImport(module) android.InitPrebuiltModule(module, &module.properties.Srcs) InitDroiddocModule(module, android.HostAndDeviceSupported) |