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	"android/soong/android"
18	"android/soong/tradefed"
19
20	"github.com/google/blueprint"
21	"github.com/google/blueprint/proptools"
22)
23
24func init() {
25	RegisterRavenwoodBuildComponents(android.InitRegistrationContext)
26}
27
28func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) {
29	ctx.RegisterModuleType("android_ravenwood_test", ravenwoodTestFactory)
30	ctx.RegisterModuleType("android_ravenwood_libgroup", ravenwoodLibgroupFactory)
31}
32
33var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
34var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
35var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
36
37const ravenwoodUtilsName = "ravenwood-utils"
38const ravenwoodRuntimeName = "ravenwood-runtime"
39
40type ravenwoodLibgroupJniDepProviderInfo struct {
41	// All the jni_libs module names with transient dependencies.
42	names map[string]bool
43}
44
45var ravenwoodLibgroupJniDepProvider = blueprint.NewProvider[ravenwoodLibgroupJniDepProviderInfo]()
46
47func getLibPath(archType android.ArchType) string {
48	if archType.Multilib == "lib64" {
49		return "lib64"
50	}
51	return "lib"
52}
53
54type ravenwoodTestProperties struct {
55	Jni_libs []string
56}
57
58type ravenwoodTest struct {
59	Library
60
61	ravenwoodTestProperties ravenwoodTestProperties
62
63	testProperties testProperties
64	testConfig     android.Path
65
66	forceOSType   android.OsType
67	forceArchType android.ArchType
68}
69
70func ravenwoodTestFactory() android.Module {
71	module := &ravenwoodTest{}
72
73	module.addHostAndDeviceProperties()
74	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
75
76	module.Module.dexpreopter.isTest = true
77	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
78
79	module.testProperties.Test_suites = []string{
80		"general-tests",
81		"ravenwood-tests",
82	}
83	module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false)
84
85	InitJavaModule(module, android.DeviceSupported)
86	android.InitDefaultableModule(module)
87
88	return module
89}
90
91func (r *ravenwoodTest) InstallInTestcases() bool { return true }
92func (r *ravenwoodTest) InstallForceOS() (*android.OsType, *android.ArchType) {
93	return &r.forceOSType, &r.forceArchType
94}
95func (r *ravenwoodTest) TestSuites() []string {
96	return r.testProperties.Test_suites
97}
98
99func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) {
100	r.Library.DepsMutator(ctx)
101
102	// Generically depend on the runtime so that it's installed together with us
103	ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName)
104
105	// Directly depend on any utils so that we link against them
106	utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0]
107	if utils != nil {
108		for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs {
109			ctx.AddVariationDependencies(nil, libTag, lib)
110		}
111	}
112
113	// Add jni libs
114	for _, lib := range r.ravenwoodTestProperties.Jni_libs {
115		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
116	}
117}
118
119func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
120	r.forceOSType = ctx.Config().BuildOS
121	r.forceArchType = ctx.Config().BuildArch
122
123	r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
124		TestConfigProp:         r.testProperties.Test_config,
125		TestConfigTemplateProp: r.testProperties.Test_config_template,
126		TestSuites:             r.testProperties.Test_suites,
127		AutoGenConfig:          r.testProperties.Auto_gen_config,
128		DeviceTemplate:         "${RavenwoodTestConfigTemplate}",
129		HostTemplate:           "${RavenwoodTestConfigTemplate}",
130	})
131
132	r.Library.GenerateAndroidBuildActions(ctx)
133
134	// Start by depending on all files installed by dependencies
135	var installDeps android.InstallPaths
136
137	// All JNI libraries included in the runtime
138	var runtimeJniModuleNames map[string]bool
139
140	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
141		for _, installFile := range utils.FilesToInstall() {
142			installDeps = append(installDeps, installFile)
143		}
144		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
145		if ok {
146			runtimeJniModuleNames = jniDeps.names
147		}
148	}
149
150	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
151		for _, installFile := range runtime.FilesToInstall() {
152			installDeps = append(installDeps, installFile)
153		}
154		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
155		if ok {
156			runtimeJniModuleNames = jniDeps.names
157		}
158	}
159
160	// Also remember what JNI libs are in the runtime.
161
162	// Also depend on our config
163	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
164	installConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
165	installDeps = append(installDeps, installConfig)
166
167	// Depend on the JNI libraries, but don't install the ones that the runtime already
168	// contains.
169	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
170	for _, jniLib := range collectTransitiveJniDeps(ctx) {
171		if _, ok := runtimeJniModuleNames[jniLib.name]; ok {
172			continue // Runtime already includes it.
173		}
174		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
175		installDeps = append(installDeps, installJni)
176	}
177
178	// Install our JAR with all dependencies
179	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
180}
181
182func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
183	entriesList := r.Library.AndroidMkEntries()
184	entries := &entriesList[0]
185	entries.ExtraEntries = append(entries.ExtraEntries,
186		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
187			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
188			entries.AddStrings("LOCAL_COMPATIBILITY_SUITE",
189				"general-tests", "ravenwood-tests")
190			if r.testConfig != nil {
191				entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
192			}
193		})
194	return entriesList
195}
196
197type ravenwoodLibgroupProperties struct {
198	Libs []string
199
200	Jni_libs []string
201}
202
203type ravenwoodLibgroup struct {
204	android.ModuleBase
205
206	ravenwoodLibgroupProperties ravenwoodLibgroupProperties
207
208	forceOSType   android.OsType
209	forceArchType android.ArchType
210}
211
212func ravenwoodLibgroupFactory() android.Module {
213	module := &ravenwoodLibgroup{}
214	module.AddProperties(&module.ravenwoodLibgroupProperties)
215
216	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
217	return module
218}
219
220func (r *ravenwoodLibgroup) InstallInTestcases() bool { return true }
221func (r *ravenwoodLibgroup) InstallForceOS() (*android.OsType, *android.ArchType) {
222	return &r.forceOSType, &r.forceArchType
223}
224func (r *ravenwoodLibgroup) TestSuites() []string {
225	return []string{
226		"general-tests",
227		"ravenwood-tests",
228	}
229}
230
231func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) {
232	// Always depends on our underlying libs
233	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
234		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
235	}
236	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
237		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
238	}
239}
240
241func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
242	r.forceOSType = ctx.Config().BuildOS
243	r.forceArchType = ctx.Config().BuildArch
244
245	// Collect the JNI dependencies, including the transitive deps.
246	jniDepNames := make(map[string]bool)
247	jniLibs := collectTransitiveJniDeps(ctx)
248
249	for _, jni := range jniLibs {
250		jniDepNames[jni.name] = true
251	}
252	android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{
253		names: jniDepNames,
254	})
255
256	// Install our runtime into expected location for packaging
257	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
258	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
259		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
260		libJar := android.OutputFileForModule(ctx, libModule, "")
261		ctx.InstallFile(installPath, lib+".jar", libJar)
262	}
263	soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType))
264
265	for _, jniLib := range jniLibs {
266		ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
267	}
268
269	// Normal build should perform install steps
270	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
271}
272
273// collectTransitiveJniDeps returns all JNI dependencies, including transitive
274// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries)
275func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib {
276	libs, _ := collectJniDeps(ctx, true, false, nil)
277	return libs
278}
279