blob: ec7d64f1e832cfb5594c355e1ea0a8b0ad741976 [file] [log] [blame]
LaMont Jonesb9014c72024-04-11 17:41:15 -07001package main
2
3import (
LaMont Jonesff387ea2024-04-29 16:20:59 -07004 "cmp"
LaMont Jonesb9014c72024-04-11 17:41:15 -07005 "flag"
6 "fmt"
7 "os"
8 "path/filepath"
LaMont Jonesff387ea2024-04-29 16:20:59 -07009 "slices"
LaMont Jonesb9014c72024-04-11 17:41:15 -070010 "strings"
11
12 rc_lib "android/soong/cmd/release_config/release_config_lib"
13 rc_proto "android/soong/cmd/release_config/release_config_proto"
14
15 "google.golang.org/protobuf/proto"
16)
17
18type Flags struct {
19 // The path to the top of the workspace. Default: ".".
20 top string
21
22 // Pathlist of release config map textproto files.
23 // If not specified, then the value is (if present):
24 // - build/release/release_config_map.textproto
25 // - vendor/google_shared/build/release/release_config_map.textproto
26 // - vendor/google/release/release_config_map.textproto
27 //
28 // Additionally, any maps specified in the environment variable
29 // `PRODUCT_RELEASE_CONFIG_MAPS` are used.
30 maps rc_lib.StringList
31
32 // Output directory (relative to `top`).
33 outDir string
34
35 // Which $TARGET_RELEASE(s) should we use. Some commands will only
36 // accept one value, others also accept `--release --all`.
37 targetReleases rc_lib.StringList
38
39 // Disable warning messages
40 quiet bool
LaMont Jonesff387ea2024-04-29 16:20:59 -070041
42 // Show all release configs
43 allReleases bool
44
45 // Call get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get the
46 // product-specific map directories.
47 useGetBuildVar bool
48
49 // Panic on errors.
50 debug bool
LaMont Jonesb9014c72024-04-11 17:41:15 -070051}
52
53type CommandFunc func(*rc_lib.ReleaseConfigs, Flags, string, []string) error
54
55var commandMap map[string]CommandFunc = map[string]CommandFunc{
56 "get": GetCommand,
57 "set": SetCommand,
58 "trace": GetCommand, // Also handled by GetCommand
59}
60
61// Find the top of the release config contribution directory.
62// Returns the parent of the flag_declarations and flag_values directories.
63func GetMapDir(path string) (string, error) {
64 for p := path; p != "."; p = filepath.Dir(p) {
65 switch filepath.Base(p) {
66 case "flag_declarations":
67 return filepath.Dir(p), nil
68 case "flag_values":
69 return filepath.Dir(p), nil
70 }
71 }
72 return "", fmt.Errorf("Could not determine directory from %s", path)
73}
74
LaMont Jonesff387ea2024-04-29 16:20:59 -070075func MarshalFlagDefaultValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
76 fa, ok := config.FlagArtifacts[name]
77 if !ok {
78 return "", fmt.Errorf("%s not found in %s", name, config.Name)
79 }
80 return rc_lib.MarshalValue(fa.Traces[0].Value), nil
81}
82
LaMont Jonesb9014c72024-04-11 17:41:15 -070083func MarshalFlagValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
84 fa, ok := config.FlagArtifacts[name]
85 if !ok {
86 return "", fmt.Errorf("%s not found in %s", name, config.Name)
87 }
88 return rc_lib.MarshalValue(fa.Value), nil
89}
90
LaMont Jonesff387ea2024-04-29 16:20:59 -070091// Returns a list of ReleaseConfig objects for which to process flags.
LaMont Jonesb9014c72024-04-11 17:41:15 -070092func GetReleaseArgs(configs *rc_lib.ReleaseConfigs, commonFlags Flags) ([]*rc_lib.ReleaseConfig, error) {
93 var all bool
LaMont Jonesff387ea2024-04-29 16:20:59 -070094 relFlags := flag.NewFlagSet("releaseFlags", flag.ExitOnError)
95 relFlags.BoolVar(&all, "all", false, "Display all releases")
LaMont Jonesb9014c72024-04-11 17:41:15 -070096 relFlags.Parse(commonFlags.targetReleases)
97 var ret []*rc_lib.ReleaseConfig
LaMont Jonesff387ea2024-04-29 16:20:59 -070098 if all || commonFlags.allReleases {
99 sortMap := map[string]int{
100 "trunk_staging": 0,
101 "trunk_food": 10,
102 "trunk": 20,
103 // Anything not listed above, uses this for key 1 in the sort.
104 "-default": 100,
105 }
106
LaMont Jonesb9014c72024-04-11 17:41:15 -0700107 for _, config := range configs.ReleaseConfigs {
108 ret = append(ret, config)
109 }
LaMont Jonesff387ea2024-04-29 16:20:59 -0700110 slices.SortFunc(ret, func(a, b *rc_lib.ReleaseConfig) int {
111 mapValue := func(v *rc_lib.ReleaseConfig) int {
112 if v, ok := sortMap[v.Name]; ok {
113 return v
114 }
115 return sortMap["-default"]
116 }
117 if n := cmp.Compare(mapValue(a), mapValue(b)); n != 0 {
118 return n
119 }
120 return cmp.Compare(a.Name, b.Name)
121 })
LaMont Jonesb9014c72024-04-11 17:41:15 -0700122 return ret, nil
123 }
124 for _, arg := range relFlags.Args() {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700125 // Return releases in the order that they were given.
LaMont Jonesb9014c72024-04-11 17:41:15 -0700126 config, err := configs.GetReleaseConfig(arg)
127 if err != nil {
128 return nil, err
129 }
130 ret = append(ret, config)
131 }
132 return ret, nil
133}
134
135func GetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
136 isTrace := cmd == "trace"
LaMont Jonesff387ea2024-04-29 16:20:59 -0700137 isSet := cmd == "set"
138
LaMont Jonesb9014c72024-04-11 17:41:15 -0700139 var all bool
LaMont Jonesff387ea2024-04-29 16:20:59 -0700140 getFlags := flag.NewFlagSet("get", flag.ExitOnError)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700141 getFlags.BoolVar(&all, "all", false, "Display all flags")
142 getFlags.Parse(args)
143 args = getFlags.Args()
144
LaMont Jonesff387ea2024-04-29 16:20:59 -0700145 if isSet {
146 commonFlags.allReleases = true
147 }
LaMont Jonesb9014c72024-04-11 17:41:15 -0700148 releaseConfigList, err := GetReleaseArgs(configs, commonFlags)
149 if err != nil {
150 return err
151 }
152 if isTrace && len(releaseConfigList) > 1 {
153 return fmt.Errorf("trace command only allows one --release argument. Got: %s", strings.Join(commonFlags.targetReleases, " "))
154 }
155
156 if all {
157 args = []string{}
158 for _, fa := range configs.FlagArtifacts {
159 args = append(args, *fa.FlagDeclaration.Name)
160 }
161 }
162
LaMont Jonesff387ea2024-04-29 16:20:59 -0700163 var maxVariableNameLen, maxReleaseNameLen int
164 var releaseNameFormat, variableNameFormat string
165 valueFormat := "%s"
166 showReleaseName := len(releaseConfigList) > 1
167 showVariableName := len(args) > 1
168 if showVariableName {
LaMont Jonesb9014c72024-04-11 17:41:15 -0700169 for _, arg := range args {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700170 maxVariableNameLen = max(len(arg), maxVariableNameLen)
171 }
172 variableNameFormat = fmt.Sprintf("%%-%ds ", maxVariableNameLen)
173 valueFormat = "'%s'"
174 }
175 if showReleaseName {
176 for _, config := range releaseConfigList {
177 maxReleaseNameLen = max(len(config.Name), maxReleaseNameLen)
178 }
179 releaseNameFormat = fmt.Sprintf("%%-%ds ", maxReleaseNameLen)
180 valueFormat = "'%s'"
181 }
182
183 outputOneLine := func(variable, release, value, valueFormat string) {
184 var outStr string
185 if showVariableName {
186 outStr += fmt.Sprintf(variableNameFormat, variable)
187 }
188 if showReleaseName {
189 outStr += fmt.Sprintf(releaseNameFormat, release)
190 }
191 outStr += fmt.Sprintf(valueFormat, value)
192 fmt.Println(outStr)
193 }
194
195 for _, arg := range args {
196 if _, ok := configs.FlagArtifacts[arg]; !ok {
197 return fmt.Errorf("%s is not a defined build flag", arg)
198 }
199 }
200
201 for _, arg := range args {
202 for _, config := range releaseConfigList {
203 if isSet {
204 // If this is from the set command, format the output as:
205 // <default> ""
206 // trunk_staging ""
207 // trunk ""
208 //
209 // ap1a ""
210 // ...
211 switch {
212 case config.Name == "trunk_staging":
213 defaultValue, err := MarshalFlagDefaultValue(config, arg)
214 if err != nil {
215 return err
216 }
217 outputOneLine(arg, "<default>", defaultValue, valueFormat)
218 case config.AconfigFlagsOnly:
219 continue
220 case config.Name == "trunk":
221 fmt.Println()
222 }
LaMont Jonesb9014c72024-04-11 17:41:15 -0700223 }
LaMont Jonesff387ea2024-04-29 16:20:59 -0700224 val, err := MarshalFlagValue(config, arg)
225 if err == nil {
226 outputOneLine(arg, config.Name, val, valueFormat)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700227 } else {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700228 outputOneLine(arg, config.Name, "REDACTED", "%s")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700229 }
230 if isTrace {
231 for _, trace := range config.FlagArtifacts[arg].Traces {
232 fmt.Printf(" => \"%s\" in %s\n", rc_lib.MarshalValue(trace.Value), *trace.Source)
233 }
234 }
235 }
236 }
237 return nil
238}
239
240func SetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
241 var valueDir string
242 if len(commonFlags.targetReleases) > 1 {
243 return fmt.Errorf("set command only allows one --release argument. Got: %s", strings.Join(commonFlags.targetReleases, " "))
244 }
245 targetRelease := commonFlags.targetReleases[0]
246
247 setFlags := flag.NewFlagSet("set", flag.ExitOnError)
248 setFlags.StringVar(&valueDir, "dir", "", "Directory in which to place the value")
249 setFlags.Parse(args)
250 setArgs := setFlags.Args()
251 if len(setArgs) != 2 {
252 return fmt.Errorf("set command expected flag and value, got: %s", strings.Join(setArgs, " "))
253 }
254 name := setArgs[0]
255 value := setArgs[1]
256 release, err := configs.GetReleaseConfig(targetRelease)
257 targetRelease = release.Name
258 if err != nil {
259 return err
260 }
LaMont Jonesdc868192024-04-30 09:06:20 -0700261 if release.AconfigFlagsOnly {
262 return fmt.Errorf("%s does not allow build flag overrides", targetRelease)
263 }
LaMont Jonesb9014c72024-04-11 17:41:15 -0700264 flagArtifact, ok := release.FlagArtifacts[name]
265 if !ok {
266 return fmt.Errorf("Unknown build flag %s", name)
267 }
268 if valueDir == "" {
269 mapDir, err := GetMapDir(*flagArtifact.Traces[len(flagArtifact.Traces)-1].Source)
270 if err != nil {
271 return err
272 }
273 valueDir = mapDir
274 }
275
276 flagValue := &rc_proto.FlagValue{
277 Name: proto.String(name),
278 Value: rc_lib.UnmarshalValue(value),
279 }
280 flagPath := filepath.Join(valueDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", name))
LaMont Jonesff387ea2024-04-29 16:20:59 -0700281 err = rc_lib.WriteMessage(flagPath, flagValue)
282 if err != nil {
283 return err
284 }
285
286 // Reload the release configs.
287 configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, commonFlags.targetReleases[0], commonFlags.useGetBuildVar)
288 if err != nil {
289 return err
290 }
291 err = GetCommand(configs, commonFlags, cmd, args[0:1])
292 if err != nil {
293 return err
294 }
295 fmt.Printf("Updated: %s\n", flagPath)
296 return nil
LaMont Jonesb9014c72024-04-11 17:41:15 -0700297}
298
299func main() {
LaMont Jonesb9014c72024-04-11 17:41:15 -0700300 var commonFlags Flags
301 var configs *rc_lib.ReleaseConfigs
LaMont Jonesff387ea2024-04-29 16:20:59 -0700302 topDir, err := rc_lib.GetTopDir()
LaMont Jonesb9014c72024-04-11 17:41:15 -0700303
LaMont Jonesb9014c72024-04-11 17:41:15 -0700304 // Handle the common arguments
LaMont Jonesff387ea2024-04-29 16:20:59 -0700305 flag.StringVar(&commonFlags.top, "top", topDir, "path to top of workspace")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700306 flag.BoolVar(&commonFlags.quiet, "quiet", false, "disable warning messages")
307 flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
LaMont Jonesff387ea2024-04-29 16:20:59 -0700308 flag.StringVar(&commonFlags.outDir, "out-dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700309 flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
LaMont Jonesff387ea2024-04-29 16:20:59 -0700310 flag.BoolVar(&commonFlags.allReleases, "all-releases", false, "operate on all releases. (Ignored for set command)")
311 flag.BoolVar(&commonFlags.useGetBuildVar, "use-get-build-var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get needed maps")
312 flag.BoolVar(&commonFlags.debug, "debug", false, "turn on debugging output for errors")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700313 flag.Parse()
314
LaMont Jonesff387ea2024-04-29 16:20:59 -0700315 errorExit := func(err error) {
316 if commonFlags.debug {
317 panic(err)
318 }
319 fmt.Fprintf(os.Stderr, "%s\n", err)
320 os.Exit(1)
321 }
322
LaMont Jonesb9014c72024-04-11 17:41:15 -0700323 if commonFlags.quiet {
324 rc_lib.DisableWarnings()
325 }
326
327 if len(commonFlags.targetReleases) == 0 {
328 commonFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
329 }
330
331 if err = os.Chdir(commonFlags.top); err != nil {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700332 errorExit(err)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700333 }
334
335 // Get the current state of flagging.
336 relName := commonFlags.targetReleases[0]
337 if relName == "--all" || relName == "-all" {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700338 commonFlags.allReleases = true
LaMont Jonesb9014c72024-04-11 17:41:15 -0700339 }
LaMont Jonesff387ea2024-04-29 16:20:59 -0700340 configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, commonFlags.useGetBuildVar)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700341 if err != nil {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700342 errorExit(err)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700343 }
344
345 if cmd, ok := commandMap[flag.Arg(0)]; ok {
346 args := flag.Args()
347 if err = cmd(configs, commonFlags, args[0], args[1:]); err != nil {
LaMont Jonesff387ea2024-04-29 16:20:59 -0700348 errorExit(err)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700349 }
350 }
351}