Skip to content

support for being built and used with newer version of Go #941

Closed
@dmitshur

Description

@dmitshur

Go 1.13 is out by now, but GopherJS doesn't support it yet (that work is tracked in issue #929). I'd like to work on a change that can be made in parallel, unrelated to Go 1.13 support, but it should help with the current situation and buy us more time.

The change can be briefly described as "to allow GopherJS 1.12-n to operate alongside with arbitrary Go distribution versions, rather than hard-requiring it to be Go 1.12.x".

/cc @neelance @hajimehoshi @myitcv

Background

At this time, it's only possible to build and use GopherJS with one major version of Go at a time. Currently, GopherJS 1.12-n requires Go 1.12.x. This is enforced by a build-time check implemented via build constraints. See:

// +build go1.12
// +build !go1.13

The check is necessary because the GopherJS requires a 1.12.x Go distribution. It needs it to be able to read the source code for all of the standard library packages (i.e., the .go files in GOROOT/src), which GopherJS needs to compile.

However, the check doesn't have to be done at compile-time. We can do it at runtime instead, which would enable doing things like:

$ go version
go version go1.13 darwin/amd64
$ go get -u github.com/gopherjs/gopherjs

$ gopherjs build some.example/project
GopherJS 1.12-2 requires a Go 1.12.x distribution, but found version go1.13

$ GOROOT=$HOME/sdk/go1.12.9 gopherjs build some.example/project
<success>

Alternatives

1. Make it easier to specify a GOROOT value that only GopherJS uses

Let's assume the user wants to have Go 1.13 as their default Go distribution, say in /usr/local/go, and a Go 1.12.9 distribution in their $HOME/sdk/go1.12.9 directory (this is easy by doing go get golang.org/dl/go1.12.9 and then go1.12.9 download).

That means they can either build GopherJS using the Go 1.12.9 distribution so the default runtime.GOROOT() value in the gopherjs binary is $HOME/sdk/go1.12.9 (edit: based on comments from @inliquid below, this option may not be working correctly today), or explicitly set the GOROOT environment variable to $HOME/sdk/go1.12.9 when running the gopherjs binary:

GOROOT=$HOME/sdk/go1.11.13 gopherjs build some.example/project

However, this has two problems/limitations:

  1. Setting GOROOT manually for each gopherjs invocation is annoying and easy to forget, or accidentally set wrong GOROOT for main Go distribution

  2. Correct GopherJS-specific GOROOT will also need to be set when using GopherJS to compile in-memory (e.g., with github.com/shurcooL/gopherjslib or github.com/shurcooL/go/gopherjs_http packages)

To avoid both these problems, I propose adding a new GopherJS-specific environment variable:

GOPHERJS_GOROOT - if set, GopherJS uses this value as the default GOROOT value,
                  instead of using the system GOROOT as the default GOROOT value

The idea would be that if you want to use GopherJS with a mis-matched version of the Go distribution, you permanently set GOPHERJS_GOROOT to point to a matching Go distribution version and forget about it (until next major GopherJS version). It won't interfere with your normal Go distribution.

2. Embed all of the required files from GOROOT/src into GopherJS itself (also see #672)

Similar to how we already have natives and gopherjspkg file systems, we can have a third one: a copy of the source code of Go standard library packages (and potentially their tests).

I've explored this idea. It is certainly possible, but the concern is about the size of the generated assets_vfsdata.go file because the Go standard library is quite sizable.

I took GOROOT/src from Go 1.12.9 and trimmed it as follows:

  • deleted all of cmd/ directory, GopherJS doesn't need it
  • deleted all non .go files (there are many cgo files, assembly files, etc., GopherJS doesn't need them)
  • deleted all testdata directories
  • deleted all _test.go files (they were pretty small, didn't make a big difference)

The end result size of the src directory was about 15.3 MB. Compressed with gzip it becomes just 4.8 MB, which is pretty small. vfsgen uses gzip compression but encodes bytes in Go byte slice literals, which have an efficiency of 1:4, so the assets_vfsdata.go file ends up being 18.8 MB. If we're checking that into the git repository, it's not small, and it'd need to change whenever we add support for a new Go major version (or minor version).

The advantage of this approach is that it'd be very user friendly: users just go get -u github.com/gopherjs/gopherjs on any version of Go and it just works, no mucking around with environment variables.

The disadvantage is the large assets_vfsdata.go file, and complexity when it comes to testing GopherJS (we need the _test.go files and testdata directories to test GopherJS on std lib packages).

I think this is something we should explore more in the future, I don't think we should implement it now.

Proposal

For now, I'm leaning towards implementing alternative 1: moving the Go distribution version check from build-time to run-time, and adding support for the GOPHERJS_GOROOT environment variable. It would have to be documented, and the error messages should mention it.

Thoughts?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions