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 will add dependencies on the device's 50 Device_common_data []string `android:"path_device_common"` 51 52 // list of shared library modules that should be installed alongside the test 53 Data_libs []string `android:"arch_variant"` 54 55 // list of binary modules that should be installed alongside the test 56 Data_bins []string `android:"arch_variant"` 57 58 // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml 59 // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true 60 // explicitly. 61 Auto_gen_config *bool 62 63 // if set, build with the standard Rust test harness. Defaults to true. 64 Test_harness *bool 65 66 // Test options. 67 Test_options android.CommonTestOptions 68 69 // Add RootTargetPreparer to auto generated test config. This guarantees the test to run 70 // with root permission. 71 Require_root *bool 72} 73 74// A test module is a binary module with extra --test compiler flag 75// and different default installation directory. 76// In golang, inheriance is written as a component. 77type testDecorator struct { 78 *binaryDecorator 79 Properties TestProperties 80 testConfig android.Path 81 82 data []android.DataPath 83} 84 85func (test *testDecorator) dataPaths() []android.DataPath { 86 return test.data 87} 88 89func (test *testDecorator) nativeCoverage() bool { 90 return true 91} 92 93func (test *testDecorator) testHarness() bool { 94 return BoolDefault(test.Properties.Test_harness, true) 95} 96 97func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { 98 // Build both 32 and 64 targets for device tests. 99 // Cannot build both for host tests yet if the test depends on 100 // something like proc-macro2 that cannot be built for both. 101 multilib := android.MultilibBoth 102 if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported { 103 multilib = android.MultilibFirst 104 } 105 module := newModule(hod, multilib) 106 107 test := &testDecorator{ 108 binaryDecorator: &binaryDecorator{ 109 baseCompiler: NewBaseCompiler("nativetest", "nativetest64", InstallInData), 110 }, 111 } 112 113 module.compiler = test 114 return module, test 115} 116 117func (test *testDecorator) compilerProps() []interface{} { 118 return append(test.binaryDecorator.compilerProps(), &test.Properties) 119} 120 121func (test *testDecorator) install(ctx ModuleContext) { 122 // TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base. 123 testInstallBase := "/data/local/tmp" 124 if ctx.RustModule().InVendorOrProduct() { 125 testInstallBase = "/data/local/tests/vendor" 126 } 127 128 var configs []tradefed.Config 129 if Bool(test.Properties.Require_root) { 130 configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil}) 131 } else { 132 var options []tradefed.Option 133 options = append(options, tradefed.Option{Name: "force-root", Value: "false"}) 134 configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options}) 135 } 136 137 test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ 138 TestConfigProp: test.Properties.Test_config, 139 TestConfigTemplateProp: test.Properties.Test_config_template, 140 TestSuites: test.Properties.Test_suites, 141 Config: configs, 142 AutoGenConfig: test.Properties.Auto_gen_config, 143 TestInstallBase: testInstallBase, 144 DeviceTemplate: "${RustDeviceTestConfigTemplate}", 145 HostTemplate: "${RustHostTestConfigTemplate}", 146 }) 147 148 dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data) 149 dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...) 150 151 ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) { 152 depName := ctx.OtherModuleName(dep) 153 linkableDep, ok := dep.(cc.LinkableInterface) 154 if !ok { 155 ctx.ModuleErrorf("data_lib %q is not a linkable module", depName) 156 } 157 if linkableDep.OutputFile().Valid() { 158 // Copy the output in "lib[64]" so that it's compatible with 159 // the default rpath values. 160 libDir := "lib" 161 if linkableDep.Target().Arch.ArchType.Multilib == "lib64" { 162 libDir = "lib64" 163 } 164 test.data = append(test.data, 165 android.DataPath{SrcPath: linkableDep.OutputFile().Path(), 166 RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath())}) 167 } 168 }) 169 170 ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) { 171 depName := ctx.OtherModuleName(dep) 172 linkableDep, ok := dep.(cc.LinkableInterface) 173 if !ok { 174 ctx.ModuleErrorf("data_bin %q is not a linkable module", depName) 175 } 176 if linkableDep.OutputFile().Valid() { 177 test.data = append(test.data, 178 android.DataPath{SrcPath: linkableDep.OutputFile().Path(), 179 RelativeInstallPath: linkableDep.RelativeInstallPath()}) 180 } 181 }) 182 183 for _, dataSrcPath := range dataSrcPaths { 184 test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath}) 185 } 186 187 // default relative install path is module name 188 if !Bool(test.Properties.No_named_install_directory) { 189 test.baseCompiler.relative = ctx.ModuleName() 190 } else if String(test.baseCompiler.Properties.Relative_install_path) == "" { 191 ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set") 192 } 193 194 if ctx.Host() && test.Properties.Test_options.Unit_test == nil { 195 test.Properties.Test_options.Unit_test = proptools.BoolPtr(true) 196 } 197 test.binaryDecorator.installTestData(ctx, test.data) 198 test.binaryDecorator.install(ctx) 199} 200 201func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { 202 flags = test.binaryDecorator.compilerFlags(ctx, flags) 203 if test.testHarness() { 204 flags.RustFlags = append(flags.RustFlags, "--test") 205 } 206 if ctx.Device() { 207 flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests") 208 } 209 210 return flags 211} 212 213func (test *testDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep { 214 return rlibAutoDep 215} 216 217func init() { 218 // Rust tests are binary files built with --test. 219 android.RegisterModuleType("rust_test", RustTestFactory) 220 android.RegisterModuleType("rust_test_host", RustTestHostFactory) 221} 222 223func RustTestFactory() android.Module { 224 module, _ := NewRustTest(android.HostAndDeviceSupported) 225 226 // NewRustTest will set MultilibBoth true, however the host variant 227 // cannot produce the non-primary target. Therefore, add the 228 // rustTestHostMultilib load hook to set MultilibFirst for the 229 // host target. 230 android.AddLoadHook(module, rustTestHostMultilib) 231 module.testModule = true 232 return module.Init() 233} 234 235func RustTestHostFactory() android.Module { 236 module, _ := NewRustTest(android.HostSupported) 237 module.testModule = true 238 return module.Init() 239} 240 241func (test *testDecorator) stdLinkage(ctx *depsContext) RustLinkage { 242 return RlibLinkage 243} 244 245func (test *testDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps { 246 deps = test.binaryDecorator.compilerDeps(ctx, deps) 247 248 deps.Rustlibs = append(deps.Rustlibs, "libtest") 249 250 deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...) 251 deps.DataBins = append(deps.DataBins, test.Properties.Data_bins...) 252 253 return deps 254} 255 256func (test *testDecorator) testBinary() bool { 257 return true 258} 259 260func rustTestHostMultilib(ctx android.LoadHookContext) { 261 type props struct { 262 Target struct { 263 Host struct { 264 Compile_multilib *string 265 } 266 } 267 } 268 p := &props{} 269 p.Target.Host.Compile_multilib = proptools.StringPtr("first") 270 ctx.AppendProperties(p) 271} 272