// Copyright 2018 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package java import ( "fmt" "path" "path/filepath" "reflect" "sort" "strings" "sync" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" ) const ( sdkStubsLibrarySuffix = ".stubs" sdkSystemApiSuffix = ".system" sdkTestApiSuffix = ".test" sdkStubsSourceSuffix = ".stubs.source" sdkXmlFileSuffix = ".xml" permissionsTemplate = `\n` + `\n` + `\n` + ` \n` + `\n` ) // A tag to associated a dependency with a specific api scope. type scopeDependencyTag struct { blueprint.BaseDependencyTag name string apiScope *apiScope } // Provides information about an api scope, e.g. public, system, test. type apiScope struct { // The name of the api scope, e.g. public, system, test name string // The api scope that this scope extends. extends *apiScope // The legacy enabled status for a specific scope can be dependent on other // properties that have been specified on the library so it is provided by // a function that can determine the status by examining those properties. legacyEnabledStatus func(module *SdkLibrary) bool // The default enabled status for non-legacy behavior, which is triggered by // explicitly enabling at least one api scope. defaultEnabledStatus bool // Gets a pointer to the scope specific properties. scopeSpecificProperties func(module *SdkLibrary) *ApiScopeProperties // The name of the field in the dynamically created structure. fieldName string // The tag to use to depend on the stubs library module. stubsTag scopeDependencyTag // The tag to use to depend on the stubs apiFileTag scopeDependencyTag // The scope specific prefix to add to the api file base of "current.txt" or "removed.txt". apiFilePrefix string // The scope specific prefix to add to the sdk library module name to construct a scope specific // module name. moduleSuffix string // SDK version that the stubs library is built against. Note that this is always // *current. Older stubs library built with a numbered SDK version is created from // the prebuilt jar. sdkVersion string // Extra arguments to pass to droidstubs for this scope. droidstubsArgs []string // Whether the api scope can be treated as unstable, and should skip compat checks. unstable bool } // Initialize a scope, creating and adding appropriate dependency tags func initApiScope(scope *apiScope) *apiScope { scope.fieldName = proptools.FieldNameForProperty(scope.name) scope.stubsTag = scopeDependencyTag{ name: scope.name + "-stubs", apiScope: scope, } scope.apiFileTag = scopeDependencyTag{ name: scope.name + "-api", apiScope: scope, } return scope } func (scope *apiScope) stubsModuleName(baseName string) string { return baseName + sdkStubsLibrarySuffix + scope.moduleSuffix } func (scope *apiScope) docsModuleName(baseName string) string { return baseName + sdkStubsSourceSuffix + scope.moduleSuffix } func (scope *apiScope) String() string { return scope.name } type apiScopes []*apiScope func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string { var list []string for _, scope := range scopes { list = append(list, accessor(scope)) } return list } var ( apiScopePublic = initApiScope(&apiScope{ name: "public", // Public scope is enabled by default for both legacy and non-legacy modes. legacyEnabledStatus: func(module *SdkLibrary) bool { return true }, defaultEnabledStatus: true, scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties { return &module.sdkLibraryProperties.Public }, sdkVersion: "current", }) apiScopeSystem = initApiScope(&apiScope{ name: "system", extends: apiScopePublic, legacyEnabledStatus: (*SdkLibrary).generateTestAndSystemScopesByDefault, scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties { return &module.sdkLibraryProperties.System }, apiFilePrefix: "system-", moduleSuffix: sdkSystemApiSuffix, sdkVersion: "system_current", droidstubsArgs: []string{"-showAnnotation android.annotation.SystemApi"}, }) apiScopeTest = initApiScope(&apiScope{ name: "test", extends: apiScopePublic, legacyEnabledStatus: (*SdkLibrary).generateTestAndSystemScopesByDefault, scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties { return &module.sdkLibraryProperties.Test }, apiFilePrefix: "test-", moduleSuffix: sdkTestApiSuffix, sdkVersion: "test_current", droidstubsArgs: []string{"-showAnnotation android.annotation.TestApi"}, unstable: true, }) apiScopeModuleLib = initApiScope(&apiScope{ name: "module_lib", extends: apiScopeSystem, // Module_lib scope is disabled by default in legacy mode. // // Enabling this would break existing usages. legacyEnabledStatus: func(module *SdkLibrary) bool { return false }, scopeSpecificProperties: func(module *SdkLibrary) *ApiScopeProperties { return &module.sdkLibraryProperties.Module_lib }, apiFilePrefix: "module-lib-", moduleSuffix: ".module_lib", sdkVersion: "module_current", droidstubsArgs: []string{ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)", }, }) allApiScopes = apiScopes{ apiScopePublic, apiScopeSystem, apiScopeTest, apiScopeModuleLib, } ) var ( javaSdkLibrariesLock sync.Mutex ) // TODO: these are big features that are currently missing // 1) disallowing linking to the runtime shared lib // 2) HTML generation func init() { RegisterSdkLibraryBuildComponents(android.InitRegistrationContext) android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) { javaSdkLibraries := javaSdkLibraries(ctx.Config()) sort.Strings(*javaSdkLibraries) ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " ")) }) // Register sdk member types. android.RegisterSdkMemberType(&sdkLibrarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_sdk_libs", SupportsSdk: true, }, }) } func RegisterSdkLibraryBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_sdk_library", SdkLibraryFactory) ctx.RegisterModuleType("java_sdk_library_import", sdkLibraryImportFactory) } // Properties associated with each api scope. type ApiScopeProperties struct { // Indicates whether the api surface is generated. // // If this is set for any scope then all scopes must explicitly specify if they // are enabled. This is to prevent new usages from depending on legacy behavior. // // Otherwise, if this is not set for any scope then the default behavior is // scope specific so please refer to the scope specific property documentation. Enabled *bool } type sdkLibraryProperties struct { // List of Java libraries that will be in the classpath when building stubs Stub_only_libs []string `android:"arch_variant"` // list of package names that will be documented and publicized as API. // This allows the API to be restricted to a subset of the source files provided. // If this is unspecified then all the source files will be treated as being part // of the API. Api_packages []string // list of package names that must be hidden from the API Hidden_api_packages []string // the relative path to the directory containing the api specification files. // Defaults to "api". Api_dir *string // If set to true there is no runtime library. Api_only *bool // local files that are used within user customized droiddoc options. Droiddoc_option_files []string // additional droiddoc options // Available variables for substitution: // // $(location