cmd/go: convert mkalldocs.sh to a Go program

mkalldocs.sh required a Unix shell, making it less accessible for
contributors on Windows. It also used a substantially different
codepath to regenerate the file than the one used to check the file
for staleness, making failures in TestDocsUpToDate more complex to
diagnose.

We can solve both of those problems by using the same technique as in
checkScriptReadme: use the test itself as the generator to update the
file. The test is already written in Go, the test binary already knows
how to mimic the 'go' command, and this approach brings the difference
between the test and the generator down to a single flag check.

Updates #26735.

Change-Id: I7c6f65cb0e0c29e334e38a45412e0a73c4d31d42
Reviewed-on: https://go-review.googlesource.com/c/go/+/468636
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
This commit is contained in:
Bryan C. Mills 2023-02-15 16:45:14 -05:00 committed by Gopher Robot
parent 85d54a7667
commit 676794f73e
5 changed files with 44 additions and 36 deletions

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Code generated by mkalldocs.sh; DO NOT EDIT. // Code generated by 'go test cmd/go -v -run=TestDocsUpToDate -fixdocs'; DO NOT EDIT.
// Edit the documentation in other files and rerun mkalldocs.sh to generate this one. // Edit the documentation in other files and rerun mkalldocs.sh to generate this one.
// Go is a tool for managing Go source code. // Go is a tool for managing Go source code.

View File

@ -5,37 +5,58 @@
package main_test package main_test
import ( import (
"bytes" "flag"
"go/format" "go/format"
diffpkg "internal/diff" "internal/diff"
"internal/testenv"
"os" "os"
"strings"
"testing" "testing"
"cmd/go/internal/help"
"cmd/go/internal/modload"
) )
var fixDocs = flag.Bool("fixdocs", false, "if true, update alldocs.go")
func TestDocsUpToDate(t *testing.T) { func TestDocsUpToDate(t *testing.T) {
t.Parallel() if !*fixDocs {
t.Parallel()
if !modload.Enabled() {
t.Skipf("help.Help in GOPATH mode is configured by main.main")
} }
buf := new(bytes.Buffer) // We run 'go help documentation' as a subprocess instead of
// Match the command in mkalldocs.sh that generates alldocs.go. // calling help.Help directly because it may be sensitive to
help.Help(buf, []string{"documentation"}) // init-time configuration
internal := buf.Bytes() cmd := testenv.Command(t, testGo, "help", "documentation")
internal, err := format.Source(internal) // Unset GO111MODULE so that the 'go get' section matches
// the default 'go get' implementation.
cmd.Env = append(cmd.Environ(), "GO111MODULE=")
cmd.Stderr = new(strings.Builder)
out, err := cmd.Output()
if err != nil { if err != nil {
t.Fatalf("gofmt docs: %v", err) t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
} }
alldocs, err := os.ReadFile("alldocs.go")
alldocs, err := format.Source(out)
if err != nil { if err != nil {
t.Fatalf("error reading alldocs.go: %v", err) t.Fatalf("format.Source($(%v)): %v", cmd, err)
} }
if !bytes.Equal(internal, alldocs) {
t.Errorf("alldocs.go is not up to date; run mkalldocs.sh to regenerate it\n%s", const srcPath = `alldocs.go`
diffpkg.Diff("go help documentation | gofmt", internal, "alldocs.go", alldocs)) old, err := os.ReadFile(srcPath)
if err != nil {
t.Fatalf("error reading %s: %v", srcPath, err)
}
diff := diff.Diff(srcPath, old, "go help documentation | gofmt", alldocs)
if diff == nil {
t.Logf("%s is up to date.", srcPath)
return
}
if *fixDocs {
if err := os.WriteFile(srcPath, alldocs, 0666); err != nil {
t.Fatal(err)
}
t.Logf("wrote %d bytes to %s", len(alldocs), srcPath)
} else {
t.Logf("\n%s", diff)
t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", srcPath)
} }
} }

View File

@ -27,7 +27,7 @@ func Help(w io.Writer, args []string) {
fmt.Fprintln(w, "// Use of this source code is governed by a BSD-style") fmt.Fprintln(w, "// Use of this source code is governed by a BSD-style")
fmt.Fprintln(w, "// license that can be found in the LICENSE file.") fmt.Fprintln(w, "// license that can be found in the LICENSE file.")
fmt.Fprintln(w) fmt.Fprintln(w)
fmt.Fprintln(w, "// Code generated by mkalldocs.sh; DO NOT EDIT.") fmt.Fprintln(w, "// Code generated by 'go test cmd/go -v -run=TestDocsUpToDate -fixdocs'; DO NOT EDIT.")
fmt.Fprintln(w, "// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.") fmt.Fprintln(w, "// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
fmt.Fprintln(w) fmt.Fprintln(w)
buf := new(strings.Builder) buf := new(strings.Builder)

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:generate ./mkalldocs.sh //go:generate go test cmd/go -v -run=TestDocsUpToDate -fixdocs
package main package main

View File

@ -1,13 +0,0 @@
#!/usr/bin/env bash
# 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.
set -e
go build -o go.latest
# If the command used to generate alldocs.go changes, update TestDocsUpToDate in
# help_test.go.
GO111MODULE='' ./go.latest help documentation >alldocs.go
gofmt -w alldocs.go
rm go.latest