Skip to content

Commit 32a0b93

Browse files
authored
Merge pull request #1042 from nevkontakte/build-contexts
Support Go Modules builds.
2 parents 69c5ea8 + 39c1917 commit 32a0b93

15 files changed

+843
-219
lines changed

build/build.go

Lines changed: 78 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// Package build implements GopherJS build system.
2+
//
3+
// WARNING: This package's API is treated as internal and currently doesn't
4+
// provide any API stability guarantee, use it at your own risk. If you need a
5+
// stable interface, prefer invoking the gopherjs CLI tool as a subprocess.
16
package build
27

38
import (
@@ -8,7 +13,6 @@ import (
813
"go/scanner"
914
"go/token"
1015
"go/types"
11-
"io"
1216
"io/ioutil"
1317
"os"
1418
"os/exec"
@@ -26,6 +30,8 @@ import (
2630
"github.com/neelance/sourcemap"
2731
"github.com/shurcooL/httpfs/vfsutil"
2832
"golang.org/x/tools/go/buildutil"
33+
34+
_ "github.com/gopherjs/gopherjs/build/versionhack" // go/build release tags hack.
2935
)
3036

3137
// DefaultGOROOT is the default GOROOT value for builds.
@@ -53,52 +59,13 @@ func (e *ImportCError) Error() string {
5359
// with GopherJS compiler.
5460
//
5561
// Core GopherJS packages (i.e., "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync")
56-
// are loaded from gopherjspkg.FS virtual filesystem rather than GOPATH.
57-
func NewBuildContext(installSuffix string, buildTags []string) *build.Context {
62+
// are loaded from gopherjspkg.FS virtual filesystem if not present in GOPATH or
63+
// go.mod.
64+
func NewBuildContext(installSuffix string, buildTags []string) XContext {
5865
gopherjsRoot := filepath.Join(DefaultGOROOT, "src", "github.com", "gopherjs", "gopherjs")
59-
return &build.Context{
60-
GOROOT: DefaultGOROOT,
61-
GOPATH: build.Default.GOPATH,
62-
GOOS: build.Default.GOOS,
63-
GOARCH: "js",
64-
InstallSuffix: installSuffix,
65-
Compiler: "gc",
66-
BuildTags: append(buildTags,
67-
"netgo", // See https://godoc.org/net#hdr-Name_Resolution.
68-
"purego", // See https://golang.org/issues/23172.
69-
"math_big_pure_go", // Use pure Go version of math/big.
70-
),
71-
ReleaseTags: build.Default.ReleaseTags[:compiler.GoVersion],
72-
CgoEnabled: true, // detect `import "C"` to throw proper error
73-
74-
IsDir: func(path string) bool {
75-
if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) {
76-
path = filepath.ToSlash(path[len(gopherjsRoot):])
77-
if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil {
78-
return fi.IsDir()
79-
}
80-
}
81-
fi, err := os.Stat(path)
82-
return err == nil && fi.IsDir()
83-
},
84-
ReadDir: func(path string) ([]os.FileInfo, error) {
85-
if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) {
86-
path = filepath.ToSlash(path[len(gopherjsRoot):])
87-
if fis, err := vfsutil.ReadDir(gopherjspkg.FS, path); err == nil {
88-
return fis, nil
89-
}
90-
}
91-
return ioutil.ReadDir(path)
92-
},
93-
OpenFile: func(path string) (io.ReadCloser, error) {
94-
if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) {
95-
path = filepath.ToSlash(path[len(gopherjsRoot):])
96-
if f, err := gopherjspkg.FS.Open(path); err == nil {
97-
return f, nil
98-
}
99-
}
100-
return os.Open(path)
101-
},
66+
return &chainedCtx{
67+
primary: goCtx(installSuffix, buildTags),
68+
secondary: embeddedCtx(&withPrefix{gopherjspkg.FS, gopherjsRoot}, installSuffix, buildTags),
10269
}
10370
}
10471

@@ -138,34 +105,12 @@ func Import(path string, mode build.ImportMode, installSuffix string, buildTags
138105
// Import will not be able to resolve relative import paths.
139106
wd = ""
140107
}
141-
bctx := NewBuildContext(installSuffix, buildTags)
142-
return importWithSrcDir(*bctx, path, wd, mode, installSuffix)
108+
xctx := NewBuildContext(installSuffix, buildTags)
109+
return importWithSrcDir(xctx, path, wd, mode, installSuffix)
143110
}
144111

145-
func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) {
146-
// bctx is passed by value, so it can be modified here.
147-
var isVirtual bool
148-
switch path {
149-
case "syscall":
150-
// syscall needs to use a typical GOARCH like amd64 to pick up definitions for _Socklen, BpfInsn, IFNAMSIZ, Timeval, BpfStat, SYS_FCNTL, Flock_t, etc.
151-
bctx.GOARCH = build.Default.GOARCH
152-
bctx.InstallSuffix = "js"
153-
if installSuffix != "" {
154-
bctx.InstallSuffix += "_" + installSuffix
155-
}
156-
case "syscall/js":
157-
// There are no buildable files in this package, but we need to use files in the virtual directory.
158-
mode |= build.FindOnly
159-
case "crypto/x509", "os/user":
160-
// These stdlib packages have cgo and non-cgo versions (via build tags); we want the latter.
161-
bctx.CgoEnabled = false
162-
case "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync":
163-
// These packages are already embedded via gopherjspkg.FS virtual filesystem (which can be
164-
// safely vendored). Don't try to use vendor directory to resolve them.
165-
mode |= build.IgnoreVendor
166-
isVirtual = true
167-
}
168-
pkg, err := bctx.Import(path, srcDir, mode)
112+
func importWithSrcDir(xctx XContext, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) {
113+
pkg, err := xctx.Import(path, srcDir, mode)
169114
if err != nil {
170115
return nil, err
171116
}
@@ -181,7 +126,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
181126
case "runtime":
182127
pkg.GoFiles = []string{} // Package sources are completely replaced in natives.
183128
case "runtime/internal/sys":
184-
pkg.GoFiles = []string{fmt.Sprintf("zgoos_%s.go", bctx.GOOS), "zversion.go"}
129+
pkg.GoFiles = []string{fmt.Sprintf("zgoos_%s.go", xctx.GOOS()), "zversion.go"}
185130
case "runtime/pprof":
186131
pkg.GoFiles = nil
187132
case "internal/poll":
@@ -201,7 +146,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
201146
// Just like above, https://github.com/gopherjs/gopherjs/issues/693 is
202147
// probably the best long-term option.
203148
pkg.GoFiles = include(
204-
exclude(pkg.GoFiles, fmt.Sprintf("root_%s.go", bctx.GOOS)),
149+
exclude(pkg.GoFiles, fmt.Sprintf("root_%s.go", xctx.GOOS())),
205150
"root_unix.go", "root_js.go")
206151
case "syscall/js":
207152
// Reuse upstream tests to ensure conformance, but completely replace
@@ -226,12 +171,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
226171
}
227172
}
228173

