• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 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 rust
16
17import (
18	"path/filepath"
19
20	"github.com/google/blueprint/proptools"
21
22	"android/soong/android"
23	"android/soong/cc"
24	"android/soong/tradefed"
25)
26
27type TestProperties struct {
28	// Disables the creation of a test-specific directory when used with
29	// relative_install_path. Useful if several tests need to be in the same
30	// directory.
31	No_named_install_directory *bool
32
33	// the name of the test configuration (for example "AndroidTest.xml") that should be
34	// installed with the module.
35	Test_config *string `android:"path,arch_variant"`
36
37	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
38	// should be installed with the module.
39	Test_config_template *string `android:"path,arch_variant"`
40
41	// list of compatibility suites (for example "cts", "vts") that the module should be
42	// installed into.
43	Test_suites []string `android:"arch_variant"`
44
45	// list of files or filegroup modules that provide data that should be installed alongside
46	// the test
47	Data []string `android:"path,arch_variant"`
48
49	// Same as data, but adds dependencies on modules using the device's os variant, and common
50	// architecture's variant. Can be useful to add device-built apps to the data of a host
51	// test.
52	Device_common_data []string `android:"path_device_common"`
53
54	// Same as data, but will add dependencies on modules using the host's os variation and
55	// the common arch variation. Useful for a device test that wants to depend on a host
56	// module, for example to include a custom Tradefed test runner.
57	Host_common_data []string `android:"path_host_common"`
58
59	// list of shared library modules that should be installed alongside the test
60	Data_libs []string `android:"arch_variant"`
61
62	// list of binary modules that should be installed alongside the test
63	Data_bins []string `android:"arch_variant"`
64
65	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
66	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
67	// explicitly.
68	Auto_gen_config *bool
69
70	// if set, build with the standard Rust test harness. Defaults to true.
71	Test_harness *bool
72
73	// Test options.
74	Test_options android.CommonTestOptions
75
76	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
77	// with root permission.
78	Require_root *bool
79}
80
81// A test module is a binary module with extra --test compiler flag
82// and different default installation directory.
83// In golang, inheriance is written as a component.
84type testDecorator struct {
85	*binaryDecorator
86	Properties TestProperties
87	testConfig android.Path
88
89	data []android.DataPath
90}
91
92func (test *testDecorator) dataPaths() []android.DataPath {
93	return test.data
94}
95
96func (test *testDecorator) nativeCoverage() bool {
97	return true
98}
99
100func (test *testDecorator) testHarness() bool {
101	return BoolDefault(test.Properties.Test_harness, true)
102}
103
104func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
105	// Build both 32 and 64 targets for device tests.
106	// Cannot build both for host tests yet if the test depends on
107	// something like proc-macro2 that cannot be built for both.
108	multilib := android.MultilibBoth
109	if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
110		multilib = android.MultilibFirst
111	}
112	module := newModule(hod, multilib)
113
114	test := &testDecorator{
115		binaryDecorator: &binaryDecorator{
116			baseCompiler: NewBaseCompiler("nativetest", "nativetest64", InstallInData),
117		},
118	}
119
120	module.compiler = test
121	return module, test
122}
123
124func (test *testDecorator) compilerProps() []interface{} {
125	return append(test.binaryDecorator.compilerProps(), &test.Properties)
126}
127
128func (test *testDecorator) install(ctx ModuleContext) {
129	// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
130	testInstallBase := "/data/local/tmp"
131	if ctx.RustModule().InVendorOrProduct() {
132		testInstallBase = "/data/local/tests/vendor"
133	}
134
135	var configs []tradefed.Config
136	if Bool(test.Properties.Require_root) {
137		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
138	} else {
139		var options []tradefed.Option
140		options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
141		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
142	}
143
144	test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
145		TestConfigProp:         test.Properties.Test_config,
146		TestConfigTemplateProp: test.Properties.Test_config_template,
147		TestSuites:             test.Properties.Test_suites,
148		Config:                 configs,
149		AutoGenConfig:          test.Properties.Auto_gen_config,
150		TestInstallBase:        testInstallBase,
151		DeviceTemplate:         "${RustDeviceTestConfigTemplate}",
152		HostTemplate:           "${RustHostTestConfigTemplate}",
153	})
154
155	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
156	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
157	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Host_common_data)...)
158
159	ctx.VisitDirectDepsProxyWithTag(dataLibDepTag, func(dep android.ModuleProxy) {
160		depName := ctx.OtherModuleName(dep)
161		linkableDep, ok := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
162		if !ok {
163			ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
164		}
165		if linkableDep.OutputFile.Valid() {
166			// Copy the output in "lib[64]" so that it's compatible with
167			// the default rpath values.
168			commonInfo := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider)
169			libDir := "lib"
170			if commonInfo.Target.Arch.ArchType.Multilib == "lib64" {
171				libDir = "lib64"
172			}
173			test.data = append(test.data,
174				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
175					RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath)})
176		}
177	})
178
179	ctx.VisitDirectDepsProxyWithTag(dataBinDepTag, func(dep android.ModuleProxy) {
180		depName := ctx.OtherModuleName(dep)
181		linkableDep, ok := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
182		if !ok {
183			ctx.ModuleErrorf("data_bin %q is not a linkable module", depName)
184		}
185		if linkableDep.OutputFile.Valid() {
186			test.data = append(test.data,
187				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
188					RelativeInstallPath: linkableDep.RelativeInstallPath})
189		}
190	})
191
192	for _, dataSrcPath := range dataSrcPaths {
193		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
194	}
195
196	// default relative install path is module name
197	if !Bool(test.Properties.No_named_install_directory) {
198		test.baseCompiler.relative = ctx.ModuleName()
199	} else if String(test.baseCompiler.Properties.Relative_install_path) == "" {
200		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
201	}
202
203	if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
204		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
205	}
206
207	if !ctx.Config().KatiEnabled() { // TODO(spandandas): Remove the special case for kati
208		// Install the test config in testcases/ directory for atest.
209		r, ok := ctx.Module().(*Module)
210		if !ok {
211			ctx.ModuleErrorf("Not a rust test module")
212		}
213		// Install configs in the root of $PRODUCT_OUT/testcases/$module
214		testCases := android.PathForModuleInPartitionInstall(ctx, "testcases", ctx.ModuleName()+r.SubName())
215		if ctx.PrimaryArch() {
216			if test.testConfig != nil {
217				ctx.InstallFile(testCases, ctx.ModuleName()+".config", test.testConfig)
218			}
219			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
220			if dynamicConfig.Valid() {
221				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
222			}
223		}
224		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
225		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
226		ctx.InstallTestData(testCases, test.data)
227		testPath := ctx.RustModule().OutputFile().Path()
228		ctx.InstallFile(testCases, testPath.Base(), testPath)
229	}
230
231	test.binaryDecorator.installTestData(ctx, test.data)
232	test.binaryDecorator.install(ctx)
233}
234
235func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
236	flags = test.binaryDecorator.compilerFlags(ctx, flags)
237	if test.testHarness() {
238		flags.RustFlags = append(flags.RustFlags, "--test")
239		flags.RustFlags = append(flags.RustFlags, "-A missing-docs")
240	}
241	if ctx.Device() {
242		flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
243	}
244
245	// Add a default rpath to allow tests to dlopen libraries specified in data_libs.
246	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, `-Wl,-rpath,\$$ORIGIN/lib64`)
247	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
248
249	return flags
250}
251
252func (test *testDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
253	return rlibAutoDep
254}
255
256func init() {
257	// Rust tests are binary files built with --test.
258	android.RegisterModuleType("rust_test", RustTestFactory)
259	android.RegisterModuleType("rust_test_host", RustTestHostFactory)
260}
261
262func RustTestFactory() android.Module {
263	module, _ := NewRustTest(android.HostAndDeviceSupported)
264
265	// NewRustTest will set MultilibBoth true, however the host variant
266	// cannot produce the non-primary target. Therefore, add the
267	// rustTestHostMultilib load hook to set MultilibFirst for the
268	// host target.
269	android.AddLoadHook(module, rustTestHostMultilib)
270	module.testModule = true
271	return module.Init()
272}
273
274func RustTestHostFactory() android.Module {
275	module, _ := NewRustTest(android.HostSupported)
276	module.testModule = true
277	return module.Init()
278}
279
280func (test *testDecorator) stdLinkage(device bool) RustLinkage {
281	return RlibLinkage
282}
283
284func (test *testDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
285	deps = test.binaryDecorator.compilerDeps(ctx, deps)
286
287	deps.Rustlibs = append(deps.Rustlibs, "libtest")
288
289	deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...)
290	deps.DataBins = append(deps.DataBins, test.Properties.Data_bins...)
291
292	return deps
293}
294
295func (test *testDecorator) testBinary() bool {
296	return true
297}
298
299func (test *testDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
300	test.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
301	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
302	if Bool(test.Properties.Test_options.Unit_test) {
303		moduleInfoJSON.IsUnitTest = "true"
304		if ctx.Host() {
305			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
306		}
307	}
308	moduleInfoJSON.TestOptionsTags = append(moduleInfoJSON.TestOptionsTags, test.Properties.Test_options.Tags...)
309	if test.testConfig != nil {
310		if _, ok := test.testConfig.(android.WritablePath); ok {
311			moduleInfoJSON.AutoTestConfig = []string{"true"}
312		}
313		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.testConfig.String())
314	}
315
316	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, test.Properties.Data_bins...)
317
318	if len(test.Properties.Test_suites) > 0 {
319		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, test.Properties.Test_suites...)
320	} else {
321		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
322	}
323
324	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
325		TestSuites: test.Properties.Test_suites,
326	})
327}
328
329func rustTestHostMultilib(ctx android.LoadHookContext) {
330	type props struct {
331		Target struct {
332			Host struct {
333				Compile_multilib *string
334			}
335		}
336	}
337	p := &props{}
338	p.Target.Host.Compile_multilib = proptools.StringPtr("first")
339	ctx.AppendProperties(p)
340}
341