func resolvePackageSpec(ctx *build.Context, cwd string, src io.Reader, spec string) string { if strings.HasSuffix(spec, ".go") { d := path.Dir(spec) if !buildutil.IsAbsPath(ctx, d) { d = buildutil.JoinPath(ctx, cwd, d) } if bpkg, err := ctx.ImportDir(d, build.FindOnly); err == nil { return bpkg.ImportPath } } path := spec switch { case strings.HasPrefix(spec, "."): if bpkg, err := ctx.Import(spec, cwd, build.FindOnly); err == nil { path = bpkg.ImportPath } case strings.HasPrefix(spec, "/"): path = spec[1:] default: if p, ok := readImports(cwd, src)[spec]; ok { path = p } } return strings.TrimSuffix(path, "/") }
// imports returns a map of all import directories (recursively) used by the app. // The return value maps full directory names to original import names. func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) { pkg, err := ctxt.ImportDir(srcDir, 0) if err != nil { return nil, fmt.Errorf("unable to analyze source: %v", err) } // Resolve all non-standard-library imports result := make(map[string]string) for _, v := range pkg.Imports { if !strings.Contains(v, ".") { continue } src, err := findInGopath(v, gopath) if err != nil { return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err) } result[src] = v im, err := imports(ctxt, src, gopath) if err != nil { return nil, fmt.Errorf("unable to parse package %v: %v", src, err) } for k, v := range im { result[k] = v } } return result, nil }
func generate_target(srcdir string, pkgdir string, prefix string, ctx build.Context) string { pkg, _ := ctx.ImportDir(srcdir+pkgdir, 0) name := pkg.Name var deps []string for _, imp := range pkg.Imports { if strings.HasPrefix(imp, prefix) { imp = strings.TrimPrefix(imp, prefix) if packages[imp] == "" { packages[imp] = generate_target(srcdir, imp, prefix, ctx) } deps = append(deps, "$(LIBS_"+packages[imp]+")") } } if pkgdir != "" { fmt.Printf("SRCDIR_%s := $(SRCDIR)%s/\n", name, pkgdir) } else { fmt.Printf("SRCDIR_%s := $(SRCDIR)\n", name) } fmt.Printf("SRC_%s := $(addprefix $(SRCDIR_%s), %s)\n", name, name, strings.Join(pkg.GoFiles, " ")) fmt.Printf("DEPS_%s := %s\n", name, strings.Join(deps, " ")) if pkgdir != "" { fmt.Printf("OBJ_%s := $(LIBDIR)/%s.o\n", name, pkgdir) fmt.Printf("LIB_%s := $(LIBDIR)/%s.a\n", name, pkgdir) fmt.Printf("LIBS_%s := $(LIB_%s) $(DEPS_%s)\n", name, name, name) fmt.Printf("$(OBJ_%s) : $(SRC_%s) $(DEPS_%s)\n", name, name, name) fmt.Printf("\t@mkdir -p $(dir $@)\n") fmt.Printf("\t$(GOC) $(GOFLAGS) -c -o $@ $(SRC_%s)\n", name) } return name }
// build gets imports from source files. func (w *walker) build(srcs []*source) ([]string, error) { // Add source files to walker, I skipped references here. w.srcs = make(map[string]*source) for _, src := range srcs { w.srcs[src.name] = src } w.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, CgoEnabled: true, 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 w.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, Compiler: "gc", } bpkg, err := ctxt.ImportDir(w.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("doc.walker.build(): " + err.Error()) } } // 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.srcs[name].data, parser.ParseComments) if err != nil { //beego.Error("doc.walker.build():", err) continue } files[name] = file } w.ImportPath = strings.Replace(w.ImportPath, "\\", "/", -1) var imports []string for _, v := range bpkg.Imports { // Skip strandard library. if !utils.IsGoRepoPath(v) && (utils.GetProjectPath(v) != utils.GetProjectPath(w.ImportPath)) { imports = append(imports, v) } } return imports, err }
// build generates data from source files. func (w *routerWalker) build(srcs []*source) (*Package, error) { // Add source files to walker, I skipped references here. w.srcs = make(map[string]*source) for _, src := range srcs { w.srcs[src.name] = src } w.fset = token.NewFileSet() // Find the package and associated files. ctxt := gobuild.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, CgoEnabled: true, 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 w.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, Compiler: "gc", } bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0) // Continue if there are no Go source files; we still want the directory info. _, nogo := err.(*gobuild.NoGoError) if err != nil { if nogo { err = nil } else { return nil, errors.New("routerWalker.build -> " + err.Error()) } } // 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.srcs[name].data, parser.ParseComments) if err != nil { return nil, errors.New("routerWalker.build -> parse go files: " + err.Error()) } files[name] = file } apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil) mode := doc.Mode(0) if w.pdoc.ImportPath == "builtin" { mode |= doc.AllDecls } pdoc := doc.New(apkg, w.pdoc.ImportPath, mode) w.pdoc.Types = w.types(pdoc.Types) return w.pdoc, err }
func ImportStdPkg(context *build.Context, path string, mode build.ImportMode) (*build.Package, error) { realpath := filepath.Join(context.GOROOT, "src", "pkg", path) if _, err := os.Stat(realpath); err != nil { realpath = filepath.Join(context.GOROOT, "src", path) } pkg, err := context.ImportDir(realpath, 0) pkg.ImportPath = path return pkg, err }
// Import returns details about the package in the directory. func (dir *Directory) Import(ctx *build.Context, mode build.ImportMode) (*build.Package, error) { safeCopy := *ctx ctx = &safeCopy ctx.JoinPath = path.Join ctx.IsAbsPath = path.IsAbs ctx.SplitPathList = func(list string) []string { return strings.Split(list, ":") } ctx.IsDir = func(path string) bool { return false } ctx.HasSubdir = func(root, dir string) (rel string, ok bool) { return "", false } ctx.ReadDir = dir.readDir ctx.OpenFile = dir.openFile return ctx.ImportDir(".", mode) }
// appFiles returns a list of all Go source files in the app. func appFiles(ctxt *build.Context) ([]string, error) { pkg, err := ctxt.ImportDir(".", 0) if err != nil { return nil, err } if !pkg.IsCommand() { return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name) } var appFiles []string for _, f := range pkg.GoFiles { n := filepath.Join(".", f) appFiles = append(appFiles, n) } return appFiles, nil }
/* Scan basepath and find the import'able paths relative to it */ func FindImports(basepath string) (Imports, error) { set := map[string]bool{} // unique keys findImportFn := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.Mode().IsRegular() && isSourceFile(path) { lib := strings.TrimPrefix(filepath.Dir(path), basepath) if strings.HasPrefix(lib, "/") { lib = strings.TrimPrefix(lib, "/") } // if the lib string is _not_ in our set and import path is sane if _, found := set[lib]; !found && isImportablePath(lib) { set[lib] = true } } return nil } pkgs := Imports{} err := filepath.Walk(basepath, findImportFn) if err != nil { return pkgs, err } var ctx build.Context = build.Default if !strings.HasPrefix(basepath, build.Default.GOROOT) && !strings.HasPrefix(basepath, build.Default.GOPATH) { // rather than messing with the build.Default ctx = build.Context{ GOROOT: build.Default.GOROOT, GOPATH: basepath, Compiler: build.Default.Compiler, JoinPath: build.Default.JoinPath, } } for lib, _ := range set { if pkg, err := ctx.ImportDir(filepath.Join(basepath, lib), 0); err == nil { pkgs = append(pkgs, pkg) } } sort.Sort(byImportPath{pkgs}) return pkgs, nil }
// FindAll returns a list of all packages in all of the GOPATH trees // in the given build context. If prefix is non-empty, only packages // whose import paths begin with prefix are returned. func FindAll(prefix string, buildContext build.Context, mode FindMode) (pkgs []*build.Package, err error) { have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !buildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } // TODO(sqs): find cmd packages as well var gorootSrcPkg = filepath.Join(buildContext.GOROOT, "src/pkg") for _, src := range buildContext.SrcDirs() { if src == gorootSrcPkg && mode&IncludeStdlib == 0 { continue // skip stdlib } src = filepath.Clean(src) + string(filepath.Separator) start := filepath.Join(src, prefix) filepath.Walk(start, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() || path == src { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := filepath.ToSlash(path[len(start):]) if have[name] { return nil } have[name] = true pkg, err := buildContext.ImportDir(path, 0) if err != nil && strings.Contains(err.Error(), "no Go source files") { return nil } pkgs = append(pkgs, pkg) return nil }) } return pkgs, nil }
// checkMain verifies that there is a single "main" function. // It also returns a list of all Go source files in the app. func checkMain(ctxt *build.Context) (bool, []string, error) { pkg, err := ctxt.ImportDir(*rootDir, 0) if err != nil { return false, nil, fmt.Errorf("unable to analyze source: %v", err) } if !pkg.IsCommand() { errorf("Your app's package needs to be changed from %q to \"main\".\n", pkg.Name) } // Search for a "func main" var hasMain bool var appFiles []string for _, f := range pkg.GoFiles { n := filepath.Join(*rootDir, f) appFiles = append(appFiles, n) if hasMain, err = readFile(n); err != nil { return false, nil, fmt.Errorf("error parsing %q: %v", n, err) } } return hasMain, appFiles, nil }
// imports returns a map of all import directories (recursively) used by the app. // The return value maps full directory names to original import names. func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) { pkg, err := ctxt.ImportDir(srcDir, 0) if err != nil { return nil, err } // Resolve imports, preferring vendored packages, then packages in the GOPATH. // Any package that could not be resolved and does not contain a "." // is assumed to be part of the standard libarry and therefore ignored. // Otherwise, unresolved packages will return an error. result := make(map[string]string) for _, v := range pkg.Imports { src, verr := findVendored(srcDir, v, gopath) if verr != nil { var perr error src, perr = findInGopath(v, gopath) if perr != nil { if !strings.Contains(v, ".") { continue } return nil, fmt.Errorf("unable to find import %v: %v, %v", v, perr, verr) } } if _, ok := result[src]; ok { // Already processed continue } result[src] = v im, err := imports(ctxt, src, gopath) if err != nil { return nil, fmt.Errorf("unable to parse package %v: %v", src, err) } for k, v := range im { result[k] = v } } return result, nil }
func (b *builder) build(srcs []*source) (*Package, error) { b.pkg.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) } } for r := range references { b.pkg.References = append(b.pkg.References, r) } if len(b.srcs) == 0 { return b.pkg, nil } b.fset = token.NewFileSet() b.importPaths = make(map[string]map[string]string) // Find the package and associated files. ctxt := build.Context{ GOOS: "linux", GOARCH: "amd64", CgoEnabled: true, 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", } pkg, err := ctxt.ImportDir(b.pkg.ImportPath, 0) if _, ok := err.(*build.NoGoError); ok { return b.pkg, nil } else if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) return b.pkg, nil } // Parse the Go files b.ast = &ast.Package{Name: pkg.Name, Files: make(map[string]*ast.File)} if pkg.IsCommand() && b.srcs["doc.go"] != nil { file, err := parser.ParseFile(b.fset, "doc.go", b.srcs["doc.go"].data, parser.ParseComments) if err == nil && file.Name.Name == "documentation" { b.ast.Files["doc.go"] = file } } if len(b.ast.Files) == 0 { for _, name := range append(pkg.GoFiles, pkg.CgoFiles...) { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) continue } b.pkg.Files = append(b.pkg.Files, &File{Name: name, URL: b.srcs[name].browseURL}) b.pkg.SourceSize += len(b.srcs[name].data) b.ast.Files[name] = file } } // Find examples in the test files. for _, name := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) continue } b.pkg.TestFiles = append(b.pkg.TestFiles, &File{Name: name, URL: b.srcs[name].browseURL}) b.pkg.TestSourceSize += len(b.srcs[name].data) b.examples = append(b.examples, doc.Examples(file)...) } b.vetPackage() mode := doc.Mode(0) if b.pkg.ImportPath == "builtin" { mode |= doc.AllDecls } pdoc := doc.New(b.ast, b.pkg.ImportPath, mode) b.pkg.Name = pdoc.Name b.pkg.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") b.pkg.Synopsis = synopsis(b.pkg.Doc) b.pkg.Examples = b.getExamples("") b.pkg.IsCmd = pkg.IsCommand() b.pkg.Consts = b.values(pdoc.Consts) b.pkg.Funcs = b.funcs(pdoc.Funcs) b.pkg.Types = b.types(pdoc.Types) b.pkg.Vars = b.values(pdoc.Vars) b.pkg.Bugs = pdoc.Bugs b.pkg.Imports = pkg.Imports b.pkg.TestImports = pkg.TestImports b.pkg.XTestImports = pkg.XTestImports return b.pkg, nil }
// build generates data from source files. func (w *walker) build(srcs []*source) (*Package, error) { // Set created time. w.pdoc.Created = time.Now().UTC() // Add source files to walker, I skipped references here. w.srcs = make(map[string]*source) for _, src := range srcs { srcName := strings.ToLower(src.name) // For readme comparation. switch { case strings.HasSuffix(src.name, ".go"): w.srcs[src.name] = src case len(w.pdoc.Tag) > 0: continue // Only save latest readme. case strings.HasPrefix(srcName, "readme_zh") || strings.HasPrefix(srcName, "readme_cn"): models.SavePkgDoc(w.pdoc.ImportPath, "zh", src.data) case strings.HasPrefix(srcName, "readme"): models.SavePkgDoc(w.pdoc.ImportPath, "en", src.data) } } w.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, CgoEnabled: true, 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 w.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, Compiler: "gc", } 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 beego.Info("doc.walker.build -> No Go Source file") } else { return w.pdoc, errors.New("doc.walker.build -> " + err.Error()) } } // 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.srcs[name].data, parser.ParseComments) if err != nil { beego.Error("doc.walker.build -> parse go files:", err) continue } w.pdoc.Files = append(w.pdoc.Files, name) //w.pdoc.SourceSize += len(w.srcs[name].data) files[name] = file } apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil) // Find examples in the test files. for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) { file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) if err != nil { beego.Error("doc.walker.build -> find examples:", err) continue } //w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL}) //w.pdoc.TestSourceSize += len(w.srcs[name].data) w.examples = append(w.examples, doc.Examples(file)...) } //w.vetPackage(apkg) mode := doc.Mode(0) if w.pdoc.ImportPath == "builtin" { mode |= doc.AllDecls } pdoc := doc.New(apkg, w.pdoc.ImportPath, mode) w.pdoc.Synopsis = utils.Synopsis(pdoc.Doc) pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") var buf bytes.Buffer doc.ToHTML(&buf, pdoc.Doc, nil) w.pdoc.Doc = w.pdoc.Doc + "<br />" + buf.String() w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "<p>", "<p><b>", 1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "</p>", "</b></p>", 1) w.pdoc.Doc = base32.StdEncoding.EncodeToString([]byte(w.pdoc.Doc)) w.pdoc.Examples = w.getExamples("") w.pdoc.IsCmd = bpkg.IsCommand() w.srcLines = make(map[string][]string) w.pdoc.Consts = w.values(pdoc.Consts) w.pdoc.Funcs = w.funcs(pdoc.Funcs) w.pdoc.Types = w.types(pdoc.Types) w.pdoc.Vars = w.values(pdoc.Vars) //w.pdoc.Notes = w.notes(pdoc.Notes) w.pdoc.Imports = bpkg.Imports w.pdoc.TestImports = bpkg.TestImports //w.pdoc.XTestImports = bpkg.XTestImports beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine()) return w.pdoc, err }
// build generates data from source files. func (w *walker) build(srcs []*source) (*Package, error) { // Set created time. w.pdoc.Created = time.Now().UTC() // Add source files to walker, I skipped references here. w.srcs = make(map[string]*source) for _, src := range srcs { if strings.HasSuffix(src.name, ".go") { w.srcs[src.name] = src } else if src.name == "doc_zh.md" { // Multi-language documentation. w.mldocs[src.name] = src } } // Check number of source files. if len(w.srcs) == 0 { // No source file. return w.pdoc, nil } w.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, CgoEnabled: true, 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 w.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, Compiler: "gc", } bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0) // Continue if there are no Go source files; we still want the directory info. if _, nogo := err.(*build.NoGoError); err != nil && !nogo { return w.pdoc, errors.New("doc.walker.build(): " + err.Error()) } // 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.srcs[name].data, parser.ParseComments) if err != nil { beego.Error("doc.walker.build():", err) continue } w.pdoc.Files = append(w.pdoc.Files, name) //w.pdoc.SourceSize += len(w.srcs[name].data) files[name] = file } apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil) // Find examples in the test files. /*for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) { file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) if err != nil { w.pdoc.Errors = append(w.pdoc.Errors, err.Error()) continue } w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL}) w.pdoc.TestSourceSize += len(w.srcs[name].data) w.examples = append(w.examples, doc.Examples(file)...) }*/ //w.vetPackage(apkg) mode := doc.Mode(0) if w.pdoc.ImportPath == "builtin" { mode |= doc.AllDecls } pdoc := doc.New(apkg, w.pdoc.ImportPath, mode) w.pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") w.pdoc.Synopsis = utils.Synopsis(w.pdoc.Doc) //w.pdoc.Examples = w.getExamples("") //w.pdoc.IsCmd = bpkg.IsCommand() w.pdoc.GOOS = ctxt.GOOS w.pdoc.GOARCH = ctxt.GOARCH w.srcLines = make(map[string][]string) w.pdoc.Consts = w.values(pdoc.Consts) w.pdoc.Funcs = w.funcs(pdoc.Funcs) w.pdoc.Types = w.types(pdoc.Types) w.pdoc.Vars = w.values(pdoc.Vars) //w.pdoc.Notes = w.notes(pdoc.Notes) w.pdoc.Imports = bpkg.Imports w.pdoc.TestImports = bpkg.TestImports //w.pdoc.XTestImports = bpkg.XTestImports beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine()) return w.pdoc, err }
func buildDoc(importPath, projectRoot, projectName, projectURL, etag string, lineFmt string, srcs []*source) (*Package, error) { b := &builder{ lineFmt: lineFmt, fset: token.NewFileSet(), importPaths: make(map[string]map[string]string), srcs: make(map[string]*source), pkg: &Package{ ImportPath: importPath, ProjectName: projectName, ProjectRoot: projectRoot, ProjectURL: projectURL, Etag: etag, Updated: time.Now(), }, } if len(srcs) == 0 { return b.pkg, nil } for _, src := range srcs { b.srcs[src.name] = src } // Find the package and associated files. ctxt := build.Context{ GOOS: "linux", GOARCH: "amd64", CgoEnabled: true, 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", } pkg, err := ctxt.ImportDir(b.pkg.ImportPath, 0) if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) return b.pkg, nil } // Parse the Go files b.ast = &ast.Package{Name: pkg.Name, Files: make(map[string]*ast.File)} if pkg.IsCommand() && b.srcs["doc.go"] != nil { file, err := parser.ParseFile(b.fset, "doc.go", b.srcs["doc.go"].data, parser.ParseComments) if err == nil && file.Name.Name == "documentation" { b.ast.Files["doc.go"] = file } } if len(b.ast.Files) == 0 { for _, name := range append(pkg.GoFiles, pkg.CgoFiles...) { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) continue } b.pkg.Files = append(b.pkg.Files, &File{Name: name, URL: b.srcs[name].browseURL}) b.pkg.SourceSize += len(b.srcs[name].data) b.ast.Files[name] = file } } // Find examples in the test files. for _, name := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) { file, err := parser.ParseFile(b.fset, name, b.srcs[name].data, parser.ParseComments) if err != nil { b.pkg.Errors = append(b.pkg.Errors, err.Error()) continue } b.pkg.TestFiles = append(b.pkg.TestFiles, &File{Name: name, URL: b.srcs[name].browseURL}) b.pkg.TestSourceSize += len(b.srcs[name].data) b.examples = append(b.examples, doc.Examples(file)...) } b.vetPackage() pdoc := doc.New(b.ast, b.pkg.ImportPath, 0) b.pkg.Name = pdoc.Name b.pkg.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") b.pkg.Synopsis = synopsis(b.pkg.Doc) b.pkg.Examples = b.getExamples("") b.pkg.IsCmd = pkg.IsCommand() b.pkg.Consts = b.values(pdoc.Consts) b.pkg.Funcs = b.funcs(pdoc.Funcs) b.pkg.Types = b.types(pdoc.Types) b.pkg.Vars = b.values(pdoc.Vars) b.pkg.Imports = pkg.Imports b.pkg.TestImports = pkg.TestImports return b.pkg, nil }
// imports returns a map of all import directories used by the app. // The return value maps full directory names to original import names. func imports(ctxt *build.Context, srcDir string) (map[string]string, error) { result := make(map[string]string) type importFrom struct { path, fromDir string } var imports []importFrom visited := make(map[importFrom]bool) pkg, err := ctxt.ImportDir(srcDir, 0) if err != nil { return nil, err } for _, v := range pkg.Imports { imports = append(imports, importFrom{ path: v, fromDir: srcDir, }) } // Resolve all non-standard-library imports for len(imports) != 0 { i := imports[0] imports = imports[1:] // shift if i.path == "C" { // ignore cgo continue } if _, ok := visited[i]; ok { // already scanned continue } visited[i] = true abs, err := filepath.Abs(i.fromDir) if err != nil { return nil, fmt.Errorf("unable to get absolute directory of %q: %v", i.fromDir, err) } pkg, err := ctxt.Import(i.path, abs, 0) if err != nil { return nil, fmt.Errorf("unable to find import %s, imported from %q: %v", i.path, i.fromDir, err) } // TODO(cbro): handle packages that are vendored by multiple imports correctly. if pkg.Goroot { // ignore standard library imports continue } vlogf("Located %q (imported from %q) -> %q", i.path, i.fromDir, pkg.Dir) result[pkg.Dir] = i.path for _, v := range pkg.Imports { imports = append(imports, importFrom{ path: v, fromDir: pkg.Dir, }) } } return result, nil }
// build generates data from source files. func (w *walker) build(srcs []*source) (*Package, error) { // Set created time. w.pdoc.Created = time.Now().UTC() // Add source files to walker, I skipped references here. w.srcs = make(map[string]*source) for _, src := range srcs { if strings.HasSuffix(src.name, ".go") { w.srcs[src.name] = src } else if strings.HasPrefix(strings.ToLower(src.name), "readme") { // Readme. w.pdoc.Doc = string(src.data) if len(w.pdoc.Doc) > 0 { if w.pdoc.Doc[0] == '\n' { w.pdoc.Doc = w.pdoc.Doc[1:] } // Remove title and `==========`. w.pdoc.Doc = w.pdoc.Doc[strings.Index(w.pdoc.Doc, "\n")+1:] if len(w.pdoc.Doc) == 0 { continue } if w.pdoc.Doc[0] == '=' { w.pdoc.Doc = w.pdoc.Doc[strings.Index(w.pdoc.Doc, "\n")+1:] } // Find all picture path of build system. for _, m := range buildPicPattern.FindAllString(w.pdoc.Doc, -1) { start := strings.Index(m, "http") end := strings.Index(m, ")") if (start > -1) && (end > -1) && (start < end) { picPath := m[start:end] w.pdoc.Doc = strings.Replace(w.pdoc.Doc, m, "![]("+picPath+")", 1) } } w.pdoc.Doc = string(blackfriday.MarkdownCommon([]byte(w.pdoc.Doc))) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h3>", "h5>", -1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h2>", "h4>", -1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "h1>", "h3>", -1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "<center>", "", -1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "</center>", "", -1) w.pdoc.Doc = "<div style='display:block; padding: 3px; border:1px solid #4F4F4F;'>" + w.pdoc.Doc + "</div>" } } } w.fset = token.NewFileSet() // Find the package and associated files. ctxt := build.Context{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, CgoEnabled: true, 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 w.readDir(dir) }, OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, Compiler: "gc", } 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 beego.Info("doc.walker.build(): No Go Source file.") } else { return w.pdoc, errors.New("doc.walker.build(): " + err.Error()) } } // 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.srcs[name].data, parser.ParseComments) if err != nil { beego.Error("doc.walker.build().[parse go files]:", err) continue } w.pdoc.Files = append(w.pdoc.Files, name) //w.pdoc.SourceSize += len(w.srcs[name].data) files[name] = file } apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil) // Find examples in the test files. // for _, name := range append(bpkg.TestGoFiles, bpkg.XTestGoFiles...) { // file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) // if err != nil { // beego.Error("doc.walker.build().[find examples]:", err) // continue // } // //w.pdoc.TestFiles = append(w.pdoc.TestFiles, &File{Name: name, URL: w.srcs[name].browseURL}) // //w.pdoc.TestSourceSize += len(w.srcs[name].data) // w.examples = append(w.examples, doc.Examples(file)...) // } //w.vetPackage(apkg) mode := doc.Mode(0) if w.pdoc.ImportPath == "builtin" { mode |= doc.AllDecls } pdoc := doc.New(apkg, w.pdoc.ImportPath, mode) w.pdoc.Synopsis = utils.Synopsis(pdoc.Doc) pdoc.Doc = strings.TrimRight(pdoc.Doc, " \t\n\r") var buf bytes.Buffer doc.ToHTML(&buf, pdoc.Doc, nil) w.pdoc.Doc = w.pdoc.Doc + "<br />" + buf.String() w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "<p>", "<p><b>", 1) w.pdoc.Doc = strings.Replace(w.pdoc.Doc, "</p>", "</b></p>", 1) w.pdoc.Doc = base32.StdEncoding.EncodeToString([]byte(w.pdoc.Doc)) //w.pdoc.Examples = w.getExamples("") //w.pdoc.IsCmd = bpkg.IsCommand() w.pdoc.GOOS = ctxt.GOOS w.pdoc.GOARCH = ctxt.GOARCH w.srcLines = make(map[string][]string) w.pdoc.Consts = w.values(pdoc.Consts) w.pdoc.Funcs = w.funcs(pdoc.Funcs) w.pdoc.Types = w.types(pdoc.Types) w.pdoc.Vars = w.values(pdoc.Vars) //w.pdoc.Notes = w.notes(pdoc.Notes) w.pdoc.Imports = bpkg.Imports w.pdoc.TestImports = bpkg.TestImports //w.pdoc.XTestImports = bpkg.XTestImports beego.Info("doc.walker.build(", pdoc.ImportPath, "), Goroutine #", runtime.NumGoroutine()) return w.pdoc, err }
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 }
// 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 }