bp2build.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright 2021 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 config
  15. import (
  16. "fmt"
  17. "reflect"
  18. "regexp"
  19. "sort"
  20. "strings"
  21. "android/soong/android"
  22. "android/soong/starlark_fmt"
  23. "github.com/google/blueprint"
  24. )
  25. type bazelVarExporter interface {
  26. asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
  27. }
  28. // Helpers for exporting cc configuration information to Bazel.
  29. var (
  30. // Maps containing toolchain variables that are independent of the
  31. // environment variables of the build.
  32. exportedStringListVars = exportedStringListVariables{}
  33. exportedStringVars = exportedStringVariables{}
  34. exportedStringListDictVars = exportedStringListDictVariables{}
  35. /// Maps containing variables that are dependent on the build config.
  36. exportedConfigDependingVars = exportedConfigDependingVariables{}
  37. )
  38. type exportedConfigDependingVariables map[string]interface{}
  39. func (m exportedConfigDependingVariables) Set(k string, v interface{}) {
  40. m[k] = v
  41. }
  42. // Ensure that string s has no invalid characters to be generated into the bzl file.
  43. func validateCharacters(s string) string {
  44. for _, c := range []string{`\n`, `"`, `\`} {
  45. if strings.Contains(s, c) {
  46. panic(fmt.Errorf("%s contains illegal character %s", s, c))
  47. }
  48. }
  49. return s
  50. }
  51. type bazelConstant struct {
  52. variableName string
  53. internalDefinition string
  54. }
  55. type exportedStringVariables map[string]string
  56. func (m exportedStringVariables) Set(k string, v string) {
  57. m[k] = v
  58. }
  59. func (m exportedStringVariables) asBazel(config android.Config,
  60. stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
  61. ret := make([]bazelConstant, 0, len(m))
  62. for k, variableValue := range m {
  63. expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
  64. if err != nil {
  65. panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
  66. }
  67. if len(expandedVar) > 1 {
  68. panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
  69. }
  70. ret = append(ret, bazelConstant{
  71. variableName: k,
  72. internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
  73. })
  74. }
  75. return ret
  76. }
  77. // Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
  78. func exportStringStaticVariable(name string, value string) {
  79. pctx.StaticVariable(name, value)
  80. exportedStringVars.Set(name, value)
  81. }
  82. type exportedStringListVariables map[string][]string
  83. func (m exportedStringListVariables) Set(k string, v []string) {
  84. m[k] = v
  85. }
  86. func (m exportedStringListVariables) asBazel(config android.Config,
  87. stringScope exportedStringVariables, stringListScope exportedStringListVariables,
  88. exportedVars exportedConfigDependingVariables) []bazelConstant {
  89. ret := make([]bazelConstant, 0, len(m))
  90. // For each exported variable, recursively expand elements in the variableValue
  91. // list to ensure that interpolated variables are expanded according to their values
  92. // in the variable scope.
  93. for k, variableValue := range m {
  94. var expandedVars []string
  95. for _, v := range variableValue {
  96. expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
  97. if err != nil {
  98. panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
  99. }
  100. expandedVars = append(expandedVars, expandedVar...)
  101. }
  102. // Assign the list as a bzl-private variable; this variable will be exported
  103. // out through a constants struct later.
  104. ret = append(ret, bazelConstant{
  105. variableName: k,
  106. internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
  107. })
  108. }
  109. return ret
  110. }
  111. // Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
  112. func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
  113. exportedConfigDependingVars.Set(name, method)
  114. return pctx.VariableConfigMethod(name, method)
  115. }
  116. // Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
  117. func exportSourcePathVariable(name string, value string) {
  118. pctx.SourcePathVariable(name, value)
  119. exportedStringVars.Set(name, value)
  120. }
  121. // Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
  122. func exportStringListStaticVariable(name string, value []string) {
  123. pctx.StaticVariable(name, strings.Join(value, " "))
  124. exportedStringListVars.Set(name, value)
  125. }
  126. func ExportStringList(name string, value []string) {
  127. exportedStringListVars.Set(name, value)
  128. }
  129. type exportedStringListDictVariables map[string]map[string][]string
  130. func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
  131. m[k] = v
  132. }
  133. // Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
  134. func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
  135. _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
  136. ret := make([]bazelConstant, 0, len(m))
  137. for k, dict := range m {
  138. ret = append(ret, bazelConstant{
  139. variableName: k,
  140. internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
  141. })
  142. }
  143. return ret
  144. }
  145. // BazelCcToolchainVars generates bzl file content containing variables for
  146. // Bazel's cc_toolchain configuration.
  147. func BazelCcToolchainVars(config android.Config) string {
  148. return bazelToolchainVars(
  149. config,
  150. exportedStringListDictVars,
  151. exportedStringListVars,
  152. exportedStringVars)
  153. }
  154. func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
  155. ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
  156. results := []bazelConstant{}
  157. for _, v := range vars {
  158. results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
  159. }
  160. sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
  161. definitions := make([]string, 0, len(results))
  162. constants := make([]string, 0, len(results))
  163. for _, b := range results {
  164. definitions = append(definitions,
  165. fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
  166. constants = append(constants,
  167. fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
  168. }
  169. // Build the exported constants struct.
  170. ret += strings.Join(definitions, "\n\n")
  171. ret += "\n\n"
  172. ret += "constants = struct(\n"
  173. ret += strings.Join(constants, "\n")
  174. ret += "\n)"
  175. return ret
  176. }
  177. // expandVar recursively expand interpolated variables in the exportedVars scope.
  178. //
  179. // We're using a string slice to track the seen variables to avoid
  180. // stackoverflow errors with infinite recursion. it's simpler to use a
  181. // string slice than to handle a pass-by-referenced map, which would make it
  182. // quite complex to track depth-first interpolations. It's also unlikely the
  183. // interpolation stacks are deep (n > 1).
  184. func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
  185. stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
  186. // e.g. "${ExternalCflags}"
  187. r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
  188. // Internal recursive function.
  189. var expandVarInternal func(string, map[string]bool) (string, error)
  190. expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
  191. var ret string
  192. remainingString := toExpand
  193. for len(remainingString) > 0 {
  194. matches := r.FindStringSubmatch(remainingString)
  195. if len(matches) == 0 {
  196. return ret + remainingString, nil
  197. }
  198. if len(matches) != 2 {
  199. panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
  200. }
  201. matchIndex := strings.Index(remainingString, matches[0])
  202. ret += remainingString[:matchIndex]
  203. remainingString = remainingString[matchIndex+len(matches[0]):]
  204. // Index 1 of FindStringSubmatch contains the subexpression match
  205. // (variable name) of the capture group.
  206. variable := matches[1]
  207. // toExpand contains a variable.
  208. if _, ok := seenVars[variable]; ok {
  209. return ret, fmt.Errorf(
  210. "Unbounded recursive interpolation of variable: %s", variable)
  211. }
  212. // A map is passed-by-reference. Create a new map for
  213. // this scope to prevent variables seen in one depth-first expansion
  214. // to be also treated as "seen" in other depth-first traversals.
  215. newSeenVars := map[string]bool{}
  216. for k := range seenVars {
  217. newSeenVars[k] = true
  218. }
  219. newSeenVars[variable] = true
  220. if unexpandedVars, ok := stringListScope[variable]; ok {
  221. expandedVars := []string{}
  222. for _, unexpandedVar := range unexpandedVars {
  223. expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
  224. if err != nil {
  225. return ret, err
  226. }
  227. expandedVars = append(expandedVars, expandedVar)
  228. }
  229. ret += strings.Join(expandedVars, " ")
  230. } else if unexpandedVar, ok := stringScope[variable]; ok {
  231. expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
  232. if err != nil {
  233. return ret, err
  234. }
  235. ret += expandedVar
  236. } else if unevaluatedVar, ok := exportedVars[variable]; ok {
  237. evalFunc := reflect.ValueOf(unevaluatedVar)
  238. validateVariableMethod(variable, evalFunc)
  239. evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
  240. evaluatedValue := evaluatedResult[0].Interface().(string)
  241. expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
  242. if err != nil {
  243. return ret, err
  244. }
  245. ret += expandedVar
  246. } else {
  247. return "", fmt.Errorf("Unbound config variable %s", variable)
  248. }
  249. }
  250. return ret, nil
  251. }
  252. var ret []string
  253. for _, v := range strings.Split(toExpand, " ") {
  254. val, err := expandVarInternal(v, map[string]bool{})
  255. if err != nil {
  256. return ret, err
  257. }
  258. ret = append(ret, val)
  259. }
  260. return ret, nil
  261. }
  262. func validateVariableMethod(name string, methodValue reflect.Value) {
  263. methodType := methodValue.Type()
  264. if methodType.Kind() != reflect.Func {
  265. panic(fmt.Errorf("method given for variable %s is not a function",
  266. name))
  267. }
  268. if n := methodType.NumIn(); n != 1 {
  269. panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
  270. name, n))
  271. }
  272. if n := methodType.NumOut(); n != 1 {
  273. panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
  274. name, n))
  275. }
  276. if kind := methodType.Out(0).Kind(); kind != reflect.String {
  277. panic(fmt.Errorf("method for variable %s does not return a string",
  278. name))
  279. }
  280. }