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