]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/godoc: use go/build to determine package and example files
authorRobert Griesemer <gri@golang.org>
Tue, 19 Feb 2013 19:19:58 +0000 (11:19 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 19 Feb 2013 19:19:58 +0000 (11:19 -0800)
Also:
- faster code for example extraction
- simplify handling of command documentation:
  all "main" packages are treated as commands
- various minor cleanups along the way

For commands written in Go, any doc.go file containing
documentation must now be part of package main (rather
then package documentation), otherwise the documentation
won't show up in godoc (it will still build, though).

For commands written in C, documentation may still be
in doc.go files defining package documentation, but the
recommended way is to explicitly ignore those files with
a +build ignore constraint to define package main.

Fixes #4806.

R=adg, rsc, dave, bradfitz
CC=golang-dev
https://golang.org/cl/7333046

36 files changed:
lib/godoc/package.html
lib/godoc/package.txt
misc/dashboard/builder/doc.go
misc/goplay/doc.go
src/cmd/5a/doc.go
src/cmd/5c/doc.go
src/cmd/5g/doc.go
src/cmd/5l/doc.go
src/cmd/6a/doc.go
src/cmd/6c/doc.go
src/cmd/6g/doc.go
src/cmd/6l/doc.go
src/cmd/8a/doc.go
src/cmd/8c/doc.go
src/cmd/8g/doc.go
src/cmd/8l/doc.go
src/cmd/cc/doc.go
src/cmd/cgo/doc.go
src/cmd/cov/doc.go
src/cmd/fix/doc.go
src/cmd/gc/doc.go
src/cmd/go/doc.go
src/cmd/go/main.go
src/cmd/godoc/dirtrees.go
src/cmd/godoc/doc.go
src/cmd/godoc/godoc.go
src/cmd/godoc/parser.go
src/cmd/gofmt/doc.go
src/cmd/ld/doc.go
src/cmd/nm/doc.go
src/cmd/pack/doc.go
src/cmd/prof/doc.go
src/cmd/vet/doc.go
src/cmd/yacc/doc.go
src/pkg/exp/ebnflint/doc.go
src/pkg/exp/gotype/doc.go

index cff91fa22645cd66646f82ddf3c58fb03757371f..85c737ec3a4cb76e8241c37187acc589f83da7d1 100644 (file)
@@ -10,7 +10,7 @@
        correspond to Go identifiers).
 -->
 {{with .PDoc}}
-       {{if $.IsPkg}}
+       {{if not $.IsMain}}
                <div id="short-nav">
                        <dl>
                        <dd><code>import "{{html .ImportPath}}"</code></dd>
index 12964b0010d5ef95d2e091b963d6130543f543b7..16678d5f01a0a4735fa9c14e8941f52499a1dbbc 100644 (file)
@@ -2,7 +2,7 @@
 
 ---------------------------------------
 
-*/}}{{with .PDoc}}{{if $.IsPkg}}PACKAGE
+*/}}{{with .PDoc}}{{if not $.IsMain}}PACKAGE
 
 package {{.Name}}
     import "{{.ImportPath}}"
index 707f8e68fd3e5667250ae0bd063a7e59e754d5da..51928617044d0c68facf7732534a62653f187a3d 100644 (file)
@@ -55,4 +55,4 @@ If the Google Code credentials are not provided the archival step
 will be skipped.
 
 */
-package documentation
+package main
index e4e88629123f326df4d9ae273cbf03a424256213..61e74a000de54b0d2e1e006d04e272cac098c829 100644 (file)
@@ -20,4 +20,4 @@
 // security mechanisms. Do not deploy it in untrusted environments.
 // By default, goplay listens only on localhost. This can be overridden with
 // the -http parameter. Do so at your own risk.
-package documentation
+package main
index 62d6ee9cd4e96ffc971365a90a437380b774eb2d..29725db04e06d2d3680ecbff1c394f23c0d28d34 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 5a is a version of the Plan 9 assembler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the ARM, referred to by these tools as arm.
 
 */
-package documentation
+package main
index 0fc027829422aaa2233345a8ee340b0f7b387829..7291d45f4df0ec2d544455b608c315e87b3ebd1c 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 5c is a version of the Plan 9 C compiler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the ARM, referred to by these tools as arm.
 
 */
-package documentation
+package main
index 5a4a772fb44b0126fc7995103df771f6aa46340c..aebdcab712d7ff50639bf49951cf3b921d9e1e81 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 5g is the version of the gc compiler for the ARM.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is arm.
 It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go.
 
 */
