@@ -13,7 +13,6 @@ import (
13
13
"go/scanner"
14
14
"go/token"
15
15
"go/types"
16
- "io/ioutil"
17
16
"os"
18
17
"os/exec"
19
18
"path"
@@ -25,10 +24,9 @@ import (
25
24
"github.com/fsnotify/fsnotify"
26
25
"github.com/gopherjs/gopherjs/compiler"
27
26
"github.com/gopherjs/gopherjs/compiler/astutil"
28
- "github.com/gopherjs/gopherjs/compiler/gopherjspkg "
27
+ log "github.com/sirupsen/logrus "
29
28
30
29
"github.com/neelance/sourcemap"
31
- "github.com/shurcooL/httpfs/vfsutil"
32
30
"golang.org/x/tools/go/buildutil"
33
31
34
32
"github.com/gopherjs/gopherjs/build/cache"
@@ -64,20 +62,6 @@ func NewBuildContext(installSuffix string, buildTags []string) XContext {
64
62
}
65
63
}
66
64
67
- // statFile returns an os.FileInfo describing the named file.
68
- // For files in "$GOROOT/src/github.com/gopherjs/gopherjs" directory,
69
- // gopherjspkg.FS is consulted first.
70
- func statFile (path string ) (os.FileInfo , error ) {
71
- gopherjsRoot := filepath .Join (DefaultGOROOT , "src" , "github.com" , "gopherjs" , "gopherjs" )
72
- if strings .HasPrefix (path , gopherjsRoot + string (filepath .Separator )) {
73
- path = filepath .ToSlash (path [len (gopherjsRoot ):])
74
- if fi , err := vfsutil .Stat (gopherjspkg .FS , path ); err == nil {
75
- return fi , nil
76
- }
77
- }
78
- return os .Stat (path )
79
- }
80
-
81
65
// Import returns details about the Go package named by the import path. If the
82
66
// path is a local import path naming a package that can be imported using
83
67
// a standard import path, the returned package will set p.ImportPath to
@@ -161,7 +145,7 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
161
145
// as an existing file from the standard library). For all identifiers that exist
162
146
// in the original AND the overrides, the original identifier in the AST gets
163
147
// replaced by `_`. New identifiers that don't exist in original package get added.
164
- func parseAndAugment (xctx XContext , pkg * PackageData , isTest bool , fileSet * token.FileSet ) ([]* ast.File , error ) {
148
+ func parseAndAugment (xctx XContext , pkg * PackageData , isTest bool , fileSet * token.FileSet ) ([]* ast.File , [] JSFile , error ) {
165
149
var files []* ast.File
166
150
replacedDeclNames := make (map [string ]bool )
167
151
pruneOriginalFuncs := make (map [string ]bool )
@@ -172,9 +156,12 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
172
156
importPath = importPath [:len (importPath )- 5 ]
173
157
}
174
158
159
+ jsFiles := []JSFile {}
160
+
175
161
nativesContext := overlayCtx (xctx .Env ())
176
162
177
163
if nativesPkg , err := nativesContext .Import (importPath , "" , 0 ); err == nil {
164
+ jsFiles = nativesPkg .JSFiles
178
165
names := nativesPkg .GoFiles
179
166
if isTest {
180
167
names = append (names , nativesPkg .TestGoFiles ... )
@@ -229,7 +216,7 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
229
216
}
230
217
r , err := buildutil .OpenFile (pkg .bctx , name )
231
218
if err != nil {
232
- return nil , err
219
+ return nil , nil , err
233
220
}
234
221
file , err := parser .ParseFile (fileSet , name , r , parser .ParseComments )
235
222
r .Close ()
@@ -298,9 +285,9 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
298
285
}
299
286
300
287
if errList != nil {
301
- return nil , errList
288
+ return nil , nil , errList
302
289
}
303
- return files , nil
290
+ return files , jsFiles , nil
304
291
}
305
292
306
293
// Options controls build process behavior.
@@ -333,11 +320,18 @@ func (o *Options) PrintSuccess(format string, a ...interface{}) {
333
320
fmt .Fprintf (os .Stderr , format , a ... )
334
321
}
335
322
323
+ // JSFile represents a *.inc.js file metadata and content.
324
+ type JSFile struct {
325
+ Path string // Full file path for the build context the file came from.
326
+ ModTime time.Time
327
+ Content []byte
328
+ }
329
+
336
330
// PackageData is an extension of go/build.Package with additional metadata
337
331
// GopherJS requires.
338
332
type PackageData struct {
339
333
* build.Package
340
- JSFiles []string
334
+ JSFiles []JSFile
341
335
// IsTest is true if the package is being built for running tests.
342
336
IsTest bool
343
337
SrcModTime time.Time
@@ -352,6 +346,43 @@ func (p PackageData) String() string {
352
346
return fmt .Sprintf ("%s [is_test=%v]" , p .ImportPath , p .IsTest )
353
347
}
354
348
349
+ // FileModTime returns the most recent modification time of the package's source
350
+ // files. This includes all .go and .inc.js that would be included in the build,
351
+ // but excludes any dependencies.
352
+ func (p PackageData ) FileModTime () time.Time {
353
+ newest := time.Time {}
354
+ for _ , file := range p .JSFiles {
355
+ if file .ModTime .After (newest ) {
356
+ newest = file .ModTime
357
+ }
358
+ }
359
+
360
+ // Unfortunately, build.Context methods don't allow us to Stat and individual
361
+ // file, only to enumerate a directory. So we first get mtimes for all files
362
+ // in the package directory, and then pick the newest for the relevant GoFiles.
363
+ mtimes := map [string ]time.Time {}
364
+ files , err := buildutil .ReadDir (p .bctx , p .Dir )
365
+ if err != nil {
366
+ log .Errorf ("Failed to enumerate files in the %q in context %v: %s. Assuming time.Now()." , p .Dir , p .bctx , err )
367
+ return time .Now ()
368
+ }
369
+ for _ , file := range files {
370
+ mtimes [file .Name ()] = file .ModTime ()
371
+ }
372
+
373
+ for _ , file := range p .GoFiles {
374
+ t , ok := mtimes [file ]
375
+ if ! ok {
376
+ log .Errorf ("No mtime found for source file %q of package %q, assuming time.Now()." , file , p .Name )
377
+ return time .Now ()
378
+ }
379
+ if t .After (newest ) {
380
+ newest = t
381
+ }
382
+ }
383
+ return newest
384
+ }
385
+
355
386
// InternalBuildContext returns the build context that produced the package.
356
387
//
357
388
// WARNING: This function is a part of internal API and will be removed in
@@ -499,11 +530,24 @@ func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath stri
499
530
}
500
531
501
532
for _ , file := range filenames {
502
- if strings .HasSuffix (file , ".inc.js" ) {
503
- pkg .JSFiles = append (pkg .JSFiles , file )
533
+ if ! strings .HasSuffix (file , ".inc.js" ) {
534
+ pkg .GoFiles = append (pkg .GoFiles , file )
504
535
continue
505
536
}
506
- pkg .GoFiles = append (pkg .GoFiles , file )
537
+
538
+ content , err := os .ReadFile (file )
539
+ if err != nil {
540
+ return fmt .Errorf ("failed to read %s: %w" , file , err )
541
+ }
542
+ info , err := os .Stat (file )
543
+ if err != nil {
544
+ return fmt .Errorf ("failed to stat %s: %w" , file , err )
545
+ }
546
+ pkg .JSFiles = append (pkg .JSFiles , JSFile {
547
+ Path : file ,
548
+ ModTime : info .ModTime (),
549
+ Content : content ,
550
+ })
507
551
}
508
552
509
553
archive , err := s .BuildPackage (pkg )
@@ -579,14 +623,8 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
579
623
}
580
624
}
581
625
582
- for _ , name := range append (pkg .GoFiles , pkg .JSFiles ... ) {
583
- fileInfo , err := statFile (filepath .Join (pkg .Dir , name ))
584
- if err != nil {
585
- return nil , err
586
- }
587
- if fileInfo .ModTime ().After (pkg .SrcModTime ) {
588
- pkg .SrcModTime = fileInfo .ModTime ()
589
- }
626
+ if pkg .FileModTime ().After (pkg .SrcModTime ) {
627
+ pkg .SrcModTime = pkg .FileModTime ()
590
628
}
591
629
592
630
if ! s .options .NoCache {
@@ -603,7 +641,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
603
641
604
642
// Existing archive is out of date or doesn't exist, let's build the package.
605
643
fileSet := token .NewFileSet ()
606
- files , err := parseAndAugment (s .xctx , pkg , pkg .IsTest , fileSet )
644
+ files , overlayJsFiles , err := parseAndAugment (s .xctx , pkg , pkg .IsTest , fileSet )
607
645
if err != nil {
608
646
return nil , err
609
647
}
@@ -617,13 +655,9 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
617
655
return nil , err
618
656
}
619
657
620
- for _ , jsFile := range pkg .JSFiles {
621
- code , err := ioutil .ReadFile (filepath .Join (pkg .Dir , jsFile ))
622
- if err != nil {
623
- return nil , err
624
- }
658
+ for _ , jsFile := range append (pkg .JSFiles , overlayJsFiles ... ) {
625
659
archive .IncJSCode = append (archive .IncJSCode , []byte ("\t (function() {\n " )... )
626
- archive .IncJSCode = append (archive .IncJSCode , code ... )
660
+ archive .IncJSCode = append (archive .IncJSCode , jsFile . Content ... )
627
661
archive .IncJSCode = append (archive .IncJSCode , []byte ("\n \t }).call($global);\n " )... )
628
662
}
629
663
@@ -721,22 +755,6 @@ func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool)
721
755
}
722
756
}
723
757
724
- // jsFilesFromDir finds and loads any *.inc.js packages in the build context
725
- // directory.
726
- func jsFilesFromDir (bctx * build.Context , dir string ) ([]string , error ) {
727
- files , err := buildutil .ReadDir (bctx , dir )
728
- if err != nil {
729
- return nil , err
730
- }
731
- var jsFiles []string
732
- for _ , file := range files {
733
- if strings .HasSuffix (file .Name (), ".inc.js" ) && file .Name ()[0 ] != '_' && file .Name ()[0 ] != '.' {
734
- jsFiles = append (jsFiles , file .Name ())
735
- }
736
- }
737
- return jsFiles , nil
738
- }
739
-
740
758
// hasGopathPrefix returns true and the length of the matched GOPATH workspace,
741
759
// iff file has a prefix that matches one of the GOPATH workspaces.
742
760
func hasGopathPrefix (file , gopath string ) (hasGopathPrefix bool , prefixLen int ) {
0 commit comments