• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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	"sort"
19
20	"github.com/google/blueprint/proptools"
21
22	"android/soong/android"
23	"android/soong/bazel"
24	"android/soong/genrule"
25	"android/soong/java"
26)
27
28const art = "art.module.public.api"
29const conscrypt = "conscrypt.module.public.api"
30const i18n = "i18n.module.public.api"
31const virtualization = "framework-virtualization"
32
33var core_libraries_modules = []string{art, conscrypt, i18n}
34
35// List of modules that are not yet updatable, and hence they can still compile
36// against hidden APIs. These modules are filtered out when building the
37// updatable-framework-module-impl (because updatable-framework-module-impl is
38// built against module_current SDK). Instead they are directly statically
39// linked into the all-framework-module-lib, which is building against hidden
40// APIs.
41// In addition, the modules in this list are allowed to contribute to test APIs
42// stubs.
43var non_updatable_modules = []string{virtualization}
44
45// The intention behind this soong plugin is to generate a number of "merged"
46// API-related modules that would otherwise require a large amount of very
47// similar Android.bp boilerplate to define. For example, the merged current.txt
48// API definitions (created by merging the non-updatable current.txt with all
49// the module current.txts). This simplifies the addition of new android
50// modules, by reducing the number of genrules etc a new module must be added to.
51
52// The properties of the combined_apis module type.
53type CombinedApisProperties struct {
54	// Module libraries in the bootclasspath
55	Bootclasspath []string
56	// Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
57	Conditional_bootclasspath []string
58	// Module libraries in system server
59	System_server_classpath []string
60}
61
62type CombinedApis struct {
63	android.ModuleBase
64	android.BazelModuleBase
65
66	properties CombinedApisProperties
67}
68
69func init() {
70	registerBuildComponents(android.InitRegistrationContext)
71}
72
73func registerBuildComponents(ctx android.RegistrationContext) {
74	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
75}
76
77var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
78
79func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
80}
81
82type genruleProps struct {
83	Name       *string
84	Cmd        *string
85	Dists      []android.Dist
86	Out        []string
87	Srcs       []string
88	Tools      []string
89	Visibility []string
90}
91
92type libraryProps struct {
93	Name        *string
94	Sdk_version *string
95	Static_libs []string
96	Visibility  []string
97}
98
99type fgProps struct {
100	Name       *string
101	Srcs       []string
102	Visibility []string
103}
104
105type Bazel_module struct {
106	Bp2build_available *bool
107}
108type bazelProperties struct {
109	*Bazel_module
110}
111
112var bp2buildNotAvailable = bazelProperties{
113	&Bazel_module{
114		Bp2build_available: proptools.BoolPtr(false),
115	},
116}
117
118// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
119type MergedTxtDefinition struct {
120	// "current.txt" or "removed.txt"
121	TxtFilename string
122	// Filename in the new dist dir. "android.txt" or "android-removed.txt"
123	DistFilename string
124	// The module for the non-updatable / non-module part of the api.
125	BaseTxt string
126	// The list of modules that are relevant for this merged txt.
127	Modules []string
128	// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
129	ModuleTag string
130	// public, system, module-lib or system-server
131	Scope string
132}
133
134func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
135	metalavaCmd := "$(location metalava)"
136	// Silence reflection warnings. See b/168689341
137	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
138	metalavaCmd += " --quiet --no-banner --format=v2 "
139
140	filename := txt.TxtFilename
141	if txt.Scope != "public" {
142		filename = txt.Scope + "-" + filename
143	}
144	props := genruleProps{}
145	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
146	props.Tools = []string{"metalava"}
147	props.Out = []string{filename}
148	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
149	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
150	props.Dists = []android.Dist{
151		{
152			Targets: []string{"droidcore"},
153			Dir:     proptools.StringPtr("api"),
154			Dest:    proptools.StringPtr(filename),
155		},
156		{
157			Targets: []string{"api_txt", "sdk"},
158			Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
159			Dest:    proptools.StringPtr(txt.DistFilename),
160		},
161	}
162	props.Visibility = []string{"//visibility:public"}
163	ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
164}
165
166func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
167	for _, i := range []struct{
168		name    string
169		tag     string
170		modules []string
171	}{
172		{
173			name: "all-modules-public-annotations",
174			tag:  "{.public.annotations.zip}",
175			modules: modules,
176		}, {
177			name: "all-modules-system-annotations",
178			tag:  "{.system.annotations.zip}",
179			modules: modules,
180		}, {
181			name: "all-modules-module-lib-annotations",
182			tag:  "{.module-lib.annotations.zip}",
183			modules: modules,
184		}, {
185			name: "all-modules-system-server-annotations",
186			tag:  "{.system-server.annotations.zip}",
187			modules: system_server_modules,
188		},
189	} {
190		props := fgProps{}
191		props.Name = proptools.StringPtr(i.name)
192		props.Srcs = createSrcs(i.modules, i.tag)
193		ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
194	}
195}
196
197func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
198	props := libraryProps{}
199	props.Name = proptools.StringPtr("all-modules-public-stubs")
200	props.Static_libs = transformArray(modules, "", ".stubs")
201	props.Sdk_version = proptools.StringPtr("module_current")
202	props.Visibility = []string{"//frameworks/base"}
203	ctx.CreateModule(java.LibraryFactory, &props)
204}
205
206func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
207	// First create the all-updatable-modules-system-stubs
208	{
209		updatable_modules := removeAll(modules, non_updatable_modules)
210		props := libraryProps{}
211		props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
212		props.Static_libs = transformArray(updatable_modules, "", ".stubs.system")
213		props.Sdk_version = proptools.StringPtr("module_current")
214		props.Visibility = []string{"//frameworks/base"}
215		ctx.CreateModule(java.LibraryFactory, &props)
216	}
217	// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
218	// into all-modules-system-stubs.
219	{
220		props := libraryProps{}
221		props.Name = proptools.StringPtr("all-modules-system-stubs")
222		props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.system")
223		props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs")
224		props.Sdk_version = proptools.StringPtr("module_current")
225		props.Visibility = []string{"//frameworks/base"}
226		ctx.CreateModule(java.LibraryFactory, &props)
227	}
228}
229
230func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
231	props := libraryProps{}
232	props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
233	props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.test")
234	props.Sdk_version = proptools.StringPtr("module_current")
235	props.Visibility = []string{"//frameworks/base"}
236	ctx.CreateModule(java.LibraryFactory, &props)
237}
238
239func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
240	// This module is for the "framework-all" module, which should not include the core libraries.
241	modules = removeAll(modules, core_libraries_modules)
242	// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
243	// against unstable APIs.
244	modules = removeAll(modules, non_updatable_modules)
245	// First create updatable-framework-module-impl, which contains all updatable modules.
246	// This module compiles against module_lib SDK.
247	{
248		props := libraryProps{}
249		props.Name = proptools.StringPtr("updatable-framework-module-impl")
250		props.Static_libs = transformArray(modules, "", ".impl")
251		props.Sdk_version = proptools.StringPtr("module_current")
252		props.Visibility = []string{"//frameworks/base"}
253		ctx.CreateModule(java.LibraryFactory, &props)
254	}
255
256	// Now create all-framework-module-impl, which contains updatable-framework-module-impl
257	// and all non-updatable modules. This module compiles against hidden APIs.
258	{
259		props := libraryProps{}
260		props.Name = proptools.StringPtr("all-framework-module-impl")
261		props.Static_libs = transformArray(non_updatable_modules, "", ".impl")
262		props.Static_libs = append(props.Static_libs, "updatable-framework-module-impl")
263		props.Sdk_version = proptools.StringPtr("core_platform")
264		props.Visibility = []string{"//frameworks/base"}
265		ctx.CreateModule(java.LibraryFactory, &props)
266	}
267}
268
269func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
270	// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
271	modules = removeAll(modules, core_libraries_modules)
272	props := libraryProps{}
273	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
274	props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
275	props.Sdk_version = proptools.StringPtr("module_current")
276	props.Visibility = []string{"//frameworks/base"}
277	ctx.CreateModule(java.LibraryFactory, &props)
278}
279
280func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
281	props := fgProps{}
282	props.Name = proptools.StringPtr("all-modules-public-stubs-source")
283	props.Srcs = createSrcs(modules, "{.public.stubs.source}")
284	props.Visibility = []string{"//frameworks/base"}
285	ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
286}
287
288func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
289	var textFiles []MergedTxtDefinition
290
291	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
292	distFilename := []string{"android.txt", "android-removed.txt"}
293	for i, f := range []string{"current.txt", "removed.txt"} {
294		textFiles = append(textFiles, MergedTxtDefinition{
295			TxtFilename:  f,
296			DistFilename: distFilename[i],
297			BaseTxt:      ":non-updatable-" + f,
298			Modules:      bootclasspath,
299			ModuleTag:    "{.public" + tagSuffix[i],
300			Scope:        "public",
301		})
302		textFiles = append(textFiles, MergedTxtDefinition{
303			TxtFilename:  f,
304			DistFilename: distFilename[i],
305			BaseTxt:      ":non-updatable-system-" + f,
306			Modules:      bootclasspath,
307			ModuleTag:    "{.system" + tagSuffix[i],
308			Scope:        "system",
309		})
310		textFiles = append(textFiles, MergedTxtDefinition{
311			TxtFilename:  f,
312			DistFilename: distFilename[i],
313			BaseTxt:      ":non-updatable-module-lib-" + f,
314			Modules:      bootclasspath,
315			ModuleTag:    "{.module-lib" + tagSuffix[i],
316			Scope:        "module-lib",
317		})
318		textFiles = append(textFiles, MergedTxtDefinition{
319			TxtFilename:  f,
320			DistFilename: distFilename[i],
321			BaseTxt:      ":non-updatable-system-server-" + f,
322			Modules:      system_server_classpath,
323			ModuleTag:    "{.system-server" + tagSuffix[i],
324			Scope:        "system-server",
325		})
326	}
327	for _, txt := range textFiles {
328		createMergedTxt(ctx, txt)
329	}
330}
331
332func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
333	bootclasspath := a.properties.Bootclasspath
334	system_server_classpath := a.properties.System_server_classpath
335	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
336		bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
337		sort.Strings(bootclasspath)
338	}
339	createMergedTxts(ctx, bootclasspath, system_server_classpath)
340
341	createMergedPublicStubs(ctx, bootclasspath)
342	createMergedSystemStubs(ctx, bootclasspath)
343	createMergedTestStubsForNonUpdatableModules(ctx)
344	createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
345	createMergedFrameworkImpl(ctx, bootclasspath)
346
347	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
348
349	createPublicStubsSourceFilegroup(ctx, bootclasspath)
350}
351
352func combinedApisModuleFactory() android.Module {
353	module := &CombinedApis{}
354	module.AddProperties(&module.properties)
355	android.InitAndroidModule(module)
356	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
357	android.InitBazelModule(module)
358	return module
359}
360
361type bazelCombinedApisAttributes struct {
362	Scope bazel.StringAttribute
363	Base  bazel.LabelAttribute
364	Deps  bazel.LabelListAttribute
365}
366
367// combined_apis bp2build converter
368func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
369	basePrefix := "non-updatable"
370	scopeToSuffix := map[string]string{
371		"public":        "-current.txt",
372		"system":        "-system-current.txt",
373		"module-lib":    "-module-lib-current.txt",
374		"system-server": "-system-server-current.txt",
375	}
376
377	for scopeName, suffix := range scopeToSuffix{
378		name := a.Name() + suffix
379
380		var scope bazel.StringAttribute
381		scope.SetValue(scopeName)
382
383		var base bazel.LabelAttribute
384		base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix))
385
386		var deps bazel.LabelListAttribute
387		classpath := a.properties.Bootclasspath
388		if scopeName == "system-server" {
389			classpath = a.properties.System_server_classpath
390		}
391		deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath))
392
393		attrs := bazelCombinedApisAttributes{
394			Scope: scope,
395			Base:  base,
396			Deps:  deps,
397		}
398		props := bazel.BazelTargetModuleProperties{
399			Rule_class:        "merged_txts",
400			Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl",
401		}
402		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs)
403	}
404}
405
406// Various utility methods below.
407
408// Creates an array of ":<m><tag>" for each m in <modules>.
409func createSrcs(modules []string, tag string) []string {
410	return transformArray(modules, ":", tag)
411}
412
413// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
414func transformArray(modules []string, prefix, suffix string) []string {
415	a := make([]string, 0, len(modules))
416	for _, module := range modules {
417		a = append(a, prefix+module+suffix)
418	}
419	return a
420}
421
422func removeAll(s []string, vs []string) []string {
423	for _, v := range vs {
424		s = remove(s, v)
425	}
426	return s
427}
428
429func remove(s []string, v string) []string {
430	s2 := make([]string, 0, len(s))
431	for _, sv := range s {
432		if sv != v {
433			s2 = append(s2, sv)
434		}
435	}
436	return s2
437}
438