Skip to content

Commit e3253c7

Browse files
committed
build: Load js, nosync packages from gopherjspkg rather than GOPATH.
The Go stdlib overrides (i.e., compiler/natives package) are already embedded into the GopherJS build system. This makes it possible for the gopherjs binary to access them even if the gopherjs repository is not present in GOPATH (e.g., because it was deleted, or because it's vendored). Doing that for natives but not the js, nosync packages makes little sense, since the gopherjs repository still needs to exist in GOPATH. This change fixes that. By embedding the core GopherJS packages analogously to natives, the gopherjs binary becomes more standalone. Now, it only requires GOROOT/src to contain the matching version of Go source code. Non-core GopherJS packages are not embedded (e.g., the GopherJS compiler itself). That means it needs to exist in GOPATH to be buildable (otherwise, the user gets a "cannot find package" error). This is expected just like with any other normal Go package. Fixes #462. Helps #415.
1 parent 1e717ff commit e3253c7

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

build/build.go

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import (
2121

2222
"github.com/fsnotify/fsnotify"
2323
"github.com/gopherjs/gopherjs/compiler"
24+
"github.com/gopherjs/gopherjs/compiler/gopherjspkg"
2425
"github.com/gopherjs/gopherjs/compiler/natives"
2526
"github.com/neelance/sourcemap"
27+
"github.com/shurcooL/httpfs/vfsutil"
28+
"golang.org/x/tools/go/buildutil"
2629
)
2730

2831
type ImportCError struct {
@@ -35,7 +38,11 @@ func (e *ImportCError) Error() string {
3538

3639
// NewBuildContext creates a build context for building Go packages
3740
// with GopherJS compiler.
41+
//
42+
// Core GopherJS packages (i.e., "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync")
43+
// are loaded from gopherjspkg.FS virtual filesystem rather than GOPATH.
3844
func NewBuildContext(installSuffix string, buildTags []string) *build.Context {
45+
gopherJSRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs")
3946
return &build.Context{
4047
GOROOT: build.Default.GOROOT,
4148
GOPATH: build.Default.GOPATH,
@@ -49,7 +56,50 @@ func NewBuildContext(installSuffix string, buildTags []string) *build.Context {
4956
),
5057
ReleaseTags: build.Default.ReleaseTags,
5158
CgoEnabled: true, // detect `import "C"` to throw proper error
59+
60+
IsDir: func(path string) bool {
61+
if strings.HasPrefix(path, gopherJSRoot+string(filepath.Separator)) {
62+
path = filepath.ToSlash(path[len(gopherJSRoot):])
63+
if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil {
64+
return fi.IsDir()
65+
}
66+
}
67+
fi, err := os.Stat(path)
68+
return err == nil && fi.IsDir()
69+
},
70+
ReadDir: func(path string) ([]os.FileInfo, error) {
71+
if strings.HasPrefix(path, gopherJSRoot+string(filepath.Separator)) {
72+
path = filepath.ToSlash(path[len(gopherJSRoot):])
73+
if fis, err := vfsutil.ReadDir(gopherjspkg.FS, path); err == nil {
74+
return fis, nil
75+
}
76+
}
77+
return ioutil.ReadDir(path)
78+
},
79+
OpenFile: func(path string) (io.ReadCloser, error) {
80+
if strings.HasPrefix(path, gopherJSRoot+string(filepath.Separator)) {
81+
path = filepath.ToSlash(path[len(gopherJSRoot):])
82+
if f, err := gopherjspkg.FS.Open(path); err == nil {
83+
return f, nil
84+
}
85+
}
86+
return os.Open(path)
87+
},
88+
}
89+
}
90+
91+
// statFile returns an os.FileInfo describing the named file.
92+
// For files in "$GOROOT/src/github.com/gopherjs/gopherjs" directory,
93+
// gopherjspkg.FS is consulted first.
94+
func statFile(path string) (os.FileInfo, error) {
95+
gopherJSRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs")
96+
if strings.HasPrefix(path, gopherJSRoot+string(filepath.Separator)) {
97+
path = filepath.ToSlash(path[len(gopherJSRoot):])
98+
if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil {
99+
return fi, nil
100+
}
52101
}
102+
return os.Stat(path)
53103
}
54104

55105
// Import returns details about the Go package named by the import path. If the
@@ -138,7 +188,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
138188
}
139189
}
140190

141-
jsFiles, err := jsFilesFromDir(pkg.Dir)
191+
jsFiles, err := jsFilesFromDir(&bctx, pkg.Dir)
142192
if err != nil {
143193
return nil, err
144194
}
@@ -177,12 +227,13 @@ Outer:
177227
// ImportDir is like Import but processes the Go package found in the named
178228
// directory.
179229
func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) {
180-
pkg, err := NewBuildContext(installSuffix, buildTags).ImportDir(dir, mode)
230+
bctx := NewBuildContext(installSuffix, buildTags)
231+
pkg, err := bctx.ImportDir(dir, mode)
181232
if err != nil {
182233
return nil, err
183234
}
184235

185-
jsFiles, err := jsFilesFromDir(pkg.Dir)
236+
jsFiles, err := jsFilesFromDir(bctx, pkg.Dir)
186237
if err != nil {
187238
return nil, err
188239
}
@@ -201,7 +252,7 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
201252
// as an existing file from the standard library). For all identifiers that exist
202253
// in the original AND the overrides, the original identifier in the AST gets
203254
// replaced by `_`. New identifiers that don't exist in original package get added.
204-
func parseAndAugment(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) {
255+
func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) {
205256
var files []*ast.File
206257
replacedDeclNames := make(map[string]bool)
207258
funcName := func(d *ast.FuncDecl) string {
@@ -305,10 +356,10 @@ func parseAndAugment(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([
305356

306357
var errList compiler.ErrorList
307358
for _, name := range pkg.GoFiles {
308-
if !filepath.IsAbs(name) {
359+
if !filepath.IsAbs(name) { // name might be absolute if specified directly. E.g., `gopherjs build /abs/file.go`.
309360
name = filepath.Join(pkg.Dir, name)
310361
}
311-
r, err := os.Open(name)
362+
r, err := buildutil.OpenFile(bctx, name)
312363
if err != nil {
313364
return nil, err
314365
}
@@ -466,7 +517,7 @@ func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string)
466517
return err
467518
}
468519
pkg := &PackageData{Package: buildPkg}
469-
jsFiles, err := jsFilesFromDir(pkg.Dir)
520+
jsFiles, err := jsFilesFromDir(s.bctx, pkg.Dir)
470521
if err != nil {
471522
return err
472523
}
@@ -585,7 +636,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
585636
}
586637

587638
for _, name := range append(pkg.GoFiles, pkg.JSFiles...) {
588-
fileInfo, err := os.Stat(filepath.Join(pkg.Dir, name))
639+
fileInfo, err := statFile(filepath.Join(pkg.Dir, name))
589640
if err != nil {
590641
return nil, err
591642
}
@@ -619,7 +670,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
619670
}
620671

621672
fileSet := token.NewFileSet()
622-
files, err := parseAndAugment(pkg.Package, pkg.IsTest, fileSet)
673+
files, err := parseAndAugment(s.bctx, pkg.Package, pkg.IsTest, fileSet)
623674
if err != nil {
624675
return nil, err
625676
}
@@ -757,8 +808,8 @@ func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool)
757808
}
758809
}
759810

760-
func jsFilesFromDir(dir string) ([]string, error) {
761-
files, err := ioutil.ReadDir(dir)
811+
func jsFilesFromDir(bctx *build.Context, dir string) ([]string, error) {
812+
files, err := buildutil.ReadDir(bctx, dir)
762813
if err != nil {
763814
return nil, err
764815
}

build/build_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) {
7777

7878
// Use parseAndAugment to get a list of augmented AST files.
7979
fset := token.NewFileSet()
80-
files, err := parseAndAugment(bpkg, false, fset)
80+
files, err := parseAndAugment(NewBuildContext("", nil), bpkg, false, fset)
8181
if err != nil {
8282
t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
8383
}
@@ -116,7 +116,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) {
116116

117117
// Use parseAndAugment to get a list of augmented AST files.
118118
fset := token.NewFileSet()
119-
files, err := parseAndAugment(bpkg, true, fset)
119+
files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset)
120120
if err != nil {
121121
t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
122122
}
@@ -158,7 +158,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) {
158158

159159
// Use parseAndAugment to get a list of augmented AST files, then check only the external test files.
160160
fset := token.NewFileSet()
161-
files, err := parseAndAugment(bpkg, true, fset)
161+
files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset)
162162
if err != nil {
163163
t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
164164
}

0 commit comments

Comments
 (0)