Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/cmd/release_config/build_flag/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 20 kB image not shown  

Quelle  main.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

package main

import (
 "cmp"
 "flag"
 "fmt"
 "os"
 "path/filepath"
 "regexp"
 "slices"
 "strings"

 rc_lib "android/soong/cmd/release_config/release_config_lib"
 rc_proto "android/soong/cmd/release_config/release_config_proto"

 "google.golang.org/protobuf/proto"
)

type GlobalFlags struct {
 // The path to the top of the workspace.  Default: ".".
 top string

 // Pathlist of release config map textproto files.
 // If not specified, then the value is (if present):
 // - build/release/release_config_map.textproto
 // - vendor/google_shared/build/release/release_config_map.textproto
 // - vendor/google/release/release_config_map.textproto
 //
 // Additionally, any maps specified in the environment variable
 // `PRODUCT_RELEASE_CONFIG_MAPS` are used.
 maps rc_lib.StringList

 // File containing the list of maps to use, one file per line.
 // Cannot be used with --map.
 mapsFile string

 // Output directory (relative to `top`).
 outDir string

 // Which $TARGET_RELEASE(s) should we use.  Some commands will only
 // accept one value, others also accept `--release --all`.
 targetReleases rc_lib.StringList

 // The TARGET_BUILD_VARIANT to use
 targetBuildVariant string

 // Disable warning messages
 quiet bool

 // Show all release configs
 allReleases bool

 // Call get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get the
 // product-specific map directories.
 useGetBuildVar bool

 // Panic on errors.
 debug bool

 // Allow missing release config.
 // If true, and we cannot find the named release config, values for
 // `trunk_staging` will be used.
 allowMissing bool

 // Only load flag declarations, do not load values.  The output
 // will have only values provided in the declaration files.
 declarationsOnly bool
}

type CommandFunc func(*rc_lib.ReleaseConfigs, GlobalFlags, ...string) error

type CommandInfo struct {
 // The function that executes this command.
 run CommandFunc

 // Helptext for usage.
 helpText string
}

var (
 releaseFlagSet   = ReleaseFlagsFactory()
 getCommandInfo   = CommandInfo{run: GetCommandFactory(), helpText: "Prints flag values"}
 setCommandInfo   = CommandInfo{run: SetCommandFactory(), helpText: "Generates changes to flag value files"}
 traceCommandInfo = CommandInfo{run: TraceCommandFactory(), helpText: "Shows flag override history"}

 commandMap map[string]CommandInfo = map[string]CommandInfo{
  "get":   getCommandInfo,
  "set":   setCommandInfo,
  "trace": traceCommandInfo,
 }

 setArgRegexp = regexp.MustCompile(`^((?P<dir>[^:]+):)?(?P<flag>[0-9A-Z_]+)(=(?P<value>.*)|:(?P<redacted>redacted))$`)
)

// Find the top of the release config contribution directory.
// Returns the parent of the flag_declarations and flag_values directories.
func GetMapDir(path string) (string, error) {
 for p := path; p != "."; p = filepath.Dir(p) {
  switch filepath.Base(p) {
  case "flag_declarations":
   return filepath.Dir(p), nil
  case "flag_values":
   return filepath.Dir(p), nil
  }
 }
 return "", fmt.Errorf("Could not determine directory from %s", path)
}

func MarshalFlagDefaultValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
 fa, ok := config.FlagArtifacts[name]
 if !ok {
  return "", fmt.Errorf("%s not found in %s", name, config.Name)
 }
 return rc_lib.MarshalValue(fa.Traces[0].Value), nil
}

func MarshalFlagValue(config *rc_lib.ReleaseConfig, name string) (val, typ string, err error) {
 fa, ok := config.FlagArtifacts[name]
 if !ok {
  return "", "unspecified", fmt.Errorf("%s not found in %s", name, config.Name)
 }
 if fa.Redacted {
  return "==REDACTED==", "unspecified", nil
 }
 return rc_lib.MarshalValue(fa.Value), rc_lib.ValueType(fa.Value), nil
}

