github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/gover/toolchain.go (about)

     1  // Copyright 2023 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 gover
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"strings"
    12  
    13  	"github.com/go-asm/go/cmd/go/base"
    14  )
    15  
    16  // FromToolchain returns the Go version for the named toolchain,
    17  // derived from the name itself (not by running the toolchain).
    18  // A toolchain is named "goVERSION".
    19  // A suffix after the VERSION introduced by a -, space, or tab is removed.
    20  // Examples:
    21  //
    22  //	FromToolchain("go1.2.3") == "1.2.3"
    23  //	FromToolchain("go1.2.3-bigcorp") == "1.2.3"
    24  //	FromToolchain("invalid") == ""
    25  func FromToolchain(name string) string {
    26  	if strings.ContainsAny(name, "\\/") {
    27  		// The suffix must not include a path separator, since that would cause
    28  		// exec.LookPath to resolve it from a relative directory instead of from
    29  		// $PATH.
    30  		return ""
    31  	}
    32  
    33  	var v string
    34  	if strings.HasPrefix(name, "go") {
    35  		v = name[2:]
    36  	} else {
    37  		return ""
    38  	}
    39  	// Some builds use custom suffixes; strip them.
    40  	if i := strings.IndexAny(v, " \t-"); i >= 0 {
    41  		v = v[:i]
    42  	}
    43  	if !IsValid(v) {
    44  		return ""
    45  	}
    46  	return v
    47  }
    48  
    49  func maybeToolchainVersion(name string) string {
    50  	if IsValid(name) {
    51  		return name
    52  	}
    53  	return FromToolchain(name)
    54  }
    55  
    56  // ToolchainMax returns the maximum of x and y interpreted as toolchain names,
    57  // compared using Compare(FromToolchain(x), FromToolchain(y)).
    58  // If x and y compare equal, Max returns x.
    59  func ToolchainMax(x, y string) string {
    60  	if Compare(FromToolchain(x), FromToolchain(y)) < 0 {
    61  		return y
    62  	}
    63  	return x
    64  }
    65  
    66  // Startup records the information that went into the startup-time version switch.
    67  // It is initialized by switchGoToolchain.
    68  var Startup struct {
    69  	GOTOOLCHAIN   string // $GOTOOLCHAIN setting
    70  	AutoFile      string // go.mod or go.work file consulted
    71  	AutoGoVersion string // go line found in file
    72  	AutoToolchain string // toolchain line found in file
    73  }
    74  
    75  // A TooNewError explains that a module is too new for this version of Go.
    76  type TooNewError struct {
    77  	What      string
    78  	GoVersion string
    79  	Toolchain string // for callers if they want to use it, but not printed
    80  }
    81  
    82  func (e *TooNewError) Error() string {
    83  	var explain string
    84  	if Startup.GOTOOLCHAIN != "" && Startup.GOTOOLCHAIN != "auto" {
    85  		explain = "; GOTOOLCHAIN=" + Startup.GOTOOLCHAIN
    86  	}
    87  	if Startup.AutoFile != "" && (Startup.AutoGoVersion != "" || Startup.AutoToolchain != "") {
    88  		explain += fmt.Sprintf("; %s sets ", base.ShortPath(Startup.AutoFile))
    89  		if Startup.AutoToolchain != "" {
    90  			explain += "toolchain " + Startup.AutoToolchain
    91  		} else {
    92  			explain += "go " + Startup.AutoGoVersion
    93  		}
    94  	}
    95  	return fmt.Sprintf("%v requires go >= %v (running go %v%v)", e.What, e.GoVersion, Local(), explain)
    96  }
    97  
    98  var ErrTooNew = errors.New("module too new")
    99  
   100  func (e *TooNewError) Is(err error) bool {
   101  	return err == ErrTooNew
   102  }
   103  
   104  // A Switcher provides the ability to switch to a new toolchain in response to TooNewErrors.
   105  // See [github.com/go-asm/go/cmd/go/toolchain.Switcher] for documentation.
   106  type Switcher interface {
   107  	Error(err error)
   108  	Switch(ctx context.Context)
   109  }