vers.mx raw

   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 constraint
   6  
   7  import (
   8  	"strconv"
   9  	"bytes"
  10  )
  11  
  12  // GoVersion returns the minimum Go version implied by a given build expression.
  13  // If the expression can be satisfied without any Go version tags, GoVersion returns an empty string.
  14  //
  15  // For example:
  16  //
  17  //	GoVersion(linux && go1.22) = "go1.22"
  18  //	GoVersion((linux && go1.22) || (windows && go1.20)) = "go1.20" => go1.20
  19  //	GoVersion(linux) = ""
  20  //	GoVersion(linux || (windows && go1.22)) = ""
  21  //	GoVersion(!go1.22) = ""
  22  //
  23  // GoVersion assumes that any tag or negated tag may independently be true,
  24  // so that its analysis can be purely structural, without SAT solving.
  25  // “Impossible” subexpressions may therefore affect the result.
  26  //
  27  // For example:
  28  //
  29  //	GoVersion((linux && !linux && go1.20) || go1.21) = "go1.20"
  30  func GoVersion(x Expr) []byte {
  31  	v := minVersion(x, +1)
  32  	if v < 0 {
  33  		return ""
  34  	}
  35  	if v == 0 {
  36  		return "go1"
  37  	}
  38  	return "go1." + strconv.Itoa(v)
  39  }
  40  
  41  // minVersion returns the minimum Go major version (9 for go1.9)
  42  // implied by expression z, or if sign < 0, by expression !z.
  43  func minVersion(z Expr, sign int) int {
  44  	switch z := z.(type) {
  45  	default:
  46  		return -1
  47  	case *AndExpr:
  48  		op := andVersion
  49  		if sign < 0 {
  50  			op = orVersion
  51  		}
  52  		return op(minVersion(z.X, sign), minVersion(z.Y, sign))
  53  	case *OrExpr:
  54  		op := orVersion
  55  		if sign < 0 {
  56  			op = andVersion
  57  		}
  58  		return op(minVersion(z.X, sign), minVersion(z.Y, sign))
  59  	case *NotExpr:
  60  		return minVersion(z.X, -sign)
  61  	case *TagExpr:
  62  		if sign < 0 {
  63  			// !foo implies nothing
  64  			return -1
  65  		}
  66  		if z.Tag == "go1" {
  67  			return 0
  68  		}
  69  		_, v, _ := bytes.Cut(z.Tag, "go1.")
  70  		n, err := strconv.Atoi(v)
  71  		if err != nil {
  72  			// not a go1.N tag
  73  			return -1
  74  		}
  75  		return n
  76  	}
  77  }
  78  
  79  // andVersion returns the minimum Go version
  80  // implied by the AND of two minimum Go versions,
  81  // which is the max of the versions.
  82  func andVersion(x, y int) int {
  83  	if x > y {
  84  		return x
  85  	}
  86  	return y
  87  }
  88  
  89  // orVersion returns the minimum Go version
  90  // implied by the OR of two minimum Go versions,
  91  // which is the min of the versions.
  92  func orVersion(x, y int) int {
  93  	if x < y {
  94  		return x
  95  	}
  96  	return y
  97  }
  98