func (imp *importer) newPackageInfo(path string) *PackageInfo { pkg := types.NewPackage(path, "") info := &PackageInfo{ Pkg: pkg, Info: types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), }, errorFunc: imp.conf.TypeChecker.Error, } // Copy the types.Config so we can vary it across PackageInfos. tc := imp.conf.TypeChecker tc.IgnoreFuncBodies = false if f := imp.conf.TypeCheckFuncBodies; f != nil { tc.IgnoreFuncBodies = !f(path) } tc.Import = imp.doImport // doImport wraps the user's importfn, effectively tc.Error = info.appendError // appendError wraps the user's Error function info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) imp.prog.AllPackages[pkg] = info return info }
// TODO replace with encoding/gob when reflection is ready func ReadArchive(packages map[string]*types.Package, filename, id string, data io.Reader) ([]byte, *types.Package, error) { r := bufio.NewReader(data) code, err := readUntilSeparator(r) if err != nil { return nil, nil, err } importList, err := readUntilSeparator(r) if err != nil { return nil, nil, err } pkg, err := gcimporter.ImportData(packages, filename, id, r) if err != nil { return nil, nil, err } var imports []*types.Package for _, path := range strings.Split(string(importList), "\n") { if path == "" { continue } impPkg, found := packages[path] if !found { impPkg = types.NewPackage(path, "", types.NewScope(nil)) packages[path] = impPkg } imports = append(imports, impPkg) } pkg.SetImports(imports) return code, pkg, nil }
func (imp *importer) newPackageInfo(path string) *PackageInfo { // Use a copy of the types.Config so we can vary IgnoreFuncBodies. tc := imp.conf.TypeChecker tc.IgnoreFuncBodies = false if f := imp.conf.TypeCheckFuncBodies; f != nil { tc.IgnoreFuncBodies = !f(path) } if tc.Error == nil { tc.Error = func(e error) { fmt.Fprintln(os.Stderr, e) } } tc.Import = imp.doImport // doImport wraps the user's importfn, effectively pkg := types.NewPackage(path, "") info := &PackageInfo{ Pkg: pkg, Info: types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), }, } info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) imp.prog.AllPackages[pkg] = info return info }
func (p *importer) pkg() *types.Package { // if the package was seen before, i is its index (>= 0) i := p.int() if i >= 0 { return p.pkgList[i] } // otherwise, i is the package tag (< 0) if i != packageTag { panic(fmt.Sprintf("unexpected package tag %d", i)) } // read package data name := p.string() path := p.string() // if the package was imported before, use that one; otherwise create a new one pkg := p.imports[path] if pkg == nil { pkg = types.NewPackage(path, name) p.imports[path] = pkg } p.pkgList = append(p.pkgList, pkg) return pkg }
// Name = identifier | "?" | QualifiedName . // // If materializePkg is set, the returned package is guaranteed to be set. // For fully qualified names, the returned package may be a fake package // (without name, scope, and not in the p.imports map), created for the // sole purpose of providing a package path. Fake packages are created // when the package id is not found in the p.imports map; in that case // we cannot create a real package because we don't have a package name. // For non-qualified names, the returned package is the imported package. // func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) { switch p.tok { case scanner.Ident: pkg = p.imports[p.id] name = p.lit p.next() case '?': // anonymous pkg = p.imports[p.id] p.next() case '@': // exported name prefixed with package path var id string id, name = p.parseQualifiedName() if materializePkg { // we don't have a package name - if the package // doesn't exist yet, create a fake package instead pkg = p.getPkg(id, "") if pkg == nil { pkg = types.NewPackage(id, "", nil) } } default: p.error("name expected") } return }
// getPkg returns the package for a given id. If the package is // not found but we have a package name, create the package and // add it to the p.imports map. // func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if id == "unsafe" { return types.Unsafe } pkg := p.imports[id] if pkg == nil && name != "" { pkg = types.NewPackage(id, name, types.NewScope(nil)) p.imports[id] = pkg } return pkg }
// getPkg returns the package for a given path. If the package is // not found but we have a package name, create the package and // add it to the p.imports map. // func (p *parser) getPkg(pkgpath, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if pkgpath == "unsafe" { return types.Unsafe } pkg := p.imports[pkgpath] if pkg == nil && name != "" { pkg = types.NewPackage(pkgpath, name) p.imports[pkgpath] = pkg } return pkg }
// Import implements the Importer type from go/types. func (imp Importer) Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { // types.Importer does not seem to be designed for recursive // parsing like we're doing here. Specifically, each nested import // will maintain its own imports map. This will lead to duplicate // imports and in turn packages, which will lead to funny errors // such as "cannot pass argument ip (variable of type net.IP) to // variable of type net.IP" // // To work around this, we keep a global imports map, allImports, // to which we add all nested imports, and which we use as the // cache, instead of imports. // // Since all nested imports will also use this importer, there // should be no way to end up with duplicate imports. // We first try to use GcImport directly. This has the downside of // using possibly out-of-date packages, but it has the upside of // not having to parse most of the Go standard library. buildPkg, buildErr := build.Import(path, ".", 0) // If we found no build dir, assume we're dealing with installed // but no source. If we found a build dir, only use GcImport if // it's in GOROOT. This way we always use up-to-date code for // normal packages but avoid parsing the standard library. if (buildErr == nil && buildPkg.Goroot) || buildErr != nil { pkg, err = types.GcImport(imp.Imports, path) if err == nil { // We don't use imports, but per API we have to add the package. imports[pkg.Path()] = pkg imp.Imports[pkg.Path()] = pkg return pkg, nil } } // See if we already imported this package if pkg = imp.Imports[path]; pkg != nil && pkg.Complete() { return pkg, nil } // allImports failed, try to use go/build if buildErr != nil { return nil, fmt.Errorf("build.Import failed: %s", buildErr) } // TODO check if the .a file is up to date and use it instead fileSet := token.NewFileSet() isGoFile := func(d os.FileInfo) bool { allFiles := make([]string, 0, len(buildPkg.GoFiles)+len(buildPkg.CgoFiles)) allFiles = append(allFiles, buildPkg.GoFiles...) allFiles = append(allFiles, buildPkg.CgoFiles...) for _, file := range allFiles { if file == d.Name() { return true } } return false } pkgs, err := parser.ParseDir(fileSet, buildPkg.Dir, isGoFile, 0) if err != nil { return nil, err } delete(pkgs, "documentation") var astPkg *ast.Package var name string for name, astPkg = range pkgs { // Use the first non-main package, or the only package we // found. // // NOTE(dh) I can't think of a reason why there should be // multiple packages in a single directory, but ParseDir // accommodates for that possibility. if len(pkgs) == 1 || name != "main" { break } } if astPkg == nil { return nil, fmt.Errorf("can't find import: %s", name) } var ff []*ast.File for _, f := range astPkg.Files { ff = append(ff, f) } context := types.Config{ Import: imp.Import, } pkg, err = context.Check(name, fileSet, ff, nil) if err != nil { return pkg, err } if !pkg.Complete() { pkg = types.NewPackage(pkg.Pos(), pkg.Path(), pkg.Name(), pkg.Scope(), pkg.Imports(), true) } imports[path] = pkg imp.Imports[path] = pkg return pkg, nil }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { pkgs, tests, benchmarks, examples := FindTests(pkgs) if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("testmain", "testmain"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(init) } // Initialize packages to test. for _, pkg := range pkgs { var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) } init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init main := &Function{ name: "main", Signature: new(types.Signature), Synthetic: "test main function", Prog: prog, Pkg: testmain, } main.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(main) } if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() // Emit call: testing.Main(matcher, tests, benchmarks, examples). var c Call c.Call.Value = testingMain c.Call.Args = []Value{ matcher, testMainSlice(main, tests, testingMainParams.At(1).Type()), testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), testMainSlice(main, examples, testingMainParams.At(3).Type()), } emitTailCall(main, &c) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Tests or Benchmarks. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. for _, eg := range examples { var c Call c.Call.Value = eg c.setType(types.NewTuple()) main.emit(&c) } main.emit(&Return{}) main.currentBlock = nil } main.finishBody() testmain.Members["main"] = main if prog.mode&PrintPackages != 0 { printMu.Lock() testmain.WriteTo(os.Stdout) printMu.Unlock() } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }
"reflect" "unsafe" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/ssa" ) type opaqueType struct { types.Type name string } func (t *opaqueType) String() string { return t.name } // A bogus "reflect" type-checker package. Shared across interpreters. var reflectTypesPackage = types.NewPackage("reflect", "reflect", nil) // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. Since its type is opaque to the target // language, we use a types.Basic. // // type rtype <opaque> var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"}) // error is an (interpreted) named type whose underlying type is string. // The interpreter uses it for all implementations of the built-in error // interface that it creates. // We put it in the "reflect" package for expedience. // // type error string var errorType = makeNamedType("error", &opaqueType{nil, "error"})
func main() { flags := flag.NewFlagSet("", flag.ContinueOnError) cmd := "help" var cmdArgs []string if err := flags.Parse(os.Args[1:]); err == nil && flags.NArg() != 0 { cmd = flags.Arg(0) cmdArgs = flags.Args()[1:] if cmd == "help" && flags.NArg() == 2 { cmd = flags.Arg(1) cmdArgs = []string{"--help"} } } options := &gbuild.Options{CreateMapFile: true} switch cmd { case "build": buildFlags := flag.NewFlagSet("build command", flag.ExitOnError) var pkgObj string buildFlags.StringVar(&pkgObj, "o", "", "output file") buildFlags.BoolVar(&options.Verbose, "v", false, "print the names of packages as they are compiled") buildFlags.BoolVar(&options.Watch, "w", false, "watch for changes to the source files") buildFlags.BoolVar(&options.Minify, "m", false, "minify generated code") buildFlags.Parse(cmdArgs) for { s := gbuild.NewSession(options) exitCode := handleError(func() error { if buildFlags.NArg() == 0 { return s.BuildDir(currentDirectory, currentDirectory, pkgObj) } if strings.HasSuffix(buildFlags.Arg(0), ".go") { for _, arg := range buildFlags.Args() { if !strings.HasSuffix(arg, ".go") { return fmt.Errorf("named files must be .go files") } } if pkgObj == "" { basename := filepath.Base(buildFlags.Arg(0)) pkgObj = basename[:len(basename)-3] + ".js" } names := make([]string, buildFlags.NArg()) for i, name := range buildFlags.Args() { name = filepath.ToSlash(name) names[i] = name if s.Watcher != nil { s.Watcher.Watch(filepath.ToSlash(name)) } } if err := s.BuildFiles(buildFlags.Args(), pkgObj, currentDirectory); err != nil { return err } return nil } for _, pkgPath := range buildFlags.Args() { pkgPath = filepath.ToSlash(pkgPath) if s.Watcher != nil { s.Watcher.Watch(pkgPath) } buildPkg, err := gbuild.Import(pkgPath, 0, s.ArchSuffix()) if err != nil { return err } pkg := &gbuild.PackageData{Package: buildPkg} if err := s.BuildPackage(pkg); err != nil { return err } if pkgObj == "" { pkgObj = filepath.Base(buildFlags.Arg(0)) + ".js" } if err := s.WriteCommandPackage(pkg, pkgObj); err != nil { return err } } return nil }) if s.Watcher == nil { os.Exit(exitCode) } s.WaitForChange() } case "install": installFlags := flag.NewFlagSet("install command", flag.ExitOnError) installFlags.BoolVar(&options.Verbose, "v", false, "print the names of packages as they are compiled") installFlags.BoolVar(&options.Watch, "w", false, "watch for changes to the source files") installFlags.BoolVar(&options.Minify, "m", false, "minify generated code") installFlags.Parse(cmdArgs) for { s := gbuild.NewSession(options) exitCode := handleError(func() error { pkgs := installFlags.Args() if len(pkgs) == 0 { srcDir, err := filepath.EvalSymlinks(filepath.Join(build.Default.GOPATH, "src")) if err != nil { return err } if !strings.HasPrefix(currentDirectory, srcDir) { return fmt.Errorf("gopherjs install: no install location for directory %s outside GOPATH", currentDirectory) } pkgPath, err := filepath.Rel(srcDir, currentDirectory) if err != nil { return err } pkgs = []string{pkgPath} } for _, pkgPath := range pkgs { pkgPath = filepath.ToSlash(pkgPath) if _, err := s.ImportPackage(pkgPath); err != nil { return err } pkg := s.Packages[pkgPath] if err := s.WriteCommandPackage(pkg, pkg.PkgObj); err != nil { return err } } return nil }) if s.Watcher == nil { os.Exit(exitCode) } s.WaitForChange() } case "run": os.Exit(handleError(func() error { lastSourceArg := 0 for { if lastSourceArg == len(cmdArgs) || !strings.HasSuffix(cmdArgs[lastSourceArg], ".go") { break } lastSourceArg++ } if lastSourceArg == 0 { return fmt.Errorf("gopherjs run: no go files listed") } tempfile, err := ioutil.TempFile("", filepath.Base(cmdArgs[0])+".") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() s := gbuild.NewSession(options) if err := s.BuildFiles(cmdArgs[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { return err } if err := runNode(tempfile.Name(), cmdArgs[lastSourceArg:], ""); err != nil { return err } return nil })) case "test": testFlags := flag.NewFlagSet("test command", flag.ExitOnError) verbose := testFlags.Bool("v", false, "verbose") short := testFlags.Bool("short", false, "short") testFlags.Parse(cmdArgs) os.Exit(handleError(func() error { pkgs := make([]*build.Package, testFlags.NArg()) for i, pkgPath := range testFlags.Args() { pkgPath = filepath.ToSlash(pkgPath) var err error pkgs[i], err = gbuild.Import(pkgPath, 0, "js") if err != nil { return err } } if len(pkgs) == 0 { srcDir, err := filepath.EvalSymlinks(filepath.Join(build.Default.GOPATH, "src")) if err != nil { return err } var pkg *build.Package if strings.HasPrefix(currentDirectory, srcDir) { pkgPath, err := filepath.Rel(srcDir, currentDirectory) if err != nil { return err } if pkg, err = gbuild.Import(pkgPath, 0, "js"); err != nil { return err } } if pkg == nil { if pkg, err = build.ImportDir(currentDirectory, 0); err != nil { return err } pkg.ImportPath = "_" + currentDirectory } pkgs = []*build.Package{pkg} } var exitErr error for _, buildPkg := range pkgs { if len(buildPkg.TestGoFiles) == 0 && len(buildPkg.XTestGoFiles) == 0 { fmt.Printf("? \t%s\t[no test files]\n", buildPkg.ImportPath) continue } buildPkg.PkgObj = "" buildPkg.GoFiles = append(buildPkg.GoFiles, buildPkg.TestGoFiles...) pkg := &gbuild.PackageData{Package: buildPkg} s := gbuild.NewSession(options) if err := s.BuildPackage(pkg); err != nil { return err } mainPkg := &gbuild.PackageData{ Package: &build.Package{ Name: "main", ImportPath: "main", }, Archive: &compiler.Archive{ ImportPath: compiler.PkgPath("main"), }, } s.Packages["main"] = mainPkg s.ImportContext.Packages["main"] = types.NewPackage("main", "main") testingOutput, err := s.ImportPackage("testing") if err != nil { panic(err) } mainPkg.Archive.AddDependenciesOf(testingOutput) var mainFunc compiler.Decl var names []string var tests []string collectTests := func(pkg *gbuild.PackageData) { for _, name := range pkg.Archive.Tests { names = append(names, name) tests = append(tests, fmt.Sprintf(`$packages["%s"].%s`, pkg.ImportPath, name)) mainFunc.DceDeps = append(mainFunc.DceDeps, compiler.DepId(pkg.ImportPath+":"+name)) } mainPkg.Archive.AddDependenciesOf(pkg.Archive) } collectTests(pkg) if len(pkg.XTestGoFiles) != 0 { testPkg := &gbuild.PackageData{Package: &build.Package{ ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, }} if err := s.BuildPackage(testPkg); err != nil { return err } collectTests(testPkg) } mainFunc.DceDeps = append(mainFunc.DceDeps, compiler.DepId("flag:Parse")) mainFunc.BodyCode = []byte(fmt.Sprintf(` $pkg.main = function() { var testing = $packages["testing"]; testing.Main2("%s", "%s", new ($sliceType($String))(["%s"]), new ($sliceType($funcType([testing.T.Ptr], [], false)))([%s])); }; `, pkg.ImportPath, pkg.Dir, strings.Join(names, `", "`), strings.Join(tests, ", "))) mainPkg.Archive.Declarations = []compiler.Decl{mainFunc} mainPkg.Archive.AddDependency("main") tempfile, err := ioutil.TempFile("", "test.") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() if err := s.WriteCommandPackage(mainPkg, tempfile.Name()); err != nil { return err } var args []string if *verbose { args = append(args, "-test.v") } if *short { args = append(args, "-test.short") } if err := runNode(tempfile.Name(), args, ""); err != nil { if _, ok := err.(*exec.ExitError); !ok { return err } exitErr = err } } return exitErr })) case "tool": tool := cmdArgs[0] toolFlags := flag.NewFlagSet("tool command", flag.ExitOnError) toolFlags.Bool("e", false, "") toolFlags.Bool("l", false, "") toolFlags.Bool("m", false, "") toolFlags.String("o", "", "") toolFlags.String("D", "", "") toolFlags.String("I", "", "") toolFlags.Parse(flags.Args()[2:]) os.Exit(handleError(func() error { if len(tool) == 2 { switch tool[1] { case 'g': basename := filepath.Base(toolFlags.Arg(0)) s := gbuild.NewSession(options) if err := s.BuildFiles([]string{toolFlags.Arg(0)}, basename[:len(basename)-3]+".js", currentDirectory); err != nil { return err } return nil } } return fmt.Errorf("Tool not supported: " + tool) })) case "help", "": os.Stderr.WriteString(`GopherJS is a tool for compiling Go source code to JavaScript. Usage: gopherjs command [arguments] The commands are: build compile packages and dependencies install compile and install packages and dependencies run compile and run Go program (requires Node.js) test test packages (requires Node.js) Use "go help [command]" for more information about a command. `) default: fmt.Fprintf(os.Stderr, "gopherjs: unknown subcommand \"%s\"\nRun 'gopherjs help' for usage.\n", cmd) } }
func tool() error { flag.Parse() cmd := flag.Arg(0) switch cmd { case "build": buildFlags := flag.NewFlagSet("build", flag.ContinueOnError) var pkgObj string buildFlags.StringVar(&pkgObj, "o", "", "") buildFlags.Parse(flag.Args()[1:]) if buildFlags.NArg() == 0 { wd, err := os.Getwd() if err != nil { return err } buildContext := &build.Context{ GOROOT: build.Default.GOROOT, GOPATH: build.Default.GOPATH, GOOS: build.Default.GOOS, GOARCH: "js", Compiler: "gc", } buildPkg, err := buildContext.ImportDir(wd, 0) if err != nil { return err } pkg := &Package{Package: buildPkg} pkg.ImportPath = wd if err := buildPackage(pkg); err != nil { return err } if pkgObj == "" { pkgObj = path.Base(wd) + ".js" } if err := writeCommandPackage(pkg, pkgObj); err != nil { return err } return nil } if strings.HasSuffix(buildFlags.Arg(0), ".go") { for _, arg := range buildFlags.Args() { if !strings.HasSuffix(arg, ".go") { return fmt.Errorf("named files must be .go files") } } if pkgObj == "" { basename := path.Base(buildFlags.Arg(0)) pkgObj = basename[:len(basename)-3] + ".js" } if err := buildFiles(buildFlags.Args(), pkgObj); err != nil { return err } return nil } for _, pkgPath := range buildFlags.Args() { buildPkg, err := buildImport(pkgPath, 0) if err != nil { return err } pkg := &Package{Package: buildPkg} if err := buildPackage(pkg); err != nil { return err } if pkgObj == "" { pkgObj = path.Base(buildFlags.Arg(0)) + ".js" } if err := writeCommandPackage(pkg, pkgObj); err != nil { return err } } return nil case "install": installFlags := flag.NewFlagSet("install", flag.ContinueOnError) installFlags.Parse(flag.Args()[1:]) installMode = true for _, pkgPath := range installFlags.Args() { buildPkg, err := buildImport(pkgPath, 0) if err != nil { return err } pkg := &Package{Package: buildPkg} if pkg.IsCommand() { pkg.PkgObj = pkg.BinDir + "/" + path.Base(pkg.ImportPath) + ".js" } if err := buildPackage(pkg); err != nil { return err } if err := writeCommandPackage(pkg, pkg.PkgObj); err != nil { return err } } return nil case "run": lastSourceArg := 1 for { if !strings.HasSuffix(flag.Arg(lastSourceArg), ".go") { break } lastSourceArg += 1 } if lastSourceArg == 1 { return fmt.Errorf("gopherjs run: no go files listed") } tempfile, err := ioutil.TempFile("", path.Base(flag.Arg(1))+".") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() if err := buildFiles(flag.Args()[1:lastSourceArg], tempfile.Name()); err != nil { return err } if err := runNode(tempfile.Name(), flag.Args()[lastSourceArg:], ""); err != nil { return err } return nil case "test": testFlags := flag.NewFlagSet("test", flag.ContinueOnError) short := testFlags.Bool("short", false, "") verbose := testFlags.Bool("v", false, "") testFlags.Parse(flag.Args()[1:]) mainPkg := &Package{Package: &build.Package{ ImportPath: "main", }} packages["main"] = mainPkg mainPkgTypes := types.NewPackage("main", "main", types.NewScope(nil)) testingPkgTypes, _ := typesConfig.Import(typesConfig.Packages, "testing") mainPkgTypes.SetImports([]*types.Package{testingPkgTypes}) typesConfig.Packages["main"] = mainPkgTypes mainPkg.JavaScriptCode = []byte("Go$pkg.main = function() {\nGo$packages[\"flag\"].Parse();\n") for _, pkgPath := range testFlags.Args() { buildPkg, err := buildImport(pkgPath, 0) if err != nil { return err } pkg := &Package{Package: buildPkg} pkg.GoFiles = append(pkg.GoFiles, pkg.TestGoFiles...) if err := buildPackage(pkg); err != nil { return err } testPkg := &Package{Package: &build.Package{ ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, }} if err := buildPackage(testPkg); err != nil { return err } pkgTypes := typesConfig.Packages[pkg.ImportPath] testPkgTypes := typesConfig.Packages[testPkg.ImportPath] var names []string var tests []string for _, name := range pkgTypes.Scope().Names() { if strings.HasPrefix(name, "Test") { names = append(names, name) tests = append(tests, fmt.Sprintf(`Go$packages["%s"].%s`, pkg.ImportPath, name)) } } for _, name := range testPkgTypes.Scope().Names() { if strings.HasPrefix(name, "Test") { names = append(names, name) tests = append(tests, fmt.Sprintf(`Go$packages["%s"].%s`, testPkg.ImportPath, name)) } } mainPkg.JavaScriptCode = append(mainPkg.JavaScriptCode, []byte(fmt.Sprintf(`Go$packages["testing"].RunTests2("%s", "%s", ["%s"], [%s]);`+"\n", pkg.ImportPath, pkg.Dir, strings.Join(names, `", "`), strings.Join(tests, ", ")))...) mainPkgTypes.SetImports(append(mainPkgTypes.Imports(), pkgTypes, testPkgTypes)) } mainPkg.JavaScriptCode = append(mainPkg.JavaScriptCode, []byte("}; Go$pkg.init = function() {};")...) tempfile, err := ioutil.TempFile("", "test.") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() if err := writeCommandPackage(mainPkg, tempfile.Name()); err != nil { return err } var args []string if *short { args = append(args, "-test.short") } if *verbose { args = append(args, "-test.v") } return runNode(tempfile.Name(), args, "") case "tool": tool := flag.Arg(1) toolFlags := flag.NewFlagSet("tool", flag.ContinueOnError) toolFlags.Bool("e", false, "") toolFlags.Bool("l", false, "") toolFlags.Bool("m", false, "") toolFlags.String("o", "", "") toolFlags.String("D", "", "") toolFlags.String("I", "", "") toolFlags.Parse(flag.Args()[2:]) if len(tool) == 2 { switch tool[1] { case 'g': basename := path.Base(toolFlags.Arg(0)) if err := buildFiles([]string{toolFlags.Arg(0)}, basename[:len(basename)-3]+".js"); err != nil { return err } return nil } } return fmt.Errorf("Tool not supported: " + tool) case "help", "": os.Stderr.WriteString(`GopherJS is a tool for compiling Go source code to JavaScript. Usage: gopherjs command [arguments] The commands are: build compile packages and dependencies install compile and install packages and dependencies run compile and run Go program `) return nil default: return fmt.Errorf("gopherjs: unknown subcommand \"%s\"\nRun 'gopherjs help' for usage.", cmd) } }
func NewPackage(path, name string) *Package { return &Package{ types.NewPackage(path, name), } }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("testmain", "testmain"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() // TODO(adonovan): use lexical order. var expfuncs []*Function // all exported functions of *_test.go in pkgs, unordered for _, pkg := range pkgs { if pkg.Prog != prog { panic("wrong Program") } // Initialize package to test. var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) // Enumerate its possible tests/benchmarks. for _, mem := range pkg.Members { if f, ok := mem.(*Function); ok && ast.IsExported(f.Name()) && strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") { expfuncs = append(expfuncs, f) } } } init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init testingPkg := prog.ImportedPackage("testing") if testingPkg == nil { // If the program doesn't import "testing", it can't // contain any tests. // TODO(adonovan): but it might contain Examples. // Support them (by just calling them directly). return nil } testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } main := &Function{ name: "main", Signature: new(types.Signature), Synthetic: "test main function", Prog: prog, Pkg: testmain, } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() main.startBody() var c Call c.Call.Value = testingMain tests := testMainSlice(main, expfuncs, "Test", testingMainParams.At(1).Type()) benchmarks := testMainSlice(main, expfuncs, "Benchmark", testingMainParams.At(2).Type()) examples := testMainSlice(main, expfuncs, "Example", testingMainParams.At(3).Type()) _, noTests := tests.(*Const) // i.e. nil slice _, noBenchmarks := benchmarks.(*Const) _, noExamples := examples.(*Const) if noTests && noBenchmarks && noExamples { return nil } c.Call.Args = []Value{matcher, tests, benchmarks, examples} // Emit: testing.Main(nil, tests, benchmarks, examples) emitTailCall(main, &c) main.finishBody() testmain.Members["main"] = main if prog.mode&LogPackages != 0 { testmain.WriteTo(os.Stderr) } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }
func (t *Translator) NewEmptyTypesPackage(path string) { t.typesPackages[path] = types.NewPackage(path, path) }