• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 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.
14
15package cc
16
17import (
18	"path/filepath"
19	"strings"
20
21	"android/soong/android"
22	"android/soong/tradefed"
23)
24
25type TestProperties struct {
26	// if set, build against the gtest library. Defaults to true.
27	Gtest *bool
28
29	// if set, use the isolated gtest runner. Defaults to false.
30	Isolated *bool
31}
32
33// Test option struct.
34type TestOptions struct {
35	// The UID that you want to run the test as on a device.
36	Run_test_as *string
37}
38
39type TestBinaryProperties struct {
40	// Create a separate binary for each source file.  Useful when there is
41	// global state that can not be torn down and reset between each test suite.
42	Test_per_src *bool
43
44	// Disables the creation of a test-specific directory when used with
45	// relative_install_path. Useful if several tests need to be in the same
46	// directory, but test_per_src doesn't work.
47	No_named_install_directory *bool
48
49	// list of files or filegroup modules that provide data that should be installed alongside
50	// the test
51	Data []string `android:"path"`
52
53	// list of compatibility suites (for example "cts", "vts") that the module should be
54	// installed into.
55	Test_suites []string `android:"arch_variant"`
56
57	// the name of the test configuration (for example "AndroidTest.xml") that should be
58	// installed with the module.
59	Test_config *string `android:"path,arch_variant"`
60
61	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
62	// should be installed with the module.
63	Test_config_template *string `android:"path,arch_variant"`
64
65	// Test options.
66	Test_options TestOptions
67}
68
69func init() {
70	android.RegisterModuleType("cc_test", TestFactory)
71	android.RegisterModuleType("cc_test_library", TestLibraryFactory)
72	android.RegisterModuleType("cc_benchmark", BenchmarkFactory)
73	android.RegisterModuleType("cc_test_host", TestHostFactory)
74	android.RegisterModuleType("cc_benchmark_host", BenchmarkHostFactory)
75}
76
77// cc_test generates a test config file and an executable binary file to test
78// specific functionality on a device. The executable binary gets an implicit
79// static_libs dependency on libgtests unless the gtest flag is set to false.
80func TestFactory() android.Module {
81	module := NewTest(android.HostAndDeviceSupported)
82	return module.Init()
83}
84
85// cc_test_library creates an archive of files (i.e. .o files) which is later
86// referenced by another module (such as cc_test, cc_defaults or cc_test_library)
87// for archiving or linking.
88func TestLibraryFactory() android.Module {
89	module := NewTestLibrary(android.HostAndDeviceSupported)
90	return module.Init()
91}
92
93// cc_benchmark compiles an executable binary that performs benchmark testing
94// of a specific component in a device. Additional files such as test suites
95// and test configuration are installed on the side of the compiled executed
96// binary.
97func BenchmarkFactory() android.Module {
98	module := NewBenchmark(android.HostAndDeviceSupported)
99	return module.Init()
100}
101
102// cc_test_host compiles a test host binary.
103func TestHostFactory() android.Module {
104	module := NewTest(android.HostSupported)
105	return module.Init()
106}
107
108// cc_benchmark_host compiles an executable binary that performs benchmark
109// testing of a specific component in the host. Additional files such as
110// test suites and test configuration are installed on the side of the
111// compiled executed binary.
112func BenchmarkHostFactory() android.Module {
113	module := NewBenchmark(android.HostSupported)
114	return module.Init()
115}
116
117type testPerSrc interface {
118	testPerSrc() bool
119	srcs() []string
120	setSrc(string, string)
121}
122
123func (test *testBinary) testPerSrc() bool {
124	return Bool(test.Properties.Test_per_src)
125}
126
127func (test *testBinary) srcs() []string {
128	return test.baseCompiler.Properties.Srcs
129}
130
131func (test *testBinary) setSrc(name, src string) {
132	test.baseCompiler.Properties.Srcs = []string{src}
133	test.binaryDecorator.Properties.Stem = StringPtr(name)
134}
135
136var _ testPerSrc = (*testBinary)(nil)
137
138func testPerSrcMutator(mctx android.BottomUpMutatorContext) {
139	if m, ok := mctx.Module().(*Module); ok {
140		if test, ok := m.linker.(testPerSrc); ok {
141			if test.testPerSrc() && len(test.srcs()) > 0 {
142				if duplicate, found := checkDuplicate(test.srcs()); found {
143					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
144					return
145				}
146				testNames := make([]string, len(test.srcs()))
147				for i, src := range test.srcs() {
148					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
149				}
150				tests := mctx.CreateLocalVariations(testNames...)
151				for i, src := range test.srcs() {
152					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
153				}
154			}
155		}
156	}
157}
158
159func checkDuplicate(values []string) (duplicate string, found bool) {
160	seen := make(map[string]string)
161	for _, v := range values {
162		if duplicate, found = seen[v]; found {
163			return
164		}
165		seen[v] = v
166	}
167	return
168}
169
170type testDecorator struct {
171	Properties TestProperties
172	linker     *baseLinker
173}
174
175func (test *testDecorator) gtest() bool {
176	return BoolDefault(test.Properties.Gtest, true)
177}
178
179func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
180	if !test.gtest() {
181		return flags
182	}
183
184	flags.CFlags = append(flags.CFlags, "-DGTEST_HAS_STD_STRING")
185	if ctx.Host() {
186		flags.CFlags = append(flags.CFlags, "-O0", "-g")
187
188		switch ctx.Os() {
189		case android.Windows:
190			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_WINDOWS")
191		case android.Linux:
192			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX")
193		case android.Darwin:
194			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_MAC")
195		}
196	} else {
197		flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX_ANDROID")
198	}
199
200	return flags
201}
202
203func (test *testDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
204	if test.gtest() {
205		if ctx.useSdk() && ctx.Device() {
206			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
207		} else if BoolDefault(test.Properties.Isolated, false) {
208			deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
209		} else {
210			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
211		}
212	}
213
214	return deps
215}
216
217func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
218	// 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
219	// find out/host/linux-x86/lib[64]/library.so
220	// 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
221	// also find out/host/linux-x86/lib[64]/library.so
222	runpaths := []string{"../../lib", "../../../lib"}
223	for _, runpath := range runpaths {
224		if ctx.toolchain().Is64Bit() {
225			runpath += "64"
226		}
227		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
228	}
229
230	// add "" to rpath so that test binaries can find libraries in their own test directory
231	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
232}
233
234func (test *testDecorator) linkerProps() []interface{} {
235	return []interface{}{&test.Properties}
236}
237
238func NewTestInstaller() *baseInstaller {
239	return NewBaseInstaller("nativetest", "nativetest64", InstallInData)
240}
241
242type testBinary struct {
243	testDecorator
244	*binaryDecorator
245	*baseCompiler
246	Properties TestBinaryProperties
247	data       android.Paths
248	testConfig android.Path
249}
250
251func (test *testBinary) linkerProps() []interface{} {
252	props := append(test.testDecorator.linkerProps(), test.binaryDecorator.linkerProps()...)
253	props = append(props, &test.Properties)
254	return props
255}
256
257func (test *testBinary) linkerInit(ctx BaseModuleContext) {
258	test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
259	test.binaryDecorator.linkerInit(ctx)
260}
261
262func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
263	deps = test.testDecorator.linkerDeps(ctx, deps)
264	deps = test.binaryDecorator.linkerDeps(ctx, deps)
265	return deps
266}
267
268func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
269	flags = test.binaryDecorator.linkerFlags(ctx, flags)
270	flags = test.testDecorator.linkerFlags(ctx, flags)
271	return flags
272}
273
274func (test *testBinary) install(ctx ModuleContext, file android.Path) {
275	test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
276	optionsMap := map[string]string{}
277	if Bool(test.testDecorator.Properties.Isolated) {
278		optionsMap["not-shardable"] = "true"
279	}
280
281	if test.Properties.Test_options.Run_test_as != nil {
282		optionsMap["run-test-as"] = String(test.Properties.Test_options.Run_test_as)
283	}
284
285	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
286		test.Properties.Test_config_template,
287		test.Properties.Test_suites, optionsMap)
288
289	test.binaryDecorator.baseInstaller.dir = "nativetest"
290	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
291
292	if !Bool(test.Properties.No_named_install_directory) {
293		test.binaryDecorator.baseInstaller.relative = ctx.ModuleName()
294	} else if String(test.binaryDecorator.baseInstaller.Properties.Relative_install_path) == "" {
295		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
296	}
297
298	test.binaryDecorator.baseInstaller.install(ctx, file)
299}
300
301func NewTest(hod android.HostOrDeviceSupported) *Module {
302	module, binary := NewBinary(hod)
303	module.multilib = android.MultilibBoth
304	binary.baseInstaller = NewTestInstaller()
305
306	test := &testBinary{
307		testDecorator: testDecorator{
308			linker: binary.baseLinker,
309		},
310		binaryDecorator: binary,
311		baseCompiler:    NewBaseCompiler(),
312	}
313	module.compiler = test
314	module.linker = test
315	module.installer = test
316	return module
317}
318
319type testLibrary struct {
320	testDecorator
321	*libraryDecorator
322}
323
324func (test *testLibrary) linkerProps() []interface{} {
325	return append(test.testDecorator.linkerProps(), test.libraryDecorator.linkerProps()...)
326}
327
328func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
329	test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
330	test.libraryDecorator.linkerInit(ctx)
331}
332
333func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
334	deps = test.testDecorator.linkerDeps(ctx, deps)
335	deps = test.libraryDecorator.linkerDeps(ctx, deps)
336	return deps
337}
338
339func (test *testLibrary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
340	flags = test.libraryDecorator.linkerFlags(ctx, flags)
341	flags = test.testDecorator.linkerFlags(ctx, flags)
342	return flags
343}
344
345func NewTestLibrary(hod android.HostOrDeviceSupported) *Module {
346	module, library := NewLibrary(android.HostAndDeviceSupported)
347	library.baseInstaller = NewTestInstaller()
348	test := &testLibrary{
349		testDecorator: testDecorator{
350			linker: library.baseLinker,
351		},
352		libraryDecorator: library,
353	}
354	module.linker = test
355	return module
356}
357
358type BenchmarkProperties struct {
359	// list of files or filegroup modules that provide data that should be installed alongside
360	// the test
361	Data []string `android:"path"`
362
363	// list of compatibility suites (for example "cts", "vts") that the module should be
364	// installed into.
365	Test_suites []string `android:"arch_variant"`
366
367	// the name of the test configuration (for example "AndroidTest.xml") that should be
368	// installed with the module.
369	Test_config *string `android:"path,arch_variant"`
370
371	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
372	// should be installed with the module.
373	Test_config_template *string `android:"path,arch_variant"`
374}
375
376type benchmarkDecorator struct {
377	*binaryDecorator
378	Properties BenchmarkProperties
379	data       android.Paths
380	testConfig android.Path
381}
382
383func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
384	runpath := "../../lib"
385	if ctx.toolchain().Is64Bit() {
386		runpath += "64"
387	}
388	benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
389	benchmark.binaryDecorator.linkerInit(ctx)
390}
391
392func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
393	props := benchmark.binaryDecorator.linkerProps()
394	props = append(props, &benchmark.Properties)
395	return props
396}
397
398func (benchmark *benchmarkDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
399	deps = benchmark.binaryDecorator.linkerDeps(ctx, deps)
400	deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark")
401	return deps
402}
403
404func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
405	benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
406	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
407		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites)
408
409	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
410	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
411	benchmark.binaryDecorator.baseInstaller.install(ctx, file)
412}
413
414func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
415	module, binary := NewBinary(hod)
416	module.multilib = android.MultilibBoth
417	binary.baseInstaller = NewBaseInstaller("benchmarktest", "benchmarktest64", InstallInData)
418
419	benchmark := &benchmarkDecorator{
420		binaryDecorator: binary,
421	}
422	module.linker = benchmark
423	module.installer = benchmark
424	return module
425}
426