• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_module_type = 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	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
121	module.Module.sourceProperties.Top_level_test_target = true
122
123	InitJavaModule(module, android.DeviceSupported)
124	android.InitDefaultableModule(module)
125
126	return module
127}
128
129func (r *ravenwoodTest) InstallInTestcases() bool { return true }
130func (r *ravenwoodTest) InstallForceOS() (*android.OsType, *android.ArchType) {
131	return &r.forceOSType, &r.forceArchType
132}
133func (r *ravenwoodTest) TestSuites() []string {
134	return r.testProperties.Test_suites
135}
136
137func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) {
138	r.Library.DepsMutator(ctx)
139
140	// Generically depend on the runtime so that it's installed together with us
141	ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName)
142
143	// Directly depend on any utils so that we link against them
144	utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0]
145	if utils != nil {
146		for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs {
147			ctx.AddVariationDependencies(nil, libTag, lib)
148		}
149	}
150
151	// Add jni libs
152	for _, lib := range r.ravenwoodTestProperties.Jni_libs.GetOrDefault(ctx, nil) {
153		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
154	}
155
156	// Resources APK
157	if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" {
158		ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk)
159	}
160
161	if resourceApk := proptools.String(r.ravenwoodTestProperties.Inst_resource_apk); resourceApk != "" {
162		ctx.AddVariationDependencies(nil, ravenwoodTestInstResourceApkTag, resourceApk)
163	}
164}
165
166func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
167	r.forceOSType = ctx.Config().BuildOS
168	r.forceArchType = ctx.Config().BuildArch
169
170	r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
171		TestConfigProp:         r.testProperties.Test_config,
172		TestConfigTemplateProp: r.testProperties.Test_config_template,
173		TestSuites:             r.testProperties.Test_suites,
174		AutoGenConfig:          r.testProperties.Auto_gen_config,
175		DeviceTemplate:         "${RavenwoodTestConfigTemplate}",
176		HostTemplate:           "${RavenwoodTestConfigTemplate}",
177	})
178
179	// Always enable Ravenizer for ravenwood tests.
180	r.Library.ravenizer.enabled = true
181
182	r.Library.GenerateAndroidBuildActions(ctx)
183
184	// Start by depending on all files installed by dependencies
185	var installDeps android.InstallPaths
186
187	// All JNI libraries included in the runtime
188	var runtimeJniModuleNames map[string]bool
189
190	utils := ctx.GetDirectDepsProxyWithTag(ravenwoodUtilsTag)[0]
191	for _, installFile := range android.OtherModuleProviderOrDefault(
192		ctx, utils, android.InstallFilesProvider).InstallFiles {
193		installDeps = append(installDeps, installFile)
194	}
195	jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
196	if ok {
197		runtimeJniModuleNames = jniDeps.names
198	}
199
200	runtime := ctx.GetDirectDepsProxyWithTag(ravenwoodRuntimeTag)[0]
201	for _, installFile := range android.OtherModuleProviderOrDefault(
202		ctx, runtime, android.InstallFilesProvider).InstallFiles {
203		installDeps = append(installDeps, installFile)
204	}
205	jniDeps, ok = android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
206	if ok {
207		runtimeJniModuleNames = jniDeps.names
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.GetDirectDepsProxyWithTag(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	moduleInfoJSON := ctx.ModuleInfoJSON()
265	if _, ok := r.testConfig.(android.WritablePath); ok {
266		moduleInfoJSON.AutoTestConfig = []string{"true"}
267	}
268	if r.testConfig != nil {
269		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
270	}
271	moduleInfoJSON.CompatibilitySuites = []string{"general-tests", "ravenwood-tests"}
272
273	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
274		TestSuites: r.TestSuites(),
275	})
276}
277
278func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
279	entriesList := r.Library.AndroidMkEntries()
280	entries := &entriesList[0]
281	entries.ExtraEntries = append(entries.ExtraEntries,
282		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
283			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
284			entries.AddStrings("LOCAL_COMPATIBILITY_SUITE",
285				"general-tests", "ravenwood-tests")
286			if r.testConfig != nil {
287				entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
288			}
289		})
290	return entriesList
291}
292
293type ravenwoodLibgroupProperties struct {
294	Libs []string
295
296	Jni_libs proptools.Configurable[[]string]
297
298	// We use this to copy framework-res.apk to the ravenwood runtime directory.
299	Data []string `android:"path,arch_variant"`
300
301	// We use this to copy font files to the ravenwood runtime directory.
302	Fonts []string `android:"path,arch_variant"`
303}
304
305type ravenwoodLibgroup struct {
306	android.ModuleBase
307
308	ravenwoodLibgroupProperties ravenwoodLibgroupProperties
309
310	forceOSType   android.OsType
311	forceArchType android.ArchType
312}
313
314func ravenwoodLibgroupFactory() android.Module {
315	module := &ravenwoodLibgroup{}
316	module.AddProperties(&module.ravenwoodLibgroupProperties)
317
318	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
319	return module
320}
321
322func (r *ravenwoodLibgroup) InstallInTestcases() bool { return true }
323func (r *ravenwoodLibgroup) InstallForceOS() (*android.OsType, *android.ArchType) {
324	return &r.forceOSType, &r.forceArchType
325}
326func (r *ravenwoodLibgroup) TestSuites() []string {
327	return []string{
328		"general-tests",
329		"ravenwood-tests",
330	}
331}
332
333func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) {
334	// Always depends on our underlying libs
335	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
336		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
337	}
338	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs.GetOrDefault(ctx, nil) {
339		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
340	}
341}
342
343func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
344	r.forceOSType = ctx.Config().BuildOS
345	r.forceArchType = ctx.Config().BuildArch
346
347	// Collect the JNI dependencies, including the transitive deps.
348	jniDepNames := make(map[string]bool)
349	jniLibs := collectTransitiveJniDeps(ctx)
350
351	for _, jni := range jniLibs {
352		jniDepNames[jni.name] = true
353	}
354	android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{
355		names: jniDepNames,
356	})
357
358	// Install our runtime into expected location for packaging
359	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
360	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
361		libModule := ctx.GetDirectDepProxyWithTag(lib, ravenwoodLibContentTag)
362		if libModule == nil {
363			if ctx.Config().AllowMissingDependencies() {
364				ctx.AddMissingDependencies([]string{lib})
365			} else {
366				ctx.PropertyErrorf("lib", "missing dependency %q", lib)
367			}
368			continue
369		}
370		libJar := android.OutputFileForModule(ctx, libModule, "")
371		ctx.InstallFile(installPath, lib+".jar", libJar)
372	}
373	soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType))
374
375	for _, jniLib := range jniLibs {
376		ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
377	}
378
379	dataInstallPath := installPath.Join(ctx, "ravenwood-data")
380	data := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Data)
381	for _, file := range data {
382		ctx.InstallFile(dataInstallPath, file.Base(), file)
383	}
384
385	fontsInstallPath := installPath.Join(ctx, "fonts")
386	fonts := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Fonts)
387	for _, file := range fonts {
388		ctx.InstallFile(fontsInstallPath, file.Base(), file)
389	}
390
391	// Normal build should perform install steps
392	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
393
394	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
395		TestSuites: r.TestSuites(),
396	})
397}
398
399// collectTransitiveJniDeps returns all JNI dependencies, including transitive
400// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries)
401func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib {
402	libs, _ := collectJniDeps(ctx, true, false, nil)
403	return libs
404}
405