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, but test_per_src doesn't work.
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	// list of shared library modules that should be installed alongside the test
50	Data_libs []string `android:"arch_variant"`
51
52	// list of binary modules that should be installed alongside the test
53	Data_bins []string `android:"arch_variant"`
54
55	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
56	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
57	// explicitly.
58	Auto_gen_config *bool
59
60	// if set, build with the standard Rust test harness. Defaults to true.
61	Test_harness *bool
62
63	// Test options.
64	Test_options android.CommonTestOptions
65
66	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
67	// with root permission.
68	Require_root *bool
69}
70
71// A test module is a binary module with extra --test compiler flag
72// and different default installation directory.
73// In golang, inheriance is written as a component.
74type testDecorator struct {
75	*binaryDecorator
76	Properties TestProperties
77	testConfig android.Path
78
79	data []android.DataPath
80}
81
82func (test *testDecorator) dataPaths() []android.DataPath {
83	return test.data
84}
85
86func (test *testDecorator) nativeCoverage() bool {
87	return true
88}
89
90func (test *testDecorator) testHarness() bool {
91	return BoolDefault(test.Properties.Test_harness, true)
92}
93
94func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
95	// Build both 32 and 64 targets for device tests.
96	// Cannot build both for host tests yet if the test depends on
97	// something like proc-macro2 that cannot be built for both.
98	multilib := android.MultilibBoth
99	if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
100		multilib = android.MultilibFirst
101	}
102	module := newModule(hod, multilib)
103
104	test := &testDecorator{
105		binaryDecorator: &binaryDecorator{
106			baseCompiler: NewBaseCompiler("nativetest", "nativetest64", InstallInData),
107		},
108	}
109
110	module.compiler = test
111	return module, test
112}
113
114func (test *testDecorator) compilerProps() []interface{} {
115	return append(test.binaryDecorator.compilerProps(), &test.Properties)
116}
117
118func (test *testDecorator) install(ctx ModuleContext) {
119	testInstallBase := "/data/local/tests/unrestricted"
120	if ctx.RustModule().InVendorOrProduct() {
121		testInstallBase = "/data/local/tests/vendor"
122	}
123
124	var configs []tradefed.Config
125	if Bool(test.Properties.Require_root) {
126		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
127	} else {
128		var options []tradefed.Option
129		options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
130		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
131	}
132
133	test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
134		TestConfigProp:         test.Properties.Test_config,
135		TestConfigTemplateProp: test.Properties.Test_config_template,
136		TestSuites:             test.Properties.Test_suites,
137		Config:                 configs,
138		AutoGenConfig:          test.Properties.Auto_gen_config,
139		TestInstallBase:        testInstallBase,
140		DeviceTemplate:         "${RustDeviceTestConfigTemplate}",
141		HostTemplate:           "${RustHostTestConfigTemplate}",
142	})
143
144	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
145
146	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
147		depName := ctx.OtherModuleName(dep)
148		linkableDep, ok := dep.(cc.LinkableInterface)
149		if !ok {
150			ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
151		}
152		if linkableDep.OutputFile().Valid() {
153			// Copy the output in "lib[64]" so that it's compatible with
154			// the default rpath values.
155			libDir := "lib"
156			if linkableDep.Target().Arch.ArchType.Multilib == "lib64" {
157				libDir = "lib64"
158			}
159			test.data = append(test.data,
160				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
161					RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath())})
162		}
163	})
164
165	ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
166		depName := ctx.OtherModuleName(dep)
167		linkableDep, ok := dep.(cc.LinkableInterface)
168		if !ok {
169			ctx.ModuleErrorf("data_bin %q is not a linkable module", depName)
170		}
171		if linkableDep.OutputFile().Valid() {
172			test.data = append(test.data,
173				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
174					RelativeInstallPath: linkableDep.RelativeInstallPath()})
175		}
176	})
177
178	for _, dataSrcPath := range dataSrcPaths {
179		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
180	}
181
182	// default relative install path is module name
183	if !Bool(test.Properties.No_named_install_directory) {
184		test.baseCompiler.relative = ctx.ModuleName()
185	} else if String(test.baseCompiler.Properties.Relative_install_path) == "" {
186		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
187	}
188
189	if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
190		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
191	}
192	test.binaryDecorator.installTestData(ctx, test.data)
193	test.binaryDecorator.install(ctx)
194}
195
196func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
197	flags = test.binaryDecorator.compilerFlags(ctx, flags)
198	if test.testHarness() {
199		flags.RustFlags = append(flags.RustFlags, "--test")
200	}
201	if ctx.Device() {
202		flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
203	}
204
205	return flags
206}
207
208func (test *testDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
209	return rlibAutoDep
210}
211
212func init() {
213	// Rust tests are binary files built with --test.
214	android.RegisterModuleType("rust_test", RustTestFactory)
215	android.RegisterModuleType("rust_test_host", RustTestHostFactory)
216}
217
218func RustTestFactory() android.Module {
219	module, _ := NewRustTest(android.HostAndDeviceSupported)
220
221	// NewRustTest will set MultilibBoth true, however the host variant
222	// cannot produce the non-primary target. Therefore, add the
223	// rustTestHostMultilib load hook to set MultilibFirst for the
224	// host target.
225	android.AddLoadHook(module, rustTestHostMultilib)
226	module.testModule = true
227	return module.Init()
228}
229
230func RustTestHostFactory() android.Module {
231	module, _ := NewRustTest(android.HostSupported)
232	module.testModule = true
233	return module.Init()
234}
235
236func (test *testDecorator) stdLinkage(ctx *depsContext) RustLinkage {
237	return RlibLinkage
238}
239
240func (test *testDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
241	deps = test.binaryDecorator.compilerDeps(ctx, deps)
242
243	deps.Rustlibs = append(deps.Rustlibs, "libtest")
244
245	deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...)
246	deps.DataBins = append(deps.DataBins, test.Properties.Data_bins...)
247
248	return deps
249}
250
251func (test *testDecorator) testBinary() bool {
252	return true
253}
254
255func rustTestHostMultilib(ctx android.LoadHookContext) {
256	type props struct {
257		Target struct {
258			Host struct {
259				Compile_multilib *string
260			}
261		}
262	}
263	p := &props{}
264	p.Target.Host.Compile_multilib = proptools.StringPtr("first")
265	ctx.AppendProperties(p)
266}
267