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
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
new file mode 100644
index 0000000..50318bb
--- /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
+}