Source file src/cmd/go/internal/gover/toolchain.go

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

View as plain text