Support passing multiple last api files to metalava in droidstubs

This change supports passing multiple last api files to metalava,
instead of emitting an error when multiple files are passed as inputs.

The last api file providing modules are filegroups or genrule modules
that output exactly one output file. This change modifies that behavior
so that the filegroups can output multiple api files.

The last api file providing modules are (mostly) generated from the
`prebuilt_apis` sdk module. However, these modules only provide a single
api scope txt files (e.g. system api scope last api file filegroup
would only provide last system api file, not system + public). This
change modifies `prebuilt_apis` by generating "combine" filegroup
modules, that include api files of the subset api scopes as well.

The ordering of the files are handled when generating the combined api
filegroup modules, and droidstubs module does not check whether the
passed api files are in order from the narrowest api scope to the widest
api scope.

Submission of this change will be done once metalava fully supports
handling multiple api files input for api lint and nullness migration.

Test: m nothing
Bug: 321827591
Change-Id: I2066e7ceb7ee7c6d0fd87cd43bfd08db906d4b8f
This commit is contained in:
Jihoon Kang 2024-01-31 23:27:26 +00:00 committed by Paul Duffin
parent d3b020a8c3
commit 5623e5425a
3 changed files with 43 additions and 26 deletions

View file

@ -532,8 +532,8 @@ func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.Ru
cmd.Flag(config.MetalavaAnnotationsFlags)
if params.migratingNullability {
previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
cmd.FlagWithInput("--migrate-nullness ", previousApi)
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 != "" {
@ -692,11 +692,11 @@ func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *andro
ctx.PropertyErrorf("out", "out property may not be combined with check_api")
}
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))
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.FlagWithInput("--check-compatibility:api:released ", apiFile)
cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
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() {
@ -950,9 +950,12 @@ func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *andro
// Add API lint options.
if doApiLint {
newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
if newSince.Valid() {
cmd.FlagWithInput("--api-lint ", newSince.Path())
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)})
}
if len(newSince) > 0 {
cmd.FlagForEachInput("--api-lint ", newSince)
} else {
cmd.Flag("--api-lint")
}

View file

@ -367,10 +367,23 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
info := latest[k]
name := PrebuiltApiCombinedModuleName(info.module, info.scope, "latest")
// Iterate until the currentApiScope does not extend any other api scopes
// i.e. is not a superset of any other api scopes
// the relationship between the api scopes is defined in java/sdk_library.go
var srcs []string
currentApiScope := scopeByName[info.scope]
srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest"))
for currentApiScope != nil {
if _, ok := latest[fmt.Sprintf("%s.%s", info.module, currentApiScope.name)]; ok {
srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest"))
}
currentApiScope = currentApiScope.extends
}
// srcs is currently listed in the order from the widest api scope to the narrowest api scopes
// e.g. module lib -> system -> public
// In order to pass the files in metalava from the narrowest api scope to the widest api scope,
// the list has to be reversed.
android.ReverseSliceInPlace(srcs)
createCombinedApiFilegroupModule(mctx, name, srcs)
}
}

View file

@ -705,10 +705,10 @@ type scopePaths struct {
annotationsZip android.OptionalPath
// The path to the latest API file.
latestApiPath android.OptionalPath
latestApiPaths android.Paths
// The path to the latest removed API file.
latestRemovedApiPath android.OptionalPath
latestRemovedApiPaths android.Paths
}
func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
@ -829,28 +829,25 @@ func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx an
})
}
func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) {
func extractOutputPaths(dep android.Module) (android.Paths, error) {
var paths android.Paths
if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
paths = sourceFileProducer.Srcs()
return paths, nil
} else {
return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep)
return nil, fmt.Errorf("module %q does not produce source files", dep)
}
if len(paths) != 1 {
return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths)
}
return android.OptionalPathForPath(paths[0]), nil
}
func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
outputPath, err := extractSingleOptionalOutputPath(dep)
paths.latestApiPath = outputPath
outputPaths, err := extractOutputPaths(dep)
paths.latestApiPaths = outputPaths
return err
}
func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
outputPath, err := extractSingleOptionalOutputPath(dep)
paths.latestRemovedApiPath = outputPath
outputPaths, err := extractOutputPaths(dep)
paths.latestRemovedApiPaths = outputPaths
return err
}
@ -1625,11 +1622,15 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext)
scopes[scope.name] = scopeInfo
scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName)
scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName)
if p := scopePaths.latestApiPath; p.Valid() {
scopeInfo["latest_api"] = p.Path().String()
if p := scopePaths.latestApiPaths; len(p) > 0 {
// The last path in the list is the one that applies to this scope, the
// preceding ones, if any, are for the scope(s) that it extends.
scopeInfo["latest_api"] = p[len(p)-1].String()
}
if p := scopePaths.latestRemovedApiPath; p.Valid() {
scopeInfo["latest_removed_api"] = p.Path().String()
if p := scopePaths.latestRemovedApiPaths; len(p) > 0 {
// The last path in the list is the one that applies to this scope, the
// preceding ones, if any, are for the scope(s) that it extends.
scopeInfo["latest_removed_api"] = p[len(p)-1].String()
}
}
android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})