Skip to content

Commit

Permalink
tour: look for content in module cache, not GOROOT/misc/tour
Browse files Browse the repository at this point in the history
Tour content hasn't been available in GOROOT since Go 1.12¹, so stop
looking for it there.

A more useful fallback place to find tour content is the module cache.
When the golang.org/x/tour is in the build list, it will be found via
build.Import. It will not be found, however, when the tour binary is
run outside of a module. This situation is more common as of Go 1.16,
since the default GO111MODULE value changed to on². If the version of
the tour binary is known, access the content for that version from the
module cache.

In the future, this complex and imperfect machinery will be replaced
with simply embedding the needed files in the binary. We can't do that
quite yet (not without duplicating a lot of code) because Go 1.15 is
supported, and because Go 1.14 is used for deployment.

It's not neccessary to provide a custom socket.Environ (which served
to provide a custom GOPATH value pointing inside GOROOT; that path
is being removed here). Delete it along with some other unused code.

¹ https://golang.org/doc/go1.12#tour
² https://golang.org/doc/go1.16#modules

Fixes golang/go#44243.

Change-Id: Ia239700170b8614d21cafe9617b51dddf3e337d5
Reviewed-on: https://go-review.googlesource.com/c/tour/+/298529
Run-TryBot: Dmitri Shuralyov <[email protected]>
TryBot-Result: Go Bot <[email protected]>
Trust: Dmitri Shuralyov <[email protected]>
Reviewed-by: Russ Cox <[email protected]>
  • Loading branch information
dmitshur committed Mar 5, 2021
1 parent b72029a commit b3263fc
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 66 deletions.
16 changes: 1 addition & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
77 changes: 37 additions & 40 deletions local.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"go/build"
"html/template"
"io"
Expand All @@ -18,6 +18,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"time"

Expand All @@ -30,7 +31,6 @@ import (
)

const (
basePkg = "golang.org/x/tour"
socketPath = "/socket"
)

Expand All @@ -40,9 +40,6 @@ var (
)

var (
// GOPATH containing the tour packages
gopath = os.Getenv("GOPATH")

httpAddr string
)

Expand All @@ -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() {
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 0 additions & 11 deletions tour.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit b3263fc

Please sign in to comment.