From 94764d093822721337243de77aeba72df1f9b230 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Fri, 30 May 2025 18:20:05 -0400 Subject: [PATCH] cmd/doc: build cmd/doc directly into the go command 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 Reviewed-by: Michael Pratt Reviewed-by: Michael Matloob --- src/cmd/doc/doc.go | 55 +++++++++++++++ src/cmd/go/go_test.go | 6 +- src/cmd/go/internal/doc/doc.go | 18 ++--- src/cmd/go/internal/doc/doc_bootstrap.go | 13 ++++ src/cmd/{ => internal}/doc/dirs.go | 2 +- src/cmd/{ => internal}/doc/doc_test.go | 6 +- src/cmd/{ => internal}/doc/main.go | 67 +++++-------------- src/cmd/{ => internal}/doc/pkg.go | 2 +- src/cmd/{ => internal}/doc/signal_notunix.go | 2 +- src/cmd/{ => internal}/doc/signal_unix.go | 2 +- .../{ => internal}/doc/testdata/merge/aa.go | 0 .../{ => internal}/doc/testdata/merge/bb.go | 0 .../doc/testdata/nested/empty/empty.go | 0 .../doc/testdata/nested/ignore.go | 0 .../doc/testdata/nested/nested/real.go | 0 src/cmd/{ => internal}/doc/testdata/pkg.go | 0 16 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 src/cmd/doc/doc.go create mode 100644 src/cmd/go/internal/doc/doc_bootstrap.go rename src/cmd/{ => internal}/doc/dirs.go (99%) rename src/cmd/{ => internal}/doc/doc_test.go (99%) rename src/cmd/{ => internal}/doc/main.go (89%) rename src/cmd/{ => internal}/doc/pkg.go (99%) rename src/cmd/{ => internal}/doc/signal_notunix.go (95%) rename src/cmd/{ => internal}/doc/signal_unix.go (95%) rename src/cmd/{ => internal}/doc/testdata/merge/aa.go (100%) rename src/cmd/{ => internal}/doc/testdata/merge/bb.go (100%) rename src/cmd/{ => internal}/doc/testdata/nested/empty/empty.go (100%) rename src/cmd/{ => internal}/doc/testdata/nested/ignore.go (100%) rename src/cmd/{ => internal}/doc/testdata/nested/nested/real.go (100%) rename src/cmd/{ => internal}/doc/testdata/pkg.go (100%) diff --git a/src/cmd/doc/doc.go b/src/cmd/doc/doc.go new file mode 100644 index 0000000000..ac15ad9c7d --- /dev/null +++ b/src/cmd/doc/doc.go @@ -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 +// go doc [.] +// go doc [.][.] +// go doc [.][.] +// +// 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 [.] +// +// 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:]) +} diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 83323aeaad..3e691abe41 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -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") diff --git a/src/cmd/go/internal/doc/doc.go b/src/cmd/go/internal/doc/doc.go index 7dfa652e15..131da81495 100644 --- a/src/cmd/go/internal/doc/doc.go +++ b/src/cmd/go/internal/doc/doc.go @@ -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 index 0000000000..8be95dc9a6 --- /dev/null +++ b/src/cmd/go/internal/doc/doc_bootstrap.go @@ -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{} diff --git a/src/cmd/doc/dirs.go b/src/cmd/internal/doc/dirs.go similarity index 99% rename from src/cmd/doc/dirs.go rename to src/cmd/internal/doc/dirs.go index 60ad6d30e6..8b1670f61c 100644 --- a/src/cmd/doc/dirs.go +++ b/src/cmd/internal/doc/dirs.go @@ -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" diff --git a/src/cmd/doc/doc_test.go b/src/cmd/internal/doc/doc_test.go similarity index 99% rename from src/cmd/doc/doc_test.go rename to src/cmd/internal/doc/doc_test.go index 3b383bdd78..bccace40c0 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/internal/doc/doc_test.go @@ -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, }, diff --git a/src/cmd/doc/main.go b/src/cmd/internal/doc/main.go similarity index 89% rename from src/cmd/doc/main.go rename to src/cmd/internal/doc/main.go index 490337a0b4..a19f36e1bd 100644 --- a/src/cmd/doc/main.go +++ b/src/cmd/internal/doc/main.go @@ -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 -// go doc [.] -// go doc [.][.] -// go doc [.][.] -// -// 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 [.] -// -// 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 \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 diff --git a/src/cmd/doc/pkg.go b/src/cmd/internal/doc/pkg.go similarity index 99% rename from src/cmd/doc/pkg.go rename to src/cmd/internal/doc/pkg.go index a21d8a4688..953b0d9a28 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/internal/doc/pkg.go @@ -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" diff --git a/src/cmd/doc/signal_notunix.go b/src/cmd/internal/doc/signal_notunix.go similarity index 95% rename from src/cmd/doc/signal_notunix.go rename to src/cmd/internal/doc/signal_notunix.go index 3b8fa9e080..b91a67eb5f 100644 --- a/src/cmd/doc/signal_notunix.go +++ b/src/cmd/internal/doc/signal_notunix.go @@ -4,7 +4,7 @@ //go:build plan9 || windows -package main +package doc import ( "os" diff --git a/src/cmd/doc/signal_unix.go b/src/cmd/internal/doc/signal_unix.go similarity index 95% rename from src/cmd/doc/signal_unix.go rename to src/cmd/internal/doc/signal_unix.go index 52431c221b..f30612ce9d 100644 --- a/src/cmd/doc/signal_unix.go +++ b/src/cmd/internal/doc/signal_unix.go @@ -4,7 +4,7 @@ //go:build unix || js || wasip1 -package main +package doc import ( "os" diff --git a/src/cmd/doc/testdata/merge/aa.go b/src/cmd/internal/doc/testdata/merge/aa.go similarity index 100% rename from src/cmd/doc/testdata/merge/aa.go rename to src/cmd/internal/doc/testdata/merge/aa.go diff --git a/src/cmd/doc/testdata/merge/bb.go b/src/cmd/internal/doc/testdata/merge/bb.go similarity index 100% rename from src/cmd/doc/testdata/merge/bb.go rename to src/cmd/internal/doc/testdata/merge/bb.go diff --git a/src/cmd/doc/testdata/nested/empty/empty.go b/src/cmd/internal/doc/testdata/nested/empty/empty.go similarity index 100% rename from src/cmd/doc/testdata/nested/empty/empty.go rename to src/cmd/internal/doc/testdata/nested/empty/empty.go diff --git a/src/cmd/doc/testdata/nested/ignore.go b/src/cmd/internal/doc/testdata/nested/ignore.go similarity index 100% rename from src/cmd/doc/testdata/nested/ignore.go rename to src/cmd/internal/doc/testdata/nested/ignore.go diff --git a/src/cmd/doc/testdata/nested/nested/real.go b/src/cmd/internal/doc/testdata/nested/nested/real.go similarity index 100% rename from src/cmd/doc/testdata/nested/nested/real.go rename to src/cmd/internal/doc/testdata/nested/nested/real.go diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/internal/doc/testdata/pkg.go similarity index 100% rename from src/cmd/doc/testdata/pkg.go rename to src/cmd/internal/doc/testdata/pkg.go -- 2.50.0