// Returns a list of ReleaseConfig objects for which to process flags.
func GetReleaseArgs(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags) ([]*rc_lib.ReleaseConfig, error) {
 releaseFlagSet.flagSet.Parse(globalFlags.targetReleases)
 var ret []*rc_lib.ReleaseConfig
 if releaseFlagSet.all || globalFlags.allReleases {
  sortMap := map[string]int{
   "trunk_staging": 0,
   "trunk_food":    10,
   "trunk":         20,
   // Anything not listed above, uses this for key 1 in the sort.
   "-default": 100,
  }

  if err := configs.GenerateAllReleaseConfigs(globalFlags.targetReleases[0]); err != nil {
   return nil, err
  }
  for _, config := range configs.ReleaseConfigs {
   ret = append(ret, config)
  }
  slices.SortFunc(ret, func(a, b *rc_lib.ReleaseConfig) int {
   mapValue := func(v *rc_lib.ReleaseConfig) int {
    if v, ok := sortMap[v.Name]; ok {
     return v
    }
    return sortMap["-default"]
   }
   if n := cmp.Compare(mapValue(a), mapValue(b)); n != 0 {
    return n
   }
   return cmp.Compare(a.Name, b.Name)
  })
  return ret, nil
 }
 for _, arg := range releaseFlagSet.flagSet.Args() {
  // Return releases in the order that they were given.
  config, err := configs.GetReleaseConfig(arg)
  if err != nil {
   return nil, err
  }
  ret = append(ret, config)
 }
 return ret, nil
}

type ReleaseFlags struct {
 flagSet *flag.FlagSet

 // Display all releases
 all bool
}

func ReleaseFlagsFactory() *ReleaseFlags {
 flags := &ReleaseFlags{
  flagSet: flag.NewFlagSet("releaseFlags", flag.ExitOnError),
 }
 flags.flagSet.BoolVar(&flags.all, "all", false, "Display all releases")
 return flags
}

type GetFlags struct {
 flagSet *flag.FlagSet

 // Display all flags
 all bool

 // Output flag as json object
 json bool

 // Show all distinct values in all releases
 distinctValues bool

 // Hide flag name
 hideName bool
}

func GetCommandFactory() CommandFunc {
 flags := &GetFlags{
  flagSet: flag.NewFlagSet("get", flag.ExitOnError),
 }
 flags.flagSet.BoolVar(&flags.all, "all", false, "Display all flags")
 flags.flagSet.BoolVar(&flags.json, "json", false, "Output flag as json object")
 flags.flagSet.BoolVar(&flags.hideName, "hide-name", false, "Hide build flag names. (True when only one flag name is given)")
 flags.flagSet.BoolVar(&flags.distinctValues, "distinct-values", false, "Show all distinct values in all releases")
 return func(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags, args ...string) error {
  return GetCommand(configs, globalFlags, flags, args...)
 }
}

func TraceCommandFactory() CommandFunc {
 // The `trace` command is also handled by GetCommand.
 flags := &GetFlags{
  flagSet: flag.NewFlagSet("trace", flag.ExitOnError),
 }
 flags.flagSet.BoolVar(&flags.all, "all", false, "Display all flags")
 return func(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags, args ...string) error {
  return GetCommand(configs, globalFlags, flags, args...)
 }
}

type SetFlags struct {
 flagSet *flag.FlagSet

 // Directory in which to place the value
 dir string

 // Whether the flag should be redacted
 redacted bool
}

func SetCommandFactory() CommandFunc {
 flags := &SetFlags{
  flagSet: flag.NewFlagSet("set", flag.ExitOnError),
 }
 flags.flagSet.StringVar(&flags.dir, "dir", "", "Directory in which to place the value")
 flags.flagSet.BoolVar(&flags.redacted, "redacted", false, "Whether the flag should be redacted")
 return func(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags, args ...string) error {
  return SetCommand(configs, globalFlags, flags, args...)
 }
}

