// Copyright 2019 The Android Open Source Project // // 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 rust import ( "path/filepath" "strings" "android/soong/android" "android/soong/tradefed" ) type TestProperties struct { // the name of the test configuration (for example "AndroidTest.xml") that should be // installed with the module. Test_config *string `android:"arch_variant"` // the name of the test configuration template (for example "AndroidTestTemplate.xml") that // should be installed with the module. Test_config_template *string `android:"arch_variant"` // list of compatibility suites (for example "cts", "vts") that the module should be // installed into. Test_suites []string `android:"arch_variant"` // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true // explicitly. Auto_gen_config *bool } // A test module is a binary module with extra --test compiler flag // and different default installation directory. // In golang, inheriance is written as a component. type testDecorator struct { *binaryDecorator Properties TestProperties testConfig android.Path } func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { module := newModule(hod, android.MultilibFirst) test := &testDecorator{ binaryDecorator: &binaryDecorator{ // TODO(chh): set up dir64? baseCompiler: NewBaseCompiler("testcases", ""), }, } module.compiler = test return module, test } func (test *testDecorator) compilerProps() []interface{} { return append(test.binaryDecorator.compilerProps(), &test.Properties) } func (test *testDecorator) getMutatedModuleSubName(moduleName string) string { stem := String(test.baseCompiler.Properties.Stem) if stem != "" && !strings.HasSuffix(moduleName, "_"+stem) { // Avoid repeated suffix in the module name. return "_" + stem } return "" } func (test *testDecorator) install(ctx ModuleContext, file android.Path) { name := ctx.ModuleName() // default executable name if ctx.Device() { // on device, use mutated module name name = name + test.getMutatedModuleSubName(name) } else { // on host, use stem name in relative_install_path if stem := String(test.baseCompiler.Properties.Stem); stem != "" { name = stem } if path := test.baseCompiler.relativeInstallPath(); path != "" { name = path + "/" + name } } test.testConfig = tradefed.AutoGenRustTestConfig(ctx, name, test.Properties.Test_config, test.Properties.Test_config_template, test.Properties.Test_suites, test.Properties.Auto_gen_config) test.binaryDecorator.install(ctx, file) } func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { flags = test.binaryDecorator.compilerFlags(ctx, flags) flags.RustFlags = append(flags.RustFlags, "--test") return flags } func init() { // Rust tests are binary files built with --test. android.RegisterModuleType("rust_test", RustTestFactory) android.RegisterModuleType("rust_test_host", RustTestHostFactory) } func RustTestFactory() android.Module { module, _ := NewRustTest(android.HostAndDeviceSupported) return module.Init() } func RustTestHostFactory() android.Module { module, _ := NewRustTest(android.HostSupported) return module.Init() } func (test *testDecorator) testPerSrc() bool { return true } func (test *testDecorator) srcs() []string { return test.binaryDecorator.Properties.Srcs } func (test *testDecorator) setSrc(name, src string) { test.binaryDecorator.Properties.Srcs = []string{src} test.baseCompiler.Properties.Stem = StringPtr(name) } func (test *testDecorator) unsetSrc() { test.binaryDecorator.Properties.Srcs = nil test.baseCompiler.Properties.Stem = StringPtr("") } type testPerSrc interface { testPerSrc() bool srcs() []string setSrc(string, string) unsetSrc() } var _ testPerSrc = (*testDecorator)(nil) func TestPerSrcMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*Module); ok { if test, ok := m.compiler.(testPerSrc); ok { numTests := len(test.srcs()) if test.testPerSrc() && numTests > 0 { if duplicate, found := android.CheckDuplicate(test.srcs()); found { mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate) return } // Rust compiler always compiles one source file at a time and // uses the crate name as output file name. // Cargo uses the test source file name as default crate name, // but that can be redefined. // So when there are multiple source files, the source file names will // be the output file names, but when there is only one test file, // use the crate name. testNames := make([]string, numTests) for i, src := range test.srcs() { testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src)) } crateName := m.compiler.crateName() if numTests == 1 && crateName != "" { testNames[0] = crateName } // TODO(chh): Add an "all tests" variation like cc/test.go? tests := mctx.CreateLocalVariations(testNames...) for i, src := range test.srcs() { tests[i].(*Module).compiler.(testPerSrc).setSrc(testNames[i], src) } } } } }