blob: 976b140f407f61f9a0501b0a4b50cedf213b5e2d [file] [log] [blame]
Anton Hansson0860aaf2021-10-08 16:48:03 +01001// Copyright (C) 2021 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package api
16
17import (
18 "github.com/google/blueprint/proptools"
19
20 "android/soong/android"
21 "android/soong/genrule"
22)
23
24// The intention behind this soong plugin is to generate a number of "merged"
25// API-related modules that would otherwise require a large amount of very
26// similar Android.bp boilerplate to define. For example, the merged current.txt
27// API definitions (created by merging the non-updatable current.txt with all
28// the module current.txts). This simplifies the addition of new android
29// modules, by reducing the number of genrules etc a new module must be added to.
30
31// The properties of the combined_apis module type.
32type CombinedApisProperties struct {
33 // Module libraries that have public APIs
34 Public []string
35 // Module libraries that have system APIs
36 System []string
37 // Module libraries that have module_library APIs
38 Module_lib []string
39 // Module libraries that have system_server APIs
40 System_server []string
41 // ART module library. The only API library not removed from the filtered api database, because
42 // 1) ART apis are available by default to all modules, while other module-to-module deps are
43 // explicit and probably receive more scrutiny anyway
44 // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
45 // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
46 // per-module lint databases that excludes just that module's APIs. Alas, that's more
47 // difficult to achieve.
48 Art_module string
49}
50
51type CombinedApis struct {
52 android.ModuleBase
53
54 properties CombinedApisProperties
55}
56
57func init() {
58 registerBuildComponents(android.InitRegistrationContext)
59}
60
61func registerBuildComponents(ctx android.RegistrationContext) {
62 ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
63}
64
65var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
66
67func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
68}
69
70type genruleProps struct {
71 Name *string
72 Cmd *string
73 Dists []android.Dist
74 Out []string
75 Srcs []string
76 Tools []string
77 Visibility []string
78}
79
80// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
81type MergedTxtDefinition struct {
82 // "current.txt" or "removed.txt"
83 TxtFilename string
84 // The module for the non-updatable / non-module part of the api.
85 BaseTxt string
86 // The list of modules that are relevant for this merged txt.
87 Modules []string
88 // The output tag for each module to use.e.g. {.public.api.txt} for current.txt
89 ModuleTag string
90 // public, system, module-lib or system-server
91 Scope string
92}
93
94func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
95 metalavaCmd := "$(location metalava)"
96 // Silence reflection warnings. See b/168689341
97 metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
98 metalavaCmd += " --quiet --no-banner --format=v2 "
99
100 filename := txt.TxtFilename
101 if txt.Scope != "public" {
102 filename = txt.Scope + "-" + filename
103 }
104
105 props := genruleProps{}
106 props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
107 props.Tools = []string{"metalava"}
108 props.Out = []string{txt.TxtFilename}
109 props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
110 props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
111 props.Dists = []android.Dist{
112 {
113 Targets: []string{"droidcore"},
114 Dir: proptools.StringPtr("api"),
115 Dest: proptools.StringPtr(filename),
116 },
117 {
118 Targets: []string{"sdk"},
119 Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
120 Dest: proptools.StringPtr(txt.TxtFilename),
121 },
122 }
123 props.Visibility = []string{"//visibility:public"}
124 ctx.CreateModule(genrule.GenRuleFactory, &props)
125}
126
127func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
128 props := genruleProps{}
129 props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
130 props.Tools = []string{"merge_zips"}
131 props.Out = []string{"current.srcjar"}
132 props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
133 props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}")
134 props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
135 ctx.CreateModule(genrule.GenRuleFactory, &props)
136}
137
138func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
139 props := genruleProps{}
140 props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
141 props.Tools = []string{"api_versions_trimmer"}
142 props.Out = []string{"api-versions-public-filtered.xml"}
143 props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
144 // Note: order matters: first parameter is the full api-versions.xml
145 // after that the stubs files in any order
146 // stubs files are all modules that export API surfaces EXCEPT ART
147 props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}")
148 props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
149 ctx.CreateModule(genrule.GenRuleFactory, &props)
150}
151
152func createSrcs(base string, modules []string, tag string) []string {
153 a := make([]string, 0, len(modules)+1)
154 a = append(a, base)
155 for _, module := range modules {
156 a = append(a, ":"+module+tag)
157 }
158 return a
159}
160
161func remove(s []string, v string) []string {
162 s2 := make([]string, 0, len(s))
163 for _, sv := range s {
164 if sv != v {
165 s2 = append(s2, sv)
166 }
167 }
168 return s2
169}
170
171func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) {
172 var textFiles []MergedTxtDefinition
173 tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
174 for i, f := range []string{"current.txt", "removed.txt"} {
175 textFiles = append(textFiles, MergedTxtDefinition{
176 TxtFilename: f,
177 BaseTxt: ":non-updatable-" + f,
178 Modules: props.Public,
179 ModuleTag: "{.public" + tagSuffix[i],
180 Scope: "public",
181 })
182 textFiles = append(textFiles, MergedTxtDefinition{
183 TxtFilename: f,
184 BaseTxt: ":non-updatable-system-" + f,
185 Modules: props.System,
186 ModuleTag: "{.system" + tagSuffix[i],
187 Scope: "system",
188 })
189 textFiles = append(textFiles, MergedTxtDefinition{
190 TxtFilename: f,
191 BaseTxt: ":non-updatable-module-lib-" + f,
192 Modules: props.Module_lib,
193 ModuleTag: "{.module-lib" + tagSuffix[i],
194 Scope: "module-lib",
195 })
196 textFiles = append(textFiles, MergedTxtDefinition{
197 TxtFilename: f,
198 BaseTxt: ":non-updatable-system-server-" + f,
199 Modules: props.System_server,
200 ModuleTag: "{.system-server" + tagSuffix[i],
201 Scope: "system-server",
202 })
203 }
204 for _, txt := range textFiles {
205 createMergedTxt(ctx, txt)
206 }
207}
208
209func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
210 createMergedTxts(ctx, a.properties)
211
212 createMergedStubsSrcjar(ctx, a.properties.Public)
213
214 // For the filtered api versions, we prune all APIs except art module's APIs.
215 createFilteredApiVersions(ctx, remove(a.properties.Public, a.properties.Art_module))
216}
217
218func combinedApisModuleFactory() android.Module {
219 module := &CombinedApis{}
220 module.AddProperties(&module.properties)
221 android.InitAndroidModule(module)
222 android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
223 return module
224}