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