blob: 19571f10d4922427c86019b1a586e7e656c599a4 [file] [log] [blame]
Jingwen Chenbf61afb2021-05-06 13:31:18 +00001// 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
15package config
16
17import (
18 "android/soong/android"
19 "fmt"
20 "regexp"
21 "strings"
22)
23
24// Helpers for exporting cc configuration information to Bazel.
Jingwen Chenbf61afb2021-05-06 13:31:18 +000025var (
26 // Map containing toolchain variables that are independent of the
27 // environment variables of the build.
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000028 exportedStringListVars = exportedStringListVariables{}
29 exportedStringVars = exportedStringVariables{}
Jingwen Chenbf61afb2021-05-06 13:31:18 +000030)
31
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000032type exportedStringVariables map[string]string
33type exportedStringListVariables map[string][]string
Jingwen Chenbf61afb2021-05-06 13:31:18 +000034
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000035func (m exportedStringVariables) Set(k string, v string) {
Jingwen Chenbf61afb2021-05-06 13:31:18 +000036 m[k] = v
37}
38
39// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000040func exportStringStaticVariable(name string, value string) {
41 pctx.StaticVariable(name, value)
42 exportedStringVars.Set(name, value)
43}
44
45func (m exportedStringListVariables) Set(k string, v []string) {
46 m[k] = v
47}
48
49// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
50func exportStringListStaticVariable(name string, value []string) {
Jingwen Chenbf61afb2021-05-06 13:31:18 +000051 pctx.StaticVariable(name, strings.Join(value, " "))
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000052 exportedStringListVars.Set(name, value)
Jingwen Chenbf61afb2021-05-06 13:31:18 +000053}
54
55// BazelCcToolchainVars generates bzl file content containing variables for
56// Bazel's cc_toolchain configuration.
57func BazelCcToolchainVars() string {
58 ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
59
60 // Ensure that string s has no invalid characters to be generated into the bzl file.
61 validateCharacters := func(s string) string {
62 for _, c := range []string{`\n`, `"`, `\`} {
63 if strings.Contains(s, c) {
64 panic(fmt.Errorf("%s contains illegal character %s", s, c))
65 }
66 }
67 return s
68 }
69
70 // For each exported variable, recursively expand elements in the variableValue
71 // list to ensure that interpolated variables are expanded according to their values
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000072 // in the variable scope.
73 for _, k := range android.SortedStringKeys(exportedStringListVars) {
74 variableValue := exportedStringListVars[k]
Jingwen Chenbf61afb2021-05-06 13:31:18 +000075 var expandedVars []string
76 for _, v := range variableValue {
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000077 expandedVars = append(expandedVars, expandVar(v, exportedStringVars, exportedStringListVars)...)
Jingwen Chenbf61afb2021-05-06 13:31:18 +000078 }
79 // Build the list for this variable.
80 list := "["
81 for _, flag := range expandedVars {
82 list += fmt.Sprintf("\n \"%s\",", validateCharacters(flag))
83 }
84 list += "\n]"
85 // Assign the list as a bzl-private variable; this variable will be exported
86 // out through a constants struct later.
87 ret += fmt.Sprintf("_%s = %s\n", k, list)
88 ret += "\n"
89 }
90
Jingwen Chen51a1e1c2021-05-20 13:40:14 +000091 for _, k := range android.SortedStringKeys(exportedStringVars) {
92 variableValue := exportedStringVars[k]
93 expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
94 if len(expandedVar) > 1 {
95 panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
96 }
97 ret += fmt.Sprintf("_%s = \"%s\"\n", k, validateCharacters(expandedVar[0]))
98 ret += "\n"
99 }
100
Jingwen Chenbf61afb2021-05-06 13:31:18 +0000101 // Build the exported constants struct.
102 ret += "constants = struct(\n"
Jingwen Chen51a1e1c2021-05-20 13:40:14 +0000103 for _, k := range android.SortedStringKeys(exportedStringVars) {
104 ret += fmt.Sprintf(" %s = _%s,\n", k, k)
105 }
106 for _, k := range android.SortedStringKeys(exportedStringListVars) {
Jingwen Chenbf61afb2021-05-06 13:31:18 +0000107 ret += fmt.Sprintf(" %s = _%s,\n", k, k)
108 }
109 ret += ")"
110 return ret
111}
112
113// expandVar recursively expand interpolated variables in the exportedVars scope.
114//
115// We're using a string slice to track the seen variables to avoid
116// stackoverflow errors with infinite recursion. it's simpler to use a
117// string slice than to handle a pass-by-referenced map, which would make it
118// quite complex to track depth-first interpolations. It's also unlikely the
119// interpolation stacks are deep (n > 1).
Jingwen Chen51a1e1c2021-05-20 13:40:14 +0000120func expandVar(toExpand string, stringScope exportedStringVariables, stringListScope exportedStringListVariables) []string {
Jingwen Chenbf61afb2021-05-06 13:31:18 +0000121 // e.g. "${ClangExternalCflags}"
122 r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
123
124 // Internal recursive function.
125 var expandVarInternal func(string, map[string]bool) []string
126 expandVarInternal = func(toExpand string, seenVars map[string]bool) []string {
127 var ret []string
128 for _, v := range strings.Split(toExpand, " ") {
129 matches := r.FindStringSubmatch(v)
130 if len(matches) == 0 {
131 return []string{v}
132 }
133
134 if len(matches) != 2 {
135 panic(fmt.Errorf(
136 "Expected to only match 1 subexpression in %s, got %d",
137 v,
138 len(matches)-1))
139 }
140
141 // Index 1 of FindStringSubmatch contains the subexpression match
142 // (variable name) of the capture group.
143 variable := matches[1]
144 // toExpand contains a variable.
145 if _, ok := seenVars[variable]; ok {
146 panic(fmt.Errorf(
147 "Unbounded recursive interpolation of variable: %s", variable))
148 }
149 // A map is passed-by-reference. Create a new map for
150 // this scope to prevent variables seen in one depth-first expansion
151 // to be also treated as "seen" in other depth-first traversals.
152 newSeenVars := map[string]bool{}
153 for k := range seenVars {
154 newSeenVars[k] = true
155 }
156 newSeenVars[variable] = true
Jingwen Chen51a1e1c2021-05-20 13:40:14 +0000157 if unexpandedVars, ok := stringListScope[variable]; ok {
158 for _, unexpandedVar := range unexpandedVars {
159 ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
160 }
161 } else if unexpandedVar, ok := stringScope[variable]; ok {
Jingwen Chenbf61afb2021-05-06 13:31:18 +0000162 ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
163 }
164 }
165 return ret
166 }
167
168 return expandVarInternal(toExpand, map[string]bool{})
169}