src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

version.go (2489B)


      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 	"slices"
     12 	"strings"
     13 )
     14 
     15 // GoVersion reports the minor version number of the highest release
     16 // tag built into the go command on the PATH.
     17 //
     18 // Note that this may be higher than the version of the go tool used
     19 // to build this application, and thus the versions of the standard
     20 // go/{scanner,parser,ast,types} packages that are linked into it.
     21 // In that case, callers should either downgrade to the version of
     22 // go used to build the application, or report an error that the
     23 // application is too old to use the go command on the PATH.
     24 func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
     25 	inv.Verb = "list"
     26 	inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
     27 	inv.BuildFlags = nil // This is not a build command.
     28 	inv.ModFlag = ""
     29 	inv.ModFile = ""
     30 	// Set GO111MODULE=off so that we are immune to errors in go.{work,mod}.
     31 	// Unfortunately, this breaks the Go 1.21+ toolchain directive and
     32 	// may affect the set of ReleaseTags; see #68495.
     33 	inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
     34 
     35 	stdoutBytes, err := r.Run(ctx, inv)
     36 	if err != nil {
     37 		return 0, err
     38 	}
     39 	stdout := stdoutBytes.String()
     40 	if len(stdout) < 3 {
     41 		return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
     42 	}
     43 	// Split up "[go1.1 go1.15]" and return highest go1.X value.
     44 	tags := strings.Fields(stdout[1 : len(stdout)-2])
     45 	for _, tag := range slices.Backward(tags) {
     46 		var version int
     47 		if _, err := fmt.Sscanf(tag, "go1.%d", &version); err != nil {
     48 			continue
     49 		}
     50 		return version, nil
     51 	}
     52 	return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
     53 }
     54 
     55 // GoVersionOutput returns the complete output of the go version command.
     56 func GoVersionOutput(ctx context.Context, inv Invocation, r *Runner) (string, error) {
     57 	inv.Verb = "version"
     58 	goVersion, err := r.Run(ctx, inv)
     59 	if err != nil {
     60 		return "", err
     61 	}
     62 	return goVersion.String(), nil
     63 }
     64 
     65 // ParseGoVersionOutput extracts the Go version string
     66 // from the output of the "go version" command.
     67 // Given an unrecognized form, it returns an empty string.
     68 func ParseGoVersionOutput(data string) string {
     69 	re := regexp.MustCompile(`^go version (go\S+|devel \S+)`)
     70 	m := re.FindStringSubmatch(data)
     71 	if len(m) != 2 {
     72 		return "" // unrecognized version
     73 	}
     74 	return m[1]
     75 }