func GetCommand(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags, getFlags *GetFlags, args ...string) error {
 if len(args) < 1 {
  panic("missing command")
 }
 cmd := args[0]
 if cmd == "printdefaults" {
  getFlags.flagSet.PrintDefaults()
  return nil
 }
 getFlags.flagSet.Parse(args[1:])
 args = getFlags.flagSet.Args()
 isTrace := cmd == "trace"
 isSet := cmd == "set"

 if isSet || getFlags.distinctValues {
  globalFlags.allReleases = true
 }
 releaseConfigList, err := GetReleaseArgs(configs, globalFlags)
 if err != nil {
  return err
 }

 if len(releaseConfigList) > 1 {
  switch {
  case isTrace:
   return fmt.Errorf("trace command only allows one --release argument.  Got: %s", strings.Join(globalFlags.targetReleases, " "))
  case getFlags.json:
   return fmt.Errorf("--json only allows one --release argument.  Got: %s", strings.Join(globalFlags.targetReleases, " "))
  }
 }

 if getFlags.all {
  args = []string{}
  for _, fa := range configs.FlagArtifacts {
   args = append(args, *fa.FlagDeclaration.Name)
  }
  slices.Sort(args)
 }

 var maxVariableNameLen, maxReleaseNameLen int
 var releaseNameFormat, variableNameFormat string
 valueFormat := "%s"
 showReleaseName := len(releaseConfigList) > 1 && !getFlags.distinctValues
 showVariableName := (len(args) > 1 && !getFlags.hideName) || getFlags.json
 if getFlags.json {
  variableNameFormat = `    "%s": `
  valueFormat = `"%s"`
 } else if showVariableName {
  for _, arg := range args {
   maxVariableNameLen = max(len(arg), maxVariableNameLen)
  }
  variableNameFormat = fmt.Sprintf("%%-%ds ", maxVariableNameLen)
  valueFormat = "'%s'"
  if getFlags.distinctValues {
   variableNameFormat = "%s:"
   valueFormat = "%s"
  }
 }
 if showReleaseName {
  for _, config := range releaseConfigList {
   maxReleaseNameLen = max(len(config.Name), maxReleaseNameLen)
  }
  releaseNameFormat = fmt.Sprintf("%%-%ds ", maxReleaseNameLen)
  valueFormat = "'%s'"
 }

 outputOneLine := func(variable, release, value, valueFormat string, last bool) {
  var outStr string
  if showVariableName {
   outStr += fmt.Sprintf(variableNameFormat, variable)
  }
  if showReleaseName {
   outStr += fmt.Sprintf(releaseNameFormat, release)
  }
  outStr += fmt.Sprintf(valueFormat, value)
  if getFlags.json && !last {
   outStr += ","
  }
  fmt.Println(outStr)
 }

 newArgs := []string{}
 for _, arg := range args {
  if configs.IgnoredFlags[arg] {
   fmt.Fprintf(os.Stderr, "%s is a deleted flag\n", arg)
   continue
  }
  newArgs = append(newArgs, arg)
  if _, ok := configs.FlagArtifacts[arg]; !ok {
   return fmt.Errorf("%s is not a defined build flag", arg)
  }
 }
 args = newArgs

 if getFlags.distinctValues {
  values := map[string]bool{}
  for _, arg := range args {
   for _, config := range releaseConfigList {
    val, _, err := MarshalFlagValue(config, arg)
    if err == nil && val != "" {
     values[val] = true
    }
   }
   sortedValues := rc_lib.SortedKeys(values)
   numValues := len(sortedValues)
   for idx, val := range sortedValues {
    outputOneLine(arg, "various", val, valueFormat, idx == numValues-1)
   }
  }
  return nil
 }

 if getFlags.json {
  fmt.Println(`"BuildFlags": {`)
 }

 numArgs := len(args)
 flagTypes := make(map[string]string)
 for argIdx, arg := range args {
  for _, config := range releaseConfigList {
   if isSet {
    // If this is from the set command, format the output as:
    // <default>           ""
    // trunk_staging       ""
    // trunk               ""
    //
    // ap1a                ""
    // ...
    switch {
    case config.Name == "trunk_staging":
     defaultValue, err := MarshalFlagDefaultValue(config, arg)
     if err != nil {
      return err
     }
     outputOneLine(arg, "<default>", defaultValue, valueFormat, argIdx == numArgs-1)
    case config.AconfigFlagsOnly:
     continue
    case config.Name == "trunk":
     fmt.Println()
    }
   }
   val, typ, err := MarshalFlagValue(config, arg)
   if err == nil {
    outputOneLine(arg, config.Name, val, valueFormat, argIdx == numArgs-1)
    flagTypes[arg] = typ
   } else if !getFlags.json {
    outputOneLine(arg, config.Name, "REDACTED", "%s", argIdx == numArgs-1)
   }
   if err == nil && isTrace {
    for _, trace := range config.FlagArtifacts[arg].Traces {
     fmt.Printf("  => \"%s\" in %s\n", rc_lib.MarshalValue(trace.Value), *trace.Source)
    }
   }
  }
 }
 if getFlags.json {
  fmt.Println("},")
  fmt.Println(`"BuildFlagTypes": {`)
  for argIdx, arg := range args {
   for _, config := range releaseConfigList {
    outputOneLine(arg, config.Name, flagTypes[arg], valueFormat, argIdx == numArgs-1)
   }
  }
  fmt.Println("}")
 }
 return nil
}

