func getPackage(pkgpath string) (*build.Package, error) { var err error var pkg *build.Package pkg, err = build.Import(pkgpath, "", 0) if err != nil { return nil, err } else { for i, filename := range pkg.GoFiles { pkg.GoFiles[i] = path.Join(pkg.Dir, filename) } for i, filename := range pkg.CFiles { pkg.CFiles[i] = path.Join(pkg.Dir, filename) } // Look for .ll files, treat them the same as .s. // TODO look for build tags in the .ll file, check filename // for GOOS/GOARCH, etc. var llfiles []string llfiles, err = filepath.Glob(pkg.Dir + "/*.ll") for _, file := range llfiles { pkg.SFiles = append(pkg.SFiles, file) } } return pkg, nil }
func (b *builder) build(srcs []*source) (*Package, error) { b.pdoc.Updated = time.Now().UTC() references := make(map[string]bool) b.srcs = make(map[string]*source) for _, src := range srcs { if strings.HasSuffix(src.name, ".go") { b.srcs[src.name] = src } else { addReferences(references, src.data) fn := strings.ToLower(src.name) if fn == "readme" || strings.HasPrefix(fn, "readme.") { if b.pdoc.ReadmeFiles == nil { b.pdoc.ReadmeFiles = make(map[string][]byte) } b.pdoc.ReadmeFiles[src.name] = src.data } } } for r := range references { b.pdoc.References = append(b.pdoc.References, r) } if len(b.srcs) == 0 { return b.pdoc, nil } b.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: "linux", GOARCH: "amd64", CgoEnabled: true, ReleaseTags: build.Default.ReleaseTags, JoinPath: path.Join, IsAbsPath: path.IsAbs, SplitPathList: func(list string) []string { return strings.Split(list, ":") }, IsDir: func(path string) bool { panic("unexpected") }, HasSubdir: func(root, dir string) (rel string, ok bool) { panic("unexpected") }, ReadDir: func(dir string) (fi []os.FileInfo, err error) { return b.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return b.openFile(path) }, Compiler: "gc", } var err error var bpkg *build.Package for _, env := range goEnvs { ctxt.GOOS = env.GOOS ctxt.GOARCH = env.GOARCH bpkg, err = ctxt.ImportDir("/", 0) if _, ok := err.(*build.NoGoError); !ok { break } } if err != nil { if _, ok := err.(*build.NoGoError); !ok { b.pdoc.Errors = append(b.pdoc.Errors, err.Error()) } return b.pdoc, nil } // Parse the Go files files := make(map[string]*ast.File) names := append(bpkg.GoFiles, bpkg.CgoFiles...) sort.Strings(names) b.pdoc.Files = make([]*File, len(names)) for i, name := range names { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pdoc.Errors = append(b.pdoc.Errors, err.Error()) continue } src := b.srcs[name] src.index = i b.pdoc.Files[i] = &File{Name: name, URL: src.browseURL} b.pdoc.SourceSize += len(src.data) files[name] = file } apkg, _ := ast.NewPackage(b.fset, files, simpleImporter, nil) // Find examples in the test files. names = append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) sort.Strings(names) b.pdoc.TestFiles = make([]*File, len(names)) for i, name := range names { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pdoc.Errors = append(b.pdoc.Errors, err.Error()) continue } b.pdoc.TestFiles[i] = &File{Name: name, URL: b.srcs[name].browseURL} b.pdoc.TestSourceSize += len(b.srcs[name].data) b.examples = append(b.examples, doc.Examples(file)...) } b.vetPackage(apkg) mode := doc.Mode(0) if b.pdoc.ImportPath == "builtin" { mode |= doc.AllDecls } dpkg := doc.New(apkg, b.pdoc.ImportPath, mode) b.pdoc.Name = dpkg.Name b.pdoc.Doc = strings.TrimRight(dpkg.Doc, " \t\n\r") b.pdoc.Synopsis = synopsis(b.pdoc.Doc) b.pdoc.Examples = b.getExamples("") b.pdoc.IsCmd = bpkg.IsCommand() b.pdoc.GOOS = ctxt.GOOS b.pdoc.GOARCH = ctxt.GOARCH b.pdoc.Consts = b.values(dpkg.Consts) b.pdoc.Funcs = b.funcs(dpkg.Funcs) b.pdoc.Types = b.types(dpkg.Types) b.pdoc.Vars = b.values(dpkg.Vars) b.pdoc.Notes = b.notes(dpkg.Notes) b.pdoc.Imports = bpkg.Imports b.pdoc.TestImports = bpkg.TestImports b.pdoc.XTestImports = bpkg.XTestImports return b.pdoc, nil }
func main() { flag.Parse() cmd := flag.Arg(0) switch cmd { case "build": buildFlags := flag.NewFlagSet("build", flag.ContinueOnError) var pkgObj string buildFlags.StringVar(&pkgObj, "o", "", "") verbose := buildFlags.Bool("v", false, "print the names of packages as they are compiled") watch := buildFlags.Bool("w", false, "watch for changes to the source files") buildFlags.Parse(flag.Args()[1:]) for { s := NewSession(*verbose, *watch) exitCode := handleError(func() error { if buildFlags.NArg() == 0 { buildContext := &build.Context{ GOROOT: build.Default.GOROOT, GOPATH: build.Default.GOPATH, GOOS: build.Default.GOOS, GOARCH: "js", Compiler: "gc", } if s.watcher != nil { s.watcher.Watch(currentDirectory) } buildPkg, err := buildContext.ImportDir(currentDirectory, 0) if err != nil { return err } pkg := &packageData{Package: buildPkg} pkg.ImportPath = currentDirectory if err := s.buildPackage(pkg); err != nil { return err } if pkgObj == "" { pkgObj = filepath.Base(currentDirectory) + ".js" } if err := s.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 := 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); 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 := buildImport(pkgPath, 0) if err != nil { return err } pkg := &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", flag.ContinueOnError) verbose := installFlags.Bool("v", false, "print the names of packages as they are compiled") watch := installFlags.Bool("w", false, "watch for changes to the source files") installFlags.Parse(flag.Args()[1:]) for { s := NewSession(*verbose, *watch) 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 := 1 for { if !strings.HasSuffix(flag.Arg(lastSourceArg), ".go") { break } lastSourceArg++ } if lastSourceArg == 1 { return fmt.Errorf("gopherjs run: no go files listed") } tempfile, err := ioutil.TempFile("", filepath.Base(flag.Arg(1))+".") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() s := NewSession(false, false) if err := s.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) verbose := testFlags.Bool("v", false, "verbose") short := testFlags.Bool("short", false, "short") testFlags.Parse(flag.Args()[1:]) 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 = buildImport(pkgPath, 0) 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 = buildImport(pkgPath, 0); 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 := &packageData{Package: buildPkg} s := NewSession(false, false) if err := s.buildPackage(pkg); err != nil { return err } mainPkg := &packageData{ Package: &build.Package{ Name: "main", ImportPath: "main", }, Archive: &translator.Archive{ ImportPath: "main", }, } s.packages["main"] = mainPkg s.t.NewEmptyTypesPackage("main") testingOutput, err := s.importPackage("testing") if err != nil { panic(err) } mainPkg.Archive.AddDependenciesOf(testingOutput) var mainFunc translator.Decl var names []string var tests []string collectTests := func(pkg *packageData) { for _, name := range pkg.Archive.Tests { names = append(names, name) tests = append(tests, fmt.Sprintf(`go$packages["%s"].%s`, pkg.ImportPath, name)) mainFunc.DceDeps = append(mainFunc.DceDeps, translator.DepId(pkg.ImportPath+":"+name)) } mainPkg.Archive.AddDependenciesOf(pkg.Archive) } collectTests(pkg) if len(pkg.XTestGoFiles) != 0 { testPkg := &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, translator.DepId("flag:Parse")) mainFunc.BodyCode = []byte(fmt.Sprintf(` go$pkg.main = function() { var testing = go$packages["testing"]; testing.Main2("%s", "%s", new (go$sliceType(Go$String))(["%s"]), new (go$sliceType(go$funcType([testing.T.Ptr], [], false)))([%s])); }; `, pkg.ImportPath, pkg.Dir, strings.Join(names, `", "`), strings.Join(tests, ", "))) mainPkg.Archive.Declarations = []translator.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 := 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:]) os.Exit(handleError(func() error { if len(tool) == 2 { switch tool[1] { case 'g': basename := filepath.Base(toolFlags.Arg(0)) s := NewSession(false, false) if err := s.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 (requires Node.js) test test packages (requires Node.js) `) default: fmt.Fprintf(os.Stderr, "gopherjs: unknown subcommand \"%s\"\nRun 'gopherjs help' for usage.\n", cmd) } }
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 { firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: The GOPATH workspace that contains the package source should be chosen. srcDir, err := filepath.EvalSymlinks(filepath.Join(firstGopathWorkspace, "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": runFlags := flag.NewFlagSet("run command", flag.ExitOnError) runFlags.Parse(cmdArgs) os.Exit(handleError(func() error { lastSourceArg := 0 for { if !strings.HasSuffix(runFlags.Arg(lastSourceArg), ".go") { break } lastSourceArg++ } if lastSourceArg == 0 { return fmt.Errorf("gopherjs run: no go files listed") } tempfile, err := ioutil.TempFile("", filepath.Base(runFlags.Arg(0))+".") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) }() s := gbuild.NewSession(options) if err := s.BuildFiles(runFlags.Args()[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { return err } if err := runNode(tempfile.Name(), runFlags.Args()[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.BoolVar(&options.Minify, "m", false, "minify generated code") 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 { firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: Not sure if always picking first GOPATH workspace here is the right thing. srcDir, err := filepath.EvalSymlinks(filepath.Join(firstGopathWorkspace, "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 } tests := &testFuncs{Package: buildPkg} collectTests := func(pkg *gbuild.PackageData, testPkg string) bool { for _, name := range pkg.Archive.Tests { tests.Tests = append(tests.Tests, testFunc{Package: testPkg, Name: name}) } return len(pkg.Archive.Tests) != 0 } tests.NeedTest = collectTests(pkg, "_test") 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 } tests.NeedXtest = collectTests(testPkg, "_xtest") } buf := bytes.NewBuffer(nil) err := testmainTmpl.Execute(buf, tests) if err != nil { return err } fset := token.NewFileSet() mainFile, err := parser.ParseFile(fset, "_testmain.go", buf, 0) if err != nil { return err } mainPkg := &gbuild.PackageData{ Package: &build.Package{ Name: "main", ImportPath: "main", }, } mainPkg.Archive, err = compiler.Compile("main", []*ast.File{mainFile}, fset, s.ImportContext, options.Minify) if err != nil { return err } 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") } status := "ok " start := time.Now() if err := runNode(tempfile.Name(), args, buildPkg.Dir); err != nil { if _, ok := err.(*exec.ExitError); !ok { return err } exitErr = err status = "FAIL" } fmt.Printf("%s\t%s\t%.3fs\n", status, buildPkg.ImportPath, time.Now().Sub(start).Seconds()) } 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 newPackage(dir *gosrc.Directory) (*Package, error) { pkg := &Package{ Updated: time.Now().UTC(), LineFmt: dir.LineFmt, ImportPath: dir.ImportPath, ProjectRoot: dir.ProjectRoot, ProjectName: dir.ProjectName, ProjectURL: dir.ProjectURL, BrowseURL: dir.BrowseURL, Etag: PackageVersion + "-" + dir.Etag, VCS: dir.VCS, DeadEndFork: dir.DeadEndFork, Subdirectories: dir.Subdirectories, } var b builder b.srcs = make(map[string]*source) references := make(map[string]bool) for _, file := range dir.Files { if strings.HasSuffix(file.Name, ".go") { gosrc.OverwriteLineComments(file.Data) b.srcs[file.Name] = &source{name: file.Name, browseURL: file.BrowseURL, data: file.Data} } else { addReferences(references, file.Data) } } for r := range references { pkg.References = append(pkg.References, r) } if len(b.srcs) == 0 { return pkg, nil } b.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: "linux", GOARCH: "amd64", CgoEnabled: true, ReleaseTags: build.Default.ReleaseTags, BuildTags: build.Default.BuildTags, Compiler: "gc", } var err error var bpkg *build.Package for _, env := range goEnvs { ctxt.GOOS = env.GOOS ctxt.GOARCH = env.GOARCH // TODO(garyburd): Change second argument to build.ImportComment when // gddo is upgraded to Go 1.4. bpkg, err = dir.Import(&ctxt, 0 /* build.ImportComment */) if _, ok := err.(*build.NoGoError); !ok { break } } if err != nil { if _, ok := err.(*build.NoGoError); !ok { pkg.Errors = append(pkg.Errors, err.Error()) } return pkg, nil } /* TODO(garyburd): This block of code uses the import comment feature added in Go 1.4. Uncomment this block when gddo upgraded to Go 1.4. Also, change the second argument to dir.Import above from 0 to build.ImportComment. if bpkg.ImportComment != "" && bpkg.ImportComment != dir.ImportPath { return nil, gosrc.NotFoundError{ Message: "not at canonical import path", Redirect: bpkg.ImportComment, } } */ // Parse the Go files files := make(map[string]*ast.File) names := append(bpkg.GoFiles, bpkg.CgoFiles...) sort.Strings(names) pkg.Files = make([]*File, len(names)) for i, name := range names { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { pkg.Errors = append(pkg.Errors, err.Error()) } else { files[name] = file } src := b.srcs[name] src.index = i pkg.Files[i] = &File{Name: name, URL: src.browseURL} pkg.SourceSize += len(src.data) } apkg, _ := ast.NewPackage(b.fset, files, simpleImporter, nil) // Find examples in the test files. names = append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) sort.Strings(names) pkg.TestFiles = make([]*File, len(names)) for i, name := range names { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { pkg.Errors = append(pkg.Errors, err.Error()) } else { b.examples = append(b.examples, doc.Examples(file)...) } pkg.TestFiles[i] = &File{Name: name, URL: b.srcs[name].browseURL} pkg.TestSourceSize += len(b.srcs[name].data) } b.vetPackage(pkg, apkg) mode := doc.Mode(0) if pkg.ImportPath == "builtin" { mode |= doc.AllDecls } dpkg := doc.New(apkg, pkg.ImportPath, mode) if pkg.ImportPath == "builtin" { removeAssociations(dpkg) } pkg.Name = dpkg.Name pkg.Doc = strings.TrimRight(dpkg.Doc, " \t\n\r") pkg.Synopsis = synopsis(pkg.Doc) pkg.Examples = b.getExamples("") pkg.IsCmd = bpkg.IsCommand() pkg.GOOS = ctxt.GOOS pkg.GOARCH = ctxt.GOARCH pkg.Consts = b.values(dpkg.Consts) pkg.Funcs = b.funcs(dpkg.Funcs) pkg.Types = b.types(dpkg.Types) pkg.Vars = b.values(dpkg.Vars) pkg.Notes = b.notes(dpkg.Notes) pkg.Imports = bpkg.Imports pkg.TestImports = bpkg.TestImports pkg.XTestImports = bpkg.XTestImports return pkg, nil }
func buildPackage(pkg *build.Package, output string) error { args := []string{"-c", "-triple", triple} dir, file := path.Split(pkg.ImportPath) if pkg.IsCommand() || test { if output == "" { output = file } } else { dir = filepath.Join(pkgroot, dir) err := os.MkdirAll(dir, os.FileMode(0755)) if err != nil { return err } if output == "" { output = path.Join(dir, file+".bc") } } if !pkg.IsCommand() || test { args = append(args, "-importpath", pkg.ImportPath) } var cgoCFLAGS, cgoCPPFLAGS, cgoLDFLAGS []string if len(pkg.CFiles) > 0 || len(pkg.CgoFiles) > 0 { // TODO(axw) process pkg-config cgoCFLAGS = append(envFields("CGO_CFLAGS"), pkg.CgoCFLAGS...) cgoCPPFLAGS = append(envFields("CGO_CPPFLAGS"), pkg.CgoCPPFLAGS...) //cgoCXXFLAGS = append(envFields("CGO_CXXFLAGS"), pkg.CgoCXXFLAGS...) cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", workdir, "-I", pkg.Dir) cgoLDFLAGS = append(envFields("CGO_LDFLAGS"), pkg.CgoLDFLAGS...) // Get the library dir in which to find libgcc, libstdc++, etc. // We need to do this because we rely on clang to link; in Ubuntu 14.04 // beta 1, there is no g++-4.9, but there is gccgo-4.9. Clang sees the // partial 4.9 lib directory and uses it instead of 4.8, which is what // should be used. if gcclib, err := findGcclib(); err != nil { return fmt.Errorf("failed to locate gcc lib dir: %v", err) } else if gcclib != "." { cgoLDFLAGS = append(cgoLDFLAGS, "-L", gcclib) } } var gofiles, cfiles []string if len(pkg.CgoFiles) > 0 { var err error gofiles, cfiles, err = runCgo(pkg.ImportPath, pkg.CgoFiles, cgoCPPFLAGS, cgoCFLAGS) if err != nil { return err } } gofiles = append(gofiles, pkg.GoFiles...) cfiles = append(cfiles, pkg.CFiles...) _, file = path.Split(output) tempfile := path.Join(workdir, file+".bc") args = append(args, fmt.Sprintf("-g=%v", generateDebug)) args = append(args, "-o", tempfile) args = append(args, gofiles...) if test { args = append(args, pkg.TestGoFiles...) } cmd := exec.Command(llgobin, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := runCmd(cmd); err != nil { return err } // Remove the .S files and add them to cfiles. for i := 0; i < len(pkg.SFiles); i++ { sfile := pkg.SFiles[i] if strings.HasSuffix(sfile, ".S") { cfiles = append(cfiles, sfile) pkg.SFiles = append(pkg.SFiles[:i], pkg.SFiles[i+1:]...) i-- } } // Compile and link .c files in. llvmlink := filepath.Join(llvmbindir, "llvm-link") for _, cfile := range cfiles { bcfile := filepath.Join(workdir, filepath.Base(cfile+".bc")) args = []string{"-c", "-o", bcfile} if triple != "pnacl" { args = append(args, "-target", triple, "-emit-llvm") } args = append(args, cgoCFLAGS...) args = append(args, cgoCPPFLAGS...) args = append(args, cfile) cmd := exec.Command(clang, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := runCmd(cmd) if err != nil { os.Remove(bcfile) return err } cmd = exec.Command(llvmlink, "-o", tempfile, tempfile, bcfile) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = runCmd(cmd) os.Remove(bcfile) if err != nil { return err } } // Link .ll files in. if len(pkg.SFiles) > 0 { args = []string{"-o", tempfile, tempfile} args = append(args, pkg.SFiles...) cmd := exec.Command(llvmlink, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := runCmd(cmd); err != nil { return err } } // If it's a command, link in the dependencies. if pkg.IsCommand() { if err := linkdeps(pkg, tempfile); err != nil { return err } if run { cmd := exec.Command(tempfile) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return runCmd(cmd) } } else if test { if err := linktest(pkg, tempfile); err != nil { return err } } else { if output != "-" && len(cgoLDFLAGS) > 0 { if err := writeLdflags(pkg.ImportPath, cgoLDFLAGS); err != nil { return err } } } return moveFile(tempfile, output) }
func main() { options := &gbuild.Options{CreateMapFile: true} var pkgObj string pflag.BoolVarP(&options.Verbose, "verbose", "v", false, "print the names of packages as they are compiled") flagVerbose := pflag.Lookup("verbose") pflag.BoolVarP(&options.Watch, "watch", "w", false, "watch for changes to the source files") flagWatch := pflag.Lookup("watch") pflag.BoolVarP(&options.Minify, "minify", "m", false, "minify generated code") flagMinify := pflag.Lookup("minify") pflag.BoolVar(&options.Color, "color", terminal.IsTerminal(int(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb", "colored output") flagColor := pflag.Lookup("color") tags := pflag.String("tags", "", "a list of build tags to consider satisfied during the build") flagTags := pflag.Lookup("tags") cmdBuild := &cobra.Command{ Use: "build [packages]", Short: "compile packages and dependencies", } cmdBuild.Flags().StringVarP(&pkgObj, "output", "o", "", "output file") cmdBuild.Flags().AddFlag(flagVerbose) cmdBuild.Flags().AddFlag(flagWatch) cmdBuild.Flags().AddFlag(flagMinify) cmdBuild.Flags().AddFlag(flagColor) cmdBuild.Flags().AddFlag(flagTags) cmdBuild.Run = func(cmd *cobra.Command, args []string) { options.BuildTags = strings.Fields(*tags) for { s := gbuild.NewSession(options) exitCode := handleError(func() error { if len(args) == 0 { return s.BuildDir(currentDirectory, currentDirectory, pkgObj) } if strings.HasSuffix(args[0], ".go") || strings.HasSuffix(args[0], ".inc.js") { for _, arg := range args { if !strings.HasSuffix(arg, ".go") && !strings.HasSuffix(arg, ".inc.js") { return fmt.Errorf("named files must be .go or .inc.js files") } } if pkgObj == "" { basename := filepath.Base(args[0]) pkgObj = basename[:len(basename)-3] + ".js" } names := make([]string, len(args)) for i, name := range args { name = filepath.ToSlash(name) names[i] = name if s.Watcher != nil { s.Watcher.Add(name) } } if err := s.BuildFiles(args, pkgObj, currentDirectory); err != nil { return err } return nil } for _, pkgPath := range args { pkgPath = filepath.ToSlash(pkgPath) if s.Watcher != nil { s.Watcher.Add(pkgPath) } buildPkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) if err != nil { return err } pkg := &gbuild.PackageData{Package: buildPkg} if err := s.BuildPackage(pkg); err != nil { return err } if pkgObj == "" { pkgObj = filepath.Base(args[0]) + ".js" } if err := s.WriteCommandPackage(pkg, pkgObj); err != nil { return err } } return nil }, options, nil) if s.Watcher == nil { os.Exit(exitCode) } s.WaitForChange() } } cmdInstall := &cobra.Command{ Use: "install [packages]", Short: "compile and install packages and dependencies", } cmdInstall.Flags().AddFlag(flagVerbose) cmdInstall.Flags().AddFlag(flagWatch) cmdInstall.Flags().AddFlag(flagMinify) cmdInstall.Flags().AddFlag(flagColor) cmdInstall.Flags().AddFlag(flagTags) cmdInstall.Run = func(cmd *cobra.Command, args []string) { options.BuildTags = strings.Fields(*tags) for { s := gbuild.NewSession(options) exitCode := handleError(func() error { pkgs := args if len(pkgs) == 0 { firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: The GOPATH workspace that contains the package source should be chosen. srcDir, err := filepath.EvalSymlinks(filepath.Join(firstGopathWorkspace, "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} } if cmd.Name() == "get" { goGet := exec.Command("go", append([]string{"get", "-d"}, pkgs...)...) goGet.Stdout = os.Stdout goGet.Stderr = os.Stderr if err := goGet.Run(); err != nil { return err } } 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 }, options, nil) if s.Watcher == nil { os.Exit(exitCode) } s.WaitForChange() } } cmdGet := &cobra.Command{ Use: "get [packages]", Short: "download and install packages and dependencies", } cmdGet.Flags().AddFlag(flagVerbose) cmdGet.Flags().AddFlag(flagWatch) cmdGet.Flags().AddFlag(flagMinify) cmdGet.Flags().AddFlag(flagColor) cmdGet.Flags().AddFlag(flagTags) cmdGet.Run = cmdInstall.Run cmdRun := &cobra.Command{ Use: "run [gofiles...] [arguments...]", Short: "compile and run Go program", } cmdRun.Run = func(cmd *cobra.Command, args []string) { os.Exit(handleError(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("", filepath.Base(args[0])+".") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) os.Remove(tempfile.Name() + ".map") }() s := gbuild.NewSession(options) if err := s.BuildFiles(args[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { return err } if err := runNode(tempfile.Name(), args[lastSourceArg:], ""); err != nil { return err } return nil }, options, nil)) } cmdTest := &cobra.Command{ Use: "test [packages]", Short: "test packages", } bench := cmdTest.Flags().String("bench", "", "Run benchmarks matching the regular expression. By default, no benchmarks run. To run all benchmarks, use '--bench=.'.") run := cmdTest.Flags().String("run", "", "Run only those tests and examples matching the regular expression.") short := cmdTest.Flags().Bool("short", false, "Tell long-running tests to shorten their run time.") verbose := cmdTest.Flags().BoolP("verbose", "v", false, "Log all tests as they are run. Also print all text from Log and Logf calls even if the test succeeds.") cmdTest.Flags().AddFlag(flagMinify) cmdTest.Flags().AddFlag(flagColor) cmdTest.Run = func(cmd *cobra.Command, args []string) { os.Exit(handleError(func() error { pkgs := make([]*build.Package, len(args)) for i, pkgPath := range args { pkgPath = filepath.ToSlash(pkgPath) var err error pkgs[i], err = gbuild.Import(pkgPath, 0, "", nil) if err != nil { return err } } if len(pkgs) == 0 { firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] srcDir, err := filepath.EvalSymlinks(filepath.Join(firstGopathWorkspace, "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, "", nil); 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 _, 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 := gbuild.NewSession(options) tests := &testFuncs{Package: pkg} collectTests := func(buildPkg *build.Package, testPkgName string, needVar *bool) error { testPkg := &gbuild.PackageData{Package: buildPkg, IsTest: true} if err := s.BuildPackage(testPkg); err != nil { return err } for _, decl := range testPkg.Archive.Declarations { if strings.HasPrefix(decl.FullName, testPkg.ImportPath+".Test") { tests.Tests = append(tests.Tests, testFunc{Package: testPkgName, Name: decl.FullName[len(testPkg.ImportPath)+1:]}) *needVar = true } if strings.HasPrefix(decl.FullName, testPkg.ImportPath+".Benchmark") { tests.Benchmarks = append(tests.Benchmarks, testFunc{Package: testPkgName, Name: decl.FullName[len(testPkg.ImportPath)+1:]}) *needVar = true } } return nil } if err := collectTests(&build.Package{ ImportPath: pkg.ImportPath, Dir: pkg.Dir, GoFiles: append(pkg.GoFiles, pkg.TestGoFiles...), Imports: append(pkg.Imports, pkg.TestImports...), }, "_test", &tests.NeedTest); err != nil { return err } if err := collectTests(&build.Package{ ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, }, "_xtest", &tests.NeedXtest); err != nil { return err } buf := bytes.NewBuffer(nil) if err := testmainTmpl.Execute(buf, tests); err != nil { return err } fset := token.NewFileSet() mainFile, err := parser.ParseFile(fset, "_testmain.go", buf, 0) if err != nil { return err } mainPkg := &gbuild.PackageData{ Package: &build.Package{ Name: "main", ImportPath: "main", }, } mainPkg.Archive, err = compiler.Compile("main", []*ast.File{mainFile}, fset, s.ImportContext, options.Minify) if err != nil { return err } tempfile, err := ioutil.TempFile("", "test.") if err != nil { return err } defer func() { tempfile.Close() os.Remove(tempfile.Name()) os.Remove(tempfile.Name() + ".map") }() if err := s.WriteCommandPackage(mainPkg, tempfile.Name()); err != nil { return err } var args []string if *bench != "" { args = append(args, "-test.bench", *bench) } if *run != "" { args = append(args, "-test.run", *run) } if *short { args = append(args, "-test.short") } if *verbose { args = append(args, "-test.v") } status := "ok " start := time.Now() if err := runNode(tempfile.Name(), args, pkg.Dir); err != nil { if _, ok := err.(*exec.ExitError); !ok { return err } exitErr = err status = "FAIL" } fmt.Printf("%s\t%s\t%.3fs\n", status, pkg.ImportPath, time.Now().Sub(start).Seconds()) } return exitErr }, options, nil)) } cmdTool := &cobra.Command{ Use: "tool [command] [args...]", Short: "run specified go tool", } cmdTool.Flags().BoolP("e", "e", false, "") cmdTool.Flags().BoolP("l", "l", false, "") cmdTool.Flags().BoolP("m", "m", false, "") cmdTool.Flags().StringP("o", "o", "", "") cmdTool.Flags().StringP("D", "D", "", "") cmdTool.Flags().StringP("I", "I", "", "") cmdTool.Run = func(cmd *cobra.Command, args []string) { os.Exit(handleError(func() error { if len(args) == 2 { switch args[0][1] { case 'g': basename := filepath.Base(args[1]) s := gbuild.NewSession(options) if err := s.BuildFiles([]string{args[1]}, basename[:len(basename)-3]+".js", currentDirectory); err != nil { return err } return nil } } cmdTool.Help() return nil }, options, nil)) } cmdServe := &cobra.Command{ Use: "serve", Short: "compile on-the-fly and serve", } cmdServe.Flags().AddFlag(flagVerbose) cmdServe.Flags().AddFlag(flagMinify) cmdServe.Flags().AddFlag(flagColor) cmdServe.Flags().AddFlag(flagTags) var port int cmdServe.Flags().IntVarP(&port, "port", "p", 8080, "HTTP port") cmdServe.Run = func(cmd *cobra.Command, args []string) { options.BuildTags = strings.Fields(*tags) dirs := append(filepath.SplitList(build.Default.GOPATH), build.Default.GOROOT) sourceFiles := http.FileServer(serveCommandFileSystem{options: options, dirs: dirs, sourceMaps: make(map[string][]byte)}) fmt.Printf("serving at http://localhost:%d\n", port) fmt.Println(http.ListenAndServe(fmt.Sprintf(":%d", port), sourceFiles)) } rootCmd := &cobra.Command{ Use: "gopherjs", Long: "GopherJS is a tool for compiling Go source code to JavaScript.", } rootCmd.AddCommand(cmdBuild, cmdGet, cmdInstall, cmdRun, cmdTest, cmdTool, cmdServe) rootCmd.Execute() }
// fillPackage full of info. Assumes a build.Package discovered in build.FindOnly mode func fillPackage(p *build.Package) error { if p.Goroot { return nil } var buildMatch = "+build " var buildFieldSplit = func(r rune) bool { return unicode.IsSpace(r) || r == ',' } debugln("Filling package:", p.ImportPath, "from", p.Dir) gofiles, err := filepath.Glob(filepath.Join(p.Dir, "*.go")) if err != nil { debugln("Error globbing", err) return err } var testImports []string var imports []string NextFile: for _, file := range gofiles { debugln(file) fset := token.NewFileSet() pf, err := parser.ParseFile(fset, file, nil, parser.ParseComments) if err != nil { return err } testFile := strings.HasSuffix(file, "_test.go") fname := filepath.Base(file) if testFile { p.TestGoFiles = append(p.TestGoFiles, fname) } else { p.GoFiles = append(p.GoFiles, fname) } if len(pf.Comments) > 0 { for _, c := range pf.Comments { ct := c.Text() if i := strings.Index(ct, buildMatch); i != -1 { for _, b := range strings.FieldsFunc(ct[i+len(buildMatch):], buildFieldSplit) { //TODO: appengine is a special case for now: https://github.com/tools/godep/issues/353 if b == "ignore" || b == "appengine" { p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) continue NextFile } } } } } for _, is := range pf.Imports { name, err := strconv.Unquote(is.Path.Value) if err != nil { return err // can't happen? } if testFile { testImports = append(testImports, name) } else { imports = append(imports, name) } } } imports = uniq(imports) testImports = uniq(testImports) p.Imports = imports p.TestImports = testImports return nil }
// Build generates documentation from given source files through 'WalkType'. func (w *Walker) Build(wr *WalkRes) (*Package, error) { ctxt := build.Context{ CgoEnabled: true, ReleaseTags: build.Default.ReleaseTags, BuildTags: build.Default.BuildTags, Compiler: "gc", } if w.Pdoc.PkgDecl == nil { w.Pdoc.PkgDecl = &PkgDecl{} } // Check 'WalkType'. switch wr.WalkType { case WT_Local: // Check root path. if len(wr.RootPath) == 0 { return nil, errors.New("WT_Local: empty root path") } else if !com.IsDir(wr.RootPath) { return nil, errors.New("WT_Local: cannot find specific directory or it's a file") } w.setLocalContext(&ctxt) return nil, errors.New("Hasn't supported yet!") case WT_Memory: // Convert source files. w.SrcFiles = make(map[string]*Source) w.Pdoc.Readme = make(map[string][]byte) for _, src := range wr.Srcs { srcName := strings.ToLower(src.Name()) // For readme comparation. switch { case strings.HasSuffix(src.Name(), ".go"): w.SrcFiles[src.Name()] = src case len(w.Pdoc.Tag) > 0 || (wr.WalkMode&WM_NoReadme != 0): // This means we are not on the latest version of the code, // so we do not collect the README files. continue case strings.HasPrefix(srcName, "readme_zh") || strings.HasPrefix(srcName, "readme_cn"): w.Pdoc.Readme["zh"] = src.Data() case strings.HasPrefix(srcName, "readme"): w.Pdoc.Readme["en"] = src.Data() } } // Check source files. if w.SrcFiles == nil { return nil, errors.New("WT_Memory: no Go source file") } w.setMemoryContext(&ctxt) default: return nil, errors.New("Hasn't supported yet!") } var err error var bpkg *build.Package for _, env := range goEnvs { ctxt.GOOS = env.GOOS ctxt.GOARCH = env.GOARCH bpkg, err = ctxt.ImportDir(w.Pdoc.ImportPath, 0) // Continue if there are no Go source files; we still want the directory info. _, nogo := err.(*build.NoGoError) if err != nil { if nogo { err = nil } else { return nil, errors.New("hv.Walker.Build -> ImportDir: " + err.Error()) } } } w.Pdoc.IsCmd = bpkg.IsCommand() w.Pdoc.Synopsis = synopsis(bpkg.Doc) w.Pdoc.Imports = bpkg.Imports w.Pdoc.IsCgo = w.isCgo() w.Pdoc.TestImports = bpkg.TestImports // Check depth. if wr.WalkDepth <= WD_Imports { return w.Pdoc, nil } w.Fset = token.NewFileSet() // Parse the Go files files := make(map[string]*ast.File) for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) { file, err := parser.ParseFile(w.Fset, name, w.SrcFiles[name].Data(), parser.ParseComments) if err != nil { return nil, errors.New("hv.Walker.Build -> parse Go files: " + err.Error()) continue } w.Pdoc.Files = append(w.Pdoc.Files, w.SrcFiles[name]) w.Pdoc.SourceSize += int64(len(w.SrcFiles[name].Data())) files[name] = file } w.apkg, _ = ast.NewPackage(w.Fset, files, poorMansImporter, nil) // Find examples in the test files. for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) { file, err := parser.ParseFile(w.Fset, name, w.SrcFiles[name].Data(), parser.ParseComments) if err != nil { return nil, errors.New("hv.Walker.Build -> find examples: " + err.Error()) continue } w.Pdoc.TestFiles = append(w.Pdoc.TestFiles, w.SrcFiles[name]) //w.pdoc.TestSourceSize += len(w.srcs[name].data) if wr.WalkMode&WM_NoExample != 0 { continue } w.Examples = append(w.Examples, doc.Examples(file)...) } mode := doc.Mode(0) if w.Pdoc.ImportPath == "builtin" || wr.BuildAll { mode |= doc.AllDecls } pdoc := doc.New(w.apkg, w.Pdoc.ImportPath, mode) // Get doc. pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") var buf bytes.Buffer doc.ToHTML(&buf, pdoc.Doc, nil) w.Pdoc.Doc = buf.String() // Highlight first sentence. w.Pdoc.Doc = strings.Replace(w.Pdoc.Doc, "<p>", "<p><b>", 1) w.Pdoc.Doc = strings.Replace(w.Pdoc.Doc, "</p>", "</b></p>", 1) if wr.WalkMode&WM_NoExample == 0 { w.getExamples() } w.SrcLines = make(map[string][]string) w.Pdoc.Consts = w.values(pdoc.Consts) w.Pdoc.Funcs, w.Pdoc.Ifuncs = w.funcs(pdoc.Funcs) w.Pdoc.Types, w.Pdoc.Itypes = w.types(pdoc.Types) w.Pdoc.Vars = w.values(pdoc.Vars) //w.Pdoc.Notes = w.notes(pdoc.Notes) return w.Pdoc, nil }
// linkdeps links dependencies into the specified output file. func linkdeps(pkg *build.Package, output string) error { depslist := []string{"runtime"} deps := make(map[string]bool) deps["runtime"] = true deps["unsafe"] = true if len(pkg.CgoFiles) > 0 { pkg.Imports = append(pkg.Imports, "runtime/cgo") pkg.Imports = append(pkg.Imports, "syscall") } var mkdeps func(imports ...string) error mkdeps = func(imports ...string) error { for _, path := range imports { if path == "C" { if err := mkdeps("runtime/cgo", "syscall"); err != nil { return err } continue } if !deps[path] { deps[path] = true pkg, err := build.Import(path, "", 0) if err != nil { return err } if err = mkdeps(pkg.Imports...); err != nil { return err } depslist = append(depslist, path) } } return nil } err := mkdeps(pkg.Imports...) if err != nil { return err } if test { if err = mkdeps(pkg.TestImports...); err != nil { return err } if err = mkdeps(pkg.XTestImports...); err != nil { return err } } for i := 0; i < len(depslist)/2; i++ { j := len(depslist) - i - 1 depslist[i], depslist[j] = depslist[j], depslist[i] } llvmlink := filepath.Join(llvmbindir, "llvm-link") args := []string{"-o", output, output} var ldflags []string for _, path := range depslist { if path == pkg.ImportPath { continue } bcfile := filepath.Join(pkgroot, path+".bc") if buildDeps { if _, err := os.Stat(bcfile); err != nil { if err = buildPackages([]string{path}); err != nil { return err } } } args = append(args, bcfile) if pkgldflags, err := readLdflags(path); err != nil { return err } else { ldflags = append(ldflags, pkgldflags...) } } cmd := exec.Command(llvmlink, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = runCmd(cmd); err != nil { return err } if !emitllvm || triple == "pnacl" { input := output if strings.Contains(triple, "darwin") || strings.Contains(triple, "mac") { // Not doing this intermediate step will make it invoke "dsymutil" // which then asserts and kills the build. // See discussion in issue #49 for more details. input += ".o" args := []string{"-g", "-c", "-o", input, output} cmd := exec.Command(clang, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = runCmd(cmd); err != nil { return err } } args := []string{"-pthread", "-v", "-g", "-o", output, input} if triple == "pnacl" { args = append(args, "-l", "ppapi") } args = append(args, ldflags...) cmd := exec.Command(clang+"++", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = runCmd(cmd); err != nil { return err } } return nil }
func buildPackage(pkg *build.Package, output string) error { args := []string{"-c", "-triple", triple} dir, file := path.Split(pkg.ImportPath) if pkg.IsCommand() || test { if output == "" { output = file } } else { dir = filepath.Join(pkgroot, dir) err := os.MkdirAll(dir, os.FileMode(0755)) if err != nil { return err } if output == "" { output = path.Join(dir, file+".bc") } } if !pkg.IsCommand() || test { args = append(args, "-importpath", pkg.ImportPath) } _, file = path.Split(output) tempfile := path.Join(workdir, file+".bc") args = append(args, fmt.Sprintf("-g=%v", generateDebug)) args = append(args, "-o", tempfile) args = append(args, pkg.GoFiles...) if test { args = append(args, pkg.TestGoFiles...) } cmd := exec.Command("llgo", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := runCmd(cmd) if err != nil { return err } var cgoCFLAGS []string if len(pkg.CFiles) > 0 { cgoCFLAGS = strings.Fields(os.Getenv("CGO_CFLAGS")) } // Compile and link .c files in. llvmlink := filepath.Join(llvmbindir, "llvm-link") for _, cfile := range pkg.CFiles { bcfile := filepath.Join(workdir, filepath.Base(cfile+".bc")) args = []string{"-c", "-o", bcfile} if triple != "pnacl" { args = append(args, "-target", triple, "-emit-llvm") } args = append(args, cgoCFLAGS...) args = append(args, cfile) cmd := exec.Command(clang, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = runCmd(cmd) if err != nil { os.Remove(bcfile) return err } cmd = exec.Command(llvmlink, "-o", tempfile, tempfile, bcfile) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = runCmd(cmd) os.Remove(bcfile) if err != nil { return err } } // Link .ll files in. if len(pkg.SFiles) > 0 { args = []string{"-o", tempfile, tempfile} args = append(args, pkg.SFiles...) cmd := exec.Command(llvmlink, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = runCmd(cmd) if err != nil { return err } } // If it's a command, link in the dependencies. if pkg.IsCommand() { err = linkdeps(pkg, tempfile) if err != nil { return err } } else if test { if err = linktest(pkg, tempfile); err != nil { return err } } return moveFile(tempfile, output) }
// fillPackage full of info. Assumes p.Dir is set at a minimum func fillPackage(p *build.Package) error { if p.Goroot { return nil } if p.SrcRoot == "" { for _, base := range build.Default.SrcDirs() { if strings.HasPrefix(p.Dir, base) { p.SrcRoot = base } } } if p.SrcRoot == "" { return errors.New("Unable to find SrcRoot for package " + p.ImportPath) } if p.Root == "" { p.Root = filepath.Dir(p.SrcRoot) } var buildMatch = "+build " var buildFieldSplit = func(r rune) bool { return unicode.IsSpace(r) || r == ',' } debugln("Filling package:", p.ImportPath, "from", p.Dir) gofiles, err := filepath.Glob(filepath.Join(p.Dir, "*.go")) if err != nil { debugln("Error globbing", err) return err } var testImports []string var imports []string NextFile: for _, file := range gofiles { debugln(file) pf, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ImportsOnly|parser.ParseComments) if err != nil { return err } testFile := strings.HasSuffix(file, "_test.go") fname := filepath.Base(file) for _, c := range pf.Comments { ct := c.Text() if i := strings.Index(ct, buildMatch); i != -1 { for _, t := range strings.FieldsFunc(ct[i+len(buildMatch):], buildFieldSplit) { for _, tag := range ignoreTags { if t == tag { p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) continue NextFile } } if versionMatch.MatchString(t) && !isSameOrNewer(t, majorGoVersion) { debugln("Adding", fname, "to ignored list because of version tag", t) p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) continue NextFile } if versionNegativeMatch.MatchString(t) && isSameOrNewer(t[1:], majorGoVersion) { debugln("Adding", fname, "to ignored list because of version tag", t) p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) continue NextFile } } } } if testFile { p.TestGoFiles = append(p.TestGoFiles, fname) } else { p.GoFiles = append(p.GoFiles, fname) } for _, is := range pf.Imports { name, err := strconv.Unquote(is.Path.Value) if err != nil { return err // can't happen? } if testFile { testImports = append(testImports, name) } else { imports = append(imports, name) } } } imports = uniq(imports) testImports = uniq(testImports) p.Imports = imports p.TestImports = testImports return nil }