-package documentation
+package main
index 969f502a7b4e614ca7c5bd3450d565e39f1bde54..a054a228b8467ae7064523d289897b2df51434aa 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 5l is the linker for the ARM.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is arm.
 The flags are documented in ../ld/doc.go.
 
 */
-package documentation
+package main
index f8370a05f3e239677b2c3c04d0bbb97b007a3c51..a5f3f87f09a1c7bfeaa69fb1b3133764701351a4 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 6a is a version of the Plan 9 assembler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the x86-64, referred to by these tools as amd64.
 
 */
-package documentation
+package main
index 7f6fb77f16c836a894356e02fff2b9ff387ab1bf..e0a22e78ba0bf3177f37f3cbe07aa7b0b2487189 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 6c is a version of the Plan 9 C compiler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the x86-64, referred to by these tools as amd64.
 
 */
-package documentation
+package main
index 64f1d2ba903543cb766e1e79a27f517828193b6a..07b2818da4605620ea66e2328026980dbfcbb0a0 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 6g is the version of the gc compiler for the x86-64.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64.
 It reads .go files and outputs .6 files. The flags are documented in ../gc/doc.go.
 
 */
-package documentation
+package main
index 4d94b209bc0a13fd3b7ca19451ded201e0d23e05..6287dd9bec6200cc14060a2ca7fc5294b243fcff 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 6l is the linker for the x86-64.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64.
 The flags are documented in ../ld/doc.go.
 
 */
-package documentation
+package main
index 59f286e1999a1a268bccbec8176753bfff7dd51e..737c56f1337e711bd947fec8716d8bded6f98f5e 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 8a is a version of the Plan 9 assembler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the x86, referred to by these tools for historical reasons as 386.
 
 */
-package documentation
+package main
index 5d94c4b0d95da84d01c3c3e34d2f75e811ec05d7..0d07db14da7ecbc111b9824c0493663283b616e1 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 8c is a version of the Plan 9 C compiler.  The original is documented at
@@ -11,4 +13,4 @@
 Its target architecture is the x86, referred to by these tools for historical reasons as 386.
 
 */
-package documentation
+package main
index 6d678eac8bf4dfe01119875fd0b4e79544b09086..9e46dcad8fd5d4b0bfccce0c9406bd301f9388dc 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 8g is the version of the gc compiler for the x86.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is 386.
 It reads .go files and outputs .8 files. The flags are documented in ../gc/doc.go.
 
 */
-package documentation
+package main
index 12301d4f2ebc0cde1f8d11a5749c0d887afe73c3..ff06bc3761386c96a348a8339d41d7ab243f1e10 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 8l is the linker for the 32-bit x86.
@@ -10,4 +12,4 @@ The $GOARCH for these tools is 386.
 The flags are documented in ../ld/doc.go.
 
 */
-package documentation
+package main
index 51aa8b19238fee8edd2eeba9da4453ae8d4a6a16..10901b4413ef0a787b039f0bcd0f2853f82d7813 100644 (file)
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 This directory contains the portable section of the Plan 9 C compilers.
 See ../6c, ../8c, and ../5c for more information.
 
 */
-package documentation
+package main
index 261883210118910b9f156fe730561d9dee2c5023..4504b25646106c4d8acf7ffd0790d25a19d7ec2c 100644 (file)
@@ -133,4 +133,4 @@ Cgo does not yet work with gccgo.
 See "C? Go? Cgo!" for an introduction to using cgo:
 http://golang.org/doc/articles/c_go_cgo.html
 */
-package documentation
+package main
index a5fc0036d2686abfcee178fb031eb56074f3b809..ab5d1220ad5976f86824ea2de53139ce08a1b82e 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Cov is a rudimentary code coverage tool.
@@ -31,4 +33,4 @@ The options are:
 The program is the same for all architectures: 386, amd64, and arm.
 
 */
-package documentation
+package main
index a92e0fc06f226eef55d2137178c3aa6e1396db94..5de3e08c599072e2950265b08b00f4fad6280f34 100644 (file)
@@ -33,4 +33,4 @@ Fix does not make backup copies of the files that it edits.
 Instead, use a version control system's ``diff'' functionality to inspect
 the changes that fix makes before committing them.
 */
