Skip to content

Commit 1c5f51f

Browse files
committed
runtime: Version() returns Go version provided by the compiler.
This change improves Go version detection for the provided GOROOT by falling back to `go version` command output if VERSION file doesn't exist (fixes #1018). If GOROOT-based version detection failed, it falls back further to the Go version from GopherJS release or a minor version in "go1.x" format. The detected value is then injected by the compiler into the generated JS file and picked up by the `runtime` package.
1 parent a0dbb0e commit 1c5f51f

File tree

7 files changed

+100
-12
lines changed

7 files changed

+100
-12
lines changed

build/build.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@ func (s *Session) InstallSuffix() string {
509509
return ""
510510
}
511511

512+
// GoRelease returns Go release version this session is building with.
513+
func (s *Session) GoRelease() string {
514+
return compiler.GoRelease(s.options.GOROOT)
515+
}
516+
512517
func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string) error {
513518
if s.Watcher != nil {
514519
s.Watcher.Add(packagePath)
@@ -778,7 +783,7 @@ func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string)
778783
if err != nil {
779784
return err
780785
}
781-
return compiler.WriteProgramCode(deps, sourceMapFilter)
786+
return compiler.WriteProgramCode(deps, sourceMapFilter, s.GoRelease())
782787
}
783788

784789
func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool) func(generatedLine, generatedColumn int, originalPos token.Position) {

compiler/compiler.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ type dceInfo struct {
173173
methodFilter string
174174
}
175175

176-
func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error {
176+
func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter, goVersion string) error {
177177
mainPkg := pkgs[len(pkgs)-1]
178178
minify := mainPkg.Minified
179179

@@ -242,6 +242,10 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error {
242242
if _, err := w.Write([]byte("\"use strict\";\n(function() {\n\n")); err != nil {
243243
return err
244244
}
245+
if _, err := w.Write([]byte(fmt.Sprintf("var $goVersion = %q;\n", goVersion))); err != nil {
246+
return err
247+
}
248+
245249
preludeJS := prelude.Prelude
246250
if minify {
247251
preludeJS = prelude.Minified
@@ -260,10 +264,9 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error {
260264
}
261265
}
262266

263-
if _, err := w.Write([]byte("$synthesizeMethods();\n$initAllLinknames();var $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil {
267+
if _, err := w.Write([]byte("$synthesizeMethods();\n$initAllLinknames();\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil {
264268
return err
265269
}
266-
267270
return nil
268271
}
269272

compiler/natives/src/runtime/runtime.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func init() {
7373
js.Global.Set("$jsObjectPtr", jsPkg.Get("Object").Get("ptr"))
7474
js.Global.Set("$jsErrorPtr", jsPkg.Get("Error").Get("ptr"))
7575
js.Global.Set("$throwRuntimeError", js.InternalObject(throw))
76+
buildVersion = js.Global.Get("$goVersion").String()
7677
// avoid dead code elimination
7778
var e error
7879
e = &TypeAssertionError{}
@@ -353,9 +354,10 @@ func LockOSThread() {}
353354

354355
func UnlockOSThread() {}
355356

357+
var buildVersion string // Set by init()
358+
356359
func Version() string {
357-
// TODO: Make this smarter
358-
return "go1.17rc1"
360+
return buildVersion
359361
}
360362

361363
func StartTrace() error { return nil }

compiler/version_check.go

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
package compiler
55

66
import (
7-
"bytes"
87
"fmt"
9-
"io/ioutil"
108
"os"
9+
"os/exec"
1110
"path/filepath"
1211
"strconv"
12+
"strings"
1313
)
1414

1515
// Version is the GopherJS compiler version string.
@@ -25,12 +25,56 @@ func CheckGoVersion(goroot string) error {
2525
if nvc, err := strconv.ParseBool(os.Getenv("GOPHERJS_SKIP_VERSION_CHECK")); err == nil && nvc {
2626
return nil
2727
}
28-
v, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION"))
28+
v, err := goRootVersion(goroot)
2929
if err != nil {
30-
return fmt.Errorf("GopherJS %s requires a Go 1.%d.x distribution, but failed to read its VERSION file: %v", Version, GoVersion, err)
30+
return fmt.Errorf("unable to detect Go version for %q: %w", goroot, err)
3131
}
32-
if !bytes.HasPrefix(v, []byte("go1."+strconv.Itoa(GoVersion))) {
32+
if !strings.HasPrefix(v, "go1."+strconv.Itoa(GoVersion)) {
3333
return fmt.Errorf("GopherJS %s requires a Go 1.%d.x distribution, but found version %s", Version, GoVersion, v)
3434
}
3535
return nil
3636
}
37+
38+
// goRootVersion defermines Go release for the given GOROOT installation.
39+
func goRootVersion(goroot string) (string, error) {
40+
v, err := os.ReadFile(filepath.Join(goroot, "VERSION"))
41+
if err == nil {
42+
// Standard Go distribution has VERSION file inside its GOROOT, checking it
43+
// is the most efficient option.
44+
return string(v), nil
45+
}
46+
47+
// Fall back to the "go version" command.
48+
cmd := exec.Command(filepath.Join(goroot, "bin", "go"), "version")
49+
out, err := cmd.Output()
50+
if err != nil {
51+
return "", fmt.Errorf("`go version` command failed: %w", err)
52+
}
53+
// Expected output: go version go1.17.1 linux/amd64
54+
parts := strings.Split(string(out), " ")
55+
if len(parts) != 4 {
56+
return "", fmt.Errorf("unexpected `go version` output %q, expected 4 words", string(out))
57+
}
58+
return parts[2], nil
59+
}
60+
61+
// GoRelease does a best-effort to identify Go release we are building with.
62+
// If unable to determin the precise version for the given GOROOT, falls back
63+
// to the best guess available.
64+
func GoRelease(goroot string) string {
65+
v, err := goRootVersion(goroot)
66+
if err == nil {
67+
// Prefer using the actual version of the GOROOT we are working with.
68+
return v
69+
}
70+
71+
// Use Go version GopherJS release was tested against as a fallback. By
72+
// convention, it is included in the GopherJS version after the plus sign.
73+
parts := strings.Split(Version, "+")
74+
if len(parts) == 2 {
75+
return parts[1]
76+
}
77+
78+
// If everything else fails, return just the Go version without patch level.
79+
return fmt.Sprintf("go1.%d", GoVersion)
80+
}

compiler/version_check_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package compiler
2+
3+
import (
4+
"runtime"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestGoRelease(t *testing.T) {
10+
t.Run("goroot", func(t *testing.T) {
11+
got := GoRelease(runtime.GOROOT())
12+
want := runtime.Version()
13+
if got != want {
14+
t.Fatalf("Got: goRelease(%q) returned %q. Want %s.", runtime.GOROOT(), got, want)
15+
}
16+
})
17+
18+
t.Run("fallback", func(t *testing.T) {
19+
const goroot = "./invalid goroot"
20+
got := GoRelease(goroot)
21+
if got == "" {
22+
t.Fatalf("Got: goRelease(%q) returned \"\". Want: a Go version.", goroot)
23+
}
24+
if !strings.HasSuffix(Version, "+"+got) {
25+
t.Fatalf("Got: goRelease(%q) returned %q. Want: a fallback to GopherJS version suffix %q.", goroot, got, Version)
26+
}
27+
})
28+
}

tests/misc_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,9 @@ func TestUntypedNil(t *testing.T) {
856856
f(nil)
857857
}
858858
}
859+
860+
func TestVersion(t *testing.T) {
861+
if got := runtime.Version(); !strings.HasPrefix(got, "go1.") {
862+
t.Fatalf("Got: runtime.Version() returned %q. Want: a valid Go version.", got)
863+
}
864+
}

tool.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) {
655655
if err != nil {
656656
return err
657657
}
658-
if err := compiler.WriteProgramCode(deps, sourceMapFilter); err != nil {
658+
if err := compiler.WriteProgramCode(deps, sourceMapFilter, s.GoRelease()); err != nil {
659659
return err
660660
}
661661

0 commit comments

Comments
 (0)