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.
1
6
package build
2
7
3
8
import (
@@ -8,7 +13,6 @@ import (
8
13
"go/scanner"
9
14
"go/token"
10
15
"go/types"
11
- "io"
12
16
"io/ioutil"
13
17
"os"
14
18
"os/exec"
@@ -26,6 +30,8 @@ import (
26
30
"github.com/neelance/sourcemap"
27
31
"github.com/shurcooL/httpfs/vfsutil"
28
32
"golang.org/x/tools/go/buildutil"
33
+
34
+ _ "github.com/gopherjs/gopherjs/build/versionhack" // go/build release tags hack.
29
35
)
30
36
31
37
// DefaultGOROOT is the default GOROOT value for builds.
@@ -53,52 +59,13 @@ func (e *ImportCError) Error() string {
53
59
// with GopherJS compiler.
54
60
//
55
61
// 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 {
58
65
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 ),
102
69
}
103
70
}
104
71
@@ -138,34 +105,12 @@ func Import(path string, mode build.ImportMode, installSuffix string, buildTags
138
105
// Import will not be able to resolve relative import paths.
139
106
wd = ""
140
107
}
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 )
143
110
}
144
111
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 )
169
114
if err != nil {
170
115
return nil , err
171
116
}
@@ -181,7 +126,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
181
126
case "runtime" :
182
127
pkg .GoFiles = []string {} // Package sources are completely replaced in natives.
183
128
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" }
185
130
case "runtime/pprof" :
186
131
pkg .GoFiles = nil
187
132
case "internal/poll" :
@@ -201,7 +146,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
201
146
// Just like above, https://github.com/gopherjs/gopherjs/issues/693 is
202
147
// probably the best long-term option.
203
148
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 () )),
205
150
"root_unix.go" , "root_js.go" )
206
151
case "syscall/js" :
207
152
// Reuse upstream tests to ensure conformance, but completely replace
@@ -226,12 +171,7 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build
226
171
}
227
172
}
228
173
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
235
175
}
236
176
237
177
// excludeExecutable excludes all executable implementation .go files.
@@ -271,18 +211,13 @@ func include(files []string, includes ...string) []string {
271
211
// ImportDir is like Import but processes the Go package found in the named
272
212
// directory.
273
213
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 )
276
216
if err != nil {
277
217
return nil , err
278
218
}
279
219
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
286
221
}
287
222
288
223
// parseAndAugment parses and returns all .go files of given pkg.
@@ -296,7 +231,7 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
296
231
// as an existing file from the standard library). For all identifiers that exist
297
232
// in the original AND the overrides, the original identifier in the AST gets
298
233
// 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 ) {
300
235
var files []* ast.File
301
236
replacedDeclNames := make (map [string ]bool )
302
237
pruneOriginalFuncs := make (map [string ]bool )
@@ -307,53 +242,14 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
307
242
importPath = importPath [:len (importPath )- 5 ]
308
243
}
309
244
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 )
350
246
351
247
if importPath == "syscall" {
352
248
// Special handling for the syscall package, which uses OS native
353
249
// GOOS/GOARCH pair. This will no longer be necessary after
354
250
// 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" )
357
253
}
358
254
359
255
if nativesPkg , err := nativesContext .Import (importPath , "" , 0 ); err == nil {
@@ -366,7 +262,7 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
366
262
}
367
263
for _ , name := range names {
368
264
fullPath := path .Join (nativesPkg .Dir , name )
369
- r , err := nativesContext .OpenFile (fullPath )
265
+ r , err := nativesContext .bctx . OpenFile (fullPath )
370
266
if err != nil {
371
267
panic (err )
372
268
}
@@ -406,7 +302,7 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS
406
302
if ! filepath .IsAbs (name ) { // name might be absolute if specified directly. E.g., `gopherjs build /abs/file.go`.
407
303
name = filepath .Join (pkg .Dir , name )
408
304
}
409
- r , err := buildutil .OpenFile (bctx , name )
305
+ r , err := buildutil .OpenFile (pkg . bctx , name )
410
306
if err != nil {
411
307
return nil , err
412
308
}
@@ -509,18 +405,59 @@ func (o *Options) PrintSuccess(format string, a ...interface{}) {
509
405
fmt .Fprintf (os .Stderr , format , a ... )
510
406
}
511
407
408
+ // PackageData is an extension of go/build.Package with additional metadata
409
+ // GopherJS requires.
512
410
type PackageData struct {
513
411
* build.Package
514
412
JSFiles []string
515
413
IsTest bool // IsTest is true if the package is being built for running tests.
516
414
SrcModTime time.Time
517
415
UpToDate bool
518
416
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
+ }
519
456
}
520
457
521
458
type Session struct {
522
459
options * Options
523
- bctx * build. Context
460
+ xctx XContext
524
461
Archives map [string ]* compiler.Archive
525
462
Types map [string ]* types.Package
526
463
Watcher * fsnotify.Watcher
@@ -544,7 +481,7 @@ func NewSession(options *Options) (*Session, error) {
544
481
options : options ,
545
482
Archives : make (map [string ]* compiler.Archive ),
546
483
}
547
- s .bctx = NewBuildContext (s .InstallSuffix (), s .options .BuildTags )
484
+ s .xctx = NewBuildContext (s .InstallSuffix (), s .options .BuildTags )
548
485
s .Types = make (map [string ]* types.Package )
549
486
if options .Watch {
550
487
if out , err := exec .Command ("ulimit" , "-n" ).Output (); err == nil {
@@ -563,7 +500,7 @@ func NewSession(options *Options) (*Session, error) {
563
500
}
564
501
565
502
// 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 }
567
504
568
505
func (s * Session ) InstallSuffix () string {
569
506
if s .options .Minify {
@@ -576,16 +513,11 @@ func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string)
576
513
if s .Watcher != nil {
577
514
s .Watcher .Add (packagePath )
578
515
}
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 )
585
517
if err != nil {
586
518
return err
587
519
}
588
- pkg . JSFiles = jsFiles
520
+
589
521
archive , err := s .BuildPackage (pkg )
590
522
if err != nil {
591
523
return err
@@ -608,6 +540,7 @@ func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath stri
608
540
ImportPath : "main" ,
609
541
Dir : packagePath ,
610
542
},
543
+ bctx : & goCtx (s .InstallSuffix (), s .options .BuildTags ).bctx ,
611
544
}
612
545
613
546
for _ , file := range filenames {
@@ -634,7 +567,7 @@ func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) {
634
567
}
635
568
636
569
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 ())
638
571
if s .Watcher != nil && pkg != nil { // add watch even on error
639
572
s .Watcher .Add (pkg .Dir )
640
573
}
@@ -734,7 +667,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
734
667
}
735
668
736
669
fileSet := token .NewFileSet ()
737
- files , err := parseAndAugment (s .bctx , pkg . Package , pkg .IsTest , fileSet )
670
+ files , err := parseAndAugment (s .xctx , pkg , pkg .IsTest , fileSet )
738
671
if err != nil {
739
672
return nil , err
740
673
}
0 commit comments