diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b377e85..fc231354 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,21 +22,7 @@ The gophers there will answer or ask you to file an issue if you've tripped over In order to verify changes to the slides or code examples while developing locally compile with your local toolchain: - $ go install golang.org/x/tour/gotour - $ $GOPATH/bin/gotour - -## Running the App Engine version locally - -To view the App Engine version of the slides while developing locally, install -the [Go App Engine SDK](https://cloud.google.com/appengine/downloads?hl=en) -and then: - - $ cd $GOPATH/src/golang.org/x/tour - $ $SDK_PATH/dev_appserver.py . - -The App Engine version runs code examples against the service at play.golang.org. -To verify changes to the code examples you must use your local toolchain to compile -and run `gotour` locally. + $ go run golang.org/x/tour ## Contributing code diff --git a/local.go b/local.go index 7702c8fb..076cf946 100644 --- a/local.go +++ b/local.go @@ -5,8 +5,8 @@ package main import ( + "encoding/json" "flag" - "fmt" "go/build" "html/template" "io" @@ -18,6 +18,7 @@ import ( "os/exec" "path/filepath" "runtime" + "runtime/debug" "strings" "time" @@ -30,7 +31,6 @@ import ( ) const ( - basePkg = "golang.org/x/tour" socketPath = "/socket" ) @@ -40,9 +40,6 @@ var ( ) var ( - // GOPATH containing the tour packages - gopath = os.Getenv("GOPATH") - httpAddr string ) @@ -56,20 +53,41 @@ func isRoot(path string) bool { return err == nil } -func findRoot() (string, error) { - ctx := build.Default - p, err := ctx.Import(basePkg, "", build.FindOnly) +// findRoot is a best-effort attempt to find a tour directory +// that contains the files it needs. It may not always work. +// +// TODO: Delete after Go 1.17 is out and we can just use embed; see CL 291849. +func findRoot() (string, bool) { + // Try finding the golang.org/x/tour package in the + // legacy GOPATH mode workspace or in build list. + p, err := build.Import("golang.org/x/tour", "", build.FindOnly) if err == nil && isRoot(p.Dir) { - return p.Dir, nil + return p.Dir, true } - tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour") - ctx.GOPATH = tourRoot - p, err = ctx.Import(basePkg, "", build.FindOnly) - if err == nil && isRoot(tourRoot) { - gopath = tourRoot - return tourRoot, nil + // If that didn't work, perhaps we're not inside any module + // and the binary was built in module mode (e.g., 'go install + // golang.org/x/tour@latest' or 'go get golang.org/x/tour' + // outside a module). + // In that's the case, find out what version it is, + // and access its content from the module cache. + if info, ok := debug.ReadBuildInfo(); ok && + info.Main.Path == "golang.org/x/tour" && + info.Main.Replace == nil && + info.Main.Version != "(devel)" { + // Make some assumptions for brevity: + // • the 'go' binary is in $PATH + // • the main module isn't replaced + // • the version isn't "(devel)" + // They should hold for the use cases we care about, until this + // entire mechanism is obsoleted by file embedding. + out, execError := exec.Command("go", "mod", "download", "-json", "--", "golang.org/x/tour@"+info.Main.Version).Output() + var tourRoot struct{ Dir string } + jsonError := json.Unmarshal(out, &tourRoot) + if execError == nil && jsonError == nil && isRoot(tourRoot.Dir) { + return tourRoot.Dir, true + } } - return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH") + return "", false } func main() { @@ -82,9 +100,9 @@ func main() { } // find and serve the go tour files - root, err := findRoot() - if err != nil { - log.Fatalf("Couldn't find tour files: %v", err) + root, ok := findRoot() + if !ok { + log.Fatalln("Couldn't find files for the Go tour. Try reinstalling it.") } log.Println("Serving content from", root) @@ -166,27 +184,6 @@ If you don't understand this message, hit Control-C to terminate this process. WARNING! WARNING! WARNING! ` -type response struct { - Output string `json:"output"` - Errors string `json:"compile_errors"` -} - -func init() { - socket.Environ = environ -} - -// environ returns the original execution environment with GOPATH -// replaced (or added) with the value of the global var gopath. -func environ() (env []string) { - for _, v := range os.Environ() { - if !strings.HasPrefix(v, "GOPATH=") { - env = append(env, v) - } - } - env = append(env, "GOPATH="+gopath) - return -} - // waitServer waits some time for the http Server to start // serving url. The return value reports whether it starts. func waitServer(url string) bool { diff --git a/tour.go b/tour.go index 806b54c1..b27fc50d 100644 --- a/tour.go +++ b/tour.go @@ -223,17 +223,6 @@ func renderUI(w io.Writer) error { return err } -// nocode returns true if the provided Section contains -// no Code elements with Play enabled. -func nocode(s present.Section) bool { - for _, e := range s.Elem { - if c, ok := e.(present.Code); ok && c.Play { - return false - } - } - return true -} - // initScript concatenates all the javascript files needed to render // the tour UI and serves the result on /script.js. func initScript(root string) error {