blob: 67edc351703492e40ec8e553063441c5f0169616 [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 }
LaMont Jonesdc868192024-04-30 09:06:20 -0700163 if release.AconfigFlagsOnly {
164 return fmt.Errorf("%s does not allow build flag overrides", targetRelease)
165 }
LaMont Jonesb9014c72024-04-11 17:41:15 -0700166 flagArtifact, ok := release.FlagArtifacts[name]
167 if !ok {
168 return fmt.Errorf("Unknown build flag %s", name)
169 }
170 if valueDir == "" {
171 mapDir, err := GetMapDir(*flagArtifact.Traces[len(flagArtifact.Traces)-1].Source)
172 if err != nil {
173 return err
174 }
175 valueDir = mapDir
176 }
177
178 flagValue := &rc_proto.FlagValue{
179 Name: proto.String(name),
180 Value: rc_lib.UnmarshalValue(value),
181 }
182 flagPath := filepath.Join(valueDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", name))
183 return rc_lib.WriteMessage(flagPath, flagValue)
184}
185
186func main() {
187 var err error
188 var commonFlags Flags
189 var configs *rc_lib.ReleaseConfigs
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700190 var useBuildVar bool
LaMont Jonesb9014c72024-04-11 17:41:15 -0700191
192 outEnv := os.Getenv("OUT_DIR")
193 if outEnv == "" {
194 outEnv = "out"
195 }
196 // Handle the common arguments
197 flag.StringVar(&commonFlags.top, "top", ".", "path to top of workspace")
198 flag.BoolVar(&commonFlags.quiet, "quiet", false, "disable warning messages")
199 flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
200 flag.StringVar(&commonFlags.outDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
201 flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700202 flag.BoolVar(&useBuildVar, "use_get_build_var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
LaMont Jonesb9014c72024-04-11 17:41:15 -0700203 flag.Parse()
204
205 if commonFlags.quiet {
206 rc_lib.DisableWarnings()
207 }
208
209 if len(commonFlags.targetReleases) == 0 {
210 commonFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
211 }
212
213 if err = os.Chdir(commonFlags.top); err != nil {
214 panic(err)
215 }
216
217 // Get the current state of flagging.
218 relName := commonFlags.targetReleases[0]
219 if relName == "--all" || relName == "-all" {
220 // If the users said `--release --all`, grab trunk staging for simplicity.
221 relName = "trunk_staging"
222 }
LaMont Jonese41ea1e2024-04-29 14:16:19 -0700223 configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, true)
LaMont Jonesb9014c72024-04-11 17:41:15 -0700224 if err != nil {
225 panic(err)
226 }
227
228 if cmd, ok := commandMap[flag.Arg(0)]; ok {
229 args := flag.Args()
230 if err = cmd(configs, commonFlags, args[0], args[1:]); err != nil {
231 panic(err)
232 }
233 }
234}