229-
jsFiles, err := jsFilesFromDir(&bctx, pkg.Dir)
230-
if err != nil {
231-
return nil, err
232-
}
233-
234-
return &PackageData{Package: pkg, JSFiles: jsFiles, IsVirtual: isVirtual}, nil
174+
return pkg, nil
235175
}
236176

237177
// excludeExecutable excludes all executable implementation .go files.
@@ -271,18 +211,13 @@ func include(files []string, includes ...string) []string {
271211
// ImportDir is like Import but processes the Go package found in the named
272212
// directory.
273213
func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) {
274-
bctx := NewBuildContext(installSuffix, buildTags)
275-
pkg, err := bctx.ImportDir(dir, mode)
214+
xctx := NewBuildContext(installSuffix, buildTags)
215+
pkg, err := xctx.Import(".", dir, mode)
276216
if err != nil {
277217
return nil, err
278218
}
279219

280-
jsFiles, err := jsFilesFromDir(bctx, pkg.Dir)
281-
if err != nil {
282-
return nil, err
283-
}
284-
285-
return &PackageData{Package: pkg, JSFiles: jsFiles}, nil
220+
return pkg, nil
286221
}
287222

288223
// parseAndAugment parses and returns all .go files of given pkg.
@@ -296,7 +231,7 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
296231
// as an existing file from the standard library). For all identifiers that exist
297232
// in the original AND the overrides, the original identifier in the AST gets
298233
// replaced by `_`. New identifiers that don't exist in original package get added.
299-
func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) {
234+
func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) {
300235
var files []*ast.File
301236
replacedDeclNames := make(map[string]bool)
302237
pruneOriginalFuncs := make(map[string]bool)
@@ -307,53 +242,14 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
307242
importPath = importPath[:len(importPath)-5]
308243
}
309244

