version.go (2646B)
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 5 package gocommand 6 7 import ( 8 "context" 9 "fmt" 10 "regexp" 11 "strings" 12 ) 13 14 // GoVersion reports the minor version number of the highest release 15 // tag built into the go command on the PATH. 16 // 17 // Note that this may be higher than the version of the go tool used 18 // to build this application, and thus the versions of the standard 19 // go/{scanner,parser,ast,types} packages that are linked into it. 20 // In that case, callers should either downgrade to the version of 21 // go used to build the application, or report an error that the 22 // application is too old to use the go command on the PATH. 23 func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) { 24 inv.Verb = "list" 25 inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`} 26 inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off") 27 // Unset any unneeded flags, and remove them from BuildFlags, if they're 28 // present. 29 inv.ModFile = "" 30 inv.ModFlag = "" 31 var buildFlags []string 32 for _, flag := range inv.BuildFlags { 33 // Flags can be prefixed by one or two dashes. 34 f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-") 35 if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") { 36 continue 37 } 38 buildFlags = append(buildFlags, flag) 39 } 40 inv.BuildFlags = buildFlags 41 stdoutBytes, err := r.Run(ctx, inv) 42 if err != nil { 43 return 0, err 44 } 45 stdout := stdoutBytes.String() 46 if len(stdout) < 3 { 47 return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout) 48 } 49 // Split up "[go1.1 go1.15]" and return highest go1.X value. 50 tags := strings.Fields(stdout[1 : len(stdout)-2]) 51 for i := len(tags) - 1; i >= 0; i-- { 52 var version int 53 if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil { 54 continue 55 } 56 return version, nil 57 } 58 return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags) 59 } 60 61 // GoVersionOutput returns the complete output of the go version command. 62 func GoVersionOutput(ctx context.Context, inv Invocation, r *Runner) (string, error) { 63 inv.Verb = "version" 64 goVersion, err := r.Run(ctx, inv) 65 if err != nil { 66 return "", err 67 } 68 return goVersion.String(), nil 69 } 70 71 // ParseGoVersionOutput extracts the Go version string 72 // from the output of the "go version" command. 73 // Given an unrecognized form, it returns an empty string. 74 func ParseGoVersionOutput(data string) string { 75 re := regexp.MustCompile(`^go version (go\S+|devel \S+)`) 76 m := re.FindStringSubmatch(data) 77 if len(m) != 2 { 78 return "" // unrecognized version 79 } 80 return m[1] 81 }