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