310-
nativesContext := &build.Context{
311-
GOROOT: "/",
312-
GOOS: bctx.GOOS,
313-
GOARCH: bctx.GOARCH,
314-
Compiler: "gc",
315-
JoinPath: path.Join,
316-
SplitPathList: func(list string) []string {
317-
if list == "" {
318-
return nil
319-
}
320-
return strings.Split(list, "/")
321-
},
322-
IsAbsPath: path.IsAbs,
323-
IsDir: func(name string) bool {
324-
dir, err := natives.FS.Open(name)
325-
if err != nil {
326-
return false
327-
}
328-
defer dir.Close()
329-
info, err := dir.Stat()
330-
if err != nil {
331-
return false
332-
}
333-
return info.IsDir()
334-
},
335-
HasSubdir: func(root, name string) (rel string, ok bool) {
336-
panic("not implemented")
337-
},
338-
ReadDir: func(name string) (fi []os.FileInfo, err error) {
339-
dir, err := natives.FS.Open(name)
340-
if err != nil {
341-
return nil, err
342-
}
343-
defer dir.Close()
344-
return dir.Readdir(0)
345-
},
346-
OpenFile: func(name string) (r io.ReadCloser, err error) {
347-
return natives.FS.Open(name)
348-
},
349-
}
245+
nativesContext := embeddedCtx(&withPrefix{fs: natives.FS, prefix: DefaultGOROOT}, "", nil)
350246

351247
if importPath == "syscall" {
352248
// Special handling for the syscall package, which uses OS native
353249
// GOOS/GOARCH pair. This will no longer be necessary after
354250
// https://github.com/gopherjs/gopherjs/issues/693.
355-
nativesContext.GOARCH = build.Default.GOARCH
356-
nativesContext.BuildTags = append(nativesContext.BuildTags, "js")
251+
nativesContext.bctx.GOARCH = build.Default.GOARCH
252+
nativesContext.bctx.BuildTags = append(nativesContext.bctx.BuildTags, "js")
357253
}
358254

