autogen.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2018 Google Inc. All rights reserved.
  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. package tradefed
  15. import (
  16. "fmt"
  17. "strings"
  18. "github.com/google/blueprint"
  19. "github.com/google/blueprint/proptools"
  20. "android/soong/android"
  21. )
  22. const test_xml_indent = " "
  23. func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath {
  24. return ctx.ExpandOptionalSource(prop, "test_config_template")
  25. }
  26. func getTestConfig(ctx android.ModuleContext, prop *string) android.Path {
  27. if p := ctx.ExpandOptionalSource(prop, "test_config"); p.Valid() {
  28. return p.Path()
  29. } else if p := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml"); p.Valid() {
  30. return p.Path()
  31. }
  32. return nil
  33. }
  34. var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
  35. Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{EXTRA_TEST_RUNNER_CONFIGS}&'${extraTestRunnerConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out",
  36. CommandDeps: []string{"$template"},
  37. }, "name", "template", "extraConfigs", "outputFileName", "testInstallBase", "extraTestRunnerConfigs")
  38. func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) {
  39. p := getTestConfig(ctx, prop)
  40. if !Bool(autoGenConfig) && p != nil {
  41. return p, nil
  42. } else if BoolDefault(autoGenConfig, true) && (!android.InList("cts", testSuites) || testConfigTemplateProp != nil) {
  43. outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
  44. return nil, outputFile
  45. } else {
  46. // CTS modules can be used for test data, so test config files must be
  47. // explicitly created using AndroidTest.xml or test_config_template.
  48. return nil, nil
  49. }
  50. }
  51. type Config interface {
  52. Config() string
  53. }
  54. type Option struct {
  55. Name string
  56. Key string
  57. Value string
  58. }
  59. var _ Config = Option{}
  60. func (o Option) Config() string {
  61. if o.Key != "" {
  62. return fmt.Sprintf(`<option name="%s" key="%s" value="%s" />`, o.Name, o.Key, o.Value)
  63. }
  64. return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
  65. }
  66. // It can be a template of object or target_preparer.
  67. type Object struct {
  68. // Set it as a target_preparer if object type == "target_preparer".
  69. Type string
  70. Class string
  71. Options []Option
  72. }
  73. var _ Config = Object{}
  74. func (ob Object) Config() string {
  75. var optionStrings []string
  76. for _, option := range ob.Options {
  77. optionStrings = append(optionStrings, option.Config())
  78. }
  79. var options string
  80. if len(ob.Options) == 0 {
  81. options = ""
  82. } else {
  83. optionDelimiter := fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
  84. options = optionDelimiter + strings.Join(optionStrings, optionDelimiter)
  85. }
  86. if ob.Type == "target_preparer" {
  87. return fmt.Sprintf(`<target_preparer class="%s">%s\n%s</target_preparer>`, ob.Class, options, test_xml_indent)
  88. } else {
  89. return fmt.Sprintf(`<object type="%s" class="%s">%s\n%s</object>`, ob.Type, ob.Class, options, test_xml_indent)
  90. }
  91. }
  92. func autogenTemplate(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, testRunnerConfigs []Option, outputFileName string, testInstallBase string) {
  93. if template == "" {
  94. ctx.ModuleErrorf("Empty template")
  95. }
  96. var configStrings []string
  97. for _, config := range configs {
  98. configStrings = append(configStrings, config.Config())
  99. }
  100. extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
  101. extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
  102. var testRunnerConfigStrings []string
  103. for _, config := range testRunnerConfigs {
  104. testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config())
  105. }
  106. extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent))
  107. if len(extraTestRunnerConfigs) > 0 {
  108. extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
  109. }
  110. extraTestRunnerConfigs = proptools.NinjaAndShellEscape(extraTestRunnerConfigs)
  111. ctx.Build(pctx, android.BuildParams{
  112. Rule: autogenTestConfig,
  113. Description: "test config",
  114. Output: output,
  115. Args: map[string]string{
  116. "name": name,
  117. "template": template,
  118. "extraConfigs": extraConfigs,
  119. "outputFileName": outputFileName,
  120. "testInstallBase": testInstallBase,
  121. "extraTestRunnerConfigs": extraTestRunnerConfigs,
  122. },
  123. })
  124. }
  125. // AutoGenTestConfigOptions is used so that we can supply many optional
  126. // arguments to the AutoGenTestConfig function.
  127. type AutoGenTestConfigOptions struct {
  128. Name string
  129. OutputFileName string
  130. TestConfigProp *string
  131. TestConfigTemplateProp *string
  132. TestSuites []string
  133. Config []Config
  134. OptionsForAutogenerated []Option
  135. TestRunnerOptions []Option
  136. AutoGenConfig *bool
  137. UnitTest *bool
  138. TestInstallBase string
  139. DeviceTemplate string
  140. HostTemplate string
  141. HostUnitTestTemplate string
  142. }
  143. func AutoGenTestConfig(ctx android.ModuleContext, options AutoGenTestConfigOptions) android.Path {
  144. configs := append([]Config{}, options.Config...)
  145. for _, c := range options.OptionsForAutogenerated {
  146. configs = append(configs, c)
  147. }
  148. name := options.Name
  149. if name == "" {
  150. name = ctx.ModuleName()
  151. }
  152. path, autogenPath := testConfigPath(ctx, options.TestConfigProp, options.TestSuites, options.AutoGenConfig, options.TestConfigTemplateProp)
  153. if autogenPath != nil {
  154. templatePath := getTestConfigTemplate(ctx, options.TestConfigTemplateProp)
  155. if templatePath.Valid() {
  156. autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
  157. } else {
  158. if ctx.Device() {
  159. autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
  160. } else {
  161. if Bool(options.UnitTest) {
  162. autogenTemplate(ctx, name, autogenPath, options.HostUnitTestTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
  163. } else {
  164. autogenTemplate(ctx, name, autogenPath, options.HostTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
  165. }
  166. }
  167. }
  168. return autogenPath
  169. }
  170. if len(options.OptionsForAutogenerated) > 0 {
  171. ctx.ModuleErrorf("Extra tradefed configurations were provided for an autogenerated xml file, but the autogenerated xml file was not used.")
  172. }
  173. return path
  174. }
  175. var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
  176. Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
  177. CommandDeps: []string{
  178. "${AutoGenTestConfigScript}",
  179. "${EmptyTestConfig}",
  180. "$template",
  181. },
  182. }, "name", "template", "extraConfigs")
  183. func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
  184. testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path {
  185. path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
  186. var configStrings []string
  187. if autogenPath != nil {
  188. template := "${InstrumentationTestConfigTemplate}"
  189. moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
  190. if moduleTemplate.Valid() {
  191. template = moduleTemplate.String()
  192. }
  193. for _, config := range configs {
  194. configStrings = append(configStrings, config.Config())
  195. }
  196. extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
  197. extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs)
  198. ctx.Build(pctx, android.BuildParams{
  199. Rule: autogenInstrumentationTest,
  200. Description: "test config",
  201. Input: manifest,
  202. Output: autogenPath,
  203. Args: map[string]string{
  204. "name": ctx.ModuleName(),
  205. "template": template,
  206. "extraConfigs": extraConfigs,
  207. },
  208. })
  209. return autogenPath
  210. }
  211. return path
  212. }
  213. var Bool = proptools.Bool
  214. var BoolDefault = proptools.BoolDefault