Merge "java_sdk_library does the apicheck by default"
This commit is contained in:
commit
a8d897cc49
4 changed files with 230 additions and 31 deletions
|
@ -233,6 +233,7 @@ bootstrap_go_package {
|
|||
"java/jacoco.go",
|
||||
"java/java.go",
|
||||
"java/java_resources.go",
|
||||
"java/prebuilt_apis.go",
|
||||
"java/proto.go",
|
||||
"java/sdk_library.go",
|
||||
"java/support_libraries.go",
|
||||
|
|
|
@ -84,10 +84,12 @@ func testContext(config android.Config, bp string,
|
|||
ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory))
|
||||
ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(DroiddocTemplateFactory))
|
||||
ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(sdkLibraryFactory))
|
||||
ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(prebuiltApisFactory))
|
||||
ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
|
||||
ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
|
||||
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
|
||||
ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
|
||||
ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
|
||||
ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel()
|
||||
})
|
||||
ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
|
||||
|
@ -141,19 +143,25 @@ func testContext(config android.Config, bp string,
|
|||
}
|
||||
|
||||
mockFS := map[string][]byte{
|
||||
"Android.bp": []byte(bp),
|
||||
"a.java": nil,
|
||||
"b.java": nil,
|
||||
"c.java": nil,
|
||||
"b.kt": nil,
|
||||
"a.jar": nil,
|
||||
"b.jar": nil,
|
||||
"java-res/a/a": nil,
|
||||
"java-res/b/b": nil,
|
||||
"java-res2/a": nil,
|
||||
"java-fg/a.java": nil,
|
||||
"java-fg/b.java": nil,
|
||||
"java-fg/c.java": nil,
|
||||
"Android.bp": []byte(bp),
|
||||
"a.java": nil,
|
||||
"b.java": nil,
|
||||
"c.java": nil,
|
||||
"b.kt": nil,
|
||||
"a.jar": nil,
|
||||
"b.jar": nil,
|
||||
"java-res/a/a": nil,
|
||||
"java-res/b/b": nil,
|
||||
"java-res2/a": nil,
|
||||
"java-fg/a.java": nil,
|
||||
"java-fg/b.java": nil,
|
||||
"java-fg/c.java": nil,
|
||||
"api/current.txt": nil,
|
||||
"api/removed.txt": nil,
|
||||
"api/system-current.txt": nil,
|
||||
"api/system-removed.txt": nil,
|
||||
"api/test-current.txt": nil,
|
||||
"api/test-removed.txt": nil,
|
||||
|
||||
"prebuilts/sdk/14/public/android.jar": nil,
|
||||
"prebuilts/sdk/14/public/framework.aidl": nil,
|
||||
|
@ -163,6 +171,19 @@ func testContext(config android.Config, bp string,
|
|||
"prebuilts/sdk/current/public/core.jar": nil,
|
||||
"prebuilts/sdk/current/system/android.jar": nil,
|
||||
"prebuilts/sdk/current/test/android.jar": nil,
|
||||
"prebuilts/sdk/28/public/api/foo.txt": nil,
|
||||
"prebuilts/sdk/28/system/api/foo.txt": nil,
|
||||
"prebuilts/sdk/28/test/api/foo.txt": nil,
|
||||
"prebuilts/sdk/28/public/api/foo-removed.txt": nil,
|
||||
"prebuilts/sdk/28/system/api/foo-removed.txt": nil,
|
||||
"prebuilts/sdk/28/test/api/foo-removed.txt": nil,
|
||||
"prebuilts/sdk/28/public/api/bar.txt": nil,
|
||||
"prebuilts/sdk/28/system/api/bar.txt": nil,
|
||||
"prebuilts/sdk/28/test/api/bar.txt": nil,
|
||||
"prebuilts/sdk/28/public/api/bar-removed.txt": nil,
|
||||
"prebuilts/sdk/28/system/api/bar-removed.txt": nil,
|
||||
"prebuilts/sdk/28/test/api/bar-removed.txt": nil,
|
||||
"prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "prebuilt_apis",}`),
|
||||
|
||||
// For framework-res, which is an implicit dependency for framework
|
||||
"AndroidManifest.xml": nil,
|
||||
|
@ -196,7 +217,7 @@ func testContext(config android.Config, bp string,
|
|||
|
||||
func run(t *testing.T, ctx *android.TestContext, config android.Config) {
|
||||
t.Helper()
|
||||
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
|
||||
_, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
|
||||
android.FailIfErrored(t, errs)
|
||||
_, errs = ctx.PrepareBuildActions(config)
|
||||
android.FailIfErrored(t, errs)
|
||||
|
@ -1038,10 +1059,15 @@ func TestJavaSdkLibrary(t *testing.T) {
|
|||
ctx.ModuleForTests("foo", "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkTestApiSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkTestApiSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkImplLibrarySuffix, "android_common")
|
||||
ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
|
||||
ctx.ModuleForTests("foo.api.public.28", "")
|
||||
ctx.ModuleForTests("foo.api.system.28", "")
|
||||
ctx.ModuleForTests("foo.api.test.28", "")
|
||||
|
||||
bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
|
||||
// tests if baz is actually linked to the stubs lib
|
||||
|
|
140
java/prebuilt_apis.go
Normal file
140
java/prebuilt_apis.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
// 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 (
|
||||
"android/soong/android"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint/proptools"
|
||||
)
|
||||
|
||||
// prebuilt_apis is a meta-module that generates filegroup modules for all
|
||||
// API txt files found under the directory where the Android.bp is located.
|
||||
// Specificaly, an API file located at ./<ver>/<scope>/api/<module>.txt
|
||||
// generates a filegroup module named <module>-api.<scope>.<ver>.
|
||||
//
|
||||
// It also creates <module>-api.<scope>.latest for the lastest <ver>.
|
||||
//
|
||||
func init() {
|
||||
android.RegisterModuleType("prebuilt_apis", prebuiltApisFactory)
|
||||
|
||||
android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
|
||||
ctx.TopDown("prebuilt_apis", prebuiltApisMutator).Parallel()
|
||||
})
|
||||
}
|
||||
|
||||
type prebuiltApis struct {
|
||||
android.ModuleBase
|
||||
}
|
||||
|
||||
func (module *prebuiltApis) DepsMutator(ctx android.BottomUpMutatorContext) {
|
||||
// no need to implement
|
||||
}
|
||||
|
||||
func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
// no need to implement
|
||||
}
|
||||
|
||||
func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver int, scope string) {
|
||||
elements := strings.Split(path, "/")
|
||||
ver, err := strconv.Atoi(elements[0])
|
||||
if err != nil {
|
||||
ctx.ModuleErrorf("invalid version %q found in path: %q", elements[0], path)
|
||||
return
|
||||
}
|
||||
apiver = ver
|
||||
|
||||
scope = elements[1]
|
||||
if scope != "public" && scope != "system" && scope != "test" {
|
||||
ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
|
||||
return
|
||||
}
|
||||
|
||||
// elements[2] is string literal "api". skipping.
|
||||
module = strings.TrimSuffix(elements[3], ".txt")
|
||||
return
|
||||
}
|
||||
|
||||
func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
|
||||
fgName := module + ".api." + scope + "." + apiver
|
||||
filegroupProps := struct {
|
||||
Name *string
|
||||
Srcs []string
|
||||
}{}
|
||||
filegroupProps.Name = proptools.StringPtr(fgName)
|
||||
filegroupProps.Srcs = []string{path}
|
||||
mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
|
||||
}
|
||||
|
||||
func prebuiltApisMutator(mctx android.TopDownMutatorContext) {
|
||||
if _, ok := mctx.Module().(*prebuiltApis); ok {
|
||||
mydir := mctx.ModuleDir() + "/"
|
||||
// <apiver>/<scope>/api/<module>.txt
|
||||
files, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
|
||||
if err != nil {
|
||||
mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mctx.ModuleErrorf("no api file found under %q", mydir)
|
||||
}
|
||||
|
||||
// construct a map to find out the latest api file path
|
||||
// for each (<module>, <scope>) pair.
|
||||
type latestApiInfo struct {
|
||||
module string
|
||||
scope string
|
||||
apiver int
|
||||
path string
|
||||
}
|
||||
m := make(map[string]latestApiInfo)
|
||||
|
||||
for _, f := range files {
|
||||
// create a filegroup for each api txt file
|
||||
localPath := strings.TrimPrefix(f, mydir)
|
||||
module, apiver, scope := parseApiFilePath(mctx, localPath)
|
||||
createFilegroup(mctx, module, scope, strconv.Itoa(apiver), localPath)
|
||||
|
||||
// find the latest apiver
|
||||
key := module + "." + scope
|
||||
info, ok := m[key]
|
||||
if !ok {
|
||||
m[key] = latestApiInfo{module, scope, apiver, localPath}
|
||||
} else if apiver > info.apiver {
|
||||
info.apiver = apiver
|
||||
info.path = localPath
|
||||
}
|
||||
}
|
||||
// create filegroups for the latest version of (<module>, <scope>) pairs
|
||||
// sort the keys in order to make build.ninja stable
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
info := m[k]
|
||||
createFilegroup(mctx, info.module, info.scope, "latest", info.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prebuiltApisFactory() android.Module {
|
||||
module := &prebuiltApis{}
|
||||
android.InitAndroidModule(module)
|
||||
return module
|
||||
}
|
|
@ -67,10 +67,9 @@ var (
|
|||
// classpath at runtime if requested via <uses-library>.
|
||||
//
|
||||
// TODO: these are big features that are currently missing
|
||||
// 1) check for API consistency
|
||||
// 2) ensuring that apps have appropriate <uses-library> tag
|
||||
// 3) disallowing linking to the runtime shared lib
|
||||
// 4) HTML generation
|
||||
// 1) ensuring that apps have appropriate <uses-library> tag
|
||||
// 2) disallowing linking to the runtime shared lib
|
||||
// 3) HTML generation
|
||||
|
||||
func init() {
|
||||
android.RegisterModuleType("java_sdk_library", sdkLibraryFactory)
|
||||
|
@ -260,21 +259,32 @@ func (module *sdkLibrary) apiTagName(apiScope apiScope) string {
|
|||
return apiTagName
|
||||
}
|
||||
|
||||
// returns the path (relative to this module) to the API txt file. Files are located
|
||||
// ./<api_dir>/<api_level>.txt where <api_level> is either current, system-current, removed,
|
||||
// or system-removed.
|
||||
func (module *sdkLibrary) apiFilePath(apiLevel string, apiScope apiScope) string {
|
||||
apiDir := "api"
|
||||
apiFile := apiLevel
|
||||
func (module *sdkLibrary) latestApiFilegroupName(apiScope apiScope) string {
|
||||
name := ":" + module.BaseModuleName() + ".api."
|
||||
switch apiScope {
|
||||
case apiScopePublic:
|
||||
name = name + "public"
|
||||
case apiScopeSystem:
|
||||
apiFile = "system-" + apiFile
|
||||
name = name + "system"
|
||||
case apiScopeTest:
|
||||
apiFile = "test-" + apiFile
|
||||
name = name + "test"
|
||||
}
|
||||
apiFile = apiFile + ".txt"
|
||||
name = name + ".latest"
|
||||
return name
|
||||
}
|
||||
|
||||
return path.Join(apiDir, apiFile)
|
||||
func (module *sdkLibrary) latestRemovedApiFilegroupName(apiScope apiScope) string {
|
||||
name := ":" + module.BaseModuleName() + "-removed.api."
|
||||
switch apiScope {
|
||||
case apiScopePublic:
|
||||
name = name + "public"
|
||||
case apiScopeSystem:
|
||||
name = name + "system"
|
||||
case apiScopeTest:
|
||||
name = name + "test"
|
||||
}
|
||||
name = name + ".latest"
|
||||
return name
|
||||
}
|
||||
|
||||
// Creates a static java library that has API stubs
|
||||
|
@ -331,6 +341,10 @@ func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScop
|
|||
Api_tag_name *string
|
||||
Api_filename *string
|
||||
Removed_api_filename *string
|
||||
Check_api struct {
|
||||
Current ApiToCheck
|
||||
Last_released ApiToCheck
|
||||
}
|
||||
}{}
|
||||
|
||||
props.Name = proptools.StringPtr(module.docsName(apiScope))
|
||||
|
@ -355,7 +369,6 @@ func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScop
|
|||
// List of APIs identified from the provided source files are created. They are later
|
||||
// compared against to the not-yet-released (a.k.a current) list of APIs and to the
|
||||
// last-released (a.k.a numbered) list of API.
|
||||
// TODO: If any incompatible change is detected, break the build
|
||||
currentApiFileName := "current.txt"
|
||||
removedApiFileName := "removed.txt"
|
||||
switch apiScope {
|
||||
|
@ -368,12 +381,31 @@ func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScop
|
|||
}
|
||||
currentApiFileName = path.Join("api", currentApiFileName)
|
||||
removedApiFileName = path.Join("api", removedApiFileName)
|
||||
// TODO(jiyong): remove these three props
|
||||
props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
|
||||
// Note: the exact names of these two are not important because they are always
|
||||
// referenced by the make variable $(INTERNAL_PLATFORM_<TAG_NAME>_API_FILE)
|
||||
props.Api_filename = proptools.StringPtr(currentApiFileName)
|
||||
props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
|
||||
|
||||
// check against the not-yet-release API
|
||||
props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
|
||||
props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
|
||||
// any change is reported as error
|
||||
props.Check_api.Current.Args = proptools.StringPtr("-error 2 -error 3 -error 4 -error 5 " +
|
||||
"-error 6 -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 " +
|
||||
"-error 14 -error 15 -error 16 -error 17 -error 18 -error 19 -error 20 " +
|
||||
"-error 21 -error 23 -error 24 -error 25 -error 26 -error 27")
|
||||
|
||||
// check against the latest released API
|
||||
props.Check_api.Last_released.Api_file = proptools.StringPtr(
|
||||
module.latestApiFilegroupName(apiScope))
|
||||
props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
|
||||
module.latestRemovedApiFilegroupName(apiScope))
|
||||
// backward incompatible changes are reported as error
|
||||
props.Check_api.Last_released.Args = proptools.StringPtr("-hide 2 -hide 3 -hide 4 -hide 5 " +
|
||||
"-hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 " +
|
||||
"-error 15 -error 16 -error 17 -error 18")
|
||||
|
||||
// Include the part of the framework source. This is required for the case when
|
||||
// API class is extending from the framework class. In that case, doclava needs
|
||||
// to know whether the base class is hidden or not. Since that information is
|
||||
|
|
Loading…
Reference in a new issue