From 965f1a9e696b450c3ce21e9b01c5cec12d2b3f0a Mon Sep 17 00:00:00 2001 From: Paul Jolly Date: Fri, 23 Nov 2018 20:07:37 +0000 Subject: [PATCH] build: almost complete module support This is a necessarily large PR to offer almost complete Go modules support for GopherJS. * We now use github.com/rogpeppe/go-internal to be able to write testscript tests * A suite of testscript tests have been added to cover the major GopherJS commands operating in both GOPATH mode and module mode. Includes a test to ensure that the bug raised in #27 is now properly fixed. * The GopherJS tool and build packages have been heavily refactored in places to support Go modules. GopherJS support for Go modules follows the same patterns as the go tool. * We fix the build cache to be a function of the augmented packages as opposed (incorrectly) to the unaugmented package files. This has a slight cost because of the way the augmentation code is currently written (this could be refactored in a later PR) because every file is unconditionally parsed. Main and test packages are not cached (again, we could change this in a later PR). Because it would just work. * We fix the tests/run.go wrapper around the fixedbugs tests in the Go distribution to not perform a chdir into GOROOT; this does not work with modules. * We tidy up .circleci/config.yml to split commands into their separate run sections where it makes sense to. * We add genmodstubbs.go to automatically populate the stubs we need in testdata/mod for the loading of github.com/gopherjs/gopherjs/{js,nosync}. * For a more current commentary on the Go module support in GopherJS see https://github.com/myitcv/gopherjs/wiki/Changes-in-module-aware-GopherJS Fixes #24 Fixes #27 --- .circleci/config.yml | 58 +-- build/build.go | 490 +++++++++++------- build/build_test.go | 8 +- compiler/package.go | 1 + genmodstubbs.go | 126 +++++ go.mod | 2 +- go.sum | 4 +- script_test.go | 230 +++++++- testdata/build.txt | 147 ++++++ testdata/build_gopath.txt | 142 +++++ testdata/install.txt | 147 ++++++ testdata/install_gopath.txt | 142 +++++ .../github.com_fsnotify_fsnotify_v1.4.7.txt | 7 + .../github.com_gopherjs_gopherjs_v0.0.0.txt | 7 + ...b.com_inconshreveable_mousetrap_v1.0.0.txt | 7 + ...ite_v0.0.0-20160511093645-99348263ae86.txt | 7 + ...map_v0.0.0-20151028013722-8c68805598ab.txt | 7 + ...om_rogpeppe_go-internal_v1.0.1-alpha.1.txt | 7 + ..._go_v0.0.0-20180423040247-9e1955d9fb6e.txt | 7 + ...pfs_v0.0.0-20171119174359-809beceb2371.txt | 7 + ...gen_v0.0.0-20180915214035-33ae1944be3f.txt | 7 + .../mod/github.com_spf13_cobra_v0.0.3.txt | 7 + .../mod/github.com_spf13_pflag_v1.0.1.txt | 7 + ...pto_v0.0.0-20180807104621-f027049dab0a.txt | 7 + ...sys_v0.0.0-20180807162357-acbc56fc7007.txt | 7 + ...ols_v0.0.0-20180803180156-3c07937fe18c.txt | 7 + testdata/run.txt | 129 ++++- testdata/run_gopath.txt | 126 +++++ testdata/serve.txt | 148 ++++++ testdata/serve_gopath.txt | 143 +++++ testdata/shadow.txt | 41 ++ testdata/staleness.txt | 122 ----- testdata/test.txt | 149 ++++++ testdata/test_gopath.txt | 144 +++++ testdata/vendor_gopath.txt | 32 ++ tests/run.go | 33 +- tool.go | 392 +++++++++++--- 37 files changed, 2567 insertions(+), 487 deletions(-) create mode 100644 genmodstubbs.go create mode 100644 testdata/build.txt create mode 100644 testdata/build_gopath.txt create mode 100644 testdata/install.txt create mode 100644 testdata/install_gopath.txt create mode 100644 testdata/mod/github.com_fsnotify_fsnotify_v1.4.7.txt create mode 100644 testdata/mod/github.com_gopherjs_gopherjs_v0.0.0.txt create mode 100644 testdata/mod/github.com_inconshreveable_mousetrap_v1.0.0.txt create mode 100644 testdata/mod/github.com_neelance_astrewrite_v0.0.0-20160511093645-99348263ae86.txt create mode 100644 testdata/mod/github.com_neelance_sourcemap_v0.0.0-20151028013722-8c68805598ab.txt create mode 100644 testdata/mod/github.com_rogpeppe_go-internal_v1.0.1-alpha.1.txt create mode 100644 testdata/mod/github.com_shurcoo!l_go_v0.0.0-20180423040247-9e1955d9fb6e.txt create mode 100644 testdata/mod/github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt create mode 100644 testdata/mod/github.com_shurcoo!l_vfsgen_v0.0.0-20180915214035-33ae1944be3f.txt create mode 100644 testdata/mod/github.com_spf13_cobra_v0.0.3.txt create mode 100644 testdata/mod/github.com_spf13_pflag_v1.0.1.txt create mode 100644 testdata/mod/golang.org_x_crypto_v0.0.0-20180807104621-f027049dab0a.txt create mode 100644 testdata/mod/golang.org_x_sys_v0.0.0-20180807162357-acbc56fc7007.txt create mode 100644 testdata/mod/golang.org_x_tools_v0.0.0-20180803180156-3c07937fe18c.txt create mode 100644 testdata/run_gopath.txt create mode 100644 testdata/serve.txt create mode 100644 testdata/serve_gopath.txt create mode 100644 testdata/shadow.txt delete mode 100644 testdata/staleness.txt create mode 100644 testdata/test.txt create mode 100644 testdata/test_gopath.txt create mode 100644 testdata/vendor_gopath.txt diff --git a/.circleci/config.yml b/.circleci/config.yml index 725c8991a..d9cfeeb4d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,47 +70,17 @@ jobs: # this gives us the repo-local binaries we require npm install - - - run: | - go install github.com/gopherjs/gopherjs - - - run: - # because this is a long-running test suite - no_output_timeout: 30m - command: | - - echo ">> Regenerate compiler/prelude" - go generate github.com/gopherjs/gopherjs/compiler/prelude - - echo ">> Ensure we have a tidy go.{mod,sum}" - go mod tidy - go list all > /dev/null # https://github.com/golang/go/issues/27868#issuecomment-431413621 - - git diff - - echo ">> Ensure there are no variations from the git commit" - diff -u <(echo -n) <(git status --porcelain) - - echo ">> Ensure all go code is well formatted." - diff -u <(echo -n) <(gofmt -d .) - - echo ">> Vetting github.com/gopherjs/gopherjs" - go vet . - - echo ">> vet all packages except tests" - go vet $(go list ./... | grep -v github.com/gopherjs/gopherjs/tests) - - echo ">> All those packages should have // +build js." - diff -u <(echo "github.com/gopherjs/gopherjs/compiler/natives") <(go list ./compiler/natives/...) - - echo ">> Should build successfully (can't run tests, since only client is supported)." - gopherjs install -v net/http - - echo ">> Run the core gopherjs tests; exclusions take from .std_test_pkg_exclusions." - gopherjs test --minify -v --short github.com/gopherjs/gopherjs/tests/... $(go list std | grep -v -x -f .std_test_pkg_exclusions) - - echo ">> Race tests." - go test -v -race ./... - - echo ">> Non-minified gopherjs tests should also work." - gopherjs test -v fmt + - run: go install github.com/gopherjs/gopherjs + - run: go generate github.com/gopherjs/gopherjs/compiler/prelude + - run: go mod tidy && go list all > /dev/null # https://github.com/golang/go/issues/27868#issuecomment-431413621 + - run: go run genmodstubbs.go + - run: git diff + - run: diff -u <(echo -n) <(git status --porcelain) + - run: diff -u <(echo -n) <(gofmt -d .) + - run: go vet . + - run: go vet $(go list ./... | grep -v github.com/gopherjs/gopherjs/tests) + - run: diff -u <(echo "github.com/gopherjs/gopherjs/compiler/natives") <(go list ./compiler/natives/...) + - run: gopherjs install -v net/http + - run: gopherjs test --minify -v --short github.com/gopherjs/gopherjs/tests/... $(go list std | grep -v -x -f .std_test_pkg_exclusions) + - run: go test -v ./... + - run: gopherjs test -v fmt diff --git a/build/build.go b/build/build.go index 13912dc41..0bfa9f67a 100644 --- a/build/build.go +++ b/build/build.go @@ -3,6 +3,7 @@ package build import ( "bytes" "crypto/sha256" + "encoding/json" "fmt" "go/ast" "go/build" @@ -67,22 +68,32 @@ func (e *ImportCError) Error() string { // are loaded from gopherjspkg.FS virtual filesystem rather than GOPATH. func NewBuildContext(installSuffix string, buildTags []string) *build.Context { gopherjsRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs") - return &build.Context{ - GOROOT: build.Default.GOROOT, - GOPATH: build.Default.GOPATH, - GOOS: build.Default.GOOS, - GOARCH: "js", - InstallSuffix: installSuffix, - Compiler: "gc", - BuildTags: append(buildTags, - "netgo", // See https://godoc.org/net#hdr-Name_Resolution. - "purego", // See https://golang.org/issues/23172. - "js", - ), - ReleaseTags: build.Default.ReleaseTags, - CgoEnabled: true, // detect `import "C"` to throw proper error - - IsDir: func(path string) bool { + + ctxt := build.Default + ctxt.GOARCH = "js" + ctxt.Compiler = "gc" + ctxt.BuildTags = append(buildTags, + "netgo", // See https://godoc.org/net#hdr-Name_Resolution. + "purego", // See https://golang.org/issues/23172. + "js", // this effectively identifies that we are GopherJS + "!wasm", // but not webassembly + "math_big_pure_go", // Use pure Go version of math/big; we don't want non-Go assembly versions. + ) + + // TODO this is not great; the build use by GopherJS should not + // be a function of the package imported. See below for check + + ctxt.CgoEnabled = true // detect `import "C"` to throw proper error + + foundGopherJSDev := false + for _, t := range buildTags { + if t == "gopherjsdev" { + foundGopherJSDev = true + } + } + + if !foundGopherJSDev { + ctxt.IsDir = func(path string) bool { if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { path = filepath.ToSlash(path[len(gopherjsRoot):]) if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil { @@ -91,8 +102,8 @@ func NewBuildContext(installSuffix string, buildTags []string) *build.Context { } fi, err := os.Stat(path) return err == nil && fi.IsDir() - }, - ReadDir: func(path string) ([]os.FileInfo, error) { + } + ctxt.ReadDir = func(path string) ([]os.FileInfo, error) { if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { path = filepath.ToSlash(path[len(gopherjsRoot):]) if fis, err := vfsutil.ReadDir(gopherjspkg.FS, path); err == nil { @@ -100,8 +111,8 @@ func NewBuildContext(installSuffix string, buildTags []string) *build.Context { } } return ioutil.ReadDir(path) - }, - OpenFile: func(path string) (io.ReadCloser, error) { + } + ctxt.OpenFile = func(path string) (io.ReadCloser, error) { if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { path = filepath.ToSlash(path[len(gopherjsRoot):]) if f, err := gopherjspkg.FS.Open(path); err == nil { @@ -109,22 +120,10 @@ func NewBuildContext(installSuffix string, buildTags []string) *build.Context { } } return os.Open(path) - }, - } -} - -// statFile returns an os.FileInfo describing the named file. -// For files in "$GOROOT/src/github.com/gopherjs/gopherjs" directory, -// gopherjspkg.FS is consulted first. -func statFile(path string) (os.FileInfo, error) { - gopherjsRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs") - if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { - path = filepath.ToSlash(path[len(gopherjsRoot):]) - if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil { - return fi, nil } } - return os.Stat(path) + + return &ctxt } // Import returns details about the Go package named by the import path. If the @@ -141,7 +140,7 @@ func statFile(path string) (os.FileInfo, error) { // // If an error occurs, Import returns a non-nil error and a nil // *PackageData. -func Import(path string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) { +func (s *Session) Import(path string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) { wd, err := os.Getwd() if err != nil { // Getwd may fail if we're in GOARCH=js mode. That's okay, handle @@ -150,10 +149,10 @@ func Import(path string, mode build.ImportMode, installSuffix string, buildTags wd = "." } bctx := NewBuildContext(installSuffix, buildTags) - return importWithSrcDir(*bctx, path, wd, mode, installSuffix) + return s.importWithSrcDir(*bctx, path, wd, mode, installSuffix) } -func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) { +func (s *Session) importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) { // bctx is passed by value, so it can be modified here. var isVirtual bool switch path { @@ -179,16 +178,34 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build isVirtual = true } - pkg, err := bctx.Import(path, srcDir, mode) - if err != nil { - bc := build.Default - bc.InstallSuffix = bctx.InstallSuffix - bc.BuildTags = bctx.BuildTags - bc.ReleaseTags = bctx.ReleaseTags - pkg, err = bc.Import(path, srcDir, mode) + + var pkg *build.Package + var err error + if s.modLookup == nil { + pkg, err = bctx.Import(path, srcDir, mode) if err != nil { return nil, err } + } else { + dir, ok := s.modLookup[path] + if !ok { + return nil, fmt.Errorf("failed to find import directory for %v", path) + } + + // set IgnoreVendor even in module mode to prevent go/build from doing + // anything with go list; we've already done that work. + pkg, err = bctx.ImportDir(dir, mode|build.IgnoreVendor) + if err != nil { + return nil, fmt.Errorf("build context ImportDir failed: %v", err) + } + // because ImportDir doesn't know the ImportPath, we need to set + // certain things manually + gp := filepath.SplitList(build.Default.GOPATH)[0] + pkg.ImportPath = path + pkg.BinDir = filepath.Join(gp, "bin") + if !pkg.IsCommand() { + pkg.PkgObj = filepath.Join(gp, "pkg", build.Default.GOOS+"_js", path+".a") + } } switch path { @@ -215,12 +232,15 @@ func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build pkg.PkgObj = filepath.Join(pkg.BinDir, filepath.Base(pkg.ImportPath)+".js") } - if _, err := os.Stat(pkg.PkgObj); os.IsNotExist(err) && strings.HasPrefix(pkg.PkgObj, build.Default.GOROOT) { - // fall back to GOPATH - firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: Need to check inside all GOPATH workspaces. - gopathPkgObj := filepath.Join(firstGopathWorkspace, pkg.PkgObj[len(build.Default.GOROOT):]) - if _, err := os.Stat(gopathPkgObj); err == nil { - pkg.PkgObj = gopathPkgObj + // this is pre-module behaviour. Don't touch it + if s.modLookup == nil { + if _, err := os.Stat(pkg.PkgObj); os.IsNotExist(err) && strings.HasPrefix(pkg.PkgObj, build.Default.GOROOT) { + // fall back to GOPATH + firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: Need to check inside all GOPATH workspaces. + gopathPkgObj := filepath.Join(firstGopathWorkspace, pkg.PkgObj[len(build.Default.GOROOT):]) + if _, err := os.Stat(gopathPkgObj); err == nil { + pkg.PkgObj = gopathPkgObj + } } } @@ -288,7 +308,7 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag // as an existing file from the standard library). For all identifiers that exist // in the original AND the overrides, the original identifier in the AST gets // replaced by `_`. New identifiers that don't exist in original package get added. -func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) { +func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileSet *token.FileSet, hw io.Writer) ([]*ast.File, error) { var files []*ast.File replacedDeclNames := make(map[string]bool) funcName := func(d *ast.FuncDecl) string { @@ -369,11 +389,20 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS if err != nil { panic(err) } - file, err := parser.ParseFile(fileSet, fullPath, r, parser.ParseComments) + rbyts, err := ioutil.ReadAll(r) + r.Close() + if err != nil { + return nil, fmt.Errorf("failed to read native file %v: %v", fullPath, err) + } + if hw != nil { + fmt.Fprintf(hw, "file: %v\n", fullPath) + fmt.Fprintf(hw, "%s\n", rbyts) + fmt.Fprintf(hw, "%d bytes\n", len(rbyts)) + } + file, err := parser.ParseFile(fileSet, fullPath, rbyts, parser.ParseComments) if err != nil { panic(err) } - r.Close() for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: @@ -407,8 +436,17 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS if err != nil { return nil, err } - file, err := parser.ParseFile(fileSet, name, r, parser.ParseComments) + rbyts, err := ioutil.ReadAll(r) r.Close() + if err != nil { + return nil, err + } + if hw != nil { + fmt.Fprintf(hw, "file: %v\n", name) + fmt.Fprintf(hw, "%s\n", rbyts) + fmt.Fprintf(hw, "%d bytes\n", len(rbyts)) + } + file, err := parser.ParseFile(fileSet, name, rbyts, parser.ParseComments) if err != nil { if list, isList := err.(scanner.ErrorList); isList { if len(list) > 10 { @@ -515,9 +553,16 @@ type Session struct { wd string // working directory buildCache *cache.Cache didCacheWork bool + + // map of import path to dir for module mode resolution + // a nil value implies we are not in module mode + modLookup map[string]string + + // map of module path + mods map[string]string } -func NewSession(options *Options) (*Session, error) { +func NewSession(options *Options, tests bool, imports ...string) (*Session, error) { if options.GOROOT == "" { options.GOROOT = build.Default.GOROOT } @@ -552,6 +597,9 @@ func NewSession(options *Options) (*Session, error) { wd: wd, buildCache: buildCache, } + if err := s.determineModLookup(tests, imports); err != nil { + return nil, err + } s.bctx = NewBuildContext(s.InstallSuffix(), s.options.BuildTags) s.Types = make(map[string]*types.Package) if options.Watch { @@ -570,6 +618,77 @@ func NewSession(options *Options) (*Session, error) { return s, nil } +func (s *Session) GO111MODULE() bool { + return s.modLookup != nil +} + +func (s *Session) determineModLookup(tests bool, imports []string) error { + goenvCmd := exec.Command("go", "env", "GOMOD") + output, err := goenvCmd.Output() + if err != nil { + return fmt.Errorf("failed to determine if we are module-mode or not: %v", err) + } + if strings.TrimSpace(string(output)) == "" { + return nil + } + + // we always need to be able to resolve these + imports = append(imports, "runtime", "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync") + + if tests { + imports = append(imports, "testing", "testing/internal/testdeps") + } + + var stdout, stderr bytes.Buffer + golistCmd := exec.Command("go", "list", "-deps", "-json") + if tests { + golistCmd.Args = append(golistCmd.Args, "-test") + } + golistCmd.Args = append(golistCmd.Args, imports...) + golistCmd.Stdout = &stdout + golistCmd.Stderr = &stderr + + if err := golistCmd.Run(); err != nil { + return fmt.Errorf("failed to run %v: %v\n%s", strings.Join(golistCmd.Args, " "), err, stderr.Bytes()) + } + + dec := json.NewDecoder(&stdout) + + s.modLookup = make(map[string]string) + s.mods = make(map[string]string) + + for { + var entry struct { + ImportPath string + Dir string + Module struct { + Path string + Dir string + } + } + + if err := dec.Decode(&entry); err != nil { + if err == io.EOF { + break + } + return fmt.Errorf("failed to decode list output: %v\n%s", err, stdout.Bytes()) + } + + ipParts := strings.Split(entry.ImportPath, " ") + entry.ImportPath = ipParts[0] + + s.modLookup[entry.ImportPath] = entry.Dir + s.mods[entry.Module.Path] = entry.Module.Dir + } + + return nil +} + +func (s *Session) IsModulePath(path string) (string, bool) { + dir, ok := s.mods[path] + return dir, ok +} + func (s *Session) Cleanup() error { if s.didCacheWork { s.buildCache.Trim() @@ -643,14 +762,12 @@ func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath stri return s.WriteCommandPackage(archive, pkgObj) } -func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) { - wd, _ := os.Getwd() - _, archive, err := s.buildImportPathWithSrcDir(path, wd) - return archive, err +func (s *Session) BuildImportPath(path string) (*PackageData, *compiler.Archive, error) { + return s.buildImportPathWithSrcDir(path, s.wd) } func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*PackageData, *compiler.Archive, error) { - pkg, err := importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix()) + pkg, err := s.importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix()) if s.Watcher != nil && pkg != nil { // add watch even on error s.Watcher.Add(pkg.Dir) } @@ -697,25 +814,128 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { return archive, nil } - archive, pkgHash, err := s.checkCache(pkg) - if archive != nil || err != nil { - return archive, err + var pkgHash *cache.Hash + var hw io.Writer + var hashDebugOut *bytes.Buffer + + // We never cache main or test packages because of the "hack" used to run + // arbitrary files and some legacy (at the time of writing) unknown reason + // for test packages. + // + // Where we do cache an archive, build up a hash that represents a complete + // description of a repeatable computation (command line, environment + // variables, input file contents, executable contents). This therefore + // needs to be a stable computation. The iteration through imports is, by + // definition, stable, because those imports are ordered. + if !(pkg.IsCommand() || pkg.IsTest) { + pkgHash = cache.NewHash("## build " + pkg.ImportPath) + hw = pkgHash + if hashDebug { + hashDebugOut = new(bytes.Buffer) + hw = io.MultiWriter(hashDebugOut, pkgHash) + } } - // At this point, for whatever reason, we were unable to read a build-cached archive. - // So we need to build one. + if hw != nil { + fmt.Fprintf(hw, "compiler binary hash: %v\n", compilerBinaryHash) + + orderedBuildTags := append([]string{}, s.options.BuildTags...) + sort.Strings(orderedBuildTags) + + fmt.Fprintf(hw, "build tags: %v\n", strings.Join(orderedBuildTags, ",")) + + for _, importedPkgPath := range pkg.Imports { + // Ignore all imports that aren't mentioned in import specs of pkg. For + // example, this ignores imports such as runtime/internal/sys and + // runtime/internal/atomic; nobody explicitly adds such imports to their + // packages, they are automatically added by the Go tool. + // + // TODO perhaps there is a cleaner way of doing this? + ignored := true + for _, pos := range pkg.ImportPos[importedPkgPath] { + importFile := filepath.Base(pos.Filename) + for _, file := range pkg.GoFiles { + if importFile == file { + ignored = false + break + } + } + if !ignored { + break + } + } - if s.options.Verbose { - fmt.Printf("Cache miss for %v\n", pkg.ImportPath) + if importedPkgPath == "unsafe" || ignored { + continue + } + + _, importedArchive, err := s.buildImportPathWithSrcDir(importedPkgPath, s.wd) + if err != nil { + return nil, err + } + + fmt.Fprintf(hw, "import: %v\n", importedPkgPath) + fmt.Fprintf(hw, " hash: %#x\n", importedArchive.Hash) + } } - fileSet := token.NewFileSet() - files, err := parseAndAugment(s.bctx, pkg.Package, pkg.IsTest, fileSet) + fset := token.NewFileSet() + files, err := parseAndAugment(s.bctx, pkg.Package, pkg.IsTest, fset, hw) if err != nil { return nil, err } - // TODO: localImportPathCache is probably redundent given s.Archives + if hw != nil { + for _, name := range pkg.JSFiles { + hashFile := func() error { + fp := filepath.Join(pkg.Dir, name) + file, err := s.bctx.OpenFile(fp) + if err != nil { + return fmt.Errorf("failed to open %v: %v", fp, err) + } + defer file.Close() + fmt.Fprintf(hw, "file: %v\n", fp) + n, err := io.Copy(hw, file) + if err != nil { + return fmt.Errorf("failed to hash file contents: %v", err) + } + fmt.Fprintf(hw, "%d bytes\n", n) + return nil + } + + if err := hashFile(); err != nil { + return nil, fmt.Errorf("failed to hash file %v: %v", name, err) + } + } + + if hashDebug { + fmt.Printf("%s", hashDebugOut.String()) + } + + // At this point we have a complete Hash. Hence we can check the Cache to see whether + // we already have an archive for this key. + + if objFilePath, _, err := s.buildCache.GetFile(pkgHash.Sum()); err == nil { + // Try to open objFile; we are not guaranteed it will still be available + objFile, err := os.Open(objFilePath) + if err == nil { + archive, err := compiler.ReadArchive(pkg.PkgObj, pkg.ImportPath, objFile, s.Types) + objFile.Close() + if err == nil { + s.Archives[pkg.ImportPath] = archive + return archive, nil + } + } + } + } + + // At this point, for whatever reason, we were unable to read a build-cached archive. + // So we need to build one. + + if s.options.Verbose { + fmt.Fprintf(os.Stderr, "Cache miss for %v\n", pkg.ImportPath) + } + localImportPathCache := make(map[string]*compiler.Archive) importContext := &compiler.ImportContext{ Packages: s.Types, @@ -731,7 +951,7 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { return archive, nil }, } - archive, err = compiler.Compile(pkg.ImportPath, files, fileSet, importContext, s.options.Minify) + archive, err := compiler.Compile(pkg.ImportPath, files, fset, importContext, s.options.Minify) if err != nil { return nil, err } @@ -767,132 +987,6 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { return archive, nil } -// checkCache returns the *compiler.Archive for pkg if it is present in the build cache. The *cache.Hash -// for pkg is returned for all non-main packages. -func (s *Session) checkCache(pkg *PackageData) (*compiler.Archive, *cache.Hash, error) { - // We never cache main packages because of the "hack" used to run arbitrary files. - if pkg.IsCommand() { - return nil, nil, nil - } - - // Build up a hash that represents a complete description of a repeatable - // computation (command line, environment variables, input file contents, - // executable contents). This therefore needs to be a stable computation. - // The iteration through imports is, by definition, stable, because those - // imports are ordered. - // - // We then use this hash value as a cache.ActionID - - // staleness. Set hashDebug to see this in action. The format is: - // - // ## - // compiler binary hash: 0x519d22c6ab65a950f5b6278e4d65cb75dbd3a7eb1cf16e976a40b9f1febc0446 - // build tags: - // import: - // hash: 0xb966d7680c1c8ca75026f993c153aff0102dc9551f314e5352043187b5f9c9a6 - // ... - // - // file: - // - // N bytes - // ... - - pkgHash := cache.NewHash("## build " + pkg.ImportPath) - var hw io.Writer = pkgHash - var hashDebugOut *bytes.Buffer - if hashDebug { - hashDebugOut = new(bytes.Buffer) - hw = io.MultiWriter(hashDebugOut, pkgHash) - } - - fmt.Fprintf(hw, "compiler binary hash: %v\n", compilerBinaryHash) - - orderedBuildTags := append([]string{}, s.options.BuildTags...) - sort.Strings(orderedBuildTags) - - fmt.Fprintf(hw, "build tags: %v\n", strings.Join(orderedBuildTags, ",")) - - for _, importedPkgPath := range pkg.Imports { - // Ignore all imports that aren't mentioned in import specs of pkg. For - // example, this ignores imports such as runtime/internal/sys and - // runtime/internal/atomic; nobody explicitly adds such imports to their - // packages, they are automatically added by the Go tool. - // - // TODO perhaps there is a cleaner way of doing this? - ignored := true - for _, pos := range pkg.ImportPos[importedPkgPath] { - importFile := filepath.Base(pos.Filename) - for _, file := range pkg.GoFiles { - if importFile == file { - ignored = false - break - } - } - if !ignored { - break - } - } - - if importedPkgPath == "unsafe" || ignored { - continue - } - - _, importedArchive, err := s.buildImportPathWithSrcDir(importedPkgPath, s.wd) - if err != nil { - return nil, nil, err - } - - fmt.Fprintf(hw, "import: %v\n", importedPkgPath) - fmt.Fprintf(hw, " hash: %#x\n", importedArchive.Hash) - } - - for _, name := range append(pkg.GoFiles, pkg.JSFiles...) { - hashFile := func() error { - fp := filepath.Join(pkg.Dir, name) - file, err := s.bctx.OpenFile(fp) - if err != nil { - return fmt.Errorf("failed to open %v: %v", fp, err) - } - defer file.Close() - fmt.Fprintf(hw, "file: %v\n", fp) - n, err := io.Copy(hw, file) - if err != nil { - return fmt.Errorf("failed to hash file contents: %v", err) - } - fmt.Fprintf(hw, "%d bytes\n", n) - return nil - } - - if err := hashFile(); err != nil { - return nil, nil, fmt.Errorf("failed to hash file %v: %v", name, err) - } - } - - if hashDebug { - fmt.Printf("%s", hashDebugOut.String()) - } - - // At this point we have a complete Hash. Hence we can check the Cache to see whether - // we already have an archive for this key. - - if objFilePath, _, err := s.buildCache.GetFile(pkgHash.Sum()); err == nil { - // Try to open objFile; we are not guaranteed it will still be available - objFile, err := os.Open(objFilePath) - if err != nil { - return nil, pkgHash, nil - } - - archive, err := compiler.ReadArchive(pkg.PkgObj, pkg.ImportPath, objFile, s.Types) - objFile.Close() - if err == nil { - s.Archives[pkg.ImportPath] = archive - return archive, pkgHash, nil - } - } - - return nil, pkgHash, nil -} - func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string) error { if err := os.MkdirAll(filepath.Dir(pkgObj), 0777); err != nil { return err @@ -1010,7 +1104,7 @@ func (s *Session) WaitForChange() { s.Watcher.Close() } -func ImportPaths(vs []string) ([]string, error) { +func ImportPaths(vs ...string) ([]string, error) { if len(vs) == 0 { vs = []string{"."} } diff --git a/build/build_test.go b/build/build_test.go index 634ebcb49..8de4da243 100644 --- a/build/build_test.go +++ b/build/build_test.go @@ -63,7 +63,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Then, github.com/gopherjs/gopherjs/build.parseAndAugment(*build.Package) returns []*ast.File. // Those augmented parsed Go files of the package are checked, one file at at time, one import // at a time. Each import is verified to belong in the set of allowed real imports. - ips, err := ImportPaths([]string{"std"}) + ips, err := ImportPaths("std") if err != nil { t.Fatalf("failed to resolve std package spec: %v", err) } @@ -80,7 +80,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Use parseAndAugment to get a list of augmented AST files. fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, false, fset) + files, err := parseAndAugment(NewBuildContext("", nil), bpkg, false, fset, nil) if err != nil { t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) } @@ -119,7 +119,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Use parseAndAugment to get a list of augmented AST files. fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset) + files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset, nil) if err != nil { t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) } @@ -161,7 +161,7 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Use parseAndAugment to get a list of augmented AST files, then check only the external test files. fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset) + files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset, nil) if err != nil { t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) } diff --git a/compiler/package.go b/compiler/package.go index f841b145b..b137a253d 100644 --- a/compiler/package.go +++ b/compiler/package.go @@ -107,6 +107,7 @@ func (pi packageImporter) Import(path string) (*types.Package, error) { } a, err := pi.importContext.Import(path) + if err != nil { if *pi.importError == nil { // If import failed, show first error of import only (https://github.com/gopherjs/gopherjs/issues/119). diff --git a/genmodstubbs.go b/genmodstubbs.go new file mode 100644 index 000000000..625b8c980 --- /dev/null +++ b/genmodstubbs.go @@ -0,0 +1,126 @@ +// +build ignore + +package main + +import ( + "bytes" + "encoding/json" + "flag" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + + "github.com/rogpeppe/go-internal/module" +) + +func main() { + flag.Parse() + + gmt := exec.Command("go", "mod", "tidy") + if err := gmt.Run(); err != nil { + log.Fatalf("failed to run %v: %v\n%s", strings.Join(gmt.Args, " "), err, errToBytes(err)) + } + + gme := exec.Command("go", "mod", "edit", "-json") + gme.Stderr = new(bytes.Buffer) + + out, err := gme.Output() + if err != nil { + log.Fatalf("failed to run %v: %v\n%s", strings.Join(gme.Args, " "), err, errToBytes(err)) + } + + type mod struct { + Path string + Version string + } + + var goMod struct { + Require []mod + } + + if err := json.Unmarshal(out, &goMod); err != nil { + log.Fatalf("failed to unmarshal: %v\n%s", err, out) + } + + // add in gopherjs v0.0.0 to satisfy our redirect + goMod.Require = append(goMod.Require, mod{ + Path: "github.com/gopherjs/gopherjs", + Version: "v0.0.0", + }) + + dirCmd := exec.Command("go", "list", "-m", "-f={{.Dir}}") + out, err = dirCmd.Output() + if err != nil { + log.Fatalf("failed to run %v: %v\n%s", strings.Join(dirCmd.Args, " "), err, errToBytes(err)) + } + + dir := strings.TrimSpace(string(out)) + mods := filepath.Join(dir, "testdata", "mod") + + curr, err := filepath.Glob(filepath.Join(mods, "*")) + if err != nil { + log.Fatalf("failed to glob current mod files: %v", err) + } + + for _, c := range curr { + base := filepath.Base(c) + if strings.HasPrefix(base, "example.com") { + continue + } + + if err := os.Remove(c); err != nil { + log.Fatalf("failed to remove %v: %v", c, err) + } + } + + if err := os.MkdirAll(mods, 0755); err != nil { + log.Fatalf("failed to create directory %v: %v", mods, err) + } + + tmpl := template.Must(template.New("tmpl").Parse(` +module {{.Path}}@{{.Version}} + +-- .mod -- +module "{{.Path}}" + +-- .info -- +{"Version":"{{.Version}}","Time":"2018-05-06T08:24:08Z"} +`[1:])) + + for _, r := range goMod.Require { + enc, err := module.EncodePath(r.Path) + if err != nil { + log.Fatalf("failed to encode path %q: %v", r.Path, err) + } + ver, err := module.EncodeVersion(r.Version) + if err != nil { + log.Fatalf("failed to encode version %q: %v", r.Version, err) + } + + prefix := strings.Replace(enc, "/", "_", -1) + name := filepath.Join(mods, prefix+"_"+ver+".txt") + f, err := os.Create(name) + if err != nil { + log.Fatalf("failed to create %v: %v", name, err) + } + + if err := tmpl.Execute(f, r); err != nil { + log.Fatalf("failed to execute template for %v: %v", name, err) + } + + if err := f.Close(); err != nil { + log.Fatalf("failed to close %v: %v", name, err) + } + } +} + +func errToBytes(err error) []byte { + if ee, ok := err.(*exec.ExitError); ok { + return ee.Stderr + } + + return nil +} diff --git a/go.mod b/go.mod index 89e98a10c..f0b39425f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab - github.com/rogpeppe/go-internal v1.0.1-alpha + github.com/rogpeppe/go-internal v1.0.1-alpha.1 github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 github.com/shurcooL/vfsgen v0.0.0-20180915214035-33ae1944be3f diff --git a/go.sum b/go.sum index 1a4263516..ee75e114d 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wu github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/rogpeppe/go-internal v1.0.1-alpha h1:3LYtE0jQQd8uQe4GN6zsObkftp8jUIayT1mAHh2twN8= -github.com/rogpeppe/go-internal v1.0.1-alpha/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.0.1-alpha.1 h1:NGPDe06RaK5iQFZkqFMNb986UARfoHc9FLyWI7b6+aQ= +github.com/rogpeppe/go-internal v1.0.1-alpha.1/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo= diff --git a/script_test.go b/script_test.go index ea2c7ff3d..17def0024 100644 --- a/script_test.go +++ b/script_test.go @@ -5,8 +5,14 @@ import ( "crypto/sha256" "fmt" "io" + "net" + "net/http" "os" + "path/filepath" + "strings" + "sync" "testing" + "time" "github.com/rogpeppe/go-internal/goproxytest" "github.com/rogpeppe/go-internal/gotooltest" @@ -40,16 +46,25 @@ func (m gobinMain) Run() int { } func TestScripts(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get working directory: %v", err) + } p := testscript.Params{ Dir: "testdata", Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ "modified": buildModified(), "changed": buildChanged(), + "cpr": cmdCpr, + "highport": highport, + "httpget": httpget, + "sleep": sleep, }, Setup: func(e *testscript.Env) error { e.Vars = append(e.Vars, "NODE_PATH="+os.Getenv("NODE_PATH"), "GOPROXY="+proxyURL, + "SELF="+wd, ) return nil }, @@ -60,6 +75,91 @@ func TestScripts(t *testing.T) { testscript.Run(t, p) } +// sleep for the specified duration +func sleep(ts *testscript.TestScript, neg bool, args []string) { + if neg { + ts.Fatalf("sleep does not understand negation") + } + + if len(args) != 1 { + ts.Fatalf("Usage: sleep duration") + } + + d, err := time.ParseDuration(args[0]) + if err != nil { + ts.Fatalf("failed to parse duration %q: %v", args[0], err) + } + + time.Sleep(d) +} + +// Usage: +// +// httpget url outputFile +// +func httpget(ts *testscript.TestScript, neg bool, args []string) { + if len(args) != 2 { + ts.Fatalf("Usage: httpget url outputFile") + } + + url := args[0] + ofPath := ts.MkAbs(args[1]) + + resp, err := http.Get(url) + if err != nil { + ts.Fatalf("httpget %v failed: %v", url, err) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + if !neg { + ts.Fatalf("httpget %v return status code %v", url, resp.StatusCode) + } + } + + of, err := os.Create(ofPath) + if err != nil { + ts.Fatalf("failed to create output file %v: %v", ofPath, err) + } + + if _, err := io.Copy(of, resp.Body); err != nil { + ts.Fatalf("failed to write response to output file %v: %v", ofPath, err) + } + + if err := of.Close(); err != nil { + ts.Fatalf("failed to close output file %v: %v", ofPath, err) + } +} + +// Sets the environment variable named by the single argument key to an +// available high port. +func highport(ts *testscript.TestScript, neg bool, args []string) { + if neg { + ts.Fatalf("highport does not understand negation") + } + + if len(args) != 1 { + ts.Fatalf("highport takes exactly one argument; the name of the environment variable key to set") + } + key := args[0] + + l, err := net.Listen("tcp", ":0") + if err != nil { + ts.Fatalf("could not get a free high port: %v", err) + } + defer func() { + if err := l.Close(); err != nil { + ts.Fatalf("failed to free up high port: %v", err) + } + }() + _, port, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + ts.Fatalf("could not extract port from %q: %v", l.Addr().String(), err) + } + + ts.Setenv(key, port) +} + // buildModified returns a new instance of a testscript command that determines // whether the single file argument has been modified since the command was // last called on that file. Strictly speaking it is only safe to test files @@ -70,11 +170,33 @@ func TestScripts(t *testing.T) { // The first time of calling modified for any given file path is defined to // return 0, i.e. false func buildModified() func(ts *testscript.TestScript, neg bool, args []string) { + var lock sync.Mutex cache := make(map[string]os.FileInfo) return func(ts *testscript.TestScript, neg bool, args []string) { - if len(args) != 1 { - ts.Fatalf("modified take a single file path argument") + lock.Lock() + defer lock.Unlock() + + var poorUsage bool + switch len(args) { + case 2: + if args[0] != "-clear" { + poorUsage = true + } + case 1: + if args[0] == "-clear" { + poorUsage = true + } + default: + poorUsage = true + } + if poorUsage { + ts.Fatalf("usage: modified [-clear] file") + } + + if args[0] == "-clear" { + delete(cache, args[1]) + return } fp := ts.MkAbs(args[0]) @@ -125,11 +247,33 @@ func buildModified() func(ts *testscript.TestScript, neg bool, args []string) { // The first time of calling changed for any given file path is defined to // return 0, i.e. false func buildChanged() func(ts *testscript.TestScript, neg bool, args []string) { + var lock sync.Mutex cache := make(map[string][]byte) return func(ts *testscript.TestScript, neg bool, args []string) { - if len(args) != 1 { - ts.Fatalf("changed take a single file path argument") + lock.Lock() + defer lock.Unlock() + + var poorUsage bool + switch len(args) { + case 2: + if args[0] != "-clear" { + poorUsage = true + } + case 1: + if args[0] == "-clear" { + poorUsage = true + } + default: + poorUsage = true + } + if poorUsage { + ts.Fatalf("usage: changed [-clear] file") + } + + if args[0] == "-clear" { + delete(cache, args[1]) + return } fp := ts.MkAbs(args[0]) @@ -165,3 +309,81 @@ func buildChanged() func(ts *testscript.TestScript, neg bool, args []string) { } } } + +// cmdCpr implements a recursive copy of go files. Takes two arguments: source +// directory and target directory. The source directory must exist. The target +// directory will be created if it does not exist. +func cmdCpr(ts *testscript.TestScript, neg bool, args []string) { + if neg { + ts.Fatalf("cpr does not understand negation") + } + if len(args) != 2 { + ts.Fatalf("cpr takes two arguments: got %v", len(args)) + } + + src, dst := ts.MkAbs(args[0]), ts.MkAbs(args[1]) + + sfi, err := os.Stat(src) + if err != nil { + ts.Fatalf("source %v must exist: %v", src, err) + } + if !sfi.IsDir() { + ts.Fatalf("source %v must be a directory", src) + } + + if err := os.MkdirAll(dst, 0755); err != nil { + ts.Fatalf("error trying to ensure target directory exists") + } + + err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + dstPath := path + dstPath = strings.TrimPrefix(dstPath, src) + dstPath = strings.TrimPrefix(dstPath, string(os.PathSeparator)) + + // root + if dstPath == "" { + return nil + } + + dstPath = filepath.Join(dst, dstPath) + + name := info.Name() + + if info.IsDir() { + switch name[0] { + case '_', '.': + return filepath.SkipDir + } + if err := os.MkdirAll(dstPath, info.Mode()); err != nil { + return fmt.Errorf("failed to mkdir %v: %v", dstPath, err) + } + return nil + } + + if !strings.HasSuffix(name, ".go") || name[0] == '_' || name[0] == '.' { + return nil + } + + srcf, err := os.Open(path) + if err != nil { + return fmt.Errorf("failed to open source file %v: %v", path, err) + } + dstf, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE, info.Mode()) + if err != nil { + return fmt.Errorf("failed to create target file %v: %v", dstPath, err) + } + if _, err := io.Copy(dstf, srcf); err != nil { + return fmt.Errorf("failed to copy from %v to %v: %v", path, dstPath, err) + } + + return nil + }) + + if err != nil { + ts.Fatalf("failed to recursively copy: %v", err) + } +} diff --git a/testdata/build.txt b/testdata/build.txt new file mode 100644 index 000000000..91f0a9769 --- /dev/null +++ b/testdata/build.txt @@ -0,0 +1,147 @@ +# Tests for gopherjs build command in module mode. These tests are +# largely copied from their GOPATH equivalents. Includes checks +# for behaviour, staleness and output side effects (e.g. files in +# $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go +cd hello +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF + +cp blah/blah.go.fish blah/blah.go + +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists hello.js +gopherjs build +exists hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +cp hello.js implicit.js +rm hello.js +! exists hello.js +gopherjs build example.com/hello +exists hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +cp hello.js explicit.js +rm hello.js +cmp implicit.js explicit.js +! exists hello.js main.js +gopherjs build main.go +exists main.js +exec node main.js +stdout '^Today we will eat fish$' +! stderr .+ +! exists $GOPATH/pkg/${GOOS}_js + +# tidy up +rm hello.js.map implicit.js explicit.js main.js main.js.map + +# non-command package +cd blah +gopherjs build +! exists $GOPATH/pkg/${GOOS}_js +! exists blah.js + +cd .. + +# Staleness checks +cp blah/blah.go.fish blah/blah.go +! exists hello.js hello.js.map +gopherjs build +! stdout .+ +! stderr .+ +exists hello.js hello.js.map +! modified hello.js +! changed hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs build +modified hello.js +! changed hello.js +cp blah/blah.go.chips blah/blah.go +gopherjs build +modified hello.js +changed hello.js +exec node hello.js +stdout '^Today we will eat chips$' +! stderr .+ +gopherjs build --tags thin +modified hello.js +changed hello.js +exec node hello.js +stdout '^Today we will eat thin chips$' +! stderr .+ + +-- hello/go.mod -- +module example.com/hello + +-- hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/build_gopath.txt b/testdata/build_gopath.txt new file mode 100644 index 000000000..9ddef1004 --- /dev/null +++ b/testdata/build_gopath.txt @@ -0,0 +1,142 @@ +# Tests for gopherjs build command in GOPATH mode. Includes checks for +# behaviour, staleness and output side effects (e.g. files in $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go + +cd go/src/example.com/hello + +cp blah/blah.go.fish blah/blah.go + +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists hello.js +gopherjs build +exists hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +cp hello.js implicit.js +rm hello.js +! exists hello.js +gopherjs build example.com/hello +exists hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +cp hello.js explicit.js +rm hello.js +cmp implicit.js explicit.js +! exists hello.js main.js +gopherjs build main.go +exists main.js +exec node main.js +stdout '^Today we will eat fish$' +! stderr .+ +! exists $GOPATH/pkg + +# tidy up +rm hello.js.map implicit.js explicit.js main.js main.js.map + +# non-command package +cd blah +gopherjs build +! exists $GOPATH/pkg +! exists blah.js + +cd .. + +# Staleness checks +cp blah/blah.go.fish blah/blah.go +! exists hello.js hello.js.map +gopherjs build +! stdout .+ +! stderr .+ +exists hello.js hello.js.map +! modified hello.js +! changed hello.js +exec node hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs build +modified hello.js +! changed hello.js +cp blah/blah.go.chips blah/blah.go +gopherjs build +modified hello.js +changed hello.js +exec node hello.js +stdout '^Today we will eat chips$' +! stderr .+ +gopherjs build --tags thin +modified hello.js +changed hello.js +exec node hello.js +stdout '^Today we will eat thin chips$' +! stderr .+ + +-- go/src/example.com/hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- go/src/example.com/hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- go/src/example.com/hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- go/src/example.com/hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/install.txt b/testdata/install.txt new file mode 100644 index 000000000..eaac6fb31 --- /dev/null +++ b/testdata/install.txt @@ -0,0 +1,147 @@ +# Tests for gopherjs install command in GOPATH mode. These tests +# are largely copied from their GOPATH equivalents. Includes checks +# for behaviour, staleness and output side effects (e.g. files in +# $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go +cd hello +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF + +cp blah/blah.go.fish blah/blah.go + +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +gopherjs install +! exists $GOPATH/pkg/${GOOS}_js +exists $GOPATH/bin/hello.js +! modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs install example.com/hello +! exists $GOPATH/pkg/${GOOS}_js +modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js + +rm $GOPATH/pkg $GOPATH/bin + +# non-command package +! exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +cd blah +gopherjs install +! exists $GOPATH/bin +exists $GOPATH/pkg/${GOOS}_js/example.com/hello/blah.a +! modified $GOPATH/pkg/linux_js/example.com/hello/blah.a +! changed $GOPATH/pkg/linux_js/example.com/hello/blah.a +gopherjs install example.com/hello +modified $GOPATH/pkg/linux_js/example.com/hello/blah.a +! changed $GOPATH/pkg/linux_js/example.com/hello/blah.a + +cd .. +rm $GOPATH/pkg $GOPATH/bin + +modified -clear $GOPATH/pkg/linux_js/example.com/hello/blah.a +modified -clear $GOPATH/bin/hello.js +changed -clear $GOPATH/pkg/linux_js/example.com/hello/blah.a +changed -clear $GOPATH/bin/hello.js + +# Staleness checks +! exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +cp blah/blah.go.fish blah/blah.go +gopherjs install +! stdout .+ +! stderr .+ +exists $GOPATH/bin/hello.js +! modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs install +modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +cp blah/blah.go.chips blah/blah.go +gopherjs install +modified $GOPATH/bin/hello.js +changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat chips$' +! stderr .+ +gopherjs install --tags thin +modified $GOPATH/bin/hello.js +changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat thin chips$' +! stderr .+ + +-- hello/go.mod -- +module example.com/hello + +-- hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/install_gopath.txt b/testdata/install_gopath.txt new file mode 100644 index 000000000..a385cbca7 --- /dev/null +++ b/testdata/install_gopath.txt @@ -0,0 +1,142 @@ +# Tests for gopherjs install command in GOPATH mode. Includes checks for +# behaviour, staleness and output side effects (e.g. files in $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go + +cd go/src/example.com/hello + +cp blah/blah.go.fish blah/blah.go + +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists $GOPATH/pkg $GOPATH/bin +gopherjs install +! exists $GOPATH/pkg +exists $GOPATH/bin/hello.js +! modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs install example.com/hello +! exists $GOPATH/pkg +modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js + +rm $GOPATH/pkg $GOPATH/bin + +# non-command package +! exists $GOPATH/pkg $GOPATH/bin +cd blah +gopherjs install +! exists $GOPATH/bin +exists $GOPATH/pkg/linux_js/example.com/hello/blah.a +! modified $GOPATH/pkg/linux_js/example.com/hello/blah.a +! changed $GOPATH/pkg/linux_js/example.com/hello/blah.a +gopherjs install example.com/hello +modified $GOPATH/pkg/linux_js/example.com/hello/blah.a +! changed $GOPATH/pkg/linux_js/example.com/hello/blah.a + +cd .. +rm $GOPATH/pkg $GOPATH/bin + +modified -clear $GOPATH/pkg/linux_js/example.com/hello/blah.a +modified -clear $GOPATH/bin/hello.js +changed -clear $GOPATH/pkg/linux_js/example.com/hello/blah.a +changed -clear $GOPATH/bin/hello.js + +# Staleness checks +! exists $GOPATH/pkg $GOPATH/bin +cp blah/blah.go.fish blah/blah.go +gopherjs install +! stdout .+ +! stderr .+ +exists $GOPATH/bin/hello.js +! modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat fish$' +! stderr .+ +gopherjs install +modified $GOPATH/bin/hello.js +! changed $GOPATH/bin/hello.js +cp blah/blah.go.chips blah/blah.go +gopherjs install +modified $GOPATH/bin/hello.js +changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat chips$' +! stderr .+ +gopherjs install --tags thin +modified $GOPATH/bin/hello.js +changed $GOPATH/bin/hello.js +exec node $GOPATH/bin/hello.js +stdout '^Today we will eat thin chips$' +! stderr .+ + +-- go/src/example.com/hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- go/src/example.com/hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- go/src/example.com/hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- go/src/example.com/hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/mod/github.com_fsnotify_fsnotify_v1.4.7.txt b/testdata/mod/github.com_fsnotify_fsnotify_v1.4.7.txt new file mode 100644 index 000000000..9ace9dad6 --- /dev/null +++ b/testdata/mod/github.com_fsnotify_fsnotify_v1.4.7.txt @@ -0,0 +1,7 @@ +module github.com/fsnotify/fsnotify@v1.4.7 + +-- .mod -- +module "github.com/fsnotify/fsnotify" + +-- .info -- +{"Version":"v1.4.7","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_gopherjs_gopherjs_v0.0.0.txt b/testdata/mod/github.com_gopherjs_gopherjs_v0.0.0.txt new file mode 100644 index 000000000..2bdc2d02d --- /dev/null +++ b/testdata/mod/github.com_gopherjs_gopherjs_v0.0.0.txt @@ -0,0 +1,7 @@ +module github.com/gopherjs/gopherjs@v0.0.0 + +-- .mod -- +module "github.com/gopherjs/gopherjs" + +-- .info -- +{"Version":"v0.0.0","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_inconshreveable_mousetrap_v1.0.0.txt b/testdata/mod/github.com_inconshreveable_mousetrap_v1.0.0.txt new file mode 100644 index 000000000..66a66d968 --- /dev/null +++ b/testdata/mod/github.com_inconshreveable_mousetrap_v1.0.0.txt @@ -0,0 +1,7 @@ +module github.com/inconshreveable/mousetrap@v1.0.0 + +-- .mod -- +module "github.com/inconshreveable/mousetrap" + +-- .info -- +{"Version":"v1.0.0","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_neelance_astrewrite_v0.0.0-20160511093645-99348263ae86.txt b/testdata/mod/github.com_neelance_astrewrite_v0.0.0-20160511093645-99348263ae86.txt new file mode 100644 index 000000000..d9eccf10d --- /dev/null +++ b/testdata/mod/github.com_neelance_astrewrite_v0.0.0-20160511093645-99348263ae86.txt @@ -0,0 +1,7 @@ +module github.com/neelance/astrewrite@v0.0.0-20160511093645-99348263ae86 + +-- .mod -- +module "github.com/neelance/astrewrite" + +-- .info -- +{"Version":"v0.0.0-20160511093645-99348263ae86","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_neelance_sourcemap_v0.0.0-20151028013722-8c68805598ab.txt b/testdata/mod/github.com_neelance_sourcemap_v0.0.0-20151028013722-8c68805598ab.txt new file mode 100644 index 000000000..95188ae97 --- /dev/null +++ b/testdata/mod/github.com_neelance_sourcemap_v0.0.0-20151028013722-8c68805598ab.txt @@ -0,0 +1,7 @@ +module github.com/neelance/sourcemap@v0.0.0-20151028013722-8c68805598ab + +-- .mod -- +module "github.com/neelance/sourcemap" + +-- .info -- +{"Version":"v0.0.0-20151028013722-8c68805598ab","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_rogpeppe_go-internal_v1.0.1-alpha.1.txt b/testdata/mod/github.com_rogpeppe_go-internal_v1.0.1-alpha.1.txt new file mode 100644 index 000000000..99329ae68 --- /dev/null +++ b/testdata/mod/github.com_rogpeppe_go-internal_v1.0.1-alpha.1.txt @@ -0,0 +1,7 @@ +module github.com/rogpeppe/go-internal@v1.0.1-alpha.1 + +-- .mod -- +module "github.com/rogpeppe/go-internal" + +-- .info -- +{"Version":"v1.0.1-alpha.1","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_shurcoo!l_go_v0.0.0-20180423040247-9e1955d9fb6e.txt b/testdata/mod/github.com_shurcoo!l_go_v0.0.0-20180423040247-9e1955d9fb6e.txt new file mode 100644 index 000000000..5f146bf22 --- /dev/null +++ b/testdata/mod/github.com_shurcoo!l_go_v0.0.0-20180423040247-9e1955d9fb6e.txt @@ -0,0 +1,7 @@ +module github.com/shurcooL/go@v0.0.0-20180423040247-9e1955d9fb6e + +-- .mod -- +module "github.com/shurcooL/go" + +-- .info -- +{"Version":"v0.0.0-20180423040247-9e1955d9fb6e","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt b/testdata/mod/github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt new file mode 100644 index 000000000..51a4abd95 --- /dev/null +++ b/testdata/mod/github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt @@ -0,0 +1,7 @@ +module github.com/shurcooL/httpfs@v0.0.0-20171119174359-809beceb2371 + +-- .mod -- +module "github.com/shurcooL/httpfs" + +-- .info -- +{"Version":"v0.0.0-20171119174359-809beceb2371","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_shurcoo!l_vfsgen_v0.0.0-20180915214035-33ae1944be3f.txt b/testdata/mod/github.com_shurcoo!l_vfsgen_v0.0.0-20180915214035-33ae1944be3f.txt new file mode 100644 index 000000000..ffa2b7c4e --- /dev/null +++ b/testdata/mod/github.com_shurcoo!l_vfsgen_v0.0.0-20180915214035-33ae1944be3f.txt @@ -0,0 +1,7 @@ +module github.com/shurcooL/vfsgen@v0.0.0-20180915214035-33ae1944be3f + +-- .mod -- +module "github.com/shurcooL/vfsgen" + +-- .info -- +{"Version":"v0.0.0-20180915214035-33ae1944be3f","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_spf13_cobra_v0.0.3.txt b/testdata/mod/github.com_spf13_cobra_v0.0.3.txt new file mode 100644 index 000000000..18de7c8a4 --- /dev/null +++ b/testdata/mod/github.com_spf13_cobra_v0.0.3.txt @@ -0,0 +1,7 @@ +module github.com/spf13/cobra@v0.0.3 + +-- .mod -- +module "github.com/spf13/cobra" + +-- .info -- +{"Version":"v0.0.3","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/github.com_spf13_pflag_v1.0.1.txt b/testdata/mod/github.com_spf13_pflag_v1.0.1.txt new file mode 100644 index 000000000..882a160dc --- /dev/null +++ b/testdata/mod/github.com_spf13_pflag_v1.0.1.txt @@ -0,0 +1,7 @@ +module github.com/spf13/pflag@v1.0.1 + +-- .mod -- +module "github.com/spf13/pflag" + +-- .info -- +{"Version":"v1.0.1","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/golang.org_x_crypto_v0.0.0-20180807104621-f027049dab0a.txt b/testdata/mod/golang.org_x_crypto_v0.0.0-20180807104621-f027049dab0a.txt new file mode 100644 index 000000000..c09746b08 --- /dev/null +++ b/testdata/mod/golang.org_x_crypto_v0.0.0-20180807104621-f027049dab0a.txt @@ -0,0 +1,7 @@ +module golang.org/x/crypto@v0.0.0-20180807104621-f027049dab0a + +-- .mod -- +module "golang.org/x/crypto" + +-- .info -- +{"Version":"v0.0.0-20180807104621-f027049dab0a","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/golang.org_x_sys_v0.0.0-20180807162357-acbc56fc7007.txt b/testdata/mod/golang.org_x_sys_v0.0.0-20180807162357-acbc56fc7007.txt new file mode 100644 index 000000000..30b5771f3 --- /dev/null +++ b/testdata/mod/golang.org_x_sys_v0.0.0-20180807162357-acbc56fc7007.txt @@ -0,0 +1,7 @@ +module golang.org/x/sys@v0.0.0-20180807162357-acbc56fc7007 + +-- .mod -- +module "golang.org/x/sys" + +-- .info -- +{"Version":"v0.0.0-20180807162357-acbc56fc7007","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/mod/golang.org_x_tools_v0.0.0-20180803180156-3c07937fe18c.txt b/testdata/mod/golang.org_x_tools_v0.0.0-20180803180156-3c07937fe18c.txt new file mode 100644 index 000000000..0a4bf2eb2 --- /dev/null +++ b/testdata/mod/golang.org_x_tools_v0.0.0-20180803180156-3c07937fe18c.txt @@ -0,0 +1,7 @@ +module golang.org/x/tools@v0.0.0-20180803180156-3c07937fe18c + +-- .mod -- +module "golang.org/x/tools" + +-- .info -- +{"Version":"v0.0.0-20180803180156-3c07937fe18c","Time":"2018-05-06T08:24:08Z"} diff --git a/testdata/run.txt b/testdata/run.txt index 6ca87f6f9..b35c882f1 100644 --- a/testdata/run.txt +++ b/testdata/run.txt @@ -1,30 +1,129 @@ +# Tests for gopherjs run command in GOPATH mode. These tests are largely +# copied from their GOPATH equivalents. Includes checks for behaviour, +# staleness and output side effects (e.g. files in $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install a +# dependency in $GOPATH/pkg unless explicitly asked to do so by gopherjs +# install (i.e. not this command). Also, we allow run to take zero +# argument (implies current directory) or a list of package patterns +# that resolve to a single package. + env HOME=$WORK/home mkdir $HOME -env GOPATH=$HOME/go +env GOPATH=$WORK/go +cd hello +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF -go mod tidy +cp blah/blah.go.fish blah/blah.go -go run . -stdout '^Greeting: Hello$' +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +gopherjs run main.go and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +gopherjs run example.com/hello and peas +stdout '^Today we will eat fish and peas$' ! stderr .+ +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +gopherjs run . and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +! gopherjs run +stderr '^gopherjs run: no go files listed$' +! gopherjs run ./... and peas +! stdout .+ +stderr '^gopherjs run: pattern ./... matches multiple packages:$' +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin -gopherjs run main.go -stdout '^Greeting: Hello$' +# Ensure that gopherjs run does not work for a non-main package. But +# whilst we're there, check that we can still run the main package from +# outside of its directory +cd blah +! gopherjs run . +! stdout .+ +stderr '^gopherjs run: example.com/hello/blah is not a main package$' +gopherjs run example.com/hello and peas +stdout '^Today we will eat fish and peas$' ! stderr .+ +cd .. +! gopherjs run example.com/hello/blah +! stdout .+ +stderr '^gopherjs run: example.com/hello/blah is not a main package$' --- go.mod -- -module example.com/main +# Staleness checks +cp blah/blah.go.chips blah/blah.go +gopherjs run . and peas +stdout '^Today we will eat chips and peas$' +! stderr .+ +gopherjs run --tags thin . and peas +stdout '^Today we will eat thin chips and peas$' +! stderr .+ --- main.go -- -package main +-- hello/go.mod -- +module example.com/hello -import ( - "fmt" +-- hello/main.go -- +package main - "example.com/hello" -) +import "strings" +import "os" +import "example.com/hello/blah" func main() { - fmt.Printf("Greeting: %v\n", hello.Greeting) + print("Today we will eat", blah.Name, strings.Join(os.Args[1:], " ")) +} + +-- hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } } +-- hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/run_gopath.txt b/testdata/run_gopath.txt new file mode 100644 index 000000000..03437b6bf --- /dev/null +++ b/testdata/run_gopath.txt @@ -0,0 +1,126 @@ +# Tests for gopherjs run command in GOPATH mode. Includes checks +# for behaviour, staleness and output side effects (e.g. files in +# $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install a +# dependency in $GOPATH/pkg unless explicitly asked to do so by gopherjs +# install (i.e. not this command). Also, we allow run to take zero +# argument (implies current directory) or a list of package patterns +# that resolve to a single package. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go + +cd go/src/example.com/hello + +cp blah/blah.go.fish blah/blah.go + +# Ensure that we get the "same" output from a file arg or an implicit +# or explicit package argument. We don't have a neat way (yet) of +# comparing the main.js output with the package output, allowing for the +# difference in pkg name +! exists $GOPATH/pkg $GOPATH/bin +gopherjs run main.go and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +! exists $GOPATH/pkg $GOPATH/bin +gopherjs run example.com/hello and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +! exists $GOPATH/pkg $GOPATH/bin +gopherjs run . and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +! exists $GOPATH/pkg $GOPATH/bin +! gopherjs run +stderr '^gopherjs run: no go files listed$' +! gopherjs run ./... and peas +! stdout .+ +stderr '^gopherjs run: pattern ./... matches multiple packages:$' +! exists $GOPATH/pkg $GOPATH/bin + +# Ensure that gopherjs run does not work for a non-main package. But +# whilst we're there, check that we can still run the main package from +# outside of its directory +cd blah +! gopherjs run . +! stdout .+ +stderr '^gopherjs run: example.com/hello/blah is not a main package$' +gopherjs run example.com/hello and peas +stdout '^Today we will eat fish and peas$' +! stderr .+ +cd .. +! gopherjs run example.com/hello/blah +! stdout .+ +stderr '^gopherjs run: example.com/hello/blah is not a main package$' + +# Staleness checks +cp blah/blah.go.chips blah/blah.go +gopherjs run . and peas +stdout '^Today we will eat chips and peas$' +! stderr .+ +gopherjs run --tags thin . and peas +stdout '^Today we will eat thin chips and peas$' +! stderr .+ + +-- go/src/example.com/hello/main.go -- +package main + +import "strings" +import "os" +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name, strings.Join(os.Args[1:], " ")) +} + +-- go/src/example.com/hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- go/src/example.com/hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- go/src/example.com/hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/serve.txt b/testdata/serve.txt new file mode 100644 index 000000000..403633f1c --- /dev/null +++ b/testdata/serve.txt @@ -0,0 +1,148 @@ +# Tests for gopherjs serve command in module mode.These tests are +# largely copied from their GOPATH equivalents. Includes checks +# for behaviour, staleness and output side effects (e.g. files in +# $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install a +# dependency in $GOPATH/pkg unless explicitly asked to do so by gopherjs +# install (which is not this command). + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go +cd hello +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF + +cp blah/blah.go.fish blah/blah.go + +highport GOPHERJSPORT +! gopherjs serve --http :$GOPHERJSPORT & +sleep 500ms +! httpget http://:$GOPHERJSPORT/doesnotexist 404.resp +cmp 404.golden 404.resp +# httpget http://:$GOPHERJSPORT/example.com/ list.resp +# cmp list.golden list.resp +httpget http://:$GOPHERJSPORT/example.com/hello/blah blahlist.resp +cmp blahlist.golden blahlist.resp +httpget http://:$GOPHERJSPORT/example.com/hello/ naked.resp +cmp index.html naked.resp +httpget http://:$GOPHERJSPORT/example.com/hello/index.html hello.resp +cmp index.html hello.resp +httpget http://:$GOPHERJSPORT/example.com/hello/cmd/ cmd.resp +cmp cmd/index.golden cmd.resp +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js.map js.map.resp +grep '^{"version":3,"file":"hello.js"' js.map.resp + +# Staleness checks +cp blah/blah.go.fish blah/blah.go +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp +cp blah/blah.go.chips blah/blah.go +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp + +# Use of --tags +highport GOPHERJSPORT +! gopherjs serve --tags thin --http :$GOPHERJSPORT & +sleep 500ms +gopherjs build --tags thin +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp + +stop + +-- hello/go.mod -- +module example.com/hello + +-- hello/index.html -- +Our index +-- hello/cmd/main.go -- +package main + +func main() {} +-- hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + +-- hello/list.golden -- +
+hello/
+
+-- hello/404.golden -- +404 page not found +-- hello/blahlist.golden -- +
+blah.go
+blah.go.chips
+blah.go.fish
+blahthin.go
+
+-- hello/cmd/index.golden -- + diff --git a/testdata/serve_gopath.txt b/testdata/serve_gopath.txt new file mode 100644 index 000000000..497eb23c8 --- /dev/null +++ b/testdata/serve_gopath.txt @@ -0,0 +1,143 @@ +# Tests for gopherjs serve command in GOPATH mode. Includes checks for +# behaviour, staleness and output side effects (e.g. files in $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install (which is not this command). + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go + +cd go/src/example.com/hello + +cp blah/blah.go.fish blah/blah.go + +highport GOPHERJSPORT +! gopherjs serve --http :$GOPHERJSPORT & +sleep 500ms +! httpget http://:$GOPHERJSPORT/doesnotexist 404.resp +cmp 404.golden 404.resp +httpget http://:$GOPHERJSPORT/example.com/ list.resp +cmp list.golden list.resp +httpget http://:$GOPHERJSPORT/example.com/hello/blah/ blahlist.resp +cmp blahlist.golden blahlist.resp +httpget http://:$GOPHERJSPORT/example.com/hello/ naked.resp +cmp index.html naked.resp +httpget http://:$GOPHERJSPORT/example.com/hello/index.html hello.resp +cmp index.html hello.resp +httpget http://:$GOPHERJSPORT/example.com/hello/cmd/ cmd.resp +cmp cmd/index.golden cmd.resp +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js.map js.map.resp +grep '^{"version":3,"file":"hello.js"' js.map.resp + +# Staleness checks +cp blah/blah.go.fish blah/blah.go +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp +cp blah/blah.go.chips blah/blah.go +gopherjs build +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp + +# Use of --tags +highport GOPHERJSPORT +! gopherjs serve --tags thin --http :$GOPHERJSPORT & +sleep 500ms +gopherjs build --tags thin +cp hello.js js.golden +rm hello.js +httpget http://:$GOPHERJSPORT/example.com/hello/hello.js js.resp +cmp js.golden js.resp + +stop + +-- go/src/example.com/hello/index.html -- +Our index +-- go/src/example.com/hello/cmd/main.go -- +package main + +func main() {} +-- go/src/example.com/hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- go/src/example.com/hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.chips -- +// +build !thin + +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- go/src/example.com/hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- go/src/example.com/hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + +-- go/src/example.com/hello/list.golden -- +
+hello/
+
+-- go/src/example.com/hello/404.golden -- +404 page not found +-- go/src/example.com/hello/blahlist.golden -- +
+blah.go
+blah.go.chips
+blah.go.fish
+blahthin.go
+
+-- go/src/example.com/hello/cmd/index.golden -- + diff --git a/testdata/shadow.txt b/testdata/shadow.txt new file mode 100644 index 000000000..7961dd3ca --- /dev/null +++ b/testdata/shadow.txt @@ -0,0 +1,41 @@ +# Tests to ensure that in module mode we never take from GOPATH + +env HOME=$WORK/home +env GOPATH=$WORK/gopath + +cd foomood +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF +go run . +stdout '^Hello from module foo/bar!$' +gopherjs run main.go +stdout '^Hello from module foo/bar!$' + +-- foomood/go.mod -- +module foo + +-- foomood/main.go -- +package main + +import "foo/bar" + +func main() { + bar.Bar() +} + +-- foomood/bar/bar.go -- +package bar + +import "fmt" + +func Bar() { + fmt.Println("Hello from module foo/bar!") +} + +-- gopath/src/foo/bar/bar.go -- +package bar + +import "fmt" + +func Bar() { + fmt.Println("Hello from GOPATH foo/bar!") +} diff --git a/testdata/staleness.txt b/testdata/staleness.txt deleted file mode 100644 index 77324e0a5..000000000 --- a/testdata/staleness.txt +++ /dev/null @@ -1,122 +0,0 @@ -# Various tests that ensure that, across the GopherJS commands, we do not -# use stale build artefacts. - -env HOME=$WORK/home -mkdir $HOME -env GOPATH=$HOME/go - -# gopherjs test -cp blah/blah.go.fish blah/blah.go -cp main_test.go.fish main_test.go -gopherjs test -stdout '^fish$' -! stderr .+ -cp blah/blah.go.chips blah/blah.go -cp main_test.go.chips main_test.go -gopherjs test -stdout '^chips$' -! stderr .+ - -# gopherjs run -cp blah/blah.go.fish blah/blah.go -gopherjs run main.go -stdout '^Today we will eat fish$' -! stderr .+ -cp blah/blah.go.chips blah/blah.go -gopherjs run main.go -stdout '^Today we will eat chips$' -! stderr .+ -gopherjs run --tags thin main.go -stdout '^Today we will eat thin chips$' -! stderr .+ - -# gopherjs build -cp blah/blah.go.fish blah/blah.go -! exists output.js output.js.map -gopherjs build -o output.js -! stdout .+ -! stderr .+ -exists output.js output.js.map -! modified output.js -! changed output.js -exec node output.js -stdout '^Today we will eat fish$' -! stderr .+ -gopherjs build -o output.js -modified output.js -! changed output.js -cp blah/blah.go.chips blah/blah.go -gopherjs build -o output.js -modified output.js -changed output.js -exec node output.js -stdout '^Today we will eat chips$' -! stderr .+ -gopherjs build --tags thin -o output.js -modified output.js -changed output.js -exec node output.js -stdout '^Today we will eat thin chips$' -! stderr .+ - - --- go.mod -- -module example.com/hello - --- main.go -- -package main - -import "example.com/hello/blah" - -func main() { - print("Today we will eat", blah.Name) -} - --- main_test.go.fish -- -package main - -import "fmt" -import "testing" -import "example.com/hello/blah" - - -func TestBlah(t *testing.T) { - fmt.Println(blah.Name) - if exp := "fish"; blah.Name != exp { - t.Fatalf("expected %v; got %v", exp, blah.Name) - } -} - --- main_test.go.chips -- -// +build !thin - -package main - -import "fmt" -import "testing" -import "example.com/hello/blah" - - -func TestBlah(t *testing.T) { - fmt.Println(blah.Name) - if exp := "chips"; blah.Name != exp { - t.Fatalf("expected %v; got %v", exp, blah.Name) - } -} - --- blah/blah.go.fish -- -package blah - -const Name = "fish" - --- blah/blah.go.chips -- -// +build !thin - -package blah -const Name = "chips" - --- blah/blahthin.go -- -// +build thin - -package blah -const Name = "thin chips" diff --git a/testdata/test.txt b/testdata/test.txt new file mode 100644 index 000000000..b9f413678 --- /dev/null +++ b/testdata/test.txt @@ -0,0 +1,149 @@ +# Tests for gopherjs test command in module mode. These tests are +# largely copied from their GOPATH equivalents. Includes checks +# for behaviour, staleness and output side effects (e.g. files in +# $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install (which is not this command). + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go +cd hello +go mod edit -require=github.com/gopherjs/gopherjs@v0.0.0 -replace=github.com/gopherjs/gopherjs=$SELF + +cp blah/blah.go.fish blah/blah.go + +# No test files then test files +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +gopherjs test +stdout '^?\s+example.com/hello\s+\[no test files\]$' +! stderr .+ +gopherjs test example.com/hello +stdout '^?\s+example.com/hello\s+\[no test files\]$' +! stderr .+ +cp main_test.go.fish main_test.go +gopherjs test +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! stderr .+ +gopherjs test example.com/hello +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! stderr .+ +! exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin + +# mutliple packages +gopherjs test . ./blah +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +stdout '^?\s+example.com/hello/blah\s+\[no test files\]$' +! stderr .+ +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin + +# Staleness checks +! exists exists $GOPATH/pkg/${GOOS}_js $GOPATH/bin +cp blah/blah.go.fish blah/blah.go +gopherjs test +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +cp blah/blah.go.chips blah/blah.go +! gopherjs test +stdout '^--- FAIL: TestBlah' +stdout 'expected fish; got chips$' +stdout '^FAIL\s+example.com/hello\s+' +cp main_test.go.chips main_test.go +gopherjs test +stdout 'chips' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! gopherjs test --tags thin +stdout '^--- FAIL: TestBlah' +stdout 'expected chips; got thin chips$' +stdout '^FAIL\s+example.com/hello\s+' +! stderr .+ +cp main_test.go.thinchips main_test.go +gopherjs test --tags thin +stdout 'thin chips' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' + +-- hello/go.mod -- +module example.com/hello + +-- hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/main_test.go.chips -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/main_test.go.thinchips -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "thin chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/test_gopath.txt b/testdata/test_gopath.txt new file mode 100644 index 000000000..f88e74e5c --- /dev/null +++ b/testdata/test_gopath.txt @@ -0,0 +1,144 @@ +# Tests for gopherjs test command in GOPATH mode. Includes checks for +# behaviour, staleness and output side effects (e.g. files in $GOPATH/pkg) +# +# A change from the original GopherJS here, we do _not_ now install +# a dependency in $GOPATH/pkg unless explicitly asked to do so by +# gopherjs install (which is not this command). + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go + +cd go/src/example.com/hello + +cp blah/blah.go.fish blah/blah.go + +# No test files then test files +! exists $GOPATH/pkg $GOPATH/bin +gopherjs test +stdout '^?\s+example.com/hello\s+\[no test files\]$' +! stderr .+ +gopherjs test example.com/hello +stdout '^?\s+example.com/hello\s+\[no test files\]$' +! stderr .+ +cp main_test.go.fish main_test.go +gopherjs test +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! stderr .+ +gopherjs test example.com/hello +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! stderr .+ +! exists $GOPATH/pkg $GOPATH/bin + +# mutliple packages +gopherjs test . ./blah +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +stdout '^?\s+example.com/hello/blah\s+\[no test files\]$' +! stderr .+ +! exists $GOPATH/pkg $GOPATH/bin + +# Staleness checks +! exists $GOPATH/pkg $GOPATH/bin +cp blah/blah.go.fish blah/blah.go +gopherjs test +stdout 'fish' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +cp blah/blah.go.chips blah/blah.go +! gopherjs test +stdout '^--- FAIL: TestBlah' +stdout 'expected fish; got chips$' +stdout '^FAIL\s+example.com/hello\s+' +cp main_test.go.chips main_test.go +gopherjs test +stdout 'chips' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' +! gopherjs test --tags thin +stdout '^--- FAIL: TestBlah' +stdout 'expected chips; got thin chips$' +stdout '^FAIL\s+example.com/hello\s+' +! stderr .+ +cp main_test.go.thinchips main_test.go +gopherjs test --tags thin +stdout 'thin chips' +stdout '^PASS$' +stdout '^ok\s+example.com/hello\s+' + +-- go/src/example.com/hello/main.go -- +package main + +import "example.com/hello/blah" + +func main() { + print("Today we will eat", blah.Name) +} + +-- go/src/example.com/hello/main_test.go.fish -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "fish"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.chips -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/main_test.go.thinchips -- +package main + +import "fmt" +import "testing" +import "example.com/hello/blah" + + +func TestBlah(t *testing.T) { + fmt.Println(blah.Name) + if exp := "thin chips"; blah.Name != exp { + t.Fatalf("expected %v; got %v", exp, blah.Name) + } +} + +-- go/src/example.com/hello/blah/blah.go.fish -- +package blah + +const Name = "fish" + +-- go/src/example.com/hello/blah/blah.go.chips -- +// +build !thin + +package blah +const Name = "chips" + +-- go/src/example.com/hello/blah/blahthin.go -- +// +build thin + +package blah +const Name = "thin chips" + diff --git a/testdata/vendor_gopath.txt b/testdata/vendor_gopath.txt new file mode 100644 index 000000000..3ea924a9a --- /dev/null +++ b/testdata/vendor_gopath.txt @@ -0,0 +1,32 @@ +# In GOPATH mode, it should be possible to use GopherJS standalone, that is +# without relying on the source for github.com/gopherjs/gopherjs/... being +# available. The flip is that in module mode we _have_ to have the source. + +env HOME=$WORK/home +mkdir $HOME +env GOPATH=$WORK/go +cd go/src/example.com/hello + +# use the version of github.com/gopherjs/gopherjs/... compiled into the binary +gopherjs run main.go +stdout '^hello using js pkg$' + +# check that we get an error if we choose not to use the compiled in pacakges +! gopherjs run --tags gopherjsdev main.go +stderr '^cannot find package "github.com/gopherjs/gopherjs/js"' + +# make the source available +cpr $SELF $GOPATH/src/github.com/gopherjs/gopherjs + +# verify we can now actually run with --tags gopherjsdev +gopherjs run --tags gopherjsdev main.go +stdout '^hello using js pkg$' + +-- go/src/example.com/hello/main.go -- +package main + +import "github.com/gopherjs/gopherjs/js" + +func main() { + js.Global.Get("console").Call("log", "hello using js pkg") +} diff --git a/tests/run.go b/tests/run.go index 19554d1e9..399e6f8f4 100644 --- a/tests/run.go +++ b/tests/run.go @@ -151,7 +151,7 @@ var ( // dirs are the directories to look for *.go files in. // TODO(bradfitz): just use all directories? - dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs"} + dirs = []string{"fixedbugs"} // ratec controls the max number of tests running at a time. ratec chan bool @@ -163,6 +163,8 @@ var ( // rungatec controls the max number of runoutput tests // executed in parallel as they can each consume a lot of memory. rungatec chan bool + + testRoot = filepath.Join(runtime.GOROOT(), "test") ) // maxTests is an upper bound on the total number of tests. @@ -172,15 +174,11 @@ const maxTests = 5000 func main() { flag.Parse() - // GOPHERJS. - err := os.Chdir(filepath.Join(runtime.GOROOT(), "test")) - if err != nil { - log.Fatalln(err) + mkAbs := func(p string) string { + return filepath.Join(testRoot, p) } goos = getenv("GOOS", runtime.GOOS) - //goarch = getenv("GOARCH", runtime.GOARCH) - // GOPHERJS. goarch = getenv("GOARCH", "js") // We're running this script natively, but the tests are executed with js architecture. if *verbose { @@ -221,6 +219,7 @@ func main() { } } else { for _, dir := range dirs { + dir = mkAbs(dir) for _, baseGoFile := range goFiles(dir) { tests = append(tests, startTest(dir, baseGoFile)) } @@ -408,12 +407,16 @@ func runTests() { var cwd, _ = os.Getwd() -func (t *test) goFileName() string { +func (t *test) filename() string { return filepath.Join(t.dir, t.gofile) } +func (t *test) goFileName() string { + return strings.TrimPrefix(filepath.Join(t.dir, t.gofile), testRoot+string(os.PathSeparator)) +} + func (t *test) goDirName() string { - return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1)) + return filepath.Join(t.dir, strings.Replace(t.goFileName(), ".go", ".dir", -1)) } func goDirFiles(longdir string) (filter []os.FileInfo, err error) { @@ -552,7 +555,7 @@ func (t *test) run() { return } - srcBytes, err := ioutil.ReadFile(t.goFileName()) + srcBytes, err := ioutil.ReadFile(filepath.Join(t.dir, t.gofile)) if err != nil { t.err = err return @@ -602,7 +605,7 @@ func (t *test) run() { // GOPHERJS: For now, only run with "run", "cmpout" actions, in "fixedbugs" dir. Skip all others. switch action { case "run", "cmpout": - if filepath.Clean(t.dir) != "fixedbugs" { + if filepath.Base(t.dir) != "fixedbugs" { action = "skip" } default: @@ -669,7 +672,7 @@ func (t *test) run() { return buf.Bytes(), err } - long := filepath.Join(cwd, t.goFileName()) + long := t.filename() switch action { default: t.err = fmt.Errorf("unimplemented action %q", action) @@ -792,7 +795,7 @@ func (t *test) run() { case "run": useTmp = false // GOPHERJS. - out, err := runcmd(append([]string{"gopherjs", "run", "-q", t.goFileName()}, args...)...) + out, err := runcmd(append([]string{"gopherjs", "run", "-q", t.filename()}, args...)...) if err != nil { t.err = err return @@ -807,7 +810,7 @@ func (t *test) run() { <-rungatec }() useTmp = false - out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) + out, err := runcmd(append([]string{"go", "run", t.filename()}, args...)...) if err != nil { t.err = err return @@ -828,7 +831,7 @@ func (t *test) run() { case "errorcheckoutput": useTmp = false - out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) + out, err := runcmd(append([]string{"go", "run", t.filename()}, args...)...) if err != nil { t.err = err return diff --git a/tool.go b/tool.go index 14d39124a..5ac2fc216 100644 --- a/tool.go +++ b/tool.go @@ -41,7 +41,10 @@ import ( var currentDirectory string -var errorFail = errors.New("command exited with non-zero exit code") +var ( + errorFail = errors.New("command exited with non-zero exit code") + testsFail = errors.New("tests failed") +) func init() { var err error @@ -99,13 +102,8 @@ func main1() int { cmdBuild.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) for { - s, err := gbuild.NewSession(options) - if err := handleError(err, options, nil); err != nil { - return err - } - defer s.Cleanup() - err = func() error { + err := func() error { // Handle "gopherjs build [files]" ad-hoc package mode. if len(args) > 0 && (strings.HasSuffix(args[0], ".go") || strings.HasSuffix(args[0], ".inc.js")) { for _, arg := range args { @@ -113,6 +111,15 @@ func main1() int { return fmt.Errorf("named files must be .go or .inc.js files") } } + imports, err := importsFromFiles(args) + if err != nil { + return err + } + s, err := gbuild.NewSession(options, false, imports...) + if err := handleError(err, options, nil); err != nil { + return err + } + defer s.Cleanup() if pkgObj == "" { basename := filepath.Base(args[0]) pkgObj = basename[:len(basename)-3] + ".js" @@ -125,16 +132,24 @@ func main1() int { s.Watcher.Add(name) } } - err := s.BuildFiles(args, pkgObj, currentDirectory) - return err + if s.Watcher != nil { + s.WaitForChange() + } + return s.BuildFiles(args, pkgObj, currentDirectory) } // Expand import path patterns. - pkgs, err := gbuild.ImportPaths(args) + pkgs, err := gbuild.ImportPaths(args...) if err != nil { return err } + s, err := gbuild.NewSession(options, false, pkgs...) + if err := handleError(err, options, nil); err != nil { + return err + } + defer s.Cleanup() + for _, pkgPath := range pkgs { if s.Watcher != nil { pkg, err := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags).Import(pkgPath, "", build.FindOnly) @@ -143,7 +158,7 @@ func main1() int { } s.Watcher.Add(pkg.Dir) } - pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) + pkg, err := s.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) if err != nil { return err } @@ -162,16 +177,16 @@ func main1() int { } } } + + if s.Watcher != nil { + s.WaitForChange() + } return nil }() if err := handleError(err, options, nil); err != nil { return err } - if s.Watcher != nil { - s.WaitForChange() - } - return nil } } @@ -187,19 +202,19 @@ func main1() int { cmdInstall.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) for { - s, err := gbuild.NewSession(options) - if err := handleError(err, options, nil); err != nil { - return err - } - defer s.Cleanup() - - err = func() error { + err := func() error { // Expand import path patterns. - pkgs, err := gbuild.ImportPaths(args) + pkgs, err := gbuild.ImportPaths(args...) if err != nil { return err } + s, err := gbuild.NewSession(options, false, pkgs...) + if err := handleError(err, options, nil); err != nil { + return err + } + defer s.Cleanup() + if cmd.Name() == "get" { goGet := exec.Command("go", append([]string{"get", "-d", "-tags=js"}, pkgs...)...) goGet.Stdout = os.Stdout @@ -209,7 +224,7 @@ func main1() int { } } for _, pkgPath := range pkgs { - pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) + pkg, err := s.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) if s.Watcher != nil && pkg != nil { // add watch even on error s.Watcher.Add(pkg.Dir) } @@ -222,22 +237,21 @@ func main1() int { return err } - if pkg.IsCommand() && !pkg.UpToDate { - if err := s.WriteCommandPackage(archive, pkg.PkgObj); err != nil { - return err - } + if err := s.WriteCommandPackage(archive, pkg.PkgObj); err != nil { + return err } } + + if s.Watcher != nil { + s.WaitForChange() + } + return nil }() if err := handleError(err, options, nil); err != nil { return err } - if s.Watcher != nil { - s.WaitForChange() - } - return nil } } @@ -274,38 +288,70 @@ func main1() int { cmdRun.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) err := func() error { - lastSourceArg := 0 - for { - if lastSourceArg == len(args) || !(strings.HasSuffix(args[lastSourceArg], ".go") || strings.HasSuffix(args[lastSourceArg], ".inc.js")) { - break - } - lastSourceArg++ - } - if lastSourceArg == 0 { - return fmt.Errorf("gopherjs run: no go files listed") - } - tempfile, err := ioutil.TempFile(currentDirectory, filepath.Base(args[0])+".") - if err != nil && strings.HasPrefix(currentDirectory, runtime.GOROOT()) { - tempfile, err = ioutil.TempFile("", filepath.Base(args[0])+".") - } + tempDir, err := ioutil.TempDir("", "gopherjs-run-*") if err != nil { - return err + return fmt.Errorf("gopherjs run: failed to create temp directory: %v", err) } + tempFile := filepath.Join(tempDir, "main.js") defer func() { - tempfile.Close() - os.Remove(tempfile.Name()) - os.Remove(tempfile.Name() + ".map") + os.RemoveAll(tempDir) }() - s, err := gbuild.NewSession(options) - if err != nil { - return err + + i := 0 + for i < len(args) && (strings.HasSuffix(args[i], ".go") || strings.HasSuffix(args[i], ".inc.js")) { + i++ } - defer s.Cleanup() - if err := s.BuildFiles(args[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { - return err + if i > 0 { + files := args[:i] + for _, f := range files { + if strings.HasSuffix(f, "_test.go") { + return fmt.Errorf("gopherjs run: cannot run test files") + } + } + imports, err := importsFromFiles(files) + if err != nil { + return err + } + s, err := gbuild.NewSession(options, false, imports...) + if err != nil { + return err + } + defer s.Cleanup() + if err := s.BuildFiles(files, tempFile, currentDirectory); err != nil { + return err + } + } else if len(args) > 0 && !strings.HasPrefix(args[0], "-") { + pkgs, err := gbuild.ImportPaths(args[0]) + if err != nil { + return fmt.Errorf("gopherjs run: failed to resolve package pattern %v: %v", args[0], err) + } + if len(pkgs) > 1 { + return fmt.Errorf("gopherjs run: pattern %s matches multiple packages:\n\t%s", args[0], strings.Join(pkgs, "\n\t")) + } + s, err := gbuild.NewSession(options, false, pkgs[0]) + if err != nil { + return err + } + defer s.Cleanup() + pkg, arc, err := s.BuildImportPath(pkgs[0]) + if err != nil { + return fmt.Errorf("gopherjs run: failed to build %v: %v", pkgs[0], err) + } + if !pkg.IsCommand() { + return fmt.Errorf("gopherjs run: %v is not a main package", pkg.ImportPath) + } + if err := s.WriteCommandPackage(arc, tempFile); err != nil { + return fmt.Errorf("gopherjs run: failed to write build output to %v: %v", tempFile, err) + } + i++ + } else { + return fmt.Errorf("gopherjs run: no go files listed") } - if err := runNode(tempfile.Name(), args[lastSourceArg:], "", options.Quiet); err != nil { + + cmdArgs := args[i:] + + if err := runNode(tempFile, cmdArgs, "", options.Quiet); err != nil { return err } return nil @@ -329,8 +375,9 @@ func main1() int { cmdTest.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) err := func() error { + var err error // Expand import path patterns. - args, err := gbuild.ImportPaths(args) + args, err := gbuild.ImportPaths(args...) if err != nil { return err } @@ -342,26 +389,27 @@ func main1() int { return errors.New("cannot use -o flag with multiple packages") } - pkgs := make([]*gbuild.PackageData, len(args)) - for i, pkgPath := range args { - var err error - pkgs[i], err = gbuild.Import(pkgPath, 0, "", options.BuildTags) + var exitErr error + var s *gbuild.Session + for _, pkgPath := range args { + if s != nil { + s.Cleanup() + } + s, err = gbuild.NewSession(options, true, pkgPath) + if err != nil { + return err + } + defer s.Cleanup() + + pkg, err := s.Import(pkgPath, 0, "", options.BuildTags) if err != nil { return err } - } - var exitErr error - for _, pkg := range pkgs { if len(pkg.TestGoFiles) == 0 && len(pkg.XTestGoFiles) == 0 { fmt.Printf("? \t%s\t[no test files]\n", pkg.ImportPath) continue } - s, err := gbuild.NewSession(options) - if err != nil { - return err - } - defer s.Cleanup() tests := &testFuncs{BuildContext: s.BuildContext(), Package: pkg.Package} collectTests := func(testPkg *gbuild.PackageData, testPkgName string, needVar *bool) error { @@ -378,6 +426,8 @@ func main1() int { } } } + // this call is simply used for its side effect of populating s.Archives + // which is referenced below int he test main package's import resolution _, err := s.BuildPackage(testPkg) return err } @@ -424,7 +474,8 @@ func main1() int { if path == pkg.ImportPath || path == pkg.ImportPath+"_test" { return s.Archives[path], nil } - return s.BuildImportPath(path) + _, arc, err := s.BuildImportPath(path) + return arc, err }, } mainPkgArchive, err := compiler.Compile("main", []*ast.File{mainFile}, fset, importContext, options.Minify) @@ -489,11 +540,18 @@ func main1() int { if _, ok := err.(*exec.ExitError); !ok { return err } - exitErr = err + exitErr = testsFail status = "FAIL" } fmt.Printf("%s\t%s\t%.3fs\n", status, pkg.ImportPath, time.Since(start).Seconds()) } + s.Cleanup() + + // at this point we know we have "successfully" run the test main. It + // may have failed, but we don't want usage information in case it + // has failed. See https://github.com/spf13/cobra/issues/340 + cmdTest.SilenceUsage = true + return exitErr }() return handleError(err, options, nil) @@ -560,8 +618,9 @@ func main1() int { } rootCmd := &cobra.Command{ - Use: "gopherjs", - Long: "GopherJS is a tool for compiling Go source code to JavaScript.", + Use: "gopherjs", + Long: "GopherJS is a tool for compiling Go source code to JavaScript.", + SilenceErrors: true, } rootCmd.AddCommand(cmdBuild, cmdGet, cmdInstall, cmdRun, cmdTest, cmdServe, cmdVersion, cmdDoc) err := rootCmd.Execute() @@ -572,6 +631,45 @@ func main1() int { return 0 } +func importsFromFiles(files []string) ([]string, error) { + fset := token.NewFileSet() + find := &importFinder{ + imports: make(map[string]bool), + } + for _, fn := range files { + if !strings.HasSuffix(fn, ".go") { + continue + } + fd, err := os.Open(fn) + if err != nil { + return nil, fmt.Errorf("failed to open %v: %v", fn, err) + } + f, err := parser.ParseFile(fset, fn, fd, parser.ImportsOnly) + fd.Close() + if err != nil { + return nil, fmt.Errorf("failed to parse %v: %v", fn, err) + } + ast.Walk(find, f) + } + var res []string + for k := range find.imports { + res = append(res, k) + } + return res, nil +} + +type importFinder struct { + imports map[string]bool +} + +func (i *importFinder) Visit(node ast.Node) ast.Visitor { + switch node := node.(type) { + case *ast.ImportSpec: + i.imports[node.Path.Value[1:len(node.Path.Value)-1]] = true + } + return i +} + // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted // connections. It's used by ListenAndServe and ListenAndServeTLS so // dead TCP connections (e.g. closing laptop mid-download) eventually @@ -598,6 +696,130 @@ type serveCommandFileSystem struct { } func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { + wd := fs.serveRoot + if wd == "" { + wd = "all" + } + s, err := gbuild.NewSession(fs.options, false, wd) + if err != nil { + return nil, err + } + defer s.Cleanup() + + if s.GO111MODULE() { + return fs.openModule(s, requestName) + } else { + return fs.openGOPATH(s, requestName) + } +} + +func (fs serveCommandFileSystem) openModule(s *gbuild.Session, requestName string) (http.File, error) { + reqPath := requestName[1:] // remove leading / + + var err error + var modDir string + var inPkg string + + check := reqPath + + var pkg *gbuild.PackageData + + // try to resolve a package path + for { + pkg, err = s.Import(check, 0, s.InstallSuffix(), fs.options.BuildTags) + if err == nil { + break + } + + if dir, ok := s.IsModulePath(check); ok { + modDir = dir + break + } + + inPkg = path.Join(path.Base(check), inPkg) + check = path.Dir(check) + if check == "." { + break + } + } + + var httpDir string + + if pkg == nil { + if modDir == "" { + return nil, os.ErrNotExist + } + httpDir = modDir + } else { + // we're going to fall through to trying to simply open the file + httpDir = pkg.Dir + } + + dir := http.Dir(httpDir) + + if pkg.IsCommand() { + base := path.Base(pkg.ImportPath) + + switch { + case inPkg == "index.html": + if f, err := dir.Open(inPkg); err == nil { + return f, nil + } + return newFakeFile(inPkg, fs.index(base)), nil + case inPkg == base+".js": + buf := new(bytes.Buffer) + browserErrors := new(bytes.Buffer) + err := func() error { + archive, err := s.BuildPackage(pkg) + if err != nil { + return err + } + + sourceMapFilter := &compiler.SourceMapFilter{Writer: buf} + m := &sourcemap.Map{File: base + ".js"} + sourceMapFilter.MappingCallback = gbuild.NewMappingCallback(m, fs.options.GOROOT, fs.options.GOPATH, fs.options.MapToLocalDisk) + + deps, err := compiler.ImportDependencies(archive, func(ip string) (*compiler.Archive, error) { + _, arc, err := s.BuildImportPath(ip) + return arc, err + }) + if err != nil { + return err + } + if err := compiler.WriteProgramCode(deps, sourceMapFilter); err != nil { + return err + } + + mapBuf := new(bytes.Buffer) + m.WriteTo(mapBuf) + buf.WriteString("//# sourceMappingURL=" + base + ".js.map\n") + fs.sourceMaps[pkg.ImportPath+".map"] = mapBuf.Bytes() + + return nil + }() + handleError(err, fs.options, browserErrors) + if err != nil { + buf = browserErrors + } + return newFakeFile(base+".js", buf.Bytes()), nil + case inPkg == base+".js.map": + // TODO this will fail unless we have requested the .js file first + // could probably easily fix this. And it suffers from the .js.map + // potentially going stale. + if content, ok := fs.sourceMaps[pkg.ImportPath+".map"]; ok { + return newFakeFile(base+".js.map", content), nil + } + } + + } + + // if we get here we could have a main package or a non-main + // package. All we are trying to do at this point is serve + // the file system + return dir.Open(inPkg) +} + +func (fs serveCommandFileSystem) openGOPATH(s *gbuild.Session, requestName string) (http.File, error) { name := path.Join(fs.serveRoot, requestName[1:]) // requestName[0] == '/' dir, file := path.Split(name) @@ -609,12 +831,7 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { if isPkg || isMap || isIndex { // If we're going to be serving our special files, make sure there's a Go command in this folder. - s, err := gbuild.NewSession(fs.options) - if err != nil { - return nil, err - } - defer s.Cleanup() - pkg, err := gbuild.Import(path.Dir(name), 0, s.InstallSuffix(), fs.options.BuildTags) + pkg, err := s.Import(path.Dir(name), 0, s.InstallSuffix(), fs.options.BuildTags) if err != nil || pkg.Name != "main" { isPkg = false isMap = false @@ -635,7 +852,10 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { m := &sourcemap.Map{File: base + ".js"} sourceMapFilter.MappingCallback = gbuild.NewMappingCallback(m, fs.options.GOROOT, fs.options.GOPATH, fs.options.MapToLocalDisk) - deps, err := compiler.ImportDependencies(archive, s.BuildImportPath) + deps, err := compiler.ImportDependencies(archive, func(ip string) (*compiler.Archive, error) { + _, arc, err := s.BuildImportPath(ip) + return arc, err + }) if err != nil { return err } @@ -680,12 +900,16 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { if isIndex { // If there was no index.html file in any dirs, supply our own. - return newFakeFile("index.html", []byte(``)), nil + return newFakeFile("index.html", fs.index(base)), nil } return nil, os.ErrNotExist } +func (fs serveCommandFileSystem) index(base string) []byte { + return []byte(`` + "\n") +} + type fakeFile struct { name string size int @@ -735,6 +959,10 @@ func (f *fakeFile) Sys() interface{} { // handleError handles err and returns an appropriate exit code. // If browserErrors is non-nil, errors are written for presentation in browser. func handleError(err error, options *gbuild.Options, browserErrors *bytes.Buffer) error { + if err == testsFail { + return err + } + switch err := err.(type) { case nil: case compiler.ErrorList: