Skip to content

Commit c3c09e9

Browse files
committed
consider GOROOT/api as fallback to determine Go version
1 parent 2dc46a8 commit c3c09e9

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

builder/env.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,123 @@
11
package builder
22

33
import (
4+
"errors"
5+
"fmt"
6+
"io"
47
"io/ioutil"
58
"os"
69
"os/exec"
710
"path/filepath"
11+
"regexp"
812
"sort"
913
"strings"
1014

1115
"tinygo.org/x/go-llvm"
1216
)
1317

18+
// parseGorootVersion returns the major and minor version for a given Go version
19+
// string of the form `goX.Y.Z`.
20+
// Returns (0, 0) if the version cannot be determined.
21+
func parseGorootVersion(version string) (int, int, error) {
22+
var (
23+
maj, min int
24+
pch string
25+
)
26+
n, err := fmt.Sscanf(version, "go%d.%d%s", &maj, &min, &pch)
27+
if n == 2 && io.EOF == err {
28+
// Means there were no trailing characters (i.e., not an alpha/beta)
29+
err = nil
30+
}
31+
if nil != err {
32+
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
33+
}
34+
return maj, min, nil
35+
}
36+
37+
// getGorootVersion returns the major and minor version for a given GOROOT path.
38+
// If the version cannot be determined, (0, 0) is returned.
39+
func getGorootVersion(goroot string) (int, int, error) {
40+
const errPrefix = "could not parse Go version"
41+
s, err := GorootVersionString(goroot)
42+
if err != nil {
43+
return 0, 0, err
44+
}
45+
46+
if "" == s {
47+
return 0, 0, fmt.Errorf("%s: version string is empty", errPrefix)
48+
}
49+
50+
if strings.HasPrefix(s, "devel") {
51+
maj, min, err := getGorootApiVersion(goroot)
52+
if nil != err {
53+
return 0, 0, fmt.Errorf("%s: invalid GOROOT API version: %s", errPrefix, err)
54+
}
55+
return maj, min, nil
56+
}
57+
58+
if !strings.HasPrefix(s, "go") {
59+
return 0, 0, fmt.Errorf("%s: version does not start with 'go' prefix", errPrefix)
60+
}
61+
62+
parts := strings.Split(s[2:], ".")
63+
if len(parts) < 2 {
64+
return 0, 0, fmt.Errorf("%s: version has less than two parts", errPrefix)
65+
}
66+
67+
return parseGorootVersion(s)
68+
}
69+
70+
// getGorootApiVersion returns the major and minor version of the Go API files
71+
// defined for a given GOROOT path.
72+
// If the version cannot be determined, (0, 0) is returned.
73+
func getGorootApiVersion(goroot string) (int, int, error) {
74+
info, err := ioutil.ReadDir(filepath.Join(goroot, "api"))
75+
if nil != err {
76+
return 0, 0, fmt.Errorf("could not read API feature directory: %s", err)
77+
}
78+
maj, min := -1, -1
79+
for _, f := range info {
80+
if !strings.HasPrefix(f.Name(), "go") || f.IsDir() {
81+
continue
82+
}
83+
vers := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
84+
part := strings.Split(vers[2:], ".")
85+
if len(part) < 2 {
86+
continue
87+
}
88+
vmaj, vmin, err := parseGorootVersion(vers)
89+
if nil != err {
90+
continue
91+
}
92+
if vmaj >= maj && vmin > min {
93+
maj, min = vmaj, vmin
94+
}
95+
}
96+
if maj < 0 || min < 0 {
97+
return 0, 0, errors.New("no valid API feature files")
98+
}
99+
return maj, min, nil
100+
}
101+
102+
// GorootVersionString returns the version string as reported by the Go
103+
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
104+
// can have some variations (for beta releases, for example).
105+
func GorootVersionString(goroot string) (string, error) {
106+
if data, err := ioutil.ReadFile(filepath.Join(
107+
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
108+
r := regexp.MustCompile("const TheVersion = `(.*)`")
109+
matches := r.FindSubmatch(data)
110+
if len(matches) != 2 {
111+
return "", errors.New("Invalid go version output:\n" + string(data))
112+
}
113+
return string(matches[1]), nil
114+
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
115+
return string(data), nil
116+
} else {
117+
return "", err
118+
}
119+
}
120+
14121
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
15122
// multiple locations, which should make it find the directory when installed in
16123
// various ways.

0 commit comments

Comments
 (0)