golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/gorebuild/gover.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  // This code is copied from cmd/go/internal/gover.
     6  // Perhaps some day a go/version package will provide this functionality.
     7  
     8  package main
     9  
    10  import "fmt"
    11  
    12  // Go returns the version string for Go N.M ("goN.M")
    13  func Go(N, M int) string {
    14  	return fmt.Sprintf("go%d.%d", N, M)
    15  }
    16  
    17  // Compare returns -1, 0, or +1 depending on whether
    18  // x < y, x == y, or x > y, interpreted as toolchain versions.
    19  // The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
    20  // Malformed versions compare less than well-formed versions and equal to each other.
    21  // The language version "go1.21" compares less than the release candidate and eventual releases "go1.21rc1" and "go1.21.0".
    22  func Compare(x, y string) int {
    23  	vx := parse(x)
    24  	vy := parse(y)
    25  
    26  	if c := cmpInt(vx.major, vy.major); c != 0 {
    27  		return c
    28  	}
    29  	if c := cmpInt(vx.minor, vy.minor); c != 0 {
    30  		return c
    31  	}
    32  	if c := cmpInt(vx.patch, vy.patch); c != 0 {
    33  		return c
    34  	}
    35  	if vx.kind != vy.kind {
    36  		if vx.kind < vy.kind {
    37  			return -1
    38  		}
    39  		return +1
    40  	}
    41  	if c := cmpInt(vx.pre, vy.pre); c != 0 {
    42  		return c
    43  	}
    44  	return 0
    45  }
    46  
    47  // IsValid reports whether the version x is valid.
    48  func IsValid(x string) bool {
    49  	return parse(x) != version{}
    50  }
    51  
    52  // parse parses the Go version string x into a version.
    53  // It returns the zero version if x is malformed.
    54  func parse(x string) version {
    55  	var v version
    56  	// Parse major version.
    57  	if len(x) < 2 || x[:2] != "go" {
    58  		return version{}
    59  	}
    60  	x = x[2:]
    61  	var ok bool
    62  	v.major, x, ok = cutInt(x)
    63  	if !ok {
    64  		return version{}
    65  	}
    66  	if x == "" {
    67  		// Interpret "1" as "1.0.0".
    68  		v.minor = "0"
    69  		v.patch = "0"
    70  		return v
    71  	}
    72  
    73  	// Parse . before minor version.
    74  	if x[0] != '.' {
    75  		return version{}
    76  	}
    77  
    78  	// Parse minor version.
    79  	v.minor, x, ok = cutInt(x[1:])
    80  	if !ok {
    81  		return version{}
    82  	}
    83  	if x == "" {
    84  		// Patch missing is same as "0" for older versions.
    85  		// Starting in Go 1.21, patch missing is different from explicit .0.
    86  		if cmpInt(v.minor, "21") < 0 {
    87  			v.patch = "0"
    88  		}
    89  		return v
    90  	}
    91  
    92  	// Parse patch if present.
    93  	if x[0] == '.' {
    94  		v.patch, x, ok = cutInt(x[1:])
    95  		if !ok || x != "" {
    96  			// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
    97  			// Allowing them would be a bit confusing because we already have:
    98  			//	1.21 < 1.21rc1
    99  			// But a prerelease of a patch would have the opposite effect:
   100  			//	1.21.3rc1 < 1.21.3
   101  			// We've never needed them before, so let's not start now.
   102  			return version{}
   103  		}
   104  		return v
   105  	}
   106  
   107  	// Parse prerelease.
   108  	i := 0
   109  	for i < len(x) && (x[i] < '0' || '9' < x[i]) {
   110  		if x[i] < 'a' || 'z' < x[i] {
   111  			return version{}
   112  		}
   113  		i++
   114  	}
   115  	if i == 0 {
   116  		return version{}
   117  	}
   118  	v.kind, x = x[:i], x[i:]
   119  	if x == "" {
   120  		return v
   121  	}
   122  	v.pre, x, ok = cutInt(x)
   123  	if !ok || x != "" {
   124  		return version{}
   125  	}
   126  
   127  	return v
   128  }
   129  
   130  // cutInt scans the leading decimal number at the start of x to an integer
   131  // and returns that value and the rest of the string.
   132  func cutInt(x string) (n, rest string, ok bool) {
   133  	i := 0
   134  	for i < len(x) && '0' <= x[i] && x[i] <= '9' {
   135  		i++
   136  	}
   137  	if i == 0 || x[0] == '0' && i != 1 {
   138  		return "", "", false
   139  	}
   140  	return x[:i], x[i:], true
   141  }
   142  
   143  // cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
   144  // (Copied from golang.org/x/mod/semver's compareInt.)
   145  func cmpInt(x, y string) int {
   146  	if x == y {
   147  		return 0
   148  	}
   149  	if len(x) < len(y) {
   150  		return -1
   151  	}
   152  	if len(x) > len(y) {
   153  		return +1
   154  	}
   155  	if x < y {
   156  		return -1
   157  	} else {
   158  		return +1
   159  	}
   160  }
   161  
   162  // A version is a parsed Go version: major[.minor[.patch]][kind[pre]]
   163  // The numbers are the original decimal strings to avoid integer overflows
   164  // and since there is very little actual math. (Probably overflow doesn't matter in practice,
   165  // but at the time this code was written, there was an existing test that used
   166  // go1.99999999999, which does not fit in an int on 32-bit platforms.
   167  // The "big decimal" representation avoids the problem entirely.)
   168  type version struct {
   169  	major string // decimal
   170  	minor string // decimal or ""
   171  	patch string // decimal or ""
   172  	kind  string // "", "alpha", "beta", "rc"
   173  	pre   string // decimal or ""
   174  }