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 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 }
// 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 }
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 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) }