359255
if nativesPkg, err := nativesContext.Import(importPath, "", 0); err == nil {
@@ -366,7 +262,7 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
366262
}
367263
for _, name := range names {
368264
fullPath := path.Join(nativesPkg.Dir, name)
369-
r, err := nativesContext.OpenFile(fullPath)
265+
r, err := nativesContext.bctx.OpenFile(fullPath)
370266
if err != nil {
371267
panic(err)
372268
}
@@ -406,7 +302,7 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
406302
if !filepath.IsAbs(name) { // name might be absolute if specified directly. E.g., `gopherjs build /abs/file.go`.
407303
name = filepath.Join(pkg.Dir, name)
408304
}
409-
r, err := buildutil.OpenFile(bctx, name)
305+
r, err := buildutil.OpenFile(pkg.bctx, name)
410306
if err != nil {
411307
return nil, err
412308
}
@@ -509,18 +405,59 @@ func (o *Options) PrintSuccess(format string, a ...interface{}) {
509405
fmt.Fprintf(os.Stderr, format, a...)
510406
}
511407

408+
// PackageData is an extension of go/build.Package with additional metadata
409+
// GopherJS requires.
512410
type PackageData struct {
513411
*build.Package
514412
JSFiles []string
515413
IsTest bool // IsTest is true if the package is being built for running tests.
516414
SrcModTime time.Time
517415
UpToDate bool
518416
IsVirtual bool // If true, the package does not have a corresponding physical directory on disk.
417+
418+
bctx *build.Context // The original build context this package came from.
419+
}
420+
421+
// InternalBuildContext returns the build context that produced the package.
422+
//
423+
// WARNING: This function is a part of internal API and will be removed in
424+
// future.
425+
func (p *PackageData) InternalBuildContext() *build.Context {
426+
return p.bctx
427+
}
428+
429+
// TestPackage returns a variant of the package with "internal" tests.
430+
func (p *PackageData) TestPackage() *PackageData {
431+
return &PackageData{
432+
Package: &build.Package{
433+
ImportPath: p.ImportPath,
434+
Dir: p.Dir,
435+
GoFiles: append(p.GoFiles, p.TestGoFiles...),
436+
Imports: append(p.Imports, p.TestImports...),
437+
},
438+
IsTest: true,
439+
JSFiles: p.JSFiles,
440+
bctx: p.bctx,
441+
}
442+
}
443+
444+
// XTestPackage returns a variant of the package with "external" tests.
445+
func (p *PackageData) XTestPackage() *PackageData {
446+
return &PackageData{
447+
Package: &build.Package{
448+
ImportPath: p.ImportPath + "_test",
449+
Dir: p.Dir,
450+
GoFiles: p.XTestGoFiles,
451+
Imports: p.XTestImports,
452+
},
453+
IsTest: true,
454+
bctx: p.bctx,
455+
}
519456
}
520457

521458
type Session struct {
522459
options *Options
523-
bctx *build.Context
460+
xctx XContext
524461
Archives map[string]*compiler.Archive
525462
Types map[string]*types.Package
526463
Watcher *fsnotify.Watcher
@@ -544,7 +481,7 @@ func NewSession(options *Options) (*Session, error) {
544481
options: options,
545482
Archives: make(map[string]*compiler.Archive),
546483
}
547-
s.bctx = NewBuildContext(s.InstallSuffix(), s.options.BuildTags)
484+
s.xctx = NewBuildContext(s.InstallSuffix(), s.options.BuildTags)
548485
s.Types = make(map[string]*types.Package)
549486
if options.Watch {
550487
if out, err := exec.Command("ulimit", "-n").Output(); err == nil {
@@ -563,7 +500,7 @@ func NewSession(options *Options) (*Session, error) {
563500
}
564501

565502
// BuildContext returns the session's build context.
566-
func (s *Session) BuildContext() *build.Context { return s.bctx }
503+
func (s *Session) XContext() XContext { return s.xctx }
567504

568505
func (s *Session) InstallSuffix() string {
569506
if s.options.Minify {
@@ -576,16 +513,11 @@ func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string)
576513
if s.Watcher != nil {
577514
s.Watcher.Add(packagePath)
578515
}
579-
buildPkg, err := s.bctx.ImportDir(packagePath, 0)
580-
if err != nil {
581-
return err
582-
}
583-
pkg := &PackageData{Package: buildPkg}
584-
jsFiles, err := jsFilesFromDir(s.bctx, pkg.Dir)
516+
pkg, err := s.xctx.Import(".", packagePath, 0)
585517
if err != nil {
586518
return err
587519
}
588-
pkg.JSFiles = jsFiles
520+
589521
archive, err := s.BuildPackage(pkg)
590522
if err != nil {
591523
return err
@@ -608,6 +540,7 @@ func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath stri
608540
ImportPath: "main",
609541
Dir: packagePath,
610542
},
543+
bctx: &goCtx(s.InstallSuffix(), s.options.BuildTags).bctx,
611544
}
612545

613546
for _, file := range filenames {
@@ -634,7 +567,7 @@ func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) {
634567
}
635568

636569
func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*PackageData, *compiler.Archive, error) {
637-
pkg, err := importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix())
570+
pkg, err := importWithSrcDir(s.xctx, path, srcDir, 0, s.InstallSuffix())
638571
if s.Watcher != nil && pkg != nil { // add watch even on error
639572
s.Watcher.Add(pkg.Dir)
640573
}
@@ -734,7 +667,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
734667
}
735668

736669
fileSet := token.NewFileSet()
737-
files, err := parseAndAugment(s.bctx, pkg.Package, pkg.IsTest, fileSet)
670+
files, err := parseAndAugment(s.xctx, pkg, pkg.IsTest, fileSet)
738671
if err != nil {
739672
return nil, err
740673
}

0 commit comments

Comments
 (0)