blob: a8e63265870bc9f80b405f72b216288b46c9b77a [file] [log] [blame]
LaMont Jonesb9014c72024-04-11 17:41:15 -07001package main
2
3import (
4 "flag"
5 "fmt"
6 "os"
7 "path/filepath"
8 "strings"
9
10 rc_lib "android/soong/cmd/release_config/release_config_lib"
11 rc_proto "android/soong/cmd/release_config/release_config_proto"
12
13 "google.golang.org/protobuf/proto"
14)
15
16type Flags struct {
17 // The path to the top of the workspace. Default: ".".
18 top string
19
20 // Pathlist of release config map textproto files.
21 // If not specified, then the value is (if present):
22 // - build/release/release_config_map.textproto
23 // - vendor/google_shared/build/release/release_config_map.textproto
24 // - vendor/google/release/release_config_map.textproto
25 //
26 // Additionally, any maps specified in the environment variable
27 // `PRODUCT_RELEASE_CONFIG_MAPS` are used.
28 maps rc_lib.StringList
29
30 // Output directory (relative to `top`).
31 outDir string
32
33 // Which $TARGET_RELEASE(s) should we use. Some commands will only
34 // accept one value, others also accept `--release --all`.
35 targetReleases rc_lib.StringList
36
37 // Disable warning messages
38 quiet bool
39}
40
41type CommandFunc func(*rc_lib.ReleaseConfigs, Flags, string, []string) error
42
43var commandMap map[string]CommandFunc = map[string]CommandFunc{
44 "get": GetCommand,
45 "set": SetCommand,
46 "trace": GetCommand, // Also handled by GetCommand
47}
48
49// Find the top of the release config contribution directory.
50// Returns the parent of the flag_declarations and flag_values directories.
51func GetMapDir(path string) (string, error) {
52 for p := path; p != "."; p = filepath.Dir(p) {
53 switch filepath.Base(p) {
54 case "flag_declarations":
55 return filepath.Dir(p), nil
56 case "flag_values":
57 return filepath.Dir(p), nil
58 }
59 }
60 return "", fmt.Errorf("Could not determine directory from %s", path)
61}
62
63func MarshalFlagValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
64 fa, ok := config.FlagArtifacts[name]
65 if !ok {
66 return "", fmt.Errorf("%s not found in %s", name, config.Name)
67 }
68 return rc_lib.MarshalValue(fa.Value), nil
69}
70
71func GetReleaseArgs(configs *rc_lib.ReleaseConfigs, commonFlags Flags) ([]*rc_lib.ReleaseConfig, error) {
72 var all bool
73 relFlags := flag.NewFlagSet("set", flag.ExitOnError)
74 relFlags.BoolVar(&all, "all", false, "Display all flags")
75 relFlags.Parse(commonFlags.targetReleases)
76 var ret []*rc_lib.ReleaseConfig
77 if all {
78 for _, config := range configs.ReleaseConfigs {
79 ret = append(ret, config)
80 }
81 return ret, nil
82 }
83 for _, arg := range relFlags.Args() {
84 config, err := configs.GetReleaseConfig(arg)
85 if err != nil {
86 return nil, err
87 }
88 ret = append(ret, config)
89 }
90 return ret, nil
91}
92
93func GetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
94 isTrace := cmd == "trace"
95 var all bool
96 getFlags := flag.NewFlagSet("set", flag.ExitOnError)
97 getFlags.BoolVar(&all, "all", false, "Display all flags")
98 getFlags.Parse(args)
99 args = getFlags.Args()
100
101 releaseConfigList, err := GetReleaseArgs(configs, commonFlags)
102 if err != nil {
103 return err
104 }
105 if isTrace && len(releaseConfigList) > 1 {
106 return fmt.Errorf("trace command only allows one --release argument. Got: %s", strings.Join(commonFlags.targetReleases, " "))
107 }
108
109 if all {
110 args = []string{}
111 for _, fa := range configs.FlagArtifacts {
112 args = append(args, *fa.FlagDeclaration.Name)
113 }
114 }
115
116 showName := len(releaseConfigList) > 1 || len(args) > 1
117 for _, config := range releaseConfigList {
118 var configName string
119 if len(releaseConfigList) > 1 {
120 configName = fmt.Sprintf("%s.", config.Name)
121 }
122 for _, arg := range args {
123 val, err := MarshalFlagValue(config, arg)
124 if err != nil {
125 return err
126 }
127 if showName {
128 fmt.Printf("%s%s=%s\n", configName, arg, val)
129 } else {
130 fmt.Printf("%s\n", val)
131 }
132 if isTrace {
133 for _, trace := range config.FlagArtifacts[arg].Traces {
134 fmt.Printf(" => \"%s\" in %s\n", rc_lib.MarshalValue(trace.Value), *trace.Source)
135 }
136 }
137 }
138 }
139 return nil
140}
141
142func SetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
143 var valueDir string
144 if len(commonFlags.targetReleases) > 1 {
145 return fmt.Errorf("set command only allows one --release argument. Got: %s", strings.Join(commonFlags.targetReleases, " "))
146 }
147 targetRelease := commonFlags.targetReleases[0]
148
149 setFlags := flag.NewFlagSet("set", flag.ExitOnError)
150 setFlags.StringVar(&valueDir, "dir", "", "Directory in which to place the value")
151 setFlags.Parse(args)
152 setArgs := setFlags.Args()
153 if len(setArgs) != 2 {
154 return fmt.Errorf("set command expected flag and value, got: %s", strings.Join(setArgs, " "))
155 }
156 name := setArgs[0]
157 value := setArgs[1]
158 release, err := configs.GetReleaseConfig(targetRelease)
159 targetRelease = release.Name
160 if err != nil {
161 return err
162 }
163 flagArtifact, ok := release.FlagArtifacts[name]
164 if !ok {
165 return fmt.Errorf("Unknown build flag %s", name)
166 }
167 if valueDir == "" {
168 mapDir, err := GetMapDir(*flagArtifact.Traces[len(flagArtifact.Traces)-1].Source)
169 if err != nil {
170 return err
171 }
172 valueDir = mapDir
173 }
174
175 flagValue := &rc_proto.FlagValue{
176 Name: proto.String(name),
177 Value: rc_lib.UnmarshalValue(value),
178 }
179 flagPath := filepath.Join(valueDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", name))
180 return rc_lib.WriteMessage(flagPath, flagValue)
181}
182
183func main() {
184 var err error
185 var commonFlags Flags
186 var configs *rc_lib.ReleaseConfigs
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700187 var useBuildVar bool
LaMont Jonesb9014c72024-04-11 17:41:15 -0700188
189 outEnv := os.Getenv("OUT_DIR")
190 if outEnv == "" {
191 outEnv = "out"
192 }
193 // Handle the common arguments
194 flag.StringVar(&commonFlags.top, "top", ".", "path to top of workspace")
195 flag.BoolVar(&commonFlags.quiet, "quiet", false, "disable warning messages")
196 flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
197 flag.StringVar(&commonFlags.outDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
198 flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700199 flag.BoolVar(&useBuildVar, "use_get_build_var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700200 flag.Parse()
201
202 if commonFlags.quiet {
203 rc_lib.DisableWarnings()
204 }
205
206 if len(commonFlags.targetReleases) == 0 {
207 commonFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
208 }
209
210 if err = os.Chdir(commonFlags.top); err != nil {
211 panic(err)
212 }
213
214 // Get the current state of flagging.
215 relName := commonFlags.targetReleases[0]
216 if relName == "--all" || relName == "-all" {
217 // If the users said `--release --all`, grab trunk staging for simplicity.
218 relName = "trunk_staging"
219 }
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700220 configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, true)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700221 if err != nil {
222 panic(err)
223 }
224
225 if cmd, ok := commandMap[flag.Arg(0)]; ok {
226 args := flag.Args()
227 if err = cmd(configs, commonFlags, args[0], args[1:]); err != nil {
228 panic(err)
229 }
230 }
231}