-package documentation
+package main
index c2eff88f6e11176d31173755a6872baaefeca259..791967708c81a81a6aaa3185d2874be2b98a7dbb 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Gc is the generic label for the family of Go compilers
@@ -86,4 +88,4 @@ in Go) does not allow any of the pointers passed as arguments to escape into the
 heap or into the values returned from the function. This information can be used as
 during the compiler's escape analysis of Go code calling the function.
 */
-package documentation
+package main
index 101ecf1c2d6fc2bb3c936724902fd1e30a1a22a2..0297b76022e1a60148c7514e539518e3ec259303 100644 (file)
@@ -812,6 +812,6 @@ See the documentation of the testing package for more information.
 
 
 */
-package documentation
+package main
 
 // NOTE: cmdDoc is in fmt.go.
index 9abe5913b09baac9857b1541f756e43cc38fb0e8..a7841d2655133a582f1e7a93e806600f8a63b5ec 100644 (file)
@@ -200,7 +200,7 @@ var documentationTemplate = `// Copyright 2011 The Go Authors.  All rights reser
 
 
 {{end}}*/
-package documentation
+package main
 
 // NOTE: cmdDoc is in fmt.go.
 `
index 08dbfc2e8b6b7568b43f1f688e93b5c23d27678e..fda7adce523b3c8eb5dd20c0ad83695a4820becc 100644 (file)
@@ -74,7 +74,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
        // determine number of subdirectories and if there are package files
        ndirs := 0
        hasPkgFiles := false
