xref: /aosp_15_r20/build/soong/java/ravenwood.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2023 Google Inc. All rights reserved.
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.
14package java
15
16import (
17	"strconv"
18
19	"android/soong/android"
20	"android/soong/tradefed"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/proptools"
24)
25
26func init() {
27	RegisterRavenwoodBuildComponents(android.InitRegistrationContext)
28}
29
30func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) {
31	ctx.RegisterModuleType("android_ravenwood_test", ravenwoodTestFactory)
32	ctx.RegisterModuleType("android_ravenwood_libgroup", ravenwoodLibgroupFactory)
33}
34
35var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
36var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
37var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
38var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"}
39var ravenwoodTestInstResourceApkTag = dependencyTag{name: "ravenwoodtest-inst-res-apk"}
40
41var genManifestProperties = pctx.AndroidStaticRule("genManifestProperties",
42	blueprint.RuleParams{
43		Command: "echo targetSdkVersionInt=$targetSdkVersionInt > $out && " +
44			"echo targetSdkVersionRaw=$targetSdkVersionRaw >> $out && " +
45			"echo packageName=$packageName >> $out && " +
46			"echo instPackageName=$instPackageName >> $out",
47	}, "targetSdkVersionInt", "targetSdkVersionRaw", "packageName", "instPackageName")
48
49const ravenwoodUtilsName = "ravenwood-utils"
50const ravenwoodRuntimeName = "ravenwood-runtime"
51
52type ravenwoodLibgroupJniDepProviderInfo struct {
53	// All the jni_libs module names with transient dependencies.
54	names map[string]bool
55}
56
57var ravenwoodLibgroupJniDepProvider = blueprint.NewProvider[ravenwoodLibgroupJniDepProviderInfo]()
58
59func getLibPath(archType android.ArchType) string {
60	if archType.Multilib == "lib64" {
61		return "lib64"
62	}
63	return "lib"
64}
65
66type ravenwoodTestProperties struct {
67	Jni_libs proptools.Configurable[[]string]
68
69	// Specify another android_app module here to copy it to the test directory, so that
70	// the ravenwood test can access it. This APK will be loaded as resources of the test
71	// target app.
72	// TODO: For now, we simply refer to another android_app module and copy it to the
73	// test directory. Eventually, android_ravenwood_test should support all the resource
74	// related properties and build resources from the `res/` directory.
75	Resource_apk *string
76
77	// Specify another android_app module here to copy it to the test directory, so that
78	// the ravenwood test can access it. This APK will be loaded as resources of the test
79	// instrumentation app itself.
80	Inst_resource_apk *string
81
82	// Specify the package name of the test target apk.
83	// This will be set to the target Context's package name.
84	// (i.e. Instrumentation.getTargetContext().getPackageName())
85	// If this is omitted, Package_name will be used.
86	Package_name *string
87
88	// Specify the package name of this test module.
89	// This will be set to the test Context's package name.
90	//(i.e. Instrumentation.getContext().getPackageName())
91	Inst_package_name *string
92}
93
94type ravenwoodTest struct {
95	Library
96
97	ravenwoodTestProperties ravenwoodTestProperties
98
99	testProperties testProperties
100	testConfig     android.Path
101
102	forceOSType   android.OsType
103	forceArchType android.ArchType
104}
105
106func ravenwoodTestFactory() android.Module {
107	module := &ravenwoodTest{}
108
109	module.addHostAndDeviceProperties()
110	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
111
112	module.Module.dexpreopter.isTest = true
113	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
114
115	module.testProperties.Test_suites = []string{
116		"general-tests",
117		"ravenwood-tests",
118	}
119	module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false)
120
121	InitJavaModule(module, android.DeviceSupported)
122	android.InitDefaultableModule(module)
123
124	return module
125}
126
127func (r *ravenwoodTest) InstallInTestcases() bool { return true }
128func (r *ravenwoodTest) InstallForceOS() (*android.OsType, *android.ArchType) {
129	return &r.forceOSType, &r.forceArchType
130}
131func (r *ravenwoodTest) TestSuites() []string {
132	return r.testProperties.Test_suites
133}
134
135func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) {
136	r.Library.DepsMutator(ctx)
137
138	// Generically depend on the runtime so that it's installed together with us
139	ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName)
140
141	// Directly depend on any utils so that we link against them
142	utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0]
143	if utils != nil {
144		for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs {
145			ctx.AddVariationDependencies(nil, libTag, lib)
146		}
147	}
148
149	// Add jni libs
150	for _, lib := range r.ravenwoodTestProperties.Jni_libs.GetOrDefault(ctx, nil) {
151		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
152	}
153
154	// Resources APK
155	if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" {
156		ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk)
157	}
158
159	if resourceApk := proptools.String(r.ravenwoodTestProperties.Inst_resource_apk); resourceApk != "" {
160		ctx.AddVariationDependencies(nil, ravenwoodTestInstResourceApkTag, resourceApk)
161	}
162}
163
164func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
165	r.forceOSType = ctx.Config().BuildOS
166	r.forceArchType = ctx.Config().BuildArch
167
168	r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
169		TestConfigProp:         r.testProperties.Test_config,
170		TestConfigTemplateProp: r.testProperties.Test_config_template,
171		TestSuites:             r.testProperties.Test_suites,
172		AutoGenConfig:          r.testProperties.Auto_gen_config,
173		DeviceTemplate:         "${RavenwoodTestConfigTemplate}",
174		HostTemplate:           "${RavenwoodTestConfigTemplate}",
175	})
176
177	// Always enable Ravenizer for ravenwood tests.
178	r.Library.ravenizer.enabled = true
179
180	r.Library.GenerateAndroidBuildActions(ctx)
181
182	// Start by depending on all files installed by dependencies
183	var installDeps android.InstallPaths
184
185	// All JNI libraries included in the runtime
186	var runtimeJniModuleNames map[string]bool
187
188	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
189		for _, installFile := range android.OtherModuleProviderOrDefault(
190			ctx, utils, android.InstallFilesProvider).InstallFiles {
191			installDeps = append(installDeps, installFile)
192		}
193		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
194		if ok {
195			runtimeJniModuleNames = jniDeps.names
196		}
197	}
198
199	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
200		for _, installFile := range android.OtherModuleProviderOrDefault(
201			ctx, runtime, android.InstallFilesProvider).InstallFiles {
202			installDeps = append(installDeps, installFile)
203		}
204		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
205		if ok {
206			runtimeJniModuleNames = jniDeps.names
207		}
208	}
209
210	// Also remember what JNI libs are in the runtime.
211
212	// Also depend on our config
213	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
214	installConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
215	installDeps = append(installDeps, installConfig)
216
217	// Depend on the JNI libraries, but don't install the ones that the runtime already
218	// contains.
219	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
220	for _, jniLib := range collectTransitiveJniDeps(ctx) {
221		if _, ok := runtimeJniModuleNames[jniLib.name]; ok {
222			continue // Runtime already includes it.
223		}
224		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
225		installDeps = append(installDeps, installJni)
226	}
227
228	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
229
230	copyResApk := func(tag blueprint.DependencyTag, toFileName string) {
231		if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 {
232			installFile := android.OutputFileForModule(ctx, resApk[0], "")
233			installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile)
234			installDeps = append(installDeps, installResApk)
235		}
236	}
237	copyResApk(ravenwoodTestResourceApkTag, "ravenwood-res.apk")
238	copyResApk(ravenwoodTestInstResourceApkTag, "ravenwood-inst-res.apk")
239
240	// Generate manifest properties
241	propertiesOutputPath := android.PathForModuleGen(ctx, "ravenwood.properties")
242
243	targetSdkVersion := proptools.StringDefault(r.deviceProperties.Target_sdk_version, "")
244	targetSdkVersionInt := r.TargetSdkVersion(ctx).FinalOrFutureInt() // FinalOrFutureInt may be 10000.
245	packageName := proptools.StringDefault(r.ravenwoodTestProperties.Package_name, "")
246	instPackageName := proptools.StringDefault(r.ravenwoodTestProperties.Inst_package_name, "")
247	ctx.Build(pctx, android.BuildParams{
248		Rule:        genManifestProperties,
249		Description: "genManifestProperties",
250		Output:      propertiesOutputPath,
251		Args: map[string]string{
252			"targetSdkVersionInt": strconv.Itoa(targetSdkVersionInt),
253			"targetSdkVersionRaw": targetSdkVersion,
254			"packageName":         packageName,
255			"instPackageName":     instPackageName,
256		},
257	})
258	installProps := ctx.InstallFile(installPath, "ravenwood.properties", propertiesOutputPath)
259	installDeps = append(installDeps, installProps)
260
261	// Install our JAR with all dependencies
262	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
263}
264
265func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
266	entriesList := r.Library.AndroidMkEntries()
267	entries := &entriesList[0]
268	entries.ExtraEntries = append(entries.ExtraEntries,
269		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
270			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
271			entries.AddStrings("LOCAL_COMPATIBILITY_SUITE",
272				"general-tests", "ravenwood-tests")
273			if r.testConfig != nil {
274				entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
275			}
276		})
277	return entriesList
278}
279
280type ravenwoodLibgroupProperties struct {
281	Libs []string
282
283	Jni_libs proptools.Configurable[[]string]
284
285	// We use this to copy framework-res.apk to the ravenwood runtime directory.
286	Data []string `android:"path,arch_variant"`
287
288	// We use this to copy font files to the ravenwood runtime directory.
289	Fonts []string `android:"path,arch_variant"`
290}
291
292type ravenwoodLibgroup struct {
293	android.ModuleBase
294
295	ravenwoodLibgroupProperties ravenwoodLibgroupProperties
296
297	forceOSType   android.OsType
298	forceArchType android.ArchType
299}
300
301func ravenwoodLibgroupFactory() android.Module {
302	module := &ravenwoodLibgroup{}
303	module.AddProperties(&module.ravenwoodLibgroupProperties)
304
305	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
306	return module
307}
308
309func (r *ravenwoodLibgroup) InstallInTestcases() bool { return true }
310func (r *ravenwoodLibgroup) InstallForceOS() (*android.OsType, *android.ArchType) {
311	return &r.forceOSType, &r.forceArchType
312}
313func (r *ravenwoodLibgroup) TestSuites() []string {
314	return []string{
315		"general-tests",
316		"ravenwood-tests",
317	}
318}
319
320func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) {
321	// Always depends on our underlying libs
322	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
323		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
324	}
325	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs.GetOrDefault(ctx, nil) {
326		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
327	}
328}
329
330func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
331	r.forceOSType = ctx.Config().BuildOS
332	r.forceArchType = ctx.Config().BuildArch
333
334	// Collect the JNI dependencies, including the transitive deps.
335	jniDepNames := make(map[string]bool)
336	jniLibs := collectTransitiveJniDeps(ctx)
337
338	for _, jni := range jniLibs {
339		jniDepNames[jni.name] = true
340	}
341	android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{
342		names: jniDepNames,
343	})
344
345	// Install our runtime into expected location for packaging
346	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
347	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
348		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
349		if libModule == nil {
350			if ctx.Config().AllowMissingDependencies() {
351				ctx.AddMissingDependencies([]string{lib})
352			} else {
353				ctx.PropertyErrorf("lib", "missing dependency %q", lib)
354			}
355			continue
356		}
357		libJar := android.OutputFileForModule(ctx, libModule, "")
358		ctx.InstallFile(installPath, lib+".jar", libJar)
359	}
360	soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType))
361
362	for _, jniLib := range jniLibs {
363		ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
364	}
365
366	dataInstallPath := installPath.Join(ctx, "ravenwood-data")
367	data := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Data)
368	for _, file := range data {
369		ctx.InstallFile(dataInstallPath, file.Base(), file)
370	}
371
372	fontsInstallPath := installPath.Join(ctx, "fonts")
373	fonts := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Fonts)
374	for _, file := range fonts {
375		ctx.InstallFile(fontsInstallPath, file.Base(), file)
376	}
377
378	// Normal build should perform install steps
379	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
380}
381
382// collectTransitiveJniDeps returns all JNI dependencies, including transitive
383// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries)
384func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib {
385	libs, _ := collectJniDeps(ctx, true, false, nil)
386	return libs
387}
388