// Copyright 2016 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cc import ( "path/filepath" "strings" "github.com/google/blueprint" "android/soong" "android/soong/android" ) type TestLinkerProperties struct { // if set, build against the gtest library. Defaults to true. Gtest bool // Create a separate binary for each source file. Useful when there is // global state that can not be torn down and reset between each test suite. Test_per_src *bool } func init() { soong.RegisterModuleType("cc_test", testFactory) soong.RegisterModuleType("cc_test_library", testLibraryFactory) soong.RegisterModuleType("cc_benchmark", benchmarkFactory) soong.RegisterModuleType("cc_test_host", testHostFactory) soong.RegisterModuleType("cc_benchmark_host", benchmarkHostFactory) } // Module factory for tests func testFactory() (blueprint.Module, []interface{}) { module := NewTest(android.HostAndDeviceSupported) return module.Init() } // Module factory for test libraries func testLibraryFactory() (blueprint.Module, []interface{}) { module := NewTestLibrary(android.HostAndDeviceSupported) return module.Init() } // Module factory for benchmarks func benchmarkFactory() (blueprint.Module, []interface{}) { module := NewBenchmark(android.HostAndDeviceSupported) return module.Init() } // Module factory for host tests func testHostFactory() (blueprint.Module, []interface{}) { module := NewTest(android.HostSupported) return module.Init() } // Module factory for host benchmarks func benchmarkHostFactory() (blueprint.Module, []interface{}) { module := NewBenchmark(android.HostSupported) return module.Init() } func testPerSrcMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*Module); ok { if test, ok := m.linker.(*testBinaryLinker); ok { if Bool(test.testLinker.Properties.Test_per_src) { testNames := make([]string, len(m.compiler.(*baseCompiler).Properties.Srcs)) for i, src := range m.compiler.(*baseCompiler).Properties.Srcs { testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) } tests := mctx.CreateLocalVariations(testNames...) for i, src := range m.compiler.(*baseCompiler).Properties.Srcs { tests[i].(*Module).compiler.(*baseCompiler).Properties.Srcs = []string{src} tests[i].(*Module).linker.(*testBinaryLinker).binaryLinker.Properties.Stem = testNames[i] } } } } } type testLinker struct { Properties TestLinkerProperties } func (test *testLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { if !test.Properties.Gtest { return flags } flags.CFlags = append(flags.CFlags, "-DGTEST_HAS_STD_STRING") if ctx.Host() { flags.CFlags = append(flags.CFlags, "-O0", "-g") switch ctx.Os() { case android.Windows: flags.CFlags = append(flags.CFlags, "-DGTEST_OS_WINDOWS") case android.Linux: flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX") flags.LdFlags = append(flags.LdFlags, "-lpthread") case android.Darwin: flags.CFlags = append(flags.CFlags, "-DGTEST_OS_MAC") flags.LdFlags = append(flags.LdFlags, "-lpthread") } } else { flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX_ANDROID") } return flags } func (test *testLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps { if test.Properties.Gtest { if ctx.sdk() && ctx.Device() { switch ctx.selectedStl() { case "ndk_libc++_shared", "ndk_libc++_static": deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_libcxx", "libgtest_ndk_libcxx") case "ndk_libgnustl_static": deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_gnustl", "libgtest_ndk_gnustl") default: deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk", "libgtest_ndk") } } else { deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest") } } return deps } type testBinaryLinker struct { testLinker binaryLinker } func (test *testBinaryLinker) linkerInit(ctx BaseModuleContext) { test.binaryLinker.linkerInit(ctx) runpath := "../../lib" if ctx.toolchain().Is64Bit() { runpath += "64" } test.dynamicProperties.RunPaths = append([]string{runpath}, test.dynamicProperties.RunPaths...) } func (test *testBinaryLinker) linkerProps() []interface{} { return append(test.binaryLinker.linkerProps(), &test.testLinker.Properties) } func (test *testBinaryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags = test.binaryLinker.linkerFlags(ctx, flags) flags = test.testLinker.linkerFlags(ctx, flags) return flags } func (test *testBinaryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps { deps = test.testLinker.linkerDeps(ctx, deps) deps = test.binaryLinker.linkerDeps(ctx, deps) return deps } type testLibraryLinker struct { testLinker *libraryLinker } func (test *testLibraryLinker) linkerProps() []interface{} { return append(test.libraryLinker.linkerProps(), &test.testLinker.Properties) } func (test *testLibraryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags = test.libraryLinker.linkerFlags(ctx, flags) flags = test.testLinker.linkerFlags(ctx, flags) return flags } func (test *testLibraryLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps { deps = test.testLinker.linkerDeps(ctx, deps) deps = test.libraryLinker.linkerDeps(ctx, deps) return deps } type testInstaller struct { baseInstaller } func (installer *testInstaller) install(ctx ModuleContext, file android.Path) { installer.dir = filepath.Join(installer.dir, ctx.ModuleName()) installer.dir64 = filepath.Join(installer.dir64, ctx.ModuleName()) installer.baseInstaller.install(ctx, file) } func NewTest(hod android.HostOrDeviceSupported) *Module { module := newModule(hod, android.MultilibBoth) module.compiler = &baseCompiler{} linker := &testBinaryLinker{} linker.testLinker.Properties.Gtest = true module.linker = linker module.installer = &testInstaller{ baseInstaller: baseInstaller{ dir: "nativetest", dir64: "nativetest64", data: true, }, } return module } func NewTestLibrary(hod android.HostOrDeviceSupported) *Module { module := NewLibrary(android.HostAndDeviceSupported, false, true) linker := &testLibraryLinker{ libraryLinker: module.linker.(*libraryLinker), } linker.testLinker.Properties.Gtest = true module.linker = linker module.installer = &testInstaller{ baseInstaller: baseInstaller{ dir: "nativetest", dir64: "nativetest64", data: true, }, } return module } type benchmarkLinker struct { testBinaryLinker } func (benchmark *benchmarkLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps { deps = benchmark.testBinaryLinker.linkerDeps(ctx, deps) deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark") return deps } func NewBenchmark(hod android.HostOrDeviceSupported) *Module { module := newModule(hod, android.MultilibFirst) module.compiler = &baseCompiler{} module.linker = &benchmarkLinker{} module.installer = &testInstaller{ baseInstaller: baseInstaller{ dir: "nativetest", dir64: "nativetest64", data: true, }, } return module }