-       var synopses [4]string // prioritized package documentation (0 == highest priority)
+       var synopses [3]string // prioritized package documentation (0 == highest priority)
        for _, d := range list {
                switch {
                case isPkgDir(d):
@@ -95,12 +95,10 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
                                                switch file.Name.Name {
                                                case name:
                                                        i = 0 // normal case: directory name matches package name
-                                               case fakePkgName:
-                                                       i = 1 // synopses for commands
                                                case "main":
-                                                       i = 2 // directory contains a main package
+                                                       i = 1 // directory contains a main package
                                                default:
-                                                       i = 3 // none of the above
+                                                       i = 2 // none of the above
                                                }
                                                if 0 <= i && i < len(synopses) && synopses[i] == "" {
                                                        synopses[i] = doc.Synopsis(file.Doc.Text())
index 956ec0ba425dcadce93ab9787b9753e70e96f86b..ddb6d2687256137935dedee97ce24e4f552aa9fa 100644 (file)
@@ -127,4 +127,4 @@ See "Godoc: documenting Go code" for how to write good comments for godoc:
 http://golang.org/doc/articles/godoc_documenting_go_code.html
 
 */
-package documentation
+package main
index 887480911aa52115b3864f5d16a196e9501fd80b..5a29033b4916dbfad0223315684a9fb90c15a141 100644 (file)
@@ -86,8 +86,8 @@ var (
 
 func initHandlers() {
        fileServer = http.FileServer(&httpFS{fs})
-       cmdHandler = docServer{"/cmd/", "/src/cmd", false}
-       pkgHandler = docServer{"/pkg/", "/src/pkg", true}
+       cmdHandler = docServer{"/cmd/", "/src/cmd"}
+       pkgHandler = docServer{"/pkg/", "/src/pkg"}
 }
 
 func registerPublicHandlers(mux *http.ServeMux) {
@@ -794,10 +794,6 @@ func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
 // ----------------------------------------------------------------------------
 // Packages
 
-// Fake package file and name for commands. Contains the command documentation.
-const fakePkgFile = "doc.go"
-const fakePkgName = "documentation"
-
 // Fake relative package path for built-ins. Documentation for all globals
 // (not just exported ones) will be shown for packages in this directory.
 const builtinPkgPath = "builtin"
@@ -854,16 +850,20 @@ func remoteSearchURL(query string, html bool) string {
 }
 
 type PageInfo struct {
-       Dirname  string         // directory containing the package
-       FSet     *token.FileSet // corresponding file set
-       PAst     *ast.File      // nil if no single AST with package exports
-       PDoc     *doc.Package   // nil if no single package documentation
+       Dirname string // directory containing the package
+       Err     error  // error or nil
+
+       // package info
+       FSet     *token.FileSet // nil if no package documentation
+       PDoc     *doc.Package   // nil if no package documentation
        Examples []*doc.Example // nil if no example code
-       Dirs     *DirList       // nil if no directory information
-       DirTime  time.Time      // directory time stamp
-       DirFlat  bool           // if set, show directory in a flat (non-indented) manner
-       IsPkg    bool           // false if this is not documenting a real package
-       Err      error          // I/O error or nil
+       PAst     *ast.File      // nil if no AST with package exports
+       IsMain   bool           // true for package main
+
+       // directory info
+       Dirs    *DirList  // nil if no directory information
+       DirTime time.Time // directory time stamp
+       DirFlat bool      // if set, show directory in a flat (non-indented) manner
 }
 
 func (info *PageInfo) IsEmpty() bool {
@@ -873,7 +873,6 @@ func (info *PageInfo) IsEmpty() bool {
 type docServer struct {
        pattern string // url pattern; e.g. "/pkg/"
        fsRoot  string // file system root to which the pattern is mapped
-       isPkg   bool   // true if this handler serves real package documentation (as opposed to command documentation)
 }
 
 // fsReadDir implements ReadDir for the go/build package.
@@ -890,15 +889,6 @@ func fsOpenFile(name string) (r io.ReadCloser, err error) {
        return ioutil.NopCloser(bytes.NewReader(data)), nil
 }
 
-func inList(name string, list []string) bool {
-       for _, l := range list {
-               if name == l {
-                       return true
-               }
-       }
-       return false
-}
-
 // packageExports is a local implementation of ast.PackageExports
 // which correctly updates each package file's comment list.
 // (The ast.PackageExports signature is frozen, hence the local
@@ -912,9 +902,9 @@ func packageExports(fset *token.FileSet, pkg *ast.Package) {
        }
 }
 
-// declNames returns the names declared by decl.
-// Method names are returned in the form Receiver_Method.
-func declNames(decl ast.Decl) (names []string) {
+// addNames adds the names declared by decl to the names set.
+// Method names are added in the form ReceiverTypeName_Method.
+func addNames(names map[string]bool, decl ast.Decl) {
        switch d := decl.(type) {
        case *ast.FuncDecl:
                name := d.Name.Name
@@ -928,64 +918,52 @@ func declNames(decl ast.Decl) (names []string) {
                        }
                        name = typeName + "_" + name
                }
-               names = []string{name}
+               names[name] = true
        case *ast.GenDecl:
                for _, spec := range d.Specs {
                        switch s := spec.(type) {
                        case *ast.TypeSpec:
-                               names = append(names, s.Name.Name)
+                               names[s.Name.Name] = true
                        case *ast.ValueSpec:
                                for _, id := range s.Names {
-                                       names = append(names, id.Name)
+                                       names[id.Name] = true
                                }
                        }
                }
        }
-       return
 }
 
-// globalNames finds all top-level declarations in pkgs and returns a map
-// with the identifier names as keys.
-func globalNames(pkgs map[string]*ast.Package) map[string]bool {
+// globalNames returns a set of the names declared by all package-level
+// declarations. Method names are returned in the form Receiver_Method.
+func globalNames(pkg *ast.Package) map[string]bool {
        names := make(map[string]bool)
-       for _, pkg := range pkgs {
-               for _, file := range pkg.Files {
-                       for _, decl := range file.Decls {
-                               for _, name := range declNames(decl) {
-                                       names[name] = true
-                               }
-                       }
+       for _, file := range pkg.Files {
+               for _, decl := range file.Decls {
+                       addNames(names, decl)
                }
        }
        return names
 }
 
-// parseExamples gets examples for packages in pkgs from *_test.go files in dir.
-func parseExamples(fset *token.FileSet, pkgs map[string]*ast.Package, dir string) ([]*doc.Example, error) {
-       var examples []*doc.Example
-       filter := func(d os.FileInfo) bool {
-               return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
-       }
-       testpkgs, err := parseDir(fset, dir, filter)
-       if err != nil {
-               return nil, err
+// collectExamples collects examples for pkg from testfiles.
+func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example {
+       var files []*ast.File
+       for _, f := range testfiles {
+               files = append(files, f)
        }
-       globals := globalNames(pkgs)
-       for _, testpkg := range testpkgs {
-               var files []*ast.File
-               for _, f := range testpkg.Files {
-                       files = append(files, f)
-               }
-               for _, e := range doc.Examples(files...) {
-                       name := stripExampleSuffix(e.Name)
-                       if name == "" || globals[name] {
-                               examples = append(examples, e)
-                       } else {
-                               log.Printf("skipping example Example%s: refers to unknown function or type", e.Name)
-                       }
+
+       var examples []*doc.Example
+       globals := globalNames(pkg)
+       for _, e := range doc.Examples(files...) {
+               name := stripExampleSuffix(e.Name)
+               if name == "" || globals[name] {
+                       examples = append(examples, e)
+               } else {
+                       log.Printf("skipping example Example%s: refers to unknown function or type", e.Name)
                }
        }
-       return examples, nil
+
+       return examples
 }
 
 // getPageInfo returns the PageInfo for a package directory abspath. If the
@@ -993,83 +971,56 @@ func parseExamples(fset *token.FileSet, pkgs map[string]*ast.Package, dir string
 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
 // is extracted from the AST. If there is no corresponding package in the
 // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub-
-// directories, PageInfo.Dirs is nil. If a directory read error occurred,
-// PageInfo.Err is set to the respective error but the error is not logged.
+// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is
+// set to the respective error but the error is not logged.
 //
-func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) PageInfo {
-       var pkgFiles []string
+func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (info PageInfo) {
+       info.Dirname = abspath
 
-       // Restrict to the package files
-       // that would be used when building the package on this
-       // system.  This makes sure that if there are separate
-       // implementations for, say, Windows vs Unix, we don't
+       // Restrict to the package files that would be used when building
+       // the package on this system.  This makes sure that if there are
+       // separate implementations for, say, Windows vs Unix, we don't
        // jumble them all together.
        // Note: Uses current binary's GOOS/GOARCH.
-       // To use different pair, such as if we allowed the user
-       // to choose, set ctxt.GOOS and ctxt.GOARCH before
-       // calling ctxt.ScanDir.
+       // To use different pair, such as if we allowed the user to choose,
+       // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir.
        ctxt := build.Default
        ctxt.IsAbsPath = pathpkg.IsAbs
        ctxt.ReadDir = fsReadDir
        ctxt.OpenFile = fsOpenFile
-       if dir, err := ctxt.ImportDir(abspath, 0); err == nil {
-               pkgFiles = append(dir.GoFiles, dir.CgoFiles...)
-       }
-
-       // filter function to select the desired .go files
-       filter := func(d os.FileInfo) bool {
-               // Only Go files.
-               if !isPkgFile(d) {
-                       return false
-               }
-               // If we are looking at cmd documentation, only accept
-               // the special fakePkgFile containing the documentation.
-               if !h.isPkg {
-                       return d.Name() == fakePkgFile
-               }
-               // Also restrict file list to pkgFiles.
-               return pkgFiles == nil || inList(d.Name(), pkgFiles)
-       }
-
-       // get package ASTs
-       fset := token.NewFileSet()
-       pkgs, err := parseDir(fset, abspath, filter)
-       if err != nil {
-               return PageInfo{Dirname: abspath, Err: err}
+       pkginfo, err := ctxt.ImportDir(abspath, 0)
+       // continue if there are no Go source files; we still want the directory info
+       if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
+               info.Err = err
+               return
        }
 
-       // select package
-       var pkg *ast.Package // selected package
-       if len(pkgs) == 1 {
-               // Exactly one package - select it.
-               for _, p := range pkgs {
-                       pkg = p
-               }
-
-       } else if len(pkgs) > 1 {
-               // More than one package - report an error.
-               var buf bytes.Buffer
-               for _, p := range pkgs {
-                       if buf.Len() > 0 {
-                               fmt.Fprintf(&buf, ", ")
-                       }
-                       fmt.Fprintf(&buf, p.Name)
-               }
-               return PageInfo{
-                       Dirname: abspath,
-                       Err:     fmt.Errorf("%s contains more than one package: %s", abspath, buf.Bytes()),
+       // collect package files
+       pkgname := pkginfo.Name
+       pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
+       if len(pkgfiles) == 0 {
+               // Commands written in C have no .go files in the build.
+               // Instead, documentation may be found in an ignored file.
+               // The file may be ignored via an explicit +build ignore
+               // constraint (recommended), or by defining the package
+               // documentation (historic).
+               pkgname = "main" // assume package main since pkginfo.Name == ""
+               pkgfiles = pkginfo.IgnoredGoFiles
+       }
+
+       // get package information, if any
+       if len(pkgfiles) > 0 {
+               // build package AST
+               fset := token.NewFileSet()
+               files, err := parseFiles(fset, abspath, pkgfiles)
+               if err != nil {
+                       info.Err = err
+                       return
                }
-       }
+               pkg := &ast.Package{Name: pkgname, Files: files}
 
-       examples, err := parseExamples(fset, pkgs, abspath)
-       if err != nil {
-               log.Println("parsing examples:", err)
-       }
-
-       // compute package documentation
-       var past *ast.File
-       var pdoc *doc.Package
-       if pkg != nil {
+               // extract package documentation
+               info.FSet = fset
                if mode&showSource == 0 {
                        // show extracted documentation
                        var m doc.Mode
@@ -1079,7 +1030,15 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page
                        if mode&allMethods != 0 {
                                m |= doc.AllMethods
                        }
-                       pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
+                       info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
+
+                       // collect examples
+                       testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
+                       files, err = parseFiles(fset, abspath, testfiles)
+                       if err != nil {
+                               log.Println("parsing examples:", err)
+                       }
+                       info.Examples = collectExamples(pkg, files)
                } else {
                        // show source code
                        // TODO(gri) Consider eliminating export filtering in this mode,
@@ -1087,11 +1046,12 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page
                        if mode&noFiltering == 0 {
                                packageExports(fset, pkg)
                        }
-                       past = ast.MergePackageFiles(pkg, 0)
+                       info.PAst = ast.MergePackageFiles(pkg, 0)
                }
+               info.IsMain = pkgname == "main"
        }
 
-       // get directory information
+       // get directory information, if any
        var dir *Directory
        var timestamp time.Time
        if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil {
@@ -1109,19 +1069,11 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page
                dir = newDirectory(abspath, 1)
                timestamp = time.Now()
        }
+       info.Dirs = dir.listing(true)
+       info.DirTime = timestamp
+       info.DirFlat = mode&flatDir != 0
 
-       return PageInfo{
-               Dirname:  abspath,
-               FSet:     fset,
-               PAst:     past,
-               PDoc:     pdoc,
-               Examples: examples,
-               Dirs:     dir.listing(true),
-               DirTime:  timestamp,
-               DirFlat:  mode&flatDir != 0,
-               IsPkg:    h.isPkg,
-               Err:      nil,
-       }
+       return
 }
 
 func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -1151,26 +1103,25 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        switch {
        case info.PAst != nil:
                tabtitle = info.PAst.Name.Name
-               title = "Package " + tabtitle
        case info.PDoc != nil:
-               if info.PDoc.Name == fakePkgName {
-                       // assume that the directory name is the command name
-                       _, tabtitle = pathpkg.Split(relpath)
-               } else {
-                       tabtitle = info.PDoc.Name
-               }
-               if info.IsPkg {
-                       title = "Package " + tabtitle
-               } else {
-                       title = "Command " + tabtitle
-               }
+               tabtitle = info.PDoc.Name
        default:
                tabtitle = info.Dirname
-               title = "Directory " + tabtitle
+               title = "Directory "
                if *showTimestamps {
                        subtitle = "Last update: " + info.DirTime.String()
                }
        }
+       if title == "" {
+               if info.IsMain {
+                       // assume that the directory name is the command name
+                       _, tabtitle = pathpkg.Split(relpath)
+                       title = "Command "
+               } else {
+                       title = "Package "
+               }
+       }
+       title += tabtitle
 
        // special cases for top-level package/command directories
        switch tabtitle {
index c6b7c2dc8f96f8d09ff5e1cfafc53ab17f825224..42a5d2d9826cc639faf09337b2a339c84d9dcfd1 100644 (file)
@@ -2,10 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file contains support functions for parsing .go files.
-// Similar functionality is found in package go/parser but the
-// functions here operate using godoc's file system fs instead
-// of calling the OS's file operations directly.
+// This file contains support functions for parsing .go files
+// accessed via godoc's file system fs.
 
 package main
 
@@ -13,7 +11,6 @@ import (
        "go/ast"
        "go/parser"
        "go/token"
-       "os"
        pathpkg "path"
 )
 
@@ -25,44 +22,16 @@ func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.Fil
        return parser.ParseFile(fset, filename, src, mode)
 }
 
-func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first error) {
-       pkgs = make(map[string]*ast.Package)
-       for _, filename := range filenames {
-               file, err := parseFile(fset, filename, parser.ParseComments)
+func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) {
+       files := make(map[string]*ast.File)
+       for _, f := range localnames {
+               absname := pathpkg.Join(abspath, f)
+               file, err := parseFile(fset, absname, parser.ParseComments)
                if err != nil {
-                       if first == nil {
-                               first = err
-                       }
-                       continue
-               }
-
-               name := file.Name.Name
-               pkg, found := pkgs[name]
-               if !found {
-                       // TODO(gri) Use NewPackage here; reconsider ParseFiles API.
-                       pkg = &ast.Package{Name: name, Files: make(map[string]*ast.File)}
-                       pkgs[name] = pkg
-               }
-               pkg.Files[filename] = file
-       }
-       return
-}
-
-func parseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool) (map[string]*ast.Package, error) {
-       list, err := fs.ReadDir(path)
-       if err != nil {
-               return nil, err
-       }
-
-       filenames := make([]string, len(list))
-       i := 0
-       for _, d := range list {
-               if filter == nil || filter(d) {
-                       filenames[i] = pathpkg.Join(path, d.Name())
-                       i++
+                       return nil, err
                }
+               files[absname] = file
        }
-       filenames = filenames[0:i]
 
-       return parseFiles(fset, filenames)
+       return files, nil
 }
index 65842a3b15bb81d7f5883dd5b0e98d991efe16a8..fffc7f06ea086d09d6f2eb17a16e6de253d8dd99 100644 (file)
@@ -72,6 +72,6 @@ To convert the package tree from explicit slice upper bounds to implicit ones:
 
        gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
 */
-package documentation
+package main
 
 // BUG(rsc): The implementation of -r is a bit slow.
index 108f1c6502c545d160a92071e9a7423759de727e..bad4e540f2c782ab7dd1fb7b66cca61df12947e1 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Ld is the portable code for a modified version of the Plan 9 linker.  The original is documented at
@@ -70,4 +72,4 @@ Options new in this version:
                calls, not false positives caused by dead temporaries stored in
                the current function call.
 */
-package documentation
+package main
index 004567cad2eb288ad4a99de6f55389e07df0b1e5..480c1c3dde492137f4d7d6e8a7978435df974c06 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Nm is a version of the Plan 9 nm command.  The original is documented at
@@ -18,4 +20,4 @@ Usage:
        go tool nm [-aghnsTu] file
 
 */
-package documentation
+package main
index 8b17f3ca222d707c099e33833e1c23921ee38733..67b78973184f7bc551b1a845542183c4a53baaa1 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Pack is a variant of the Plan 9 ar tool.  The original is documented at
@@ -24,4 +26,4 @@ The new option 'P' causes pack to remove the given prefix
 from file names in the line number information in object files
 that are already stored in or added to the archive.
 */
-package documentation
+package main
index 0072f9ad5dfe4c9c5a6adeaad034730a5101ef74..2640167d3f656d924fb16be6a386bc3736cbc9ab 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build ignore
+
 /*
 
 Prof is a rudimentary real-time profiler.
@@ -44,4 +46,4 @@ every 100ms until the program completes.
 It is installed as go tool prof and is architecture-independent.
 
 */
-package documentation
+package main
index 265575ab6e1b8e017e77b79623ecc05683673f8c..f164eaca2af8bc4e3d5671d4eeaf4c60bf10d1bc 100644 (file)
@@ -67,4 +67,4 @@ The other flags are:
                        -printfuncs=Warn:1,Warnf:1
 
 */
-package documentation
+package main
index 4a2c2a31487b78a74029cf1651e666ebd2bd1f7f..792c104e3df27228086f7343e1f9678d524faa72 100644 (file)
@@ -48,4 +48,4 @@ referenced by yacc's generated code.  Setting it to distinct values
 allows multiple grammars to be placed in a single package.
 
 */
-package documentation
+package main
index 4bb22a4cb8098d4dc9f5f88be56df78902812912..796a59fb65306ec6aa2a0e30183fced51548d769 100644 (file)
@@ -19,4 +19,4 @@ The --start flag specifies the name of the start production for
 the grammar; it defaults to "Start".
 
 */
-package documentation
+package main
index 1168086771fb1e7d583ebbfe2a925527781cbeed..4d980f80dc1c34e3f6112473cd5b095cd1229c3d 100644 (file)
@@ -58,6 +58,6 @@ To verify the output of a pipe:
        echo "package foo" | gotype
 
 */
-package documentation
+package main
 
 // BUG(gri): At the moment, only single-file scope analysis is performed.