]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/doc: build cmd/doc directly into the go command
authorMichael Matloob <matloob@golang.org>
Fri, 30 May 2025 22:20:05 +0000 (18:20 -0400)
committerMichael Matloob <matloob@google.com>
Tue, 3 Jun 2025 18:28:46 +0000 (11:28 -0700)
There are a couple of places where our tests expect that 'go doc'
doesn't need to do a build. Invoke the cmd/doc code directly by the go
command instead of starting the doc tool in a separate process so we can
preserve that property.

This change moves most of the doc code into the package
cmd/internal/doc, and exposes a Main function from that function that's
called both by the cmd/doc package, and by go doc.

This change makes couple of additional changes to intergrate doc into
the go command:

The counter.Open call and the increment of invocations counter are only
needed by cmd/doc. The go command will open the counters file and
increment a counter for the doc subcommand.

We add a cmd_go_bootstrap tagged variant of the file that defines go doc
so that we don't end up linking net into the bootstrap version of the go
command. We don't need doc in that version of the command.

We create a new flagSet rather than using flag.CommandLine because when
running as part of the go command, the flags to "go doc" won't be the top
level flags.

We change TestGoListTest in go_test.go to use gofmt instead of doc as an
example of a main package in cmd with an in-package test.

For #71867

Change-Id: I3e3df83e5fa266559606fdc086b461165e09f037
Reviewed-on: https://go-review.googlesource.com/c/go/+/677775
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Michael Matloob <matloob@google.com>
16 files changed:
src/cmd/doc/doc.go [new file with mode: 0644]
src/cmd/go/go_test.go
src/cmd/go/internal/doc/doc.go
src/cmd/go/internal/doc/doc_bootstrap.go [new file with mode: 0644]
src/cmd/internal/doc/dirs.go [moved from src/cmd/doc/dirs.go with 99% similarity]
src/cmd/internal/doc/doc_test.go [moved from src/cmd/doc/doc_test.go with 99% similarity]
src/cmd/internal/doc/main.go [moved from src/cmd/doc/main.go with 89% similarity]
src/cmd/internal/doc/pkg.go [moved from src/cmd/doc/pkg.go with 99% similarity]
src/cmd/internal/doc/signal_notunix.go [moved from src/cmd/doc/signal_notunix.go with 95% similarity]
src/cmd/internal/doc/signal_unix.go [moved from src/cmd/doc/signal_unix.go with 95% similarity]
src/cmd/internal/doc/testdata/merge/aa.go [moved from src/cmd/doc/testdata/merge/aa.go with 100% similarity]
src/cmd/internal/doc/testdata/merge/bb.go [moved from src/cmd/doc/testdata/merge/bb.go with 100% similarity]
src/cmd/internal/doc/testdata/nested/empty/empty.go [moved from src/cmd/doc/testdata/nested/empty/empty.go with 100% similarity]
src/cmd/internal/doc/testdata/nested/ignore.go [moved from src/cmd/doc/testdata/nested/ignore.go with 100% similarity]
src/cmd/internal/doc/testdata/nested/nested/real.go [moved from src/cmd/doc/testdata/nested/nested/real.go with 100% similarity]
src/cmd/internal/doc/testdata/pkg.go [moved from src/cmd/doc/testdata/pkg.go with 100% similarity]

