mirror of https://github.com/golang/go.git
cmd/doc: add support for starting pkgsite instance for docs
This change adds a new flag "-http" to cmd/doc which enables starting a pkgsite instance. -http will start a pkgsite instance and navigate to the page for the requested package, at the anchor for the item requested. For #68106 Change-Id: Ic1c113795cb2e1035e99c89c8e972c799342385b Reviewed-on: https://go-review.googlesource.com/c/go/+/628175 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Sam Thanawalla <samthanawalla@google.com> Reviewed-by: Jonathan Amsterdam <jba@google.com> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
39ceaf7961
commit
66e6f5c920
|
|
@ -44,17 +44,26 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/internal/browser"
|
||||
"cmd/internal/quoted"
|
||||
"cmd/internal/telemetry/counter"
|
||||
)
|
||||
|
||||
|
|
@ -66,6 +75,7 @@ var (
|
|||
showCmd bool // -cmd flag
|
||||
showSrc bool // -src flag
|
||||
short bool // -short flag
|
||||
serveHTTP bool // -http flag
|
||||
)
|
||||
|
||||
// usage is a replacement usage function for the flags package.
|
||||
|
|
@ -107,6 +117,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
|
|||
flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
|
||||
flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
|
||||
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)
|
||||
|
|
@ -152,6 +163,9 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
|
|||
panic(e)
|
||||
}()
|
||||
|
||||
if serveHTTP {
|
||||
return doPkgsite(pkg, symbol, method)
|
||||
}
|
||||
switch {
|
||||
case symbol == "":
|
||||
pkg.packageDoc() // The package exists, so we got some output.
|
||||
|
|
@ -168,6 +182,91 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func doPkgsite(pkg *Package, symbol, method string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
cmdline := "go run golang.org/x/pkgsite/cmd/pkgsite@latest -gorepo=" + buildCtx.GOROOT
|
||||
words, err := quoted.Split(cmdline)
|
||||
port, err := pickUnusedPort()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find port for documentation server: %v", err)
|
||||
}
|
||||
addr := fmt.Sprintf("localhost:%d", port)
|
||||
words = append(words, fmt.Sprintf("-http=%s", addr))
|
||||
cmd := exec.CommandContext(context.Background(), words[0], words[1:]...)
|
||||
cmd.Stdout = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
// Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)
|
||||
// and instead wait for the child process to handle the signal and
|
||||
// exit before exiting ourselves.
|
||||
signal.Ignore(signalsToIgnore...)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("starting pkgsite: %v", err)
|
||||
}
|
||||
|
||||
// Wait for pkgsite to became available.
|
||||
if !waitAvailable(ctx, addr) {
|
||||
cmd.Cancel()
|
||||
cmd.Wait()
|
||||
return errors.New("could not connect to local documentation server")
|
||||
}
|
||||
|
||||
// Open web browser.
|
||||
path := path.Join("http://"+addr, pkg.build.ImportPath)
|
||||
object := symbol
|
||||
if symbol != "" && method != "" {
|
||||
object = symbol + "." + method
|
||||
}
|
||||
if object != "" {
|
||||
path = path + "#" + object
|
||||
}
|
||||
if ok := browser.Open(path); !ok {
|
||||
cmd.Cancel()
|
||||
cmd.Wait()
|
||||
return errors.New("failed to open browser")
|
||||
}
|
||||
|
||||
// Wait for child to terminate. We expect the child process to receive signals from
|
||||
// this terminal and terminate in a timely manner, so this process will terminate
|
||||
// soon after.
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
// pickUnusedPort finds an unused port by trying to listen on port 0
|
||||
// and letting the OS pick a port, then closing that connection and
|
||||
// returning that port number.
|
||||
// This is inherently racy.
|
||||
func pickUnusedPort() (int, error) {
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
port := l.Addr().(*net.TCPAddr).Port
|
||||
if err := l.Close(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return port, nil
|
||||
}
|
||||
|
||||
func waitAvailable(ctx context.Context, addr string) bool {
|
||||
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
for ctx.Err() == nil {
|
||||
req, err := http.NewRequestWithContext(ctx, "HEAD", "http://"+addr, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// failMessage creates a nicely formatted error message when there is no result to show.
|
||||
func failMessage(paths []string, symbol, method string) error {
|
||||
var b bytes.Buffer
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2012 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 plan9 || windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var signalsToIgnore = []os.Signal{os.Interrupt}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2012 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 unix || js || wasip1
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT}
|
||||
Loading…
Reference in New Issue