github.com/undoio/delve@v1.9.0/pkg/goversion/go_version.go (about)

     1  package goversion
     2  
     3  import (
     4  	"os/exec"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  // GoVersion represents the Go version of
    10  // the Go compiler version used to compile
    11  // the target binary.
    12  type GoVersion struct {
    13  	Major    int
    14  	Minor    int
    15  	Rev      int
    16  	Beta     int
    17  	RC       int
    18  	Proposal string
    19  }
    20  
    21  var (
    22  	GoVer18Beta = GoVersion{1, 8, -1, 0, 0, ""}
    23  )
    24  
    25  // Parse parses a go version string
    26  func Parse(ver string) (GoVersion, bool) {
    27  	var r GoVersion
    28  	var err1, err2, err3 error
    29  
    30  	if strings.HasPrefix(ver, "devel") {
    31  		return GoVersion{-1, 0, 0, 0, 0, ""}, true
    32  	}
    33  
    34  	if strings.HasPrefix(ver, "go") {
    35  		ver := strings.Split(ver, " ")[0]
    36  		v := strings.SplitN(ver[2:], ".", 4)
    37  		switch len(v) {
    38  		case 2:
    39  			r.Major, err1 = strconv.Atoi(v[0])
    40  			var vr []string
    41  
    42  			if vr = strings.SplitN(v[1], "beta", 2); len(vr) == 2 {
    43  				r.Beta, err3 = strconv.Atoi(vr[1])
    44  			} else if vr = strings.SplitN(v[1], "b", 2); len(vr) == 2 {
    45  				if _, err := strconv.Atoi(vr[1]); err != nil {
    46  					return GoVersion{}, false
    47  				}
    48  			} else {
    49  				vr = strings.SplitN(v[1], "rc", 2)
    50  				if len(vr) == 2 {
    51  					r.RC, err3 = strconv.Atoi(vr[1])
    52  				} else {
    53  					r.Minor, err2 = strconv.Atoi(v[1])
    54  					if err2 != nil {
    55  						return GoVersion{}, false
    56  					}
    57  					return r, true
    58  				}
    59  			}
    60  
    61  			r.Minor, err2 = strconv.Atoi(vr[0])
    62  			r.Rev = -1
    63  			r.Proposal = ""
    64  
    65  			if err1 != nil || err2 != nil || err3 != nil {
    66  				return GoVersion{}, false
    67  			}
    68  
    69  			return r, true
    70  
    71  		case 3:
    72  
    73  			r.Major, err1 = strconv.Atoi(v[0])
    74  			r.Minor, err2 = strconv.Atoi(v[1])
    75  
    76  			vr := strings.SplitN(v[2], "b", 2)
    77  			if len(vr) == 2 {
    78  				r.Rev, err3 = strconv.Atoi(vr[0])
    79  			} else {
    80  				r.Rev, err3 = strconv.Atoi(v[2])
    81  			}
    82  
    83  			r.Proposal = ""
    84  			if err1 != nil || err2 != nil || err3 != nil {
    85  				return GoVersion{}, false
    86  			}
    87  
    88  			return r, true
    89  
    90  		case 4:
    91  
    92  			r.Major, err1 = strconv.Atoi(v[0])
    93  			r.Minor, err2 = strconv.Atoi(v[1])
    94  			r.Rev, err3 = strconv.Atoi(v[2])
    95  			r.Proposal = v[3]
    96  			if err1 != nil || err2 != nil || err3 != nil || r.Proposal == "" {
    97  				return GoVersion{}, false
    98  			}
    99  
   100  			return r, true
   101  
   102  		default:
   103  			return GoVersion{}, false
   104  		}
   105  	}
   106  
   107  	return GoVersion{}, false
   108  }
   109  
   110  // AfterOrEqual returns whether one GoVersion is after or
   111  // equal to the other.
   112  func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
   113  	if v.Major < b.Major {
   114  		return false
   115  	} else if v.Major > b.Major {
   116  		return true
   117  	}
   118  
   119  	if v.Minor < b.Minor {
   120  		return false
   121  	} else if v.Minor > b.Minor {
   122  		return true
   123  	}
   124  
   125  	if v.Rev < b.Rev {
   126  		return false
   127  	} else if v.Rev > b.Rev {
   128  		return true
   129  	}
   130  
   131  	if v.Beta < b.Beta {
   132  		return false
   133  	}
   134  
   135  	if v.RC < b.RC {
   136  		return false
   137  	}
   138  
   139  	return true
   140  }
   141  
   142  // IsDevel returns whether the GoVersion
   143  // is a development version.
   144  func (v *GoVersion) IsDevel() bool {
   145  	return v.Major < 0
   146  }
   147  
   148  const goVersionPrefix = "go version "
   149  
   150  // Installed runs "go version" and parses the output
   151  func Installed() (GoVersion, bool) {
   152  	out, err := exec.Command("go", "version").CombinedOutput()
   153  	if err != nil {
   154  		return GoVersion{}, false
   155  	}
   156  
   157  	s := string(out)
   158  
   159  	if !strings.HasPrefix(s, goVersionPrefix) {
   160  		return GoVersion{}, false
   161  	}
   162  
   163  	return Parse(s[len(goVersionPrefix):])
   164  }
   165  
   166  // VersionAfterOrEqual checks that version (as returned by runtime.Version()
   167  // or go version) is major.minor or a later version, or a development
   168  // version.
   169  func VersionAfterOrEqual(version string, major, minor int) bool {
   170  	return VersionAfterOrEqualRev(version, major, minor, -1)
   171  }
   172  
   173  // VersionAfterOrEqualRev checks that version (as returned by runtime.Version()
   174  // or go version) is major.minor or a later version, or a development
   175  // version.
   176  func VersionAfterOrEqualRev(version string, major, minor, rev int) bool {
   177  	ver, _ := Parse(version)
   178  	if ver.IsDevel() {
   179  		return true
   180  	}
   181  	return ver.AfterOrEqual(GoVersion{major, minor, rev, 0, 0, ""})
   182  }
   183  
   184  const producerVersionPrefix = "Go cmd/compile "
   185  
   186  // ProducerAfterOrEqual checks that the DW_AT_producer version is
   187  // major.minor or a later version, or a development version.
   188  func ProducerAfterOrEqual(producer string, major, minor int) bool {
   189  	ver := ParseProducer(producer)
   190  	if ver.IsDevel() {
   191  		return true
   192  	}
   193  	return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
   194  }
   195  
   196  func ParseProducer(producer string) GoVersion {
   197  	producer = strings.TrimPrefix(producer, producerVersionPrefix)
   198  	ver, _ := Parse(producer)
   199  	return ver
   200  }