version.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. // Copyright 2020 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gocommand
  5. import (
  6. "context"
  7. "fmt"
  8. "regexp"
  9. "strings"
  10. )
  11. // GoVersion reports the minor version number of the highest release
  12. // tag built into the go command on the PATH.
  13. //
  14. // Note that this may be higher than the version of the go tool used
  15. // to build this application, and thus the versions of the standard
  16. // go/{scanner,parser,ast,types} packages that are linked into it.
  17. // In that case, callers should either downgrade to the version of
  18. // go used to build the application, or report an error that the
  19. // application is too old to use the go command on the PATH.
  20. func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
  21. inv.Verb = "list"
  22. inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
  23. inv.BuildFlags = nil // This is not a build command.
  24. inv.ModFlag = ""
  25. inv.ModFile = ""
  26. inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
  27. stdoutBytes, err := r.Run(ctx, inv)
  28. if err != nil {
  29. return 0, err
  30. }
  31. stdout := stdoutBytes.String()
  32. if len(stdout) < 3 {
  33. return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
  34. }
  35. // Split up "[go1.1 go1.15]" and return highest go1.X value.
  36. tags := strings.Fields(stdout[1 : len(stdout)-2])
  37. for i := len(tags) - 1; i >= 0; i-- {
  38. var version int
  39. if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
  40. continue
  41. }
  42. return version, nil
  43. }
  44. return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
  45. }
  46. // GoVersionOutput returns the complete output of the go version command.
  47. func GoVersionOutput(ctx context.Context, inv Invocation, r *Runner) (string, error) {
  48. inv.Verb = "version"
  49. goVersion, err := r.Run(ctx, inv)
  50. if err != nil {
  51. return "", err
  52. }
  53. return goVersion.String(), nil
  54. }
  55. // ParseGoVersionOutput extracts the Go version string
  56. // from the output of the "go version" command.
  57. // Given an unrecognized form, it returns an empty string.
  58. func ParseGoVersionOutput(data string) string {
  59. re := regexp.MustCompile(`^go version (go\S+|devel \S+)`)
  60. m := re.FindStringSubmatch(data)
  61. if len(m) != 2 {
  62. return "" // unrecognized version
  63. }
  64. return m[1]
  65. }