diff --git a/src/cmd/doc/doc.go b/src/cmd/doc/doc.go
new file mode 100644 (file)
index 0000000..ac15ad9
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Doc (usually run as go doc) accepts zero, one or two arguments.
+//
+// Zero arguments:
+//
+//     go doc
+//
+// Show the documentation for the package in the current directory.
+//
+// One argument:
+//
+//     go doc <pkg>
+//     go doc <sym>[.<methodOrField>]
+//     go doc [<pkg>.]<sym>[.<methodOrField>]
+//     go doc [<pkg>.][<sym>.]<methodOrField>
+//
+// The first item in this list that succeeds is the one whose documentation
+// is printed. If there is a symbol but no package, the package in the current
+// directory is chosen. However, if the argument begins with a capital
+// letter it is always assumed to be a symbol in the current directory.
+//
+// Two arguments:
+//
+//     go doc <pkg> <sym>[.<methodOrField>]
+//
+// Show the documentation for the package, symbol, and method or field. The
+// first argument must be a full package path. This is similar to the
+// command-line usage for the godoc command.
+//
+// For commands, unless the -cmd flag is present "go doc command"
+// shows only the package-level docs for the package.
+//
+// The -src flag causes doc to print the full source code for the symbol, such
+// as the body of a struct, function or method.
+//
+// The -all flag causes doc to print all documentation for the package and
+// all its visible symbols. The argument must identify a package.
+//
+// For complete documentation, run "go help doc".
+package main
+
+import (
+       "cmd/internal/doc"
+       "cmd/internal/telemetry/counter"
+       "os"
+)
+
+func main() {
+       counter.Open()
+       counter.Inc("doc/invocations")
+       doc.Main(os.Args[1:])
+}
index 83323aeaad0e759ad21aa5dace71e342b11c0ba8..3e691abe41f702795e806b75eafc61906e5bc233 100644 (file)
@@ -1093,10 +1093,10 @@ func TestGoListTest(t *testing.T) {
        tg.grepStdoutNot(`^testing \[bytes.test\]$`, "unexpected test copy of testing")
        tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
 
-       tg.run("list", "-test", "cmd/buildid", "cmd/doc")
+       tg.run("list", "-test", "cmd/buildid", "cmd/gofmt")
        tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid")
-       tg.grepStdout(`^cmd/doc$`, "missing cmd/doc")
-       tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test")
+       tg.grepStdout(`^cmd/gofmt$`, "missing cmd/gofmt")
+       tg.grepStdout(`^cmd/gofmt\.test$`, "missing cmd/gofmt test")
        tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test")
        tg.grepStdoutNot(`^testing`, "unexpected testing")
 
index 7dfa652e155677f65428f29510134eafb63df7bc..131da814951d0f7ac7879c3b137b4d8b7c810c54 100644 (file)
@@ -2,17 +2,15 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !cmd_go_bootstrap
+
 // Package doc implements the “go doc” command.
 package doc
 
 import (
        "cmd/go/internal/base"
-       "cmd/go/internal/cfg"
+       "cmd/internal/doc"
        "context"
-       "errors"
-       "os"
-       "os/exec"
-       "path/filepath"
 )
 
 var CmdDoc = &base.Command{
@@ -134,13 +132,5 @@ Flags:
 }
 
 func runDoc(ctx context.Context, cmd *base.Command, args []string) {
-       base.StartSigHandlers()
-       err := base.RunErr(cfg.BuildToolexec, filepath.Join(cfg.GOROOTbin, "go"), "tool", "doc", args)
-       if err != nil {
-               var ee *exec.ExitError
-               if errors.As(err, &ee) {
-                       os.Exit(ee.ExitCode())
-               }
-               base.Error(err)
-       }
+       doc.Main(args)
 }
diff --git a/src/cmd/go/internal/doc/doc_bootstrap.go b/src/cmd/go/internal/doc/doc_bootstrap.go
new file mode 100644 (file)
index 0000000..8be95dc
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cmd_go_bootstrap
+
+// Don't build cmd/doc into go_bootstrap because it depends on net.
+
+package doc
+
+import "cmd/go/internal/base"
+
+var CmdDoc = &base.Command{}
similarity index 99%
rename from src/cmd/doc/dirs.go
rename to src/cmd/internal/doc/dirs.go
index 60ad6d30e6a99b89307db38f902abee15408d19c..8b1670f61c253c5e0ef5795878c0ecee2f797884 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package doc
 
 import (
        "bytes"
similarity index 99%
rename from src/cmd/doc/doc_test.go
rename to src/cmd/internal/doc/doc_test.go
index 3b383bdd78e587bdf4b6ceb71db651b29ccbfe98..bccace40c0fb5413ed14b88f77a869183034aa30 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package doc
 
 import (
        "bytes"
@@ -90,7 +90,7 @@ type test struct {
        no   []string // Regular expressions that should not match.
 }
 
-const p = "cmd/doc/testdata"
+const p = "cmd/internal/doc/testdata"
 
 var tests = []test{
        // Sanity check.
@@ -105,7 +105,7 @@ var tests = []test{
        {
                "package clause",
                []string{p},
-               []string{`package pkg.*cmd/doc/testdata`},
+               []string{`package pkg.*cmd/internal/doc/testdata`},
                nil,
        },
 
similarity index 89%
rename from src/cmd/doc/main.go
rename to src/cmd/internal/doc/main.go
index 490337a0b492d6e115114627bc87fdcada4d2dce..a19f36e1bd5419e0e5737cd1432484492e08cfe0 100644 (file)
@@ -2,45 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Doc (usually run as go doc) accepts zero, one or two arguments.
-//
-// Zero arguments:
-//
-//     go doc
-//
-// Show the documentation for the package in the current directory.
-//
-// One argument:
-//
-//     go doc <pkg>
-//     go doc <sym>[.<methodOrField>]
-//     go doc [<pkg>.]<sym>[.<methodOrField>]
-//     go doc [<pkg>.][<sym>.]<methodOrField>
-//
-// The first item in this list that succeeds is the one whose documentation
-// is printed. If there is a symbol but no package, the package in the current
-// directory is chosen. However, if the argument begins with a capital
-// letter it is always assumed to be a symbol in the current directory.
-//
-// Two arguments:
-//
-//     go doc <pkg> <sym>[.<methodOrField>]
-//
-// Show the documentation for the package, symbol, and method or field. The
-// first argument must be a full package path. This is similar to the
-// command-line usage for the godoc command.
-//
-// For commands, unless the -cmd flag is present "go doc command"
-// shows only the package-level docs for the package.
-//
-// The -src flag causes doc to print the full source code for the symbol, such
-// as the body of a struct, function or method.
-//
-// The -all flag causes doc to print all documentation for the package and
-// all its visible symbols. The argument must identify a package.
-//
-// For complete documentation, run "go help doc".
-package main
+// Package doc provides the implementation of the "go doc" subcommand and cmd/doc.
+package doc
 
 import (
        "bytes"
@@ -74,7 +37,7 @@ var (
 )
 
 // usage is a replacement usage function for the flags package.
-func usage() {
+func usage(flagSet *flag.FlagSet) {
        fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n")
        fmt.Fprintf(os.Stderr, "\tgo doc\n")
        fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n")
@@ -85,16 +48,17 @@ func usage() {
        fmt.Fprintf(os.Stderr, "For more information run\n")
        fmt.Fprintf(os.Stderr, "\tgo help doc\n\n")
        fmt.Fprintf(os.Stderr, "Flags:\n")
-       flag.PrintDefaults()
+       flagSet.PrintDefaults()
        os.Exit(2)
 }
 
-func main() {
+// Main is the entry point, invoked both by go doc and cmd/doc.
+func Main(args []string) {
        log.SetFlags(0)
        log.SetPrefix("doc: ")
-       counter.Open()
        dirsInit()
-       err := do(os.Stdout, flag.CommandLine, os.Args[1:])
+       var flagSet flag.FlagSet
+       err := do(os.Stdout, &flagSet, args)
        if err != nil {
                log.Fatal(err)
        }
@@ -102,7 +66,7 @@ func main() {
 
 // do is the workhorse, broken out of main to make testing easier.
 func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
-       flagSet.Usage = usage
+       flagSet.Usage = func() { usage(flagSet) }
        unexported = false
        matchCase = false
        flagSet.StringVar(&chdir, "C", "", "change to `dir` before running command")
@@ -114,7 +78,6 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
        flagSet.BoolVar(&serveHTTP, "http", false, "serve HTML docs over HTTP")
        flagSet.Parse(args)
-       counter.Inc("doc/invocations")
        counter.CountFlags("doc/flag:", *flag.CommandLine)
        if chdir != "" {
                if err := os.Chdir(chdir); err != nil {
@@ -151,7 +114,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        // Loop until something is printed.
        dirs.Reset()
        for i := 0; ; i++ {
-               buildPackage, userPath, sym, more := parseArgs(flagSet.Args())
+               buildPackage, userPath, sym, more := parseArgs(flagSet, flagSet.Args())
                if i > 0 && !more { // Ignore the "more" bit on the first iteration.
                        return failMessage(paths, symbol, method)
                }
@@ -165,7 +128,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
                        unexported = true
                }
 
-               symbol, method = parseSymbol(sym)
+               symbol, method = parseSymbol(flagSet, sym)
                pkg := parsePackage(writer, buildPackage, userPath)
                paths = append(paths, pkg.prettyPath())
 
@@ -338,7 +301,7 @@ func failMessage(paths []string, symbol, method string) error {
 // and there may be more matches. For example, if the argument
 // is rand.Float64, we must scan both crypto/rand and math/rand
 // to find the symbol, and the first call will return crypto/rand, true.
-func parseArgs(args []string) (pkg *build.Package, path, symbol string, more bool) {
+func parseArgs(flagSet *flag.FlagSet, args []string) (pkg *build.Package, path, symbol string, more bool) {
        wd, err := os.Getwd()
        if err != nil {
                log.Fatal(err)
@@ -356,7 +319,7 @@ func parseArgs(args []string) (pkg *build.Package, path, symbol string, more boo
        }
        switch len(args) {
        default:
-               usage()
+               usage(flagSet)
        case 1:
                // Done below.
        case 2:
@@ -499,7 +462,7 @@ func importDir(dir string) *build.Package {
 // parseSymbol breaks str apart into a symbol and method.
 // Both may be missing or the method may be missing.
 // If present, each must be a valid Go identifier.
-func parseSymbol(str string) (symbol, method string) {
+func parseSymbol(flagSet *flag.FlagSet, str string) (symbol, method string) {
        if str == "" {
                return
        }
@@ -510,7 +473,7 @@ func parseSymbol(str string) (symbol, method string) {
                method = elem[1]
        default:
                log.Printf("too many periods in symbol specification")
-               usage()
+               usage(flagSet)
        }
        symbol = elem[0]
        return
similarity index 99%
rename from src/cmd/doc/pkg.go
rename to src/cmd/internal/doc/pkg.go
index a21d8a4688233476a7648a60d8a4ee5690d5b6de..953b0d9a2840ab603a8fbe8d0c12ccc39a078639 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package doc
 
 import (
        "bufio"
similarity index 95%
rename from src/cmd/doc/signal_notunix.go
rename to src/cmd/internal/doc/signal_notunix.go
index 3b8fa9e080354d3b24b472ff479efd5977e3b473..b91a67eb5fbff0256fb61c04737ded9614397c97 100644 (file)
@@ -4,7 +4,7 @@
 
 //go:build plan9 || windows
 
-package main
+package doc
 
 import (
        "os"
similarity index 95%
rename from src/cmd/doc/signal_unix.go
rename to src/cmd/internal/doc/signal_unix.go
index 52431c221b5ba1213e62db617a6e285f9a6a78d4..f30612ce9dce458d18599c25cddd079621ee0a17 100644 (file)
@@ -4,7 +4,7 @@
 
 //go:build unix || js || wasip1
 
-package main
+package doc
 
 import (
        "os"