// Supported syntax:
//
// Legacy format to set exactly one flag:
//
// set [--dir DIR] FLAG VALUE
//
// Multi-flag format:
//
// set [--dir DIR] --redacted FLAG ...
// set [[DIR:]FLAG:redacted] [[DIR:]FLAG=VALUE] ...
func SetCommand(configs *rc_lib.ReleaseConfigs, globalFlags GlobalFlags, setFlags *SetFlags, args ...string) error {
 if len(args) < 1 {
  panic("missing command")
 }
 cmd := args[0]
 if cmd == "printdefaults" {
  setFlags.flagSet.PrintDefaults()
  return nil
 }
 if len(globalFlags.targetReleases) > 1 {
  return fmt.Errorf("set command only allows one --release argument.  Got: %s", strings.Join(globalFlags.targetReleases, " "))
 }
 setFlags.flagSet.Parse(args[1:])
 targetRelease := globalFlags.targetReleases[0]
 release, err := configs.GetReleaseConfig(targetRelease)
 targetRelease = release.Name
 if err != nil {
  return err
 }
 if release.AconfigFlagsOnly {
  return fmt.Errorf("%s does not allow build flag overrides", targetRelease)
 }

 setArgs := setFlags.flagSet.Args()
 // Handle the legacy syntax where FLAG and VALUE were separate args.
 // - --redacted FLAG
 // - FLAG VALUE
 switch {
 case len(setArgs) == 0 && setFlags.redacted:
  // No flags given.
  return fmt.Errorf("set command expected '--redacted [DIR:]FLAG' or '[DIR:]FLAG:redacted'")
 case len(setArgs) == 0:
  fmt.Printf("Nothing to do: no flags given.")
  return nil
 case setFlags.redacted || strings.HasSuffix(setArgs[0], ":redacted") || strings.Contains(setArgs[0], "="):
  // No special handling required.
 case len(setArgs) == 1 && !setFlags.redacted:
  // A single argument is only valid if we are redacting.
  return fmt.Errorf("set command expected '[DIR:]FLAG VALUE' or '[DIR:]FLAG=VALUE'")
 case len(setArgs) == 2 && !strings.Contains(setArgs[0], "="):
  // Handle the `FLAG VALUE` case.
  // Convert `FLAG VALUE` to `FLAG=VALUE` to simplify processing.
  setArgs = []string{fmt.Sprintf("%s=%s", setArgs[0], setArgs[1])}
 default:
  // No special handling.  If there are syntactic errors, they will cause errors in the for loop below.
 }

 var updatedFiles []string
 getArgs := []string{cmd}
 for _, arg := range setArgs {
  match := setArgRegexp.FindStringSubmatch(arg)
  if match == nil {
   if setFlags.redacted && !strings.HasSuffix(arg, ":redacted") {
    // Keep the rest of the logic simpler by re-parsing the flag with `:redacted` appended.
    // Leave `arg` unchanged in case we generate an error.
    match = setArgRegexp.FindStringSubmatch(arg + ":redacted")
   }
   if match == nil {
    return fmt.Errorf("Expected %q or %q, got %q", "[DIR:]FLAG=VALUE", "[DIR:]FLAG:redacted", arg)
   }
  }
  reFields := make(map[string]string)
  for i, name := range setArgRegexp.SubexpNames() {
   if i != 0 && name != "" {
    reFields[name] = match[i]
   }
  }
  flagName := reFields["flag"]
  flagArtifact, ok := release.FlagArtifacts[flagName]
  if !ok {
   return fmt.Errorf("Unknown build flag %s", flagName)
  }

  flagValue := &rc_proto.FlagValue{
   Name: proto.String(flagName),
  }
  switch {
  case reFields["value"] != "":
   flagValue.Value = rc_lib.UnmarshalValue(reFields["value"])
  case reFields["redacted"] != "" || setFlags.redacted:
   flagValue.Redacted = proto.Bool(true)
  default:
   return fmt.Errorf("Expected either '=VALUE' or ':redacted' in %q", arg)
  }
  // Write the flag to:
  // - The `DIR:` location from the arg, or
  // - The path from `--dir PATH`, or
  // - The directory from GetFlagValueDirectory() for the flag.
  var mapDir string
  switch {
  case reFields["dir"] != "":
   mapDir = reFields["dir"]
  case setFlags.dir != "":
   mapDir = setFlags.dir
  default:
   mapDir, err = configs.GetFlagValueDirectory(release, flagArtifact)
   if err != nil {
    return err
   }
  }

  rcPath := filepath.Join(mapDir, "release_configs", fmt.Sprintf("%s.textproto", targetRelease))
  // Create the release config declaration only if necessary.
  if _, err = os.Stat(rcPath); err != nil {
   if err = os.MkdirAll(filepath.Dir(rcPath), 0775); err != nil {
    return err
   }
   rcValue := &rc_proto.ReleaseConfig{
    Name: proto.String(targetRelease),
   }
   err = rc_lib.WriteMessage(rcPath, rcValue)
   if err != nil {
    return err
   }
   updatedFiles = append(updatedFiles, rcPath)
  }

  flagPath := filepath.Join(mapDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", flagName))
  err = rc_lib.WriteMessage(flagPath, flagValue)
  if err != nil {
   return err
  }
  updatedFiles = append(updatedFiles, flagPath)
  getArgs = append(getArgs, flagName)
 }

 // Reload the release configs.
 configs, err = rc_lib.ReadReleaseConfigMaps(globalFlags.maps, globalFlags.targetReleases[0], globalFlags.targetBuildVariant, globalFlags.useGetBuildVar, globalFlags.allowMissing, globalFlags.declarationsOnly)
 if err != nil {
  return err
 }
 err = getCommandInfo.run(configs, globalFlags, getArgs...)
 if err != nil {
  return err
 }
 fmt.Printf("\033[1mAdded/Updated: %s\033[0m\n", strings.Join(updatedFiles, " "))
 return nil
}

func main() {
 var globalFlags GlobalFlags
 var configs *rc_lib.ReleaseConfigs
 topDir, err := rc_lib.GetTopDir()

 // Handle the common arguments
 defaultVariant := os.Getenv("TARGET_BUILD_VARIANT")
 if defaultVariant == "" {
  defaultVariant = "eng"
 }

 flag.Usage = func() {
  cmdNames := rc_lib.SortedKeys(commandMap)
  helpHeader := "Usage:  build-flag [GLOBAL_OPTION...] COMMAND [COMMAND_OPTION...] FLAG_NAME [FLAG_NAME...]\n" +
   "Supported commands:\n" +
   func() string {
    var ret string
    for _, name := range cmdNames {
     ret += fmt.Sprintf("  %-6s %s\n", name, commandMap[name].helpText)
    }
    return ret
   }() +
   "\nSupported global options:\n"
  fmt.Fprintf(flag.CommandLine.Output(), helpHeader)
  flag.PrintDefaults()
  for _, cmdName := range cmdNames {
   cmd := commandMap[cmdName]
   fmt.Fprintf(flag.CommandLine.Output(), "\nSupported command options for command %q (%s):\n", cmdName, cmd.helpText)
   cmd.run(configs, globalFlags, "printdefaults")
  }
  // TODO: figure out if there is a good way to output releaseFlagSet.PrintDefaults().
 }
 flag.StringVar(&globalFlags.top, "top", topDir, "path to top of workspace")
 flag.BoolVar(&globalFlags.quiet, "quiet", false, "disable warning messages")
 flag.Var(&globalFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
 flag.StringVar(&globalFlags.mapsFile, "maps-file", "", "path to a file containing a list of release_config_map.textproto paths")
 flag.StringVar(&globalFlags.outDir, "out-dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
 flag.Var(&globalFlags.targetReleases, "release", "TARGET_RELEASE for this build")
 flag.StringVar(&globalFlags.targetBuildVariant, "variant", defaultVariant, "TARGET_BUILD_VARIANT for this build")
 flag.BoolVar(&globalFlags.allowMissing, "allow-missing", false, "Use trunk_staging values if release not found")
 flag.BoolVar(&globalFlags.allReleases, "all-releases", false, "operate on all releases. (Ignored for set command)")
 flag.BoolVar(&globalFlags.useGetBuildVar, "use-get-build-var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get needed maps")
 flag.BoolVar(&globalFlags.debug, "debug", false, "turn on debugging output for errors")
 flag.BoolVar(&globalFlags.declarationsOnly, "declarations-only", false, "only process flag declarations")
 flag.Parse()

 if _, ok := commandMap[flag.Arg(0)]; !ok {
  fmt.Fprintf(os.Stderr, "Unsupported command %q\n", flag.Arg(0))
  flag.Usage()
  os.Exit(2)
 }

 errorExit := func(err error) {
  if globalFlags.debug {
   panic(err)
  }
  fmt.Fprintf(os.Stderr, "%s\n", err)
  os.Exit(1)
 }

 if globalFlags.quiet {
  rc_lib.DisableWarnings()
 }

 if globalFlags.mapsFile != "" {
  if len(globalFlags.maps) > 0 {
   panic(fmt.Errorf("Cannot specify both --map and --maps-file"))
  }
  if err := globalFlags.maps.ReadFromFile(globalFlags.mapsFile); err != nil {
   panic(fmt.Errorf("Could not read %s", globalFlags.mapsFile))
  }
 }

 if len(globalFlags.targetReleases) == 0 {
  release, ok := os.LookupEnv("TARGET_RELEASE")
  if ok {
   globalFlags.targetReleases = rc_lib.StringList{release}
  } else {
   globalFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
  }
 }

 if err = os.Chdir(globalFlags.top); err != nil {
  errorExit(err)
 }

 // Get the current state of flagging.
 relName := globalFlags.targetReleases[0]
 if relName == "--all" || relName == "-all" {
  globalFlags.allReleases = true
 }
 configs, err = rc_lib.ReadReleaseConfigMaps(globalFlags.maps, relName, globalFlags.targetBuildVariant, globalFlags.useGetBuildVar, globalFlags.allowMissing, globalFlags.declarationsOnly)
 if err != nil {
  errorExit(err)
 }

 if cmd, ok := commandMap[flag.Arg(0)]; ok {
  if err = cmd.run(configs, globalFlags, flag.Args()...); err != nil {
   errorExit(err)
  }
 }
}

[Dauer der Verarbeitung: 0.29 Sekunden, vorverarbeitet 2026-06-28]