diff options
author | Jiyong Park <jiyong@google.com> | 2018-05-12 22:29:12 +0900 |
---|---|---|
committer | Jiyong Park <jiyong@google.com> | 2018-05-15 10:57:01 +0900 |
commit | 58c518b5944be18288694f6c9d7377ab174979b2 (patch) | |
tree | 70f24746da796a857c27634c4b99c8cf9f9e2d1a /java | |
parent | eeb8a6474c83327fb495d7ef4cf4b51f3f55a495 (diff) | |
download | build_soong-58c518b5944be18288694f6c9d7377ab174979b2.tar.gz build_soong-58c518b5944be18288694f6c9d7377ab174979b2.tar.bz2 build_soong-58c518b5944be18288694f6c9d7377ab174979b2.zip |
java_sdk_library does the apicheck by default
droiddoc now supports apicheck. java_sdk_library uses it to
automatically perform apichecks against the not-yet-release API and
the latest-released API.
A module type prebuilt_apis is added. It finds api txt files and creates
filegroup modules so that it can be referenced from java_sdk_library
across the module boundary.
Bug: 77575606
Test: m -j
Test: m -j checkapi
Test: m -j update-api
Change-Id: I0ba859972eac060296e1df2e71c4e047392d4877
Diffstat (limited to 'java')
-rw-r--r-- | java/java_test.go | 54 | ||||
-rw-r--r-- | java/prebuilt_apis.go | 140 | ||||
-rw-r--r-- | java/sdk_library.go | 66 |
3 files changed, 229 insertions, 31 deletions
diff --git a/java/java_test.go b/java/java_test.go index 4a0229eb..fe1c3d72 100644 --- a/java/java_test.go +++ b/java/java_test.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 diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go new file mode 100644 index 00000000..50318bb2 --- /dev/null +++ b/java/prebuilt_apis.go @@ -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 +} diff --git a/java/sdk_library.go b/java/sdk_library.go index ee6998c3..ba121ab6 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -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 |