Files
build_soong/tradefed/autogen.go
Cole Faust 8ec823cba1 Allow adding extra tradefed options in the Android.bp file
Some tests need to add custom tradefed options, but still want to
keep most of the soong autogenerated tradefed xml file.

Expose a test_options: { tradefed_options: [...] } property that
will allow tests to add more options to the autogenerated xml file.

Fixes: 184895128
Test: go test, and verified that the ninja files did not change for aosp_arm64
Change-Id: I75f7eb002c8325ce7cdc76e12e76e16195320620
2022-12-09 15:23:26 -08:00

302 lines
10 KiB
Go

// Copyright 2018 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 tradefed
import (
"fmt"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
)
const test_xml_indent = " "
func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath {
return ctx.ExpandOptionalSource(prop, "test_config_template")
}
func getTestConfig(ctx android.ModuleContext, prop *string) android.Path {
if p := ctx.ExpandOptionalSource(prop, "test_config"); p.Valid() {
return p.Path()
} else if p := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml"); p.Valid() {
return p.Path()
}
return nil
}
var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out",
CommandDeps: []string{"$template"},
}, "name", "template", "extraConfigs", "outputFileName", "testInstallBase")
func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) {
p := getTestConfig(ctx, prop)
if !Bool(autoGenConfig) && p != nil {
return p, nil
} else if BoolDefault(autoGenConfig, true) && (!android.InList("cts", testSuites) || testConfigTemplateProp != nil) {
outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
return nil, outputFile
} else {
// CTS modules can be used for test data, so test config files must be
// explicitly created using AndroidTest.xml or test_config_template.
return nil, nil
}
}
type Config interface {
Config() string
}
type Option struct {
Name string
Key string
Value string
}
var _ Config = Option{}
func (o Option) Config() string {
if o.Key != "" {
return fmt.Sprintf(`<option name="%s" key="%s" value="%s" />`, o.Name, o.Key, o.Value)
}
return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
}
// It can be a template of object or target_preparer.
type Object struct {
// Set it as a target_preparer if object type == "target_preparer".
Type string
Class string
Options []Option
}
var _ Config = Object{}
func (ob Object) Config() string {
var optionStrings []string
for _, option := range ob.Options {
optionStrings = append(optionStrings, option.Config())
}
var options string
if len(ob.Options) == 0 {
options = ""
} else {
optionDelimiter := fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
options = optionDelimiter + strings.Join(optionStrings, optionDelimiter)
}
if ob.Type == "target_preparer" {
return fmt.Sprintf(`<target_preparer class="%s">%s\n%s</target_preparer>`, ob.Class, options, test_xml_indent)
} else {
return fmt.Sprintf(`<object type="%s" class="%s">%s\n%s</object>`, ob.Type, ob.Class, options, test_xml_indent)
}
}
// MaybeAutoGenTestConfigBuilder provides a Build() method that will either
// generate a AndroidTest.xml file, or use an existing user-supplied one.
// It used to be a bunch of separate functions for each language, but was
// converted to this builder pattern to have one function that accepts many
// optional arguments.
type MaybeAutoGenTestConfigBuilder struct {
ctx android.ModuleContext
name string
outputFileName string
testConfigProp *string
testConfigTemplateProp *string
testSuites []string
config []Config
configsForAutogenerated []Config
autoGenConfig *bool
unitTest *bool
testInstallBase string
deviceTemplate string
hostTemplate string
hostUnitTestTemplate string
}
func NewMaybeAutoGenTestConfigBuilder(ctx android.ModuleContext) *MaybeAutoGenTestConfigBuilder {
return &MaybeAutoGenTestConfigBuilder{
ctx: ctx,
name: ctx.ModuleName(),
}
}
func (b *MaybeAutoGenTestConfigBuilder) SetName(name string) *MaybeAutoGenTestConfigBuilder {
b.name = name
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetOutputFileName(outputFileName string) *MaybeAutoGenTestConfigBuilder {
b.outputFileName = outputFileName
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetTestConfigProp(testConfigProp *string) *MaybeAutoGenTestConfigBuilder {
b.testConfigProp = testConfigProp
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetTestTemplateConfigProp(testConfigTemplateProp *string) *MaybeAutoGenTestConfigBuilder {
b.testConfigTemplateProp = testConfigTemplateProp
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetTestSuites(testSuites []string) *MaybeAutoGenTestConfigBuilder {
b.testSuites = testSuites
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetConfig(config []Config) *MaybeAutoGenTestConfigBuilder {
b.config = config
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetOptionsForAutogenerated(configsForAutogenerated []Option) *MaybeAutoGenTestConfigBuilder {
configs := make([]Config, 0, len(configsForAutogenerated))
for _, c := range configsForAutogenerated {
configs = append(configs, c)
}
b.configsForAutogenerated = configs
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetUnitTest(unitTest *bool) *MaybeAutoGenTestConfigBuilder {
b.unitTest = unitTest
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetAutoGenConfig(autoGenConfig *bool) *MaybeAutoGenTestConfigBuilder {
b.autoGenConfig = autoGenConfig
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetTestInstallBase(testInstallBase string) *MaybeAutoGenTestConfigBuilder {
b.testInstallBase = testInstallBase
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetDeviceTemplate(deviceTemplate string) *MaybeAutoGenTestConfigBuilder {
b.deviceTemplate = deviceTemplate
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetHostTemplate(hostTemplate string) *MaybeAutoGenTestConfigBuilder {
b.hostTemplate = hostTemplate
return b
}
func (b *MaybeAutoGenTestConfigBuilder) SetHostUnitTestTemplate(hostUnitTestTemplate string) *MaybeAutoGenTestConfigBuilder {
b.hostUnitTestTemplate = hostUnitTestTemplate
return b
}
func (b *MaybeAutoGenTestConfigBuilder) Build() android.Path {
config := append(b.config, b.configsForAutogenerated...)
path, autogenPath := testConfigPath(b.ctx, b.testConfigProp, b.testSuites, b.autoGenConfig, b.testConfigTemplateProp)
if autogenPath != nil {
templatePath := getTestConfigTemplate(b.ctx, b.testConfigTemplateProp)
if templatePath.Valid() {
autogenTemplate(b.ctx, b.name, autogenPath, templatePath.String(), config, b.outputFileName, b.testInstallBase)
} else {
if b.ctx.Device() {
autogenTemplate(b.ctx, b.name, autogenPath, b.deviceTemplate, config, b.outputFileName, b.testInstallBase)
} else {
if Bool(b.unitTest) {
autogenTemplate(b.ctx, b.name, autogenPath, b.hostUnitTestTemplate, config, b.outputFileName, b.testInstallBase)
} else {
autogenTemplate(b.ctx, b.name, autogenPath, b.hostTemplate, config, b.outputFileName, b.testInstallBase)
}
}
}
return autogenPath
}
if len(b.configsForAutogenerated) > 0 {
b.ctx.ModuleErrorf("Extra tradefed configurations were provided for an autogenerated xml file, but the autogenerated xml file was not used.")
}
return path
}
func autogenTemplate(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string, testInstallBase string) {
if template == "" {
ctx.ModuleErrorf("Empty template")
}
var configStrings []string
for _, config := range configs {
configStrings = append(configStrings, config.Config())
}
extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
ctx.Build(pctx, android.BuildParams{
Rule: autogenTestConfig,
Description: "test config",
Output: output,
Args: map[string]string{
"name": name,
"template": template,
"extraConfigs": extraConfigs,
"outputFileName": outputFileName,
"testInstallBase": testInstallBase,
},
})
}
var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
CommandDeps: []string{
"${AutoGenTestConfigScript}",
"${EmptyTestConfig}",
"$template",
},
}, "name", "template", "extraConfigs")
func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
var configStrings []string
if autogenPath != nil {
template := "${InstrumentationTestConfigTemplate}"
moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
if moduleTemplate.Valid() {
template = moduleTemplate.String()
}
for _, config := range configs {
configStrings = append(configStrings, config.Config())
}
extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs)
ctx.Build(pctx, android.BuildParams{
Rule: autogenInstrumentationTest,
Description: "test config",
Input: manifest,
Output: autogenPath,
Args: map[string]string{
"name": ctx.ModuleName(),
"template": template,
"extraConfigs": extraConfigs,
},
})
return autogenPath
}
return path
}
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault