зеркало из https://github.com/golang/vgo.git
cmd/go: import from main repo
The files update.bash and patch.txt are new. Running update.bash made the other changes. Fixes golang/go#25514 (version now auto-inserted). Fixes golang/go#26434. Change-Id: Ied836ab8c1ddfea0a3158b2e4d84fbc787140e66 Reviewed-on: https://go-review.googlesource.com/123579 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
Родитель
cc75ec08d5
Коммит
2093985079
|
@ -0,0 +1 @@
|
|||
module golang.org/x/vgo
|
|
@ -0,0 +1,568 @@
|
|||
diff -u -r ./go11.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go11.go
|
||||
--- ./go11.go 2018-05-30 20:46:08.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go11.go 2018-02-20 12:11:43.000000000 -0500
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
// +build go1.1
|
||||
|
||||
-package main
|
||||
+package Main
|
||||
|
||||
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
|
||||
const go11tag = true
|
||||
diff -u -r ./go_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_test.go
|
||||
--- ./go_test.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_test.go 2018-07-12 00:09:46.000000000 -0400
|
||||
@@ -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_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -120,8 +120,8 @@
|
||||
}
|
||||
|
||||
if canRun {
|
||||
testGo = filepath.Join(testTmpDir, "testgo"+exeSuffix)
|
||||
- args := []string{"build", "-tags", "testgo", "-o", testGo}
|
||||
+ args := []string{"build", "-tags", "testgo", "-o", testGo, "../../.."}
|
||||
if race.Enabled {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
@@ -867,7 +870,9 @@
|
||||
tg.grepBoth("FAIL.*badtest/badvar", "test did not run everything")
|
||||
}
|
||||
|
||||
func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
if testing.Short() {
|
||||
t.Skip("don't rebuild the standard library in short mode")
|
||||
}
|
||||
@@ -1342,6 +1389,8 @@
|
||||
}
|
||||
|
||||
func TestMoveHG(t *testing.T) {
|
||||
+ t.Skip("vgo") // Failing in main branch too: non-hermetic hg configuration?
|
||||
+
|
||||
testMove(t, "hg", "vcs-test.golang.org/go/custom-hg-hello", "custom-hg-hello", "vcs-test.golang.org/go/custom-hg-hello/.hg/hgrc")
|
||||
}
|
||||
|
||||
@@ -1507,6 +1556,8 @@
|
||||
}
|
||||
|
||||
func TestAccidentalGitCheckout(t *testing.T) {
|
||||
+ t.Skip("vgo") // Failing in main branch too: https://golang.org/issue/22983
|
||||
+
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
t.Skip("skipping because git binary not found")
|
||||
@@ -2114,6 +2165,8 @@
|
||||
}
|
||||
|
||||
func TestDefaultGOPATH(t *testing.T) {
|
||||
+ t.Skip("vgo") // Needs a more realistic GOROOT; see RuntimeGoroot below.
|
||||
+
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
@@ -2172,6 +2225,8 @@
|
||||
// Issue 4186. go get cannot be used to download packages to $GOROOT.
|
||||
// Test that without GOPATH set, go get should fail.
|
||||
func TestGoGetIntoGOROOT(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
tg := testgo(t)
|
||||
@@ -2771,6 +2771,8 @@ func TestTestBuildFailureOutput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCoverageFunc(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
@@ -3395,6 +3450,8 @@
|
||||
}
|
||||
|
||||
func TestGoVetWithExternalTests(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.makeTempdir()
|
||||
@@ -3404,6 +3461,8 @@
|
||||
}
|
||||
|
||||
func TestGoVetWithTags(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.makeTempdir()
|
||||
@@ -3413,6 +3472,8 @@
|
||||
}
|
||||
|
||||
func TestGoVetWithFlagsOn(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.makeTempdir()
|
||||
@@ -4754,6 +4815,8 @@
|
||||
}
|
||||
|
||||
func TestExecutableGOROOT(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
skipIfGccgo(t, "gccgo has no GOROOT")
|
||||
if runtime.GOOS == "openbsd" {
|
||||
t.Skipf("test case does not work on %s, missing os.Executable", runtime.GOOS)
|
||||
@@ -4829,6 +4892,8 @@
|
||||
// Binaries built in the new tree should report the
|
||||
// new tree when they call runtime.GOROOT.
|
||||
t.Run("RuntimeGoroot", func(t *testing.T) {
|
||||
+ t.Skip("vgo") // Needs "new/api" in GOROOT.
|
||||
+
|
||||
// Build a working GOROOT the easy way, with symlinks.
|
||||
testenv.MustHaveSymlink(t)
|
||||
if err := os.Symlink(filepath.Join(testGOROOT, "src"), tg.path("new/src")); err != nil {
|
||||
@@ -4984,6 +5049,8 @@
|
||||
}
|
||||
|
||||
func TestTestRegexps(t *testing.T) {
|
||||
+ t.Skip("vgo") // fails with Go 1.10 testing package
|
||||
+
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
@@ -5119,6 +5186,8 @@
|
||||
}
|
||||
|
||||
func TestExecBuildX(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tooSlow(t)
|
||||
if !canCgo {
|
||||
t.Skip("skipping because cgo not enabled")
|
||||
@@ -5761,6 +5830,8 @@
|
||||
}
|
||||
|
||||
func TestTestVet(t *testing.T) {
|
||||
+ t.Skip("vgo")
|
||||
+
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
@@ -5945,6 +6011,8 @@
|
||||
}
|
||||
|
||||
func TestGoTestJSON(t *testing.T) {
|
||||
+ t.Skip("vgo") // "did not see skip"
|
||||
+
|
||||
skipIfGccgo(t, "gccgo does not have standard packages")
|
||||
tooSlow(t)
|
||||
|
||||
@@ -5983,6 +6051,8 @@
|
||||
}
|
||||
|
||||
func TestFailFast(t *testing.T) {
|
||||
+ t.Skip("vgo") // fails with Go 1.10 testing package
|
||||
+
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
diff -u -r ./go_unix_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_unix_test.go
|
||||
--- ./go_unix_test.go 2018-06-04 09:24:18.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_unix_test.go 2018-02-20 12:11:43.000000000 -0500
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
-package main_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
diff -u -r ./go_windows_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_windows_test.go
|
||||
--- ./go_windows_test.go 2018-06-04 09:24:18.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/go_windows_test.go 2018-02-20 12:11:43.000000000 -0500
|
||||
@@ -2,6 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
-package main
|
||||
+package Main
|
||||
|
||||
import (
|
||||
diff -u -r ./script_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/script_test.go
|
||||
--- ./script_test.go 2018-06-04 09:24:18.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/script_test.go 2018-02-20 12:11:43.000000000 -0500
|
||||
@@ -5,6 +5,6 @@
|
||||
// Script-driven tests.
|
||||
// See testdata/script/README for an overview.
|
||||
|
||||
-package main_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
diff -u -r ./internal/base/base.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/base/base.go
|
||||
--- ./internal/base/base.go 2018-06-04 09:24:18.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/base/base.go 2018-02-23 16:13:50.000000000 -0500
|
||||
@@ -15,6 +15,7 @@
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
+ "runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -86,13 +87,20 @@
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
|
||||
+var et = flag.Bool("et", false, "print stack traces with errors")
|
||||
+
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
Errorf(format, args...)
|
||||
Exit()
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
- log.Printf(format, args...)
|
||||
+ if *et {
|
||||
+ stack := debug.Stack()
|
||||
+ log.Printf("%s\n%s", fmt.Sprintf(format, args...), stack)
|
||||
+ } else {
|
||||
+ log.Printf(format, args...)
|
||||
+ }
|
||||
SetExitStatus(1)
|
||||
}
|
||||
|
||||
diff -u -r ./internal/cfg/cfg.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/cfg/cfg.go
|
||||
--- ./internal/cfg/cfg.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/cfg/cfg.go 2018-07-12 00:03:05.000000000 -0400
|
||||
@@ -7,13 +7,14 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
+ "bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
+ "io/ioutil"
|
||||
+ "log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
-
|
||||
- "cmd/internal/objabi"
|
||||
)
|
||||
|
||||
// These are general "build flags" used by build and other commands.
|
||||
@@ -90,10 +91,10 @@
|
||||
GOROOT_FINAL = findGOROOT_FINAL()
|
||||
|
||||
// Used in envcmd.MkEnv and build ID computations.
|
||||
- GOARM = fmt.Sprint(objabi.GOARM)
|
||||
- GO386 = objabi.GO386
|
||||
- GOMIPS = objabi.GOMIPS
|
||||
- GOMIPS64 = objabi.GOMIPS64
|
||||
+ GOARM, GO386, GOMIPS, GOMIPS64 = objabi()
|
||||
+
|
||||
+ // C and C++ compilers
|
||||
+ CC, CXX = compilers()
|
||||
)
|
||||
|
||||
// Update build context to use our computed GOROOT.
|
||||
@@ -109,18 +110,75 @@
|
||||
}
|
||||
}
|
||||
|
||||
-// There is a copy of findGOROOT, isSameDir, and isGOROOT in
|
||||
-// x/tools/cmd/godoc/goroot.go.
|
||||
-// Try to keep them in sync for now.
|
||||
-
|
||||
-// findGOROOT returns the GOROOT value, using either an explicitly
|
||||
-// provided environment variable, a GOROOT that contains the current
|
||||
-// os.Executable value, or else the GOROOT that the binary was built
|
||||
-// with from runtime.GOROOT().
|
||||
-//
|
||||
-// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
|
||||
+func objabi() (GOARM, GO386, GOMIPS, GOMIPS64 string) {
|
||||
+ data, err := ioutil.ReadFile(filepath.Join(GOROOT, "src/cmd/internal/objabi/zbootstrap.go"))
|
||||
+ if err != nil {
|
||||
+ fmt.Fprintf(os.Stderr, "go objabi: %v\n", err)
|
||||
+ }
|
||||
+
|
||||
+ find := func(key string) string {
|
||||
+ if env := os.Getenv(key); env != "" {
|
||||
+ return env
|
||||
+ }
|
||||
+ i := bytes.Index(data, []byte("default"+key+" = `"))
|
||||
+ if i < 0 {
|
||||
+ if key == "GOMIPS64" { // new in Go 1.11
|
||||
+ return ""
|
||||
+ }
|
||||
+ fmt.Fprintf(os.Stderr, "go objabi: cannot find %s\n", key)
|
||||
+ os.Exit(2)
|
||||
+ }
|
||||
+ line := data[i:]
|
||||
+ line = line[bytes.IndexByte(line, '`')+1:]
|
||||
+ return string(line[:bytes.IndexByte(line, '`')])
|
||||
+ }
|
||||
+
|
||||
+ return find("GOARM"), find("GO386"), find("GOMIPS"), find("GOMIPS64")
|
||||
+}
|
||||
+
|
||||
+func compilers() (CC, CXX string) {
|
||||
+ data, err := ioutil.ReadFile(filepath.Join(GOROOT, "src/cmd/go/internal/cfg/zdefaultcc.go"))
|
||||
+ if err != nil {
|
||||
+ fmt.Fprintf(os.Stderr, "go compilers: %v\n", err)
|
||||
+ }
|
||||
+
|
||||
+ find := func(key string) string {
|
||||
+ if env := os.Getenv(key); env != "" {
|
||||
+ return env
|
||||
+ }
|
||||
+ fi := bytes.Index(data, []byte("Default"+key+"(goos, goarch string)"))
|
||||
+ if fi < 0 {
|
||||
+ fmt.Fprintf(os.Stderr, "go compilers: cannot find %s\n", key)
|
||||
+ os.Exit(2)
|
||||
+ }
|
||||
+ i := bytes.Index(data[fi:], []byte("\treturn "))
|
||||
+ if i < 0 {
|
||||
+ fmt.Fprintf(os.Stderr, "go compilers: cannot find %s\n", key)
|
||||
+ os.Exit(2)
|
||||
+ }
|
||||
+ line := data[fi+i:]
|
||||
+ line = line[bytes.IndexByte(line, '"')+1:]
|
||||
+ return string(line[:bytes.IndexByte(line, '"')])
|
||||
+ }
|
||||
+
|
||||
+ return find("CC"), find("CXX")
|
||||
+}
|
||||
+
|
||||
func findGOROOT() string {
|
||||
+ goroot := findGOROOT1()
|
||||
+ _, err := os.Stat(filepath.Join(goroot, "api/go1.10.txt"))
|
||||
+ if err != nil {
|
||||
+ log.SetFlags(0)
|
||||
+ log.Fatalf("go requires Go 1.10 but VGOROOT=%s is not a Go 1.10 source tree", goroot)
|
||||
+ }
|
||||
+ return goroot
|
||||
+}
|
||||
+
|
||||
+func findGOROOT1() string {
|
||||
+ if env := os.Getenv("VGOROOT"); env != "" {
|
||||
+ return filepath.Clean(env)
|
||||
+ }
|
||||
if env := os.Getenv("GOROOT"); env != "" {
|
||||
return filepath.Clean(env)
|
||||
}
|
||||
def := filepath.Clean(runtime.GOROOT())
|
||||
diff -u -r ./internal/help/help.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/help/help.go
|
||||
--- ./internal/help/help.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/help/help.go 2018-07-12 00:03:05.000000000 -0400
|
||||
@@ -72,6 +72,10 @@
|
||||
|
||||
var usageTemplate = `Go is a tool for managing Go source code.
|
||||
|
||||
+This is vgo, an experimental go command with support for package versioning.
|
||||
+Even though you are invoking it as vgo, most of the messages printed will
|
||||
+still say "go", not "vgo". Sorry.
|
||||
+
|
||||
Usage:
|
||||
|
||||
go command [arguments]
|
||||
diff -u -r ./internal/version/version.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/version/version.go
|
||||
--- ./internal/version/version.go 2018-06-04 09:24:18.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/version/version.go 2018-07-10 16:04:55.000000000 -0400
|
||||
@@ -10,8 +10,9 @@
|
||||
"runtime"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
+ "cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdVersion = &base.Command{
|
||||
Run: runVersion,
|
||||
UsageLine: "version",
|
||||
@@ -24,5 +27,5 @@
|
||||
cmd.Usage()
|
||||
}
|
||||
|
||||
- fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
+ fmt.Printf("go version %s %s/%s vgo:%s\n", work.RuntimeVersion, runtime.GOOS, runtime.GOARCH, version)
|
||||
}
|
||||
diff -u -r ./internal/work/build.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/build.go
|
||||
--- ./internal/work/build.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/build.go 2018-07-10 17:04:18.000000000 -0400
|
||||
@@ -5,9 +5,11 @@
|
||||
package work
|
||||
|
||||
import (
|
||||
+ "bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
+ "io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -274,7 +276,25 @@
|
||||
|
||||
var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs }
|
||||
|
||||
-var runtimeVersion = runtime.Version()
|
||||
+var runtimeVersion = getRuntimeVersion()
|
||||
+var RuntimeVersion = runtimeVersion
|
||||
+
|
||||
+func getRuntimeVersion() string {
|
||||
+ data, err := ioutil.ReadFile(filepath.Join(cfg.GOROOT, "src/runtime/internal/sys/zversion.go"))
|
||||
+ if err != nil {
|
||||
+ base.Fatalf("go: %v", err)
|
||||
+ }
|
||||
+ i := bytes.Index(data, []byte("TheVersion = `"))
|
||||
+ if i < 0 {
|
||||
+ base.Fatalf("go: cannot find TheVersion")
|
||||
+ }
|
||||
+ data = data[i+len("TheVersion = `"):]
|
||||
+ j := bytes.IndexByte(data, '`')
|
||||
+ if j < 0 {
|
||||
+ base.Fatalf("go: cannot find TheVersion")
|
||||
+ }
|
||||
+ return string(data[:j])
|
||||
+}
|
||||
|
||||
func runBuild(cmd *base.Command, args []string) {
|
||||
BuildInit()
|
||||
diff -u -r ./internal/work/buildid.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/buildid.go
|
||||
--- ./internal/work/buildid.go 2018-07-06 16:52:53.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/buildid.go 2018-06-27 21:01:27.000000000 -0400
|
||||
@@ -134,6 +134,8 @@
|
||||
return string(dst[:])
|
||||
}
|
||||
|
||||
+var oldVet = false
|
||||
+
|
||||
// toolID returns the unique ID to use for the current copy of the
|
||||
// named tool (asm, compile, cover, link).
|
||||
//
|
||||
@@ -166,6 +168,10 @@
|
||||
// build setups agree on details like $GOROOT and file name paths, but at least the
|
||||
// tool IDs do not make it impossible.)
|
||||
func (b *Builder) toolID(name string) string {
|
||||
+ if name == "vet" && oldVet {
|
||||
+ return ""
|
||||
+ }
|
||||
+
|
||||
b.id.Lock()
|
||||
id := b.toolIDCache[name]
|
||||
b.id.Unlock()
|
||||
@@ -190,6 +196,10 @@
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
+ if name == "vet" {
|
||||
+ oldVet = true
|
||||
+ return ""
|
||||
+ }
|
||||
base.Fatalf("%s: %v\n%s%s", desc, err, stdout.Bytes(), stderr.Bytes())
|
||||
}
|
||||
|
||||
diff -u -r ./internal/work/exec.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/exec.go
|
||||
--- ./internal/work/exec.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/exec.go 2018-07-12 00:03:05.000000000 -0400
|
||||
@@ -972,7 +972,7 @@
|
||||
// TODO(rsc,gri): Try to remove this for Go 1.11.
|
||||
//
|
||||
// Disabled 2018-04-20. Let's see if we can do without it.
|
||||
- // vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test"
|
||||
+ vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test"
|
||||
|
||||
js, err := json.MarshalIndent(vcfg, "", "\t")
|
||||
if err != nil {
|
||||
diff -u -r ./internal/work/gc.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/gc.go
|
||||
--- ./internal/work/gc.go 2018-07-06 16:52:53.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/internal/work/gc.go 2018-06-27 21:01:27.000000000 -0400
|
||||
@@ -20,7 +20,6 @@
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/str"
|
||||
- "cmd/internal/objabi"
|
||||
"crypto/sha1"
|
||||
)
|
||||
|
||||
@@ -167,11 +166,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- // TODO: Test and delete these conditions.
|
||||
- if objabi.Fieldtrack_enabled != 0 || objabi.Preemptibleloops_enabled != 0 || objabi.Clobberdead_enabled != 0 {
|
||||
- canDashC = false
|
||||
- }
|
||||
-
|
||||
if !canDashC {
|
||||
return 1
|
||||
}
|
||||
diff -u -r ./main.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/main.go
|
||||
--- ./main.go 2018-07-12 00:17:57.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/main.go 2018-07-10 16:04:55.000000000 -0400
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
//go:generate ./mkalldocs.sh
|
||||
|
||||
-package main
|
||||
+package Main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
-func main() {
|
||||
+func Main() {
|
||||
_ = go11tag
|
||||
flag.Usage = base.Usage
|
||||
flag.Parse()
|
||||
diff -u -r ./note_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/note_test.go
|
||||
--- ./note_test.go 2018-07-12 00:14:08.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/note_test.go 2018-07-11 23:25:10.000000000 -0400
|
||||
@@ -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_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
diff -u -r ./proxy_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/proxy_test.go
|
||||
--- ./proxy_test.go 2018-07-12 00:14:08.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/proxy_test.go 2018-07-11 23:25:10.000000000 -0400
|
||||
@@ -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_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
diff -u -r ./vendor_test.go /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/vendor_test.go
|
||||
--- ./vendor_test.go 2018-07-12 00:14:08.000000000 -0400
|
||||
+++ /Users/rsc/src/golang.org/x/vgo/vendor/cmd/go/vendor_test.go 2018-07-11 23:25:10.000000000 -0400
|
||||
@@ -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_test
|
||||
+package Main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
rm -rf ./vendor/cmd/go
|
||||
cp -a $(go env GOROOT)/src/cmd/go vendor/cmd/go
|
||||
rm -f vendor/cmd/go/alldocs.go vendor/cmd/go/mkalldocs.sh # docs are in wrong place and describe wrong command
|
||||
cd vendor/cmd/go
|
||||
patch -p0 < ../../../patch.txt
|
||||
vers=$(go version | sed 's/^go version //; s/ [A-Z][a-z][a-z].*//')
|
||||
echo "package version; const version = \"$vers\"" > internal/version/vgo.go
|
||||
gofmt -w internal
|
||||
cd ../../..
|
||||
rm $(find . -name '*.orig')
|
||||
go build
|
||||
./vgo version
|
||||
rm vgo
|
||||
git add .
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -15,6 +15,7 @@ import (
|
|||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
@ -102,6 +103,9 @@ var testGOROOT string
|
|||
var testCC string
|
||||
var testGOCACHE string
|
||||
|
||||
var testGo string
|
||||
var testTmpDir string
|
||||
|
||||
// The TestMain function creates a go command for testing purposes and
|
||||
// deletes it after the tests have been run.
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -119,8 +123,18 @@ func TestMain(m *testing.M) {
|
|||
select {}
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "cmd-go-test-")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
testTmpDir = dir
|
||||
if !*testWork {
|
||||
defer removeAll(testTmpDir)
|
||||
}
|
||||
|
||||
if canRun {
|
||||
args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix, "../../.."}
|
||||
testGo = filepath.Join(testTmpDir, "testgo"+exeSuffix)
|
||||
args := []string{"build", "-tags", "testgo", "-o", testGo, "../../.."}
|
||||
if race.Enabled {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
|
@ -173,7 +187,7 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
testCC = strings.TrimSpace(string(out))
|
||||
|
||||
if out, err := exec.Command("./testgo"+exeSuffix, "env", "CGO_ENABLED").Output(); err != nil {
|
||||
if out, err := exec.Command(testGo, "env", "CGO_ENABLED").Output(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "running testgo failed: %v\n", err)
|
||||
canRun = false
|
||||
} else {
|
||||
|
@ -217,9 +231,8 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
r := m.Run()
|
||||
|
||||
if canRun {
|
||||
os.Remove("testgo" + exeSuffix)
|
||||
if !*testWork {
|
||||
removeAll(testTmpDir) // os.Exit won't run defer
|
||||
}
|
||||
|
||||
os.Exit(r)
|
||||
|
@ -249,6 +262,7 @@ type testgoData struct {
|
|||
ran bool
|
||||
inParallel bool
|
||||
stdout, stderr bytes.Buffer
|
||||
execDir string // dir for tg.run
|
||||
}
|
||||
|
||||
// skipIfGccgo skips the test if using gccgo.
|
||||
|
@ -367,10 +381,7 @@ func (tg *testgoData) unsetenv(name string) {
|
|||
}
|
||||
|
||||
func (tg *testgoData) goTool() string {
|
||||
if tg.wd == "" {
|
||||
return "./testgo" + exeSuffix
|
||||
}
|
||||
return filepath.Join(tg.wd, "testgo"+exeSuffix)
|
||||
return testGo
|
||||
}
|
||||
|
||||
// doRun runs the test go command, recording stdout and stderr and
|
||||
|
@ -404,6 +415,7 @@ func (tg *testgoData) doRun(args []string) error {
|
|||
cmd := exec.Command(prog, args...)
|
||||
tg.stdout.Reset()
|
||||
tg.stderr.Reset()
|
||||
cmd.Dir = tg.execDir
|
||||
cmd.Stdout = &tg.stdout
|
||||
cmd.Stderr = &tg.stderr
|
||||
cmd.Env = tg.env
|
||||
|
@ -751,7 +763,11 @@ func (tg *testgoData) wantStale(pkg, reason, msg string) {
|
|||
if !stale {
|
||||
tg.t.Fatal(msg)
|
||||
}
|
||||
if reason == "" && why != "" || !strings.Contains(why, reason) {
|
||||
// We always accept the reason as being "not installed but
|
||||
// available in build cache", because when that is the case go
|
||||
// list doesn't try to sort out the underlying reason why the
|
||||
// package is not installed.
|
||||
if reason == "" && why != "" || !strings.Contains(why, reason) && !strings.Contains(why, "not installed but available in build cache") {
|
||||
tg.t.Errorf("wrong reason for Stale=true: %q, want %q", why, reason)
|
||||
}
|
||||
}
|
||||
|
@ -802,6 +818,9 @@ func removeAll(dir string) error {
|
|||
// module cache has 0444 directories;
|
||||
// make them writable in order to remove content.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil // ignore errors walking in file system
|
||||
}
|
||||
if info.IsDir() {
|
||||
os.Chmod(path, 0777)
|
||||
}
|
||||
|
@ -822,97 +841,6 @@ func (tg *testgoData) failSSH() {
|
|||
tg.setenv("PATH", fmt.Sprintf("%v%c%v", fail, filepath.ListSeparator, os.Getenv("PATH")))
|
||||
}
|
||||
|
||||
func TestBuildComplex(t *testing.T) {
|
||||
// Simple smoke test for build configuration.
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.run("build", "-x", "-o", os.DevNull, "complex")
|
||||
|
||||
if _, err := exec.LookPath("gccgo"); err == nil {
|
||||
t.Skip("golang.org/issue/22472")
|
||||
tg.run("build", "-x", "-o", os.DevNull, "-compiler=gccgo", "complex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileLineInErrorMessages(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("err.go", `package main; import "bar"`)
|
||||
path := tg.path("err.go")
|
||||
tg.runFail("run", path)
|
||||
shortPath := path
|
||||
if rel, err := filepath.Rel(tg.pwd(), path); err == nil && len(rel) < len(path) {
|
||||
shortPath = rel
|
||||
}
|
||||
tg.grepStderr("^"+regexp.QuoteMeta(shortPath)+":", "missing file:line in error message")
|
||||
}
|
||||
|
||||
func TestProgramNameInCrashMessages(t *testing.T) {
|
||||
skipIfGccgo(t, "gccgo does not use cmd/link")
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("triv.go", `package main; func main() {}`)
|
||||
tg.runFail("build", "-ldflags", "-crash_for_testing", tg.path("triv.go"))
|
||||
tg.grepStderr(`[/\\]tool[/\\].*[/\\]link`, "missing linker name in error message")
|
||||
}
|
||||
|
||||
func TestBrokenTestsWithoutTestFunctionsAllFail(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
// TODO: tg.parallel()
|
||||
tg.runFail("test", "./testdata/src/badtest/...")
|
||||
tg.grepBothNot("^ok", "test passed unexpectedly")
|
||||
tg.grepBoth("FAIL.*badtest/badexec", "test did not run everything")
|
||||
tg.grepBoth("FAIL.*badtest/badsyntax", "test did not run everything")
|
||||
tg.grepBoth("FAIL.*badtest/badvar", "test did not run everything")
|
||||
}
|
||||
|
||||
func TestGoBuildDashAInDevBranch(t *testing.T) {
|
||||
t.Skip("vgo")
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("don't rebuild the standard library in short mode")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.run("install", "math") // should be up to date already but just in case
|
||||
tg.setenv("TESTGO_IS_GO_RELEASE", "0")
|
||||
tg.run("build", "-v", "-a", "math")
|
||||
tg.grepStderr("runtime", "testgo build -a math in dev branch DID NOT build runtime, but should have")
|
||||
|
||||
// Everything is out of date. Rebuild to leave things in a better state.
|
||||
tg.run("install", "std")
|
||||
}
|
||||
|
||||
func TestGoBuildDashAInReleaseBranch(t *testing.T) {
|
||||
t.Skip("vgo")
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("don't rebuild the standard library in short mode")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.run("install", "math", "net/http") // should be up to date already but just in case
|
||||
tg.setenv("TESTGO_IS_GO_RELEASE", "1")
|
||||
tg.run("install", "-v", "-a", "math")
|
||||
tg.grepStderr("runtime", "testgo build -a math in release branch DID NOT build runtime, but should have")
|
||||
|
||||
// Now runtime.a is updated (newer mtime), so everything would look stale if not for being a release.
|
||||
tg.run("build", "-v", "net/http")
|
||||
tg.grepStderrNot("strconv", "testgo build -v net/http in release branch with newer runtime.a DID build strconv but should not have")
|
||||
tg.grepStderrNot("golang.org/x/net/http2/hpack", "testgo build -v net/http in release branch with newer runtime.a DID build .../golang.org/x/net/http2/hpack but should not have")
|
||||
tg.grepStderrNot("net/http", "testgo build -v net/http in release branch with newer runtime.a DID build net/http but should not have")
|
||||
|
||||
// Everything is out of date. Rebuild to leave things in a better state.
|
||||
tg.run("install", "std")
|
||||
}
|
||||
|
||||
func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
|
||||
t.Skip("vgo")
|
||||
|
||||
|
@ -923,13 +851,13 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
|
|||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
addNL := func(name string) (restore func()) {
|
||||
addVar := func(name string, idx int) (restore func()) {
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := data
|
||||
data = append(data, '\n')
|
||||
data = append(data, fmt.Sprintf("var DummyUnusedVar%d bool\n", idx)...)
|
||||
if err := ioutil.WriteFile(name, append(data, '\n'), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -953,19 +881,19 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
|
|||
// In fact this should be true even outside a release branch.
|
||||
sys := runtime.GOROOT() + "/src/runtime/internal/sys/sys.go"
|
||||
tg.sleep()
|
||||
restore := addNL(sys)
|
||||
restore := addVar(sys, 0)
|
||||
restore()
|
||||
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after updating mtime of runtime/internal/sys/sys.go")
|
||||
|
||||
// But changing content of any file should have an effect.
|
||||
// Previously zversion.go was the only one that mattered;
|
||||
// now they all matter, so keep using sys.go.
|
||||
restore = addNL(sys)
|
||||
restore = addVar(sys, 1)
|
||||
defer restore()
|
||||
tg.wantStale("p1", "stale dependency: runtime/internal/sys", "./testgo list claims p1 is NOT stale, incorrectly, after changing sys.go")
|
||||
restore()
|
||||
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly, after changing back to old release")
|
||||
addNL(sys)
|
||||
addVar(sys, 2)
|
||||
tg.wantStale("p1", "stale dependency: runtime/internal/sys", "./testgo list claims p1 is NOT stale, incorrectly, after changing sys.go again")
|
||||
tg.run("install", "-i", "p1")
|
||||
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after building with new release")
|
||||
|
@ -980,198 +908,6 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
|
|||
tg.run("install", "std")
|
||||
}
|
||||
|
||||
func TestGoListStandard(t *testing.T) {
|
||||
skipIfGccgo(t, "gccgo does not have GOROOT")
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
// TODO: tg.parallel()
|
||||
tg.cd(runtime.GOROOT() + "/src")
|
||||
tg.run("list", "-f", "{{if not .Standard}}{{.ImportPath}}{{end}}", "./...")
|
||||
stdout := tg.getStdout()
|
||||
for _, line := range strings.Split(stdout, "\n") {
|
||||
if strings.HasPrefix(line, "_/") && strings.HasSuffix(line, "/src") {
|
||||
// $GOROOT/src shows up if there are any .go files there.
|
||||
// We don't care.
|
||||
continue
|
||||
}
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
t.Errorf("package in GOROOT not listed as standard: %v", line)
|
||||
}
|
||||
|
||||
// Similarly, expanding std should include some of our vendored code.
|
||||
tg.run("list", "std", "cmd")
|
||||
tg.grepStdout("golang.org/x/net/http2/hpack", "list std cmd did not mention vendored hpack")
|
||||
tg.grepStdout("golang.org/x/arch/x86/x86asm", "list std cmd did not mention vendored x86asm")
|
||||
}
|
||||
|
||||
func TestGoInstallCleansUpAfterGoBuild(t *testing.T) {
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
// TODO: tg.parallel()
|
||||
tg.tempFile("src/mycmd/main.go", `package main; func main(){}`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.cd(tg.path("src/mycmd"))
|
||||
|
||||
doesNotExist := func(file, msg string) {
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
t.Fatal(msg)
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Fatal(msg, "error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
tg.run("build")
|
||||
tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary")
|
||||
tg.run("install")
|
||||
doesNotExist("mycmd"+exeSuffix, "testgo install did not remove command binary")
|
||||
tg.run("build")
|
||||
tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (second time)")
|
||||
// Running install with arguments does not remove the target,
|
||||
// even in the same directory.
|
||||
tg.run("install", "mycmd")
|
||||
tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary when run in mycmd")
|
||||
tg.run("build")
|
||||
tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (third time)")
|
||||
// And especially not outside the directory.
|
||||
tg.cd(tg.path("."))
|
||||
if data, err := ioutil.ReadFile("src/mycmd/mycmd" + exeSuffix); err != nil {
|
||||
t.Fatal("could not read file:", err)
|
||||
} else {
|
||||
if err := ioutil.WriteFile("mycmd"+exeSuffix, data, 0555); err != nil {
|
||||
t.Fatal("could not write file:", err)
|
||||
}
|
||||
}
|
||||
tg.run("install", "mycmd")
|
||||
tg.wantExecutable("src/mycmd/mycmd"+exeSuffix, "testgo install mycmd removed command binary from its source dir when run outside mycmd")
|
||||
tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary from current dir when run outside mycmd")
|
||||
}
|
||||
|
||||
func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) {
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("d1/src/p1/p1.go", `package p1
|
||||
import "p2"
|
||||
func F() { p2.F() }`)
|
||||
tg.tempFile("d2/src/p2/p2.go", `package p2
|
||||
func F() {}`)
|
||||
sep := string(filepath.ListSeparator)
|
||||
tg.setenv("GOPATH", tg.path("d1")+sep+tg.path("d2"))
|
||||
tg.run("install", "-i", "p1")
|
||||
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale, incorrectly")
|
||||
tg.wantNotStale("p2", "", "./testgo list claims p2 is stale, incorrectly")
|
||||
tg.sleep()
|
||||
if f, err := os.OpenFile(tg.path("d2/src/p2/p2.go"), os.O_WRONLY|os.O_APPEND, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err = f.WriteString(`func G() {}`); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
tg.must(f.Close())
|
||||
}
|
||||
tg.wantStale("p2", "build ID mismatch", "./testgo list claims p2 is NOT stale, incorrectly")
|
||||
tg.wantStale("p1", "stale dependency: p2", "./testgo list claims p1 is NOT stale, incorrectly")
|
||||
|
||||
tg.run("install", "-i", "p1")
|
||||
tg.wantNotStale("p2", "", "./testgo list claims p2 is stale after reinstall, incorrectly")
|
||||
tg.wantNotStale("p1", "", "./testgo list claims p1 is stale after reinstall, incorrectly")
|
||||
}
|
||||
|
||||
func TestGoInstallDetectsRemovedFiles(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("src/mypkg/x.go", `package mypkg`)
|
||||
tg.tempFile("src/mypkg/y.go", `package mypkg`)
|
||||
tg.tempFile("src/mypkg/z.go", `// +build missingtag
|
||||
|
||||
package mypkg`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("install", "mypkg")
|
||||
tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale, incorrectly")
|
||||
// z.go was not part of the build; removing it is okay.
|
||||
tg.must(os.Remove(tg.path("src/mypkg/z.go")))
|
||||
tg.wantNotStale("mypkg", "", "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale")
|
||||
// y.go was part of the package; removing it should be detected.
|
||||
tg.must(os.Remove(tg.path("src/mypkg/y.go")))
|
||||
tg.wantStale("mypkg", "build ID mismatch", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale")
|
||||
}
|
||||
|
||||
func TestWildcardMatchesSyntaxErrorDirs(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
// TODO: tg.parallel()
|
||||
tg.tempFile("src/mypkg/x.go", `package mypkg`)
|
||||
tg.tempFile("src/mypkg/y.go", `pkg mypackage`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.cd(tg.path("src/mypkg"))
|
||||
tg.runFail("list", "./...")
|
||||
tg.runFail("build", "./...")
|
||||
tg.runFail("install", "./...")
|
||||
}
|
||||
|
||||
func TestGoListWithTags(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.tempFile("src/mypkg/x.go", "// +build thetag\n\npackage mypkg\n")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.cd(tg.path("./src"))
|
||||
tg.run("list", "-tags=thetag", "./my...")
|
||||
tg.grepStdout("mypkg", "did not find mypkg")
|
||||
}
|
||||
|
||||
func TestGoInstallErrorOnCrossCompileToBin(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("don't install into GOROOT in short mode")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.tempFile("src/mycmd/x.go", `package main
|
||||
func main() {}`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.cd(tg.path("src/mycmd"))
|
||||
|
||||
tg.run("build", "mycmd")
|
||||
|
||||
goarch := "386"
|
||||
if runtime.GOARCH == "386" {
|
||||
goarch = "amd64"
|
||||
}
|
||||
tg.setenv("GOOS", "linux")
|
||||
tg.setenv("GOARCH", goarch)
|
||||
tg.run("install", "mycmd")
|
||||
tg.setenv("GOBIN", tg.path("."))
|
||||
tg.runFail("install", "mycmd")
|
||||
tg.run("install", "cmd/pack")
|
||||
}
|
||||
|
||||
func TestGoInstallDetectsRemovedFilesInPackageMain(t *testing.T) {
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("src/mycmd/x.go", `package main
|
||||
func main() {}`)
|
||||
tg.tempFile("src/mycmd/y.go", `package main`)
|
||||
tg.tempFile("src/mycmd/z.go", `// +build missingtag
|
||||
|
||||
package main`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("install", "mycmd")
|
||||
tg.wantNotStale("mycmd", "", "./testgo list mypkg claims mycmd is stale, incorrectly")
|
||||
// z.go was not part of the build; removing it is okay.
|
||||
tg.must(os.Remove(tg.path("src/mycmd/z.go")))
|
||||
tg.wantNotStale("mycmd", "", "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale")
|
||||
// y.go was part of the package; removing it should be detected.
|
||||
tg.must(os.Remove(tg.path("src/mycmd/y.go")))
|
||||
tg.wantStale("mycmd", "build ID mismatch", "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale")
|
||||
}
|
||||
|
||||
func testLocalRun(tg *testgoData, exepath, local, match string) {
|
||||
tg.t.Helper()
|
||||
out, err := exec.Command(exepath).Output()
|
||||
|
@ -1450,6 +1186,21 @@ func TestImportCycle(t *testing.T) {
|
|||
tg.run("list", "-e", "-json", "selfimport")
|
||||
}
|
||||
|
||||
func TestListImportMap(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.run("list", "-f", "{{.ImportPath}}: {{.ImportMap}}", "net", "fmt")
|
||||
tg.grepStdout(`^net: map\[(.* )?golang_org/x/net/dns/dnsmessage:vendor/golang_org/x/net/dns/dnsmessage.*\]`, "net/http should have rewritten dnsmessage import")
|
||||
tg.grepStdout(`^fmt: map\[\]`, "fmt should have no rewritten imports")
|
||||
tg.run("list", "-deps", "-test", "-f", "{{.ImportPath}} MAP: {{.ImportMap}}\n{{.ImportPath}} IMPORT: {{.Imports}}", "fmt")
|
||||
tg.grepStdout(`^flag \[fmt\.test\] MAP: map\[fmt:fmt \[fmt\.test\]\]`, "flag [fmt.test] should import fmt [fmt.test] as fmt")
|
||||
tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing")
|
||||
tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing")
|
||||
tg.grepStdoutNot(`^fmt\.test MAP: map\[(.* )?os:`, "fmt.test should not import a modified os")
|
||||
tg.grepStdout(`^fmt\.test IMPORT: \[fmt \[fmt\.test\] fmt_test \[fmt\.test\] os testing \[fmt\.test\] testing/internal/testdeps \[fmt\.test\]\]`, "wrong imports for fmt.test")
|
||||
}
|
||||
|
||||
// cmd/go: custom import path checking should not apply to Go packages without import comment.
|
||||
func TestIssue10952(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
@ -1567,7 +1318,8 @@ func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) {
|
|||
defer tg.cleanup()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.runFail("test", "syntaxerror")
|
||||
tg.grepStderr("FAIL", "go test did not say FAIL")
|
||||
tg.grepStderr("x_test.go:", "did not diagnose error")
|
||||
tg.grepStdout("FAIL", "go test did not say FAIL")
|
||||
}
|
||||
|
||||
func TestWildcardsDoNotLookInUselessDirectories(t *testing.T) {
|
||||
|
@ -2150,6 +1902,17 @@ func homeEnvName() string {
|
|||
}
|
||||
}
|
||||
|
||||
func tempEnvName() string {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return "TMP"
|
||||
case "plan9":
|
||||
return "TMPDIR" // actually plan 9 doesn't have one at all but this is fine
|
||||
default:
|
||||
return "TMPDIR"
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultGOPATH(t *testing.T) {
|
||||
t.Skip("vgo") // Needs a more realistic GOROOT; see RuntimeGoroot below.
|
||||
|
||||
|
@ -2799,6 +2562,8 @@ func TestTestBuildFailureOutput(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCoverageFunc(t *testing.T) {
|
||||
t.Skip("vgo")
|
||||
|
||||
tooSlow(t)
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
@ -3130,7 +2895,6 @@ func TestIssue7573(t *testing.T) {
|
|||
if _, err := exec.LookPath("gccgo"); err != nil {
|
||||
t.Skip("skipping because no gccgo compiler found")
|
||||
}
|
||||
t.Skip("golang.org/issue/22472")
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
@ -3324,43 +3088,6 @@ func TestGoTestBuildsAnXtestContainingOnlyNonRunnableExamples(t *testing.T) {
|
|||
tg.grepStdout("File with non-runnable example was built.", "file with non-runnable example was not built")
|
||||
}
|
||||
|
||||
// issue 24570
|
||||
func TestGoTestCoverMultiPackage(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.run("test", "-cover", "./testdata/testcover/...")
|
||||
tg.grepStdout(`\?.*testdata/testcover/pkg1.*(\d\.\d\d\ds|cached).*coverage:.*0\.0% of statements \[no test files\]`, "expected [no test files] for pkg1")
|
||||
tg.grepStdout(`ok.*testdata/testcover/pkg2.*(\d\.\d\d\ds|cached).*coverage:.*0\.0% of statements \[no tests to run\]`, "expected [no tests to run] for pkg2")
|
||||
tg.grepStdout(`ok.*testdata/testcover/pkg3.*(\d\.\d\d\ds|cached).*coverage:.*100\.0% of statements`, "expected 100% coverage for pkg3")
|
||||
}
|
||||
|
||||
// issue 24570
|
||||
func TestGoTestCoverprofileMultiPackage(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.creatingTemp("testdata/cover.out")
|
||||
tg.run("test", "-coverprofile=testdata/cover.out", "./testdata/testcover/...")
|
||||
tg.grepStdout(`\?.*testdata/testcover/pkg1.*(\d\.\d\d\ds|cached).*coverage:.*0\.0% of statements \[no test files\]`, "expected [no test files] for pkg1")
|
||||
tg.grepStdout(`ok.*testdata/testcover/pkg2.*(\d\.\d\d\ds|cached).*coverage:.*0\.0% of statements \[no tests to run\]`, "expected [no tests to run] for pkg2")
|
||||
tg.grepStdout(`ok.*testdata/testcover/pkg3.*(\d\.\d\d\ds|cached).*coverage:.*100\.0% of statements`, "expected 100% coverage for pkg3")
|
||||
if out, err := ioutil.ReadFile("testdata/cover.out"); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if !bytes.Contains(out, []byte("mode: set")) {
|
||||
t.Errorf(`missing "mode: set" in %s`, out)
|
||||
}
|
||||
if !bytes.Contains(out, []byte(`pkg1/a.go:5.10,7.2 1 0`)) && !bytes.Contains(out, []byte(`pkg1\a.go:5.10,7.2 1 0`)) {
|
||||
t.Errorf(`missing "pkg1/a.go:5.10,7.2 1 0" in %s`, out)
|
||||
}
|
||||
if !bytes.Contains(out, []byte(`pkg2/a.go:5.10,7.2 1 0`)) && !bytes.Contains(out, []byte(`pkg2\a.go:5.10,7.2 1 0`)) {
|
||||
t.Errorf(`missing "pkg2/a.go:5.10,7.2 1 0" in %s`, out)
|
||||
}
|
||||
if !bytes.Contains(out, []byte(`pkg3/a.go:5.10,7.2 1 1`)) && !bytes.Contains(out, []byte(`pkg3\a.go:5.10,7.2 1 1`)) {
|
||||
t.Errorf(`missing "pkg3/a.go:5.10,7.2 1 1" in %s`, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoGenerateHandlesSimpleCommand(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("skipping because windows has no echo command")
|
||||
|
@ -5384,81 +5111,6 @@ func TestQEMUUserMode(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestGOTMPDIR(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.makeTempdir()
|
||||
tg.setenv("GOTMPDIR", tg.tempdir)
|
||||
tg.setenv("GOCACHE", "off")
|
||||
|
||||
// complex/x is a trivial non-main package.
|
||||
tg.run("build", "-work", "-x", "complex/w")
|
||||
tg.grepStderr("WORK="+regexp.QuoteMeta(tg.tempdir), "did not work in $GOTMPDIR")
|
||||
}
|
||||
|
||||
func TestBuildCache(t *testing.T) {
|
||||
tooSlow(t)
|
||||
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.makeTempdir()
|
||||
tg.setenv("GOCACHE", tg.tempdir)
|
||||
|
||||
// complex/w is a trivial non-main package.
|
||||
// It imports nothing, so there should be no Deps.
|
||||
tg.run("list", "-f={{join .Deps \" \"}}", "complex/w")
|
||||
tg.grepStdoutNot(".+", "complex/w depends on unexpected packages")
|
||||
|
||||
tg.run("build", "-x", "complex/w")
|
||||
tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler")
|
||||
|
||||
tg.run("build", "-x", "complex/w")
|
||||
tg.grepStderrNot(`[\\/]compile|gccgo`, "ran compiler incorrectly")
|
||||
|
||||
tg.run("build", "-a", "-x", "complex/w")
|
||||
tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler with -a")
|
||||
|
||||
// complex is a non-trivial main package.
|
||||
// the link step should not be cached.
|
||||
tg.run("build", "-o", os.DevNull, "-x", "complex")
|
||||
tg.grepStderr(`[\\/]link|gccgo`, "did not run linker")
|
||||
|
||||
tg.run("build", "-o", os.DevNull, "-x", "complex")
|
||||
tg.grepStderr(`[\\/]link|gccgo`, "did not run linker")
|
||||
}
|
||||
|
||||
func TestCacheOutput(t *testing.T) {
|
||||
// Test that command output is cached and replayed too.
|
||||
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
|
||||
t.Skip("GODEBUG gocacheverify")
|
||||
}
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.makeTempdir()
|
||||
tg.setenv("GOCACHE", tg.tempdir)
|
||||
|
||||
tg.run("build", "-gcflags=-m", "errors")
|
||||
stdout1 := tg.getStdout()
|
||||
stderr1 := tg.getStderr()
|
||||
|
||||
tg.run("build", "-gcflags=-m", "errors")
|
||||
stdout2 := tg.getStdout()
|
||||
stderr2 := tg.getStderr()
|
||||
|
||||
if stdout2 != stdout1 || stderr2 != stderr1 {
|
||||
t.Errorf("cache did not reproduce output:\n\nstdout1:\n%s\n\nstdout2:\n%s\n\nstderr1:\n%s\n\nstderr2:\n%s",
|
||||
stdout1, stdout2, stderr1, stderr2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheListStale(t *testing.T) {
|
||||
tooSlow(t)
|
||||
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
|
||||
|
@ -5895,6 +5547,11 @@ func TestTestVet(t *testing.T) {
|
|||
tg.runFail("test", "vetfail/...")
|
||||
tg.grepStderr(`Printf format %d`, "did not diagnose bad Printf")
|
||||
tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2")
|
||||
|
||||
// Use -a so that we need to recompute the vet-specific export data for
|
||||
// vetfail/p1.
|
||||
tg.run("test", "-a", "vetfail/p2")
|
||||
tg.grepStderrNot(`invalid.*constraint`, "did diagnose bad build constraint in vetxonly mode")
|
||||
}
|
||||
|
||||
func TestTestVetRebuild(t *testing.T) {
|
||||
|
@ -6414,6 +6071,30 @@ echo $* >>`+tg.path("pkg-config.out"))
|
|||
}
|
||||
}
|
||||
|
||||
func TestCgoCache(t *testing.T) {
|
||||
if !canCgo {
|
||||
t.Skip("no cgo")
|
||||
}
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("src/x/a.go", `package main
|
||||
// #ifndef VAL
|
||||
// #define VAL 0
|
||||
// #endif
|
||||
// int val = VAL;
|
||||
import "C"
|
||||
import "fmt"
|
||||
func main() { fmt.Println(C.val) }
|
||||
`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
exe := tg.path("x.exe")
|
||||
tg.run("build", "-o", exe, "x")
|
||||
tg.setenv("CGO_LDFLAGS", "-lnosuchlibraryexists")
|
||||
tg.runFail("build", "-o", exe, "x")
|
||||
tg.grepStderr(`nosuchlibraryexists`, "did not run linker with changed CGO_LDFLAGS")
|
||||
}
|
||||
|
||||
// Issue 23982
|
||||
func TestFilepathUnderCwdFormat(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
|
@ -6450,15 +6131,14 @@ func TestNoRelativeTmpdir(t *testing.T) {
|
|||
tg.setenv("GOCACHE", "off")
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.setenv("GOTMPDIR", "tmp")
|
||||
tg.runFail("build", "a")
|
||||
tg.grepStderr("relative tmpdir", "wrong error")
|
||||
tg.run("build", "-work", "a")
|
||||
tg.grepStderr("WORK=[^t]", "work should be absolute path")
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
|
||||
tg.unsetenv("GOTMPDIR")
|
||||
tg.setenv("TMPDIR", "tmp")
|
||||
tg.runFail("build", "a")
|
||||
tg.grepStderr("relative tmpdir", "wrong error")
|
||||
}
|
||||
tg.unsetenv("GOTMPDIR")
|
||||
tg.setenv("TMP", "tmp") // windows
|
||||
tg.setenv("TMPDIR", "tmp") // unix
|
||||
tg.run("build", "-work", "a")
|
||||
tg.grepStderr("WORK=[^t]", "work should be absolute path")
|
||||
}
|
||||
|
||||
// Issue 24704.
|
||||
|
@ -6542,6 +6222,16 @@ func TestCDAndGOPATHAreDifferent(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Issue 26242.
|
||||
func TestGoTestWithoutTests(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
tg.run("test", "testnorun")
|
||||
tg.grepStdout(`testnorun\t\[no test files\]`, "do not want test to run")
|
||||
}
|
||||
|
||||
// Issue 25579.
|
||||
func TestGoBuildDashODevNull(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
|
@ -6552,3 +6242,23 @@ func TestGoBuildDashODevNull(t *testing.T) {
|
|||
tg.mustNotExist("hello")
|
||||
tg.mustNotExist("hello.exe")
|
||||
}
|
||||
|
||||
// Issue 25093.
|
||||
func TestCoverpkgTestOnly(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempFile("src/a/a.go", `package a
|
||||
func F(i int) int {
|
||||
return i*i
|
||||
}`)
|
||||
tg.tempFile("src/atest/a_test.go", `
|
||||
package a_test
|
||||
import ( "a"; "testing" )
|
||||
func TestF(t *testing.T) { a.F(2) }
|
||||
`)
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.run("test", "-coverpkg=a", "atest")
|
||||
tg.grepStderrNot("no packages being tested depend on matches", "bad match message")
|
||||
tg.grepStdout("coverage: 100", "no coverage")
|
||||
}
|
||||
|
|
|
@ -35,12 +35,14 @@ See golang.org to learn more about Go.
|
|||
// initDefaultCache does the work of finding the default cache
|
||||
// the first time Default is called.
|
||||
func initDefaultCache() {
|
||||
dir := DefaultDir()
|
||||
dir, showWarnings := defaultDir()
|
||||
if dir == "off" {
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
if showWarnings {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
|
||||
|
@ -50,7 +52,9 @@ func initDefaultCache() {
|
|||
|
||||
c, err := Open(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
if showWarnings {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defaultCache = c
|
||||
|
@ -59,14 +63,24 @@ func initDefaultCache() {
|
|||
// DefaultDir returns the effective GOCACHE setting.
|
||||
// It returns "off" if the cache is disabled.
|
||||
func DefaultDir() string {
|
||||
dir, _ := defaultDir()
|
||||
return dir
|
||||
}
|
||||
|
||||
// defaultDir returns the effective GOCACHE setting.
|
||||
// It returns "off" if the cache is disabled.
|
||||
// The second return value reports whether warnings should
|
||||
// be shown if the cache fails to initialize.
|
||||
func defaultDir() (string, bool) {
|
||||
dir := os.Getenv("GOCACHE")
|
||||
if dir != "" {
|
||||
return dir
|
||||
return dir, true
|
||||
}
|
||||
|
||||
// Compute default location.
|
||||
// TODO(rsc): This code belongs somewhere else,
|
||||
// like maybe ioutil.CacheDir or os.CacheDir.
|
||||
showWarnings := true
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
dir = os.Getenv("LocalAppData")
|
||||
|
@ -76,20 +90,20 @@ func DefaultDir() string {
|
|||
dir = os.Getenv("AppData")
|
||||
}
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
|
||||
case "darwin":
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
dir += "/Library/Caches"
|
||||
|
||||
case "plan9":
|
||||
dir = os.Getenv("home")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
// Plan 9 has no established per-user cache directory,
|
||||
// but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix.
|
||||
|
@ -101,10 +115,15 @@ func DefaultDir() string {
|
|||
if dir == "" {
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
if dir == "/" {
|
||||
// probably docker run with -u flag
|
||||
// https://golang.org/issue/26280
|
||||
showWarnings = false
|
||||
}
|
||||
dir += "/.cache"
|
||||
}
|
||||
}
|
||||
return filepath.Join(dir, "go-build")
|
||||
return filepath.Join(dir, "go-build"), showWarnings
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
// +build !windows,!darwin,!plan9
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultDir(t *testing.T) {
|
||||
goCacheDir := "/tmp/test-go-cache"
|
||||
xdgCacheDir := "/tmp/test-xdg-cache"
|
||||
homeDir := "/tmp/test-home"
|
||||
|
||||
// undo env changes when finished
|
||||
defer func(GOCACHE, XDG_CACHE_HOME, HOME string) {
|
||||
os.Setenv("GOCACHE", GOCACHE)
|
||||
os.Setenv("XDG_CACHE_HOME", XDG_CACHE_HOME)
|
||||
os.Setenv("HOME", HOME)
|
||||
}(os.Getenv("GOCACHE"), os.Getenv("XDG_CACHE_HOME"), os.Getenv("HOME"))
|
||||
|
||||
os.Setenv("GOCACHE", goCacheDir)
|
||||
os.Setenv("XDG_CACHE_HOME", xdgCacheDir)
|
||||
os.Setenv("HOME", homeDir)
|
||||
|
||||
dir, showWarnings := defaultDir()
|
||||
if dir != goCacheDir {
|
||||
t.Errorf("Cache DefaultDir %q should be $GOCACHE %q", dir, goCacheDir)
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $GOCACHE is set")
|
||||
}
|
||||
|
||||
os.Unsetenv("GOCACHE")
|
||||
dir, showWarnings = defaultDir()
|
||||
if !strings.HasPrefix(dir, xdgCacheDir+"/") {
|
||||
t.Errorf("Cache DefaultDir %q should be under $XDG_CACHE_HOME %q when $GOCACHE is unset", dir, xdgCacheDir)
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $XDG_CACHE_HOME is set")
|
||||
}
|
||||
|
||||
os.Unsetenv("XDG_CACHE_HOME")
|
||||
dir, showWarnings = defaultDir()
|
||||
if !strings.HasPrefix(dir, homeDir+"/.cache/") {
|
||||
t.Errorf("Cache DefaultDir %q should be under $HOME/.cache %q when $GOCACHE and $XDG_CACHE_HOME are unset", dir, homeDir+"/.cache")
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $HOME is not /")
|
||||
}
|
||||
|
||||
os.Unsetenv("HOME")
|
||||
if dir, _ := defaultDir(); dir != "off" {
|
||||
t.Error("Cache not disabled when $GOCACHE, $XDG_CACHE_HOME, and $HOME are unset")
|
||||
}
|
||||
|
||||
os.Setenv("HOME", "/")
|
||||
if _, showWarnings := defaultDir(); showWarnings {
|
||||
// https://golang.org/issue/26280
|
||||
t.Error("Cache initalization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /")
|
||||
}
|
||||
}
|
|
@ -74,6 +74,11 @@ var (
|
|||
// in module-aware mode (as opposed to GOPATH mode).
|
||||
// It is equal to modload.Enabled, but not all packages can import modload.
|
||||
ModulesEnabled bool
|
||||
|
||||
// GoModInGOPATH records whether we've found a go.mod in GOPATH/src
|
||||
// in GO111MODULE=auto mode. In that case, we don't use modules
|
||||
// but people might expect us to, so 'go get' warns.
|
||||
GoModInGOPATH string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -182,6 +187,11 @@ func findGOROOT1() string {
|
|||
return filepath.Clean(env)
|
||||
}
|
||||
def := filepath.Clean(runtime.GOROOT())
|
||||
if runtime.Compiler == "gccgo" {
|
||||
// gccgo has no real GOROOT, and it certainly doesn't
|
||||
// depend on the executable's location.
|
||||
return def
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
if err == nil {
|
||||
exe, err = filepath.Abs(exe)
|
||||
|
@ -231,6 +241,8 @@ func isSameDir(dir1, dir2 string) bool {
|
|||
// It does this by looking for the path/pkg/tool directory,
|
||||
// which is necessary for useful operation of the cmd/go tool,
|
||||
// and is not typically present in a GOPATH.
|
||||
//
|
||||
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
|
||||
func isGOROOT(path string) bool {
|
||||
stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
|
||||
if err != nil {
|
||||
|
|
|
@ -7,10 +7,10 @@ const DefaultPkgConfig = `pkg-config`
|
|||
func DefaultCC(goos, goarch string) string {
|
||||
switch goos + `/` + goarch {
|
||||
}
|
||||
return CC
|
||||
return "clang"
|
||||
}
|
||||
func DefaultCXX(goos, goarch string) string {
|
||||
switch goos + `/` + goarch {
|
||||
}
|
||||
return CXX
|
||||
return "clang++"
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ var OSArchSupportsCgo = map[string]bool{
|
|||
"linux/mipsle": true,
|
||||
"linux/ppc64": false,
|
||||
"linux/ppc64le": true,
|
||||
"linux/riscv64": true,
|
||||
"linux/s390x": true,
|
||||
"nacl/386": false,
|
||||
"nacl/amd64p32": false,
|
||||
|
|
|
@ -17,11 +17,12 @@ import (
|
|||
"cmd/go/internal/cache"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdClean = &base.Command{
|
||||
UsageLine: "clean [-i] [-r] [-n] [-x] [-cache] [-testcache] [build flags] [packages]",
|
||||
UsageLine: "clean [clean flags] [build flags] [packages]",
|
||||
Short: "remove object files and cached files",
|
||||
Long: `
|
||||
Clean removes object files from package source directories.
|
||||
|
@ -65,6 +66,10 @@ The -cache flag causes clean to remove the entire go build cache.
|
|||
The -testcache flag causes clean to expire all test results in the
|
||||
go build cache.
|
||||
|
||||
The -modcache flag causes clean to remove the entire module
|
||||
download cache, including unpacked source code of versioned
|
||||
dependencies.
|
||||
|
||||
For more about build flags, see 'go help build'.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
|
@ -75,6 +80,7 @@ var (
|
|||
cleanI bool // clean -i flag
|
||||
cleanR bool // clean -r flag
|
||||
cleanCache bool // clean -cache flag
|
||||
cleanModcache bool // clean -modcache flag
|
||||
cleanTestcache bool // clean -testcache flag
|
||||
)
|
||||
|
||||
|
@ -85,6 +91,7 @@ func init() {
|
|||
CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
|
||||
|
||||
// -n and -x are important enough to be
|
||||
|
@ -138,6 +145,29 @@ func runClean(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cleanModcache {
|
||||
if modfetch.SrcMod == "" {
|
||||
base.Fatalf("go clean -modcache: no module cache")
|
||||
}
|
||||
if err := removeAll(modfetch.SrcMod); err != nil {
|
||||
base.Errorf("go clean -modcache: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeAll(dir string) error {
|
||||
// Module cache has 0555 directories; make them writable in order to remove content.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil // ignore errors walking in file system
|
||||
}
|
||||
if info.IsDir() {
|
||||
os.Chmod(path, 0777)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
var cleaned = map[*load.Package]bool{}
|
||||
|
|
|
@ -63,9 +63,6 @@ func MkEnv() []cfg.EnvVar {
|
|||
{Name: "GOROOT", Value: cfg.GOROOT},
|
||||
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
|
||||
{Name: "GOTOOLDIR", Value: base.ToolDir},
|
||||
|
||||
// disable escape codes in clang errors
|
||||
{Name: "TERM", Value: "dumb"},
|
||||
}
|
||||
|
||||
if work.GccgoBin != "" {
|
||||
|
|
|
@ -48,6 +48,12 @@ that can be run locally. It must either be in the shell path
|
|||
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
|
||||
command alias, described below.
|
||||
|
||||
To convey to humans and machine tools that code is generated,
|
||||
generated source should have a line early in the file that
|
||||
matches the following regular expression (in Go syntax):
|
||||
|
||||
^// Code generated .* DO NOT EDIT\.$
|
||||
|
||||
Note that go generate does not parse the file, so lines that look
|
||||
like directives in comments or multiline strings will be treated
|
||||
as directives.
|
||||
|
|
|
@ -118,6 +118,11 @@ func runGet(cmd *base.Command, args []string) {
|
|||
// Should not happen: main.go should install the separate module-enabled get code.
|
||||
base.Fatalf("go get: modules not implemented")
|
||||
}
|
||||
if cfg.GoModInGOPATH != "" {
|
||||
// Warn about not using modules with GO111MODULE=auto when go.mod exists.
|
||||
// To silence the warning, users can set GO111MODULE=off.
|
||||
fmt.Fprintf(os.Stderr, "go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;\n\tignoring %s;\n\tsee 'go help modules'\n", base.ShortPath(cfg.GoModInGOPATH))
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
|
||||
|
|
|
@ -98,6 +98,23 @@ var parseMetaGoImportsTests = []struct {
|
|||
IgnoreMod,
|
||||
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
|
||||
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
|
||||
`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
|
||||
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
|
||||
`,
|
||||
PreferMod,
|
||||
[]metaImport{
|
||||
{"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{"myitcv.io", "git", "https://github.com/myitcv/x"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseMetaGoImports(t *testing.T) {
|
||||
|
|
|
@ -948,7 +948,13 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error)
|
|||
continue
|
||||
}
|
||||
|
||||
if match != -1 {
|
||||
if match >= 0 {
|
||||
if imports[match].VCS == "mod" && im.VCS != "mod" {
|
||||
// All the mod entries precede all the non-mod entries.
|
||||
// We have a mod entry and don't care about the rest,
|
||||
// matching or not.
|
||||
break
|
||||
}
|
||||
return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath)
|
||||
}
|
||||
match = i
|
||||
|
|
|
@ -401,6 +401,22 @@ func TestMatchGoImport(t *testing.T) {
|
|||
path: "different.example.com/user/foo",
|
||||
err: errors.New("meta tags do not match import path"),
|
||||
},
|
||||
{
|
||||
imports: []metaImport{
|
||||
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
path: "myitcv.io/blah2/foo",
|
||||
mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
},
|
||||
{
|
||||
imports: []metaImport{
|
||||
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
path: "myitcv.io/other",
|
||||
mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
|
@ -39,7 +39,7 @@ func Help(args []string) {
|
|||
fmt.Println("// Use of this source code is governed by a BSD-style")
|
||||
fmt.Println("// license that can be found in the LICENSE file.")
|
||||
fmt.Println()
|
||||
fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.")
|
||||
fmt.Println("// Code generated by mkalldocs.sh; DO NOT EDIT.")
|
||||
fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
|
||||
fmt.Println()
|
||||
buf := new(bytes.Buffer)
|
||||
|
|
|
@ -195,6 +195,7 @@ using the named version control system, and then the path inside
|
|||
that repository. The supported version control systems are:
|
||||
|
||||
Bazaar .bzr
|
||||
Fossil .fossil
|
||||
Git .git
|
||||
Mercurial .hg
|
||||
Subversion .svn
|
||||
|
@ -238,7 +239,7 @@ The meta tag should appear as early in the file as possible.
|
|||
In particular, it should appear before any raw JavaScript or CSS,
|
||||
to avoid confusing the go command's restricted parser.
|
||||
|
||||
The vcs is one of "git", "hg", "svn", etc,
|
||||
The vcs is one of "bzr", "fossil", "git", "hg", "svn".
|
||||
|
||||
The repo-root is the root of the version control system
|
||||
containing a scheme and not containing a .vcs qualifier.
|
||||
|
@ -260,12 +261,22 @@ the go tool will verify that https://example.org/?go-get=1 contains the
|
|||
same meta tag and then git clone https://code.org/r/p/exproj into
|
||||
GOPATH/src/example.org.
|
||||
|
||||
New downloaded packages are written to the first directory listed in the GOPATH
|
||||
environment variable (For more details see: 'go help gopath').
|
||||
When using GOPATH, downloaded packages are written to the first directory
|
||||
listed in the GOPATH environment variable.
|
||||
(See 'go help gopath-get' and 'go help gopath'.)
|
||||
|
||||
The go command attempts to download the version of the
|
||||
package appropriate for the Go release being used.
|
||||
Run 'go help get' for more.
|
||||
When using modules, downloaded packages are stored in the module cache.
|
||||
(See 'go help modules-get' and 'go help goproxy'.)
|
||||
|
||||
When using modules, an additional variant of the go-import meta tag is
|
||||
recognized and is preferred over those listing version control systems.
|
||||
That variant uses "mod" as the vcs in the content value, as in:
|
||||
|
||||
<meta name="go-import" content="example.org mod https://code.org/moduleproxy">
|
||||
|
||||
This tag means to fetch modules with paths beginning with example.org
|
||||
from the module proxy available at the URL https://code.org/moduleproxy.
|
||||
See 'go help goproxy' for details about the proxy protocol.
|
||||
|
||||
Import path checking
|
||||
|
||||
|
@ -288,6 +299,9 @@ Import path checking is disabled for code found within vendor trees.
|
|||
This makes it possible to copy code into alternate locations in vendor trees
|
||||
without needing to update import comments.
|
||||
|
||||
Import path checking is also disabled when using modules.
|
||||
Import path comments are obsoleted by the go.mod file's module statement.
|
||||
|
||||
See https://golang.org/s/go14customimport for details.
|
||||
`,
|
||||
}
|
||||
|
@ -360,6 +374,12 @@ in the list.
|
|||
|
||||
See https://golang.org/doc/code.html for an example.
|
||||
|
||||
GOPATH and Modules
|
||||
|
||||
When using modules, GOPATH is no longer used for resolving imports.
|
||||
However, it is still used to store downloaded source code (in GOPATH/src/mod)
|
||||
and compiled commands (in GOPATH/bin).
|
||||
|
||||
Internal Directories
|
||||
|
||||
Code in or below a directory named "internal" is importable only
|
||||
|
@ -471,6 +491,8 @@ General-purpose environment variables:
|
|||
Examples are linux, darwin, windows, netbsd.
|
||||
GOPATH
|
||||
For more details see: 'go help gopath'.
|
||||
GOPROXY
|
||||
URL of Go module proxy. See 'go help goproxy'.
|
||||
GORACE
|
||||
Options for the race detector.
|
||||
See https://golang.org/doc/articles/race_detector.html.
|
||||
|
@ -660,6 +682,7 @@ The default location for cache data is a subdirectory named go-build
|
|||
in the standard user cache directory for the current operating system.
|
||||
Setting the GOCACHE environment variable overrides this default,
|
||||
and running 'go env GOCACHE' prints the current cache directory.
|
||||
You can set the variable to 'off' to disable the cache.
|
||||
|
||||
The go command periodically deletes cached data that has not been
|
||||
used recently. Running 'go clean -cache' deletes all cached data.
|
||||
|
|
|
@ -183,27 +183,27 @@ func MatchFile(name string, tags map[string]bool) bool {
|
|||
l = l[:n-1]
|
||||
}
|
||||
n := len(l)
|
||||
if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
|
||||
if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
|
||||
return tags[l[n-2]] && tags[l[n-1]]
|
||||
}
|
||||
if n >= 1 && knownOS[l[n-1]] {
|
||||
if n >= 1 && KnownOS[l[n-1]] {
|
||||
return tags[l[n-1]]
|
||||
}
|
||||
if n >= 1 && knownArch[l[n-1]] {
|
||||
if n >= 1 && KnownArch[l[n-1]] {
|
||||
return tags[l[n-1]]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var knownOS = make(map[string]bool)
|
||||
var knownArch = make(map[string]bool)
|
||||
var KnownOS = make(map[string]bool)
|
||||
var KnownArch = make(map[string]bool)
|
||||
|
||||
func init() {
|
||||
for _, v := range strings.Fields(goosList) {
|
||||
knownOS[v] = true
|
||||
KnownOS[v] = true
|
||||
}
|
||||
for _, v := range strings.Fields(goarchList) {
|
||||
knownArch[v] = true
|
||||
KnownArch[v] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package imports
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
@ -12,6 +13,8 @@ import (
|
|||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -50,6 +53,8 @@ func TestScan(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestScanStar(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -90,10 +90,11 @@ to -f '{{.ImportPath}}'. The struct being passed to the template is:
|
|||
CgoPkgConfig []string // cgo: pkg-config names
|
||||
|
||||
// Dependency information
|
||||
Imports []string // import paths used by this package
|
||||
Deps []string // all (recursively) imported dependencies
|
||||
TestImports []string // imports from TestGoFiles
|
||||
XTestImports []string // imports from XTestGoFiles
|
||||
Imports []string // import paths used by this package
|
||||
ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
|
||||
Deps []string // all (recursively) imported dependencies
|
||||
TestImports []string // imports from TestGoFiles
|
||||
XTestImports []string // imports from XTestGoFiles
|
||||
|
||||
// Error information
|
||||
Incomplete bool // this package or a dependency has an error
|
||||
|
@ -501,22 +502,30 @@ func runList(cmd *base.Command, args []string) {
|
|||
// This must happen only once the build code is done
|
||||
// looking at import paths, because it will get very confused
|
||||
// if it sees these.
|
||||
old := make(map[string]string)
|
||||
for _, p := range all {
|
||||
if p.ForTest != "" {
|
||||
p.ImportPath += " [" + p.ForTest + ".test]"
|
||||
new := p.ImportPath + " [" + p.ForTest + ".test]"
|
||||
old[new] = p.ImportPath
|
||||
p.ImportPath = new
|
||||
}
|
||||
p.DepOnly = !cmdline[p]
|
||||
}
|
||||
// Update import path lists to use new strings.
|
||||
m := make(map[string]string)
|
||||
for _, p := range all {
|
||||
j := 0
|
||||
for i := range p.Imports {
|
||||
// Internal skips "C"
|
||||
if p.Imports[i] == "C" {
|
||||
continue
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
if p1.ForTest != "" {
|
||||
m[old[p1.ImportPath]] = p1.ImportPath
|
||||
}
|
||||
p.Imports[i] = p.Internal.Imports[j].ImportPath
|
||||
j++
|
||||
}
|
||||
for i, old := range p.Imports {
|
||||
if new := m[old]; new != "" {
|
||||
p.Imports[i] = new
|
||||
}
|
||||
}
|
||||
for old := range m {
|
||||
delete(m, old)
|
||||
}
|
||||
}
|
||||
// Recompute deps lists using new strings, from the leaves up.
|
||||
|
@ -536,6 +545,19 @@ func runList(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Record non-identity import mappings in p.ImportMap.
|
||||
for _, p := range pkgs {
|
||||
for i, srcPath := range p.Internal.RawImports {
|
||||
path := p.Imports[i]
|
||||
if path != srcPath {
|
||||
if p.ImportMap == nil {
|
||||
p.ImportMap = make(map[string]string)
|
||||
}
|
||||
p.ImportMap[srcPath] = path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range pkgs {
|
||||
do(&p.PackagePublic)
|
||||
}
|
||||
|
|
|
@ -98,8 +98,9 @@ type PackagePublic struct {
|
|||
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
|
||||
|
||||
// Dependency information
|
||||
Imports []string `json:",omitempty"` // import paths used by this package
|
||||
Deps []string `json:",omitempty"` // all (recursively) imported dependencies
|
||||
Imports []string `json:",omitempty"` // import paths used by this package
|
||||
ImportMap map[string]string `json:",omitempty"` // map from source import to ImportPath (identity entries omitted)
|
||||
Deps []string `json:",omitempty"` // all (recursively) imported dependencies
|
||||
|
||||
// Error information
|
||||
Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
|
||||
|
@ -1595,7 +1596,7 @@ func (p *Package) UsesCgo() bool {
|
|||
return len(p.CgoFiles) > 0
|
||||
}
|
||||
|
||||
// packageList returns the list of packages in the dag rooted at roots
|
||||
// PackageList returns the list of packages in the dag rooted at roots
|
||||
// as visited in a depth-first post-order traversal.
|
||||
func PackageList(roots []*Package) []*Package {
|
||||
seen := map[*Package]bool{}
|
||||
|
@ -1617,6 +1618,42 @@ func PackageList(roots []*Package) []*Package {
|
|||
return all
|
||||
}
|
||||
|
||||
// TestPackageList returns the list of packages in the dag rooted at roots
|
||||
// as visited in a depth-first post-order traversal, including the test
|
||||
// imports of the roots. This ignores errors in test packages.
|
||||
func TestPackageList(roots []*Package) []*Package {
|
||||
seen := map[*Package]bool{}
|
||||
all := []*Package{}
|
||||
var walk func(*Package)
|
||||
walk = func(p *Package) {
|
||||
if seen[p] {
|
||||
return
|
||||
}
|
||||
seen[p] = true
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
walk(p1)
|
||||
}
|
||||
all = append(all, p)
|
||||
}
|
||||
walkTest := func(root *Package, path string) {
|
||||
var stk ImportStack
|
||||
p1 := LoadImport(path, root.Dir, root, &stk, root.Internal.Build.TestImportPos[path], ResolveImport)
|
||||
if p1.Error == nil {
|
||||
walk(p1)
|
||||
}
|
||||
}
|
||||
for _, root := range roots {
|
||||
walk(root)
|
||||
for _, path := range root.TestImports {
|
||||
walkTest(root, path)
|
||||
}
|
||||
for _, path := range root.XTestImports {
|
||||
walkTest(root, path)
|
||||
}
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
var cmdCache = map[string]*Package{}
|
||||
|
||||
func ClearCmdCache() {
|
||||
|
|
|
@ -6,6 +6,7 @@ package load
|
|||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/search"
|
||||
|
@ -27,7 +28,13 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
|
|||
}
|
||||
dir = filepath.Join(cwd, dir)
|
||||
if pattern == "" {
|
||||
return func(p *Package) bool { return p.Dir == dir }
|
||||
return func(p *Package) bool {
|
||||
// TODO(rsc): This is wrong. See golang.org/issue/25878.
|
||||
if runtime.GOOS != "windows" {
|
||||
return p.Dir == dir
|
||||
}
|
||||
return strings.EqualFold(p.Dir, dir)
|
||||
}
|
||||
}
|
||||
matchPath := search.MatchPattern(pattern)
|
||||
return func(p *Package) bool {
|
||||
|
|
|
@ -36,7 +36,7 @@ type TestCover struct {
|
|||
Pkgs []*Package
|
||||
Paths []string
|
||||
Vars []coverInfo
|
||||
DeclVars func(string, ...string) map[string]*CoverVar
|
||||
DeclVars func(*Package, ...string) map[string]*CoverVar
|
||||
}
|
||||
|
||||
// TestPackagesFor returns three packages:
|
||||
|
@ -49,6 +49,9 @@ type TestCover struct {
|
|||
// (for example, if there are no "package p" test files and
|
||||
// package p need not be instrumented for coverage or any other reason),
|
||||
// then the returned ptest == p.
|
||||
//
|
||||
// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0,
|
||||
// or else there's no point in any of this.
|
||||
func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) {
|
||||
var imports, ximports []*Package
|
||||
var stk ImportStack
|
||||
|
@ -111,16 +114,17 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
|
|||
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
|
||||
ptest.Target = ""
|
||||
// Note: The preparation of the vet config requires that common
|
||||
// indexes in ptest.Imports, ptest.Internal.Imports, and ptest.Internal.RawImports
|
||||
// indexes in ptest.Imports and ptest.Internal.RawImports
|
||||
// all line up (but RawImports can be shorter than the others).
|
||||
// That is, for 0 ≤ i < len(RawImports),
|
||||
// RawImports[i] is the import string in the program text,
|
||||
// Imports[i] is the expanded import string (vendoring applied or relative path expanded away),
|
||||
// and Internal.Imports[i] is the corresponding *Package.
|
||||
// RawImports[i] is the import string in the program text, and
|
||||
// Imports[i] is the expanded import string (vendoring applied or relative path expanded away).
|
||||
// Any implicitly added imports appear in Imports and Internal.Imports
|
||||
// but not RawImports (because they were not in the source code).
|
||||
// We insert TestImports, imports, and rawTestImports at the start of
|
||||
// these lists to preserve the alignment.
|
||||
// Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports,
|
||||
// but we insert at the beginning there too just for consistency.
|
||||
ptest.Imports = str.StringList(p.TestImports, p.Imports)
|
||||
ptest.Internal.Imports = append(imports, p.Internal.Imports...)
|
||||
ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
|
||||
|
@ -178,6 +182,7 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
|
|||
GoFiles: []string{"_testmain.go"},
|
||||
ImportPath: p.ImportPath + ".test",
|
||||
Root: p.Root,
|
||||
Imports: str.StringList(TestMainDeps),
|
||||
},
|
||||
Internal: PackageInternal{
|
||||
Build: &build.Package{Name: "main"},
|
||||
|
@ -233,13 +238,28 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
|
|||
t.Cover = cover
|
||||
if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
|
||||
pmain.Imports = append(pmain.Imports, ptest.ImportPath)
|
||||
t.ImportTest = true
|
||||
}
|
||||
if pxtest != nil {
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
|
||||
pmain.Imports = append(pmain.Imports, pxtest.ImportPath)
|
||||
t.ImportXtest = true
|
||||
}
|
||||
|
||||
// Sort and dedup pmain.Imports.
|
||||
// Only matters for go list -test output.
|
||||
sort.Strings(pmain.Imports)
|
||||
w := 0
|
||||
for _, path := range pmain.Imports {
|
||||
if w == 0 || path != pmain.Imports[w-1] {
|
||||
pmain.Imports[w] = path
|
||||
w++
|
||||
}
|
||||
}
|
||||
pmain.Imports = pmain.Imports[:w]
|
||||
pmain.Internal.RawImports = str.StringList(pmain.Imports)
|
||||
|
||||
if ptest != p {
|
||||
// We have made modifications to the package p being tested
|
||||
// and are rebuilding p (as ptest).
|
||||
|
@ -261,7 +281,7 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
|
|||
var coverFiles []string
|
||||
coverFiles = append(coverFiles, ptest.GoFiles...)
|
||||
coverFiles = append(coverFiles, ptest.CgoFiles...)
|
||||
ptest.Internal.CoverVars = cover.DeclVars(ptest.ImportPath, coverFiles...)
|
||||
ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...)
|
||||
}
|
||||
|
||||
for _, cp := range pmain.Internal.Imports {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
|
@ -154,7 +155,8 @@ effectively imply 'go mod -fix'.
|
|||
The -sync flag synchronizes go.mod with the source code in the module.
|
||||
It adds any missing modules necessary to build the current module's
|
||||
packages and dependencies, and it removes unused modules that
|
||||
don't provide any relevant packages.
|
||||
don't provide any relevant packages. It also adds any missing entries
|
||||
to go.sum and removes any unnecessary ones.
|
||||
|
||||
The -vendor flag resets the module's vendor directory to include all
|
||||
packages needed to build and test all the module's packages.
|
||||
|
@ -207,7 +209,7 @@ func init() {
|
|||
|
||||
func runMod(cmd *base.Command, args []string) {
|
||||
if modload.Init(); !modload.Enabled() {
|
||||
base.Fatalf("go mod: cannot use outside module")
|
||||
base.Fatalf("go mod: cannot use outside module; see 'go help modules'")
|
||||
}
|
||||
if len(args) != 0 {
|
||||
base.Fatalf("go mod: mod takes no arguments")
|
||||
|
@ -291,6 +293,7 @@ func runMod(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
modload.SetBuildList(keep)
|
||||
modSyncGoSum() // updates memory copy; WriteGoMod on next line flushes it out
|
||||
}
|
||||
modload.WriteGoMod()
|
||||
if *modVendor {
|
||||
|
@ -400,7 +403,7 @@ func flagReplace(arg string) {
|
|||
if i = strings.Index(arg, "="); i < 0 {
|
||||
base.Fatalf("go mod: -replace=%s: need old@v=new[@v] (missing =)", arg)
|
||||
}
|
||||
old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+2:])
|
||||
old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
|
||||
if strings.HasPrefix(new, ">") {
|
||||
base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
|
||||
}
|
||||
|
@ -530,3 +533,24 @@ func modPrintGraph() {
|
|||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
// modSyncGoSum resets the go.sum file content
|
||||
// to be exactly what's needed for the current go.mod.
|
||||
func modSyncGoSum() {
|
||||
// Assuming go.sum already has at least enough from the successful load,
|
||||
// we only have to tell modfetch what needs keeping.
|
||||
reqs := modload.Reqs()
|
||||
keep := make(map[module.Version]bool)
|
||||
var walk func(module.Version)
|
||||
walk = func(m module.Version) {
|
||||
keep[m] = true
|
||||
list, _ := reqs.Required(m)
|
||||
for _, r := range list {
|
||||
if !keep[r] {
|
||||
walk(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(modload.Target)
|
||||
modfetch.TrimGoSum(keep)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/dirhash"
|
||||
|
@ -30,10 +29,14 @@ func runVerify() {
|
|||
|
||||
func verifyMod(mod module.Version) bool {
|
||||
ok := true
|
||||
zip := filepath.Join(modfetch.SrcMod, "cache/download", mod.Path, "/@v/", mod.Version+".zip")
|
||||
_, zipErr := os.Stat(zip)
|
||||
dir := filepath.Join(modfetch.SrcMod, mod.Path+"@"+mod.Version)
|
||||
_, dirErr := os.Stat(dir)
|
||||
zip, zipErr := modfetch.CachePath(mod, "zip")
|
||||
if zipErr == nil {
|
||||
_, zipErr = os.Stat(zip)
|
||||
}
|
||||
dir, dirErr := modfetch.DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
_, dirErr = os.Stat(dir)
|
||||
}
|
||||
data, err := ioutil.ReadFile(zip + "hash")
|
||||
if err != nil {
|
||||
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
|
||||
|
|
|
@ -63,7 +63,10 @@ func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
|
|||
}
|
||||
mu.Lock()
|
||||
path := repo.ModulePath()
|
||||
need[path] = semver.Max(need[path], info.Version)
|
||||
// Don't use semver.Max here; need to preserve +incompatible suffix.
|
||||
if v, ok := need[path]; !ok || semver.Compare(v, info.Version) < 0 {
|
||||
need[path] = info.Version
|
||||
}
|
||||
mu.Unlock()
|
||||
})
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ func TestConvertLegacyConfig(t *testing.T) {
|
|||
cloud.google.com/go v0.18.0
|
||||
github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
|
||||
github.com/golang/protobuf v1.0.0
|
||||
github.com/googleapis/gax-go v0.0.0-20170915024731-317e0006254c
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible
|
||||
golang.org/x/net v0.0.0-20180216171745-136a25c244d3
|
||||
golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
|
||||
golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
)
|
||||
|
@ -23,6 +24,48 @@ var QuietLookup bool // do not print about lookups
|
|||
|
||||
var SrcMod string // $GOPATH/src/mod; set by package modload
|
||||
|
||||
func cacheDir(path string) (string, error) {
|
||||
if SrcMod == "" {
|
||||
return "", fmt.Errorf("internal error: modfetch.SrcMod not set")
|
||||
}
|
||||
enc, err := module.EncodePath(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(SrcMod, "cache/download", enc, "/@v"), nil
|
||||
}
|
||||
|
||||
func CachePath(m module.Version, suffix string) (string, error) {
|
||||
dir, err := cacheDir(m.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !semver.IsValid(m.Version) {
|
||||
return "", fmt.Errorf("non-semver module version %q", m.Version)
|
||||
}
|
||||
if module.CanonicalVersion(m.Version) != m.Version {
|
||||
return "", fmt.Errorf("non-canonical module version %q", m.Version)
|
||||
}
|
||||
return filepath.Join(dir, m.Version+"."+suffix), nil
|
||||
}
|
||||
|
||||
func DownloadDir(m module.Version) (string, error) {
|
||||
if SrcMod == "" {
|
||||
return "", fmt.Errorf("internal error: modfetch.SrcMod not set")
|
||||
}
|
||||
enc, err := module.EncodePath(m.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !semver.IsValid(m.Version) {
|
||||
return "", fmt.Errorf("non-semver module version %q", m.Version)
|
||||
}
|
||||
if module.CanonicalVersion(m.Version) != m.Version {
|
||||
return "", fmt.Errorf("non-canonical module version %q", m.Version)
|
||||
}
|
||||
return filepath.Join(SrcMod, enc+"@"+m.Version), nil
|
||||
}
|
||||
|
||||
// A cachingRepo is a cache around an underlying Repo,
|
||||
// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip).
|
||||
// It is also safe for simultaneous use by multiple goroutines
|
||||
|
@ -245,7 +288,11 @@ func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error
|
|||
return "", nil, errNotCached
|
||||
}
|
||||
rev = rev[:12]
|
||||
dir, err := os.Open(filepath.Join(SrcMod, "cache/download", path, "@v"))
|
||||
cdir, err := cacheDir(path)
|
||||
if err != nil {
|
||||
return "", nil, errNotCached
|
||||
}
|
||||
dir, err := os.Open(cdir)
|
||||
if err != nil {
|
||||
return "", nil, errNotCached
|
||||
}
|
||||
|
@ -296,10 +343,10 @@ func readDiskGoMod(path, rev string) (file string, data []byte, err error) {
|
|||
// If the read fails, the caller can use
|
||||
// writeDiskCache(file, data) to write a new cache entry.
|
||||
func readDiskCache(path, rev, suffix string) (file string, data []byte, err error) {
|
||||
if !semver.IsValid(rev) || SrcMod == "" {
|
||||
file, err = CachePath(module.Version{Path: path, Version: rev}, suffix)
|
||||
if err != nil {
|
||||
return "", nil, errNotCached
|
||||
}
|
||||
file = filepath.Join(SrcMod, "cache/download", path, "@v", rev+"."+suffix)
|
||||
data, err = ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return file, nil, errNotCached
|
||||
|
@ -386,7 +433,7 @@ func rewriteVersionList(dir string) {
|
|||
name := info.Name()
|
||||
if strings.HasSuffix(name, ".mod") {
|
||||
v := strings.TrimSuffix(name, ".mod")
|
||||
if semver.IsValid(v) && semver.Canonical(v) == v {
|
||||
if v != "" && module.CanonicalVersion(v) == v {
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,21 @@ type Repo interface {
|
|||
// os.IsNotExist(err) returns true.
|
||||
ReadFile(rev, file string, maxSize int64) (data []byte, err error)
|
||||
|
||||
// ReadFileRevs reads a single file at multiple versions.
|
||||
// It should refuse to read more than maxSize bytes.
|
||||
// The result is a map from each requested rev strings
|
||||
// to the associated FileRev. The map must have a non-nil
|
||||
// entry for every requested rev (unless ReadFileRevs returned an error).
|
||||
// A file simply being missing or even corrupted in revs[i]
|
||||
// should be reported only in files[revs[i]].Err, not in the error result
|
||||
// from ReadFileRevs.
|
||||
// The overall call should return an error (and no map) only
|
||||
// in the case of a problem with obtaining the data, such as
|
||||
// a network failure.
|
||||
// Implementations may assume that revs only contain tags,
|
||||
// not direct commit hashes.
|
||||
ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error)
|
||||
|
||||
// ReadZip downloads a zip file for the subdir subdirectory
|
||||
// of the given revision to a new file in a given temporary directory.
|
||||
// It should refuse to read more than maxSize bytes.
|
||||
|
@ -62,6 +77,15 @@ type Repo interface {
|
|||
// contained in the zip file. All files in the zip file are expected to be
|
||||
// nested in a single top-level directory, whose name is not specified.
|
||||
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
|
||||
|
||||
// RecentTag returns the most recent tag at or before the given rev
|
||||
// with the given prefix. It should make a best-effort attempt to
|
||||
// find a tag that is a valid semantic version (following the prefix),
|
||||
// or else the result is not useful to the caller, but it need not
|
||||
// incur great expense in doing so. For example, the git implementation
|
||||
// of RecentTag limits git's search to tags matching the glob expression
|
||||
// "v[0-9]*.[0-9]*.[0-9]*" (after the prefix).
|
||||
RecentTag(rev, prefix string) (tag string, err error)
|
||||
}
|
||||
|
||||
// A Rev describes a single revision in a source code repository.
|
||||
|
@ -73,6 +97,13 @@ type RevInfo struct {
|
|||
Tags []string // known tags for commit
|
||||
}
|
||||
|
||||
// A FileRev describes the result of reading a file at a given revision.
|
||||
type FileRev struct {
|
||||
Rev string // requested revision
|
||||
Data []byte // file data
|
||||
Err error // error if any; os.IsNotExist(Err)==true if rev exists but file does not exist in that rev
|
||||
}
|
||||
|
||||
// AllHex reports whether the revision rev is entirely lower-case hexadecimal digits.
|
||||
func AllHex(rev string) bool {
|
||||
for i := 0; i < len(rev); i++ {
|
||||
|
@ -167,6 +198,10 @@ var dirLock sync.Map
|
|||
// a *RunError indicating the command, exit status, and standard error.
|
||||
// Standard error is unavailable for commands that exit successfully.
|
||||
func Run(dir string, cmdline ...interface{}) ([]byte, error) {
|
||||
return RunWithStdin(dir, nil, cmdline...)
|
||||
}
|
||||
|
||||
func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) {
|
||||
if dir != "" {
|
||||
muIface, ok := dirLock.Load(dir)
|
||||
if !ok {
|
||||
|
@ -196,6 +231,7 @@ func Run(dir string, cmdline ...interface{}) ([]byte, error) {
|
|||
var stdout bytes.Buffer
|
||||
c := exec.Command(cmd[0], cmd[1:]...)
|
||||
c.Dir = dir
|
||||
c.Stdin = stdin
|
||||
c.Stderr = &stderr
|
||||
c.Stdout = &stdout
|
||||
err := c.Run()
|
||||
|
|
|
@ -245,10 +245,12 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
|
|||
}
|
||||
|
||||
// Fast path: maybe rev is a hash we already have locally.
|
||||
didStatLocal := false
|
||||
if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
|
||||
if info, err := r.statLocal(rev, rev); err == nil {
|
||||
return info, nil
|
||||
}
|
||||
didStatLocal = true
|
||||
}
|
||||
|
||||
// Maybe rev is a tag we already have locally.
|
||||
|
@ -308,11 +310,25 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
|
|||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
// Perhaps r.localTags did not have the ref when we loaded local tags,
|
||||
// but we've since done fetches that pulled down the hash we need
|
||||
// (or already have the hash we need, just without its tag).
|
||||
// Either way, try a local stat before falling back to network I/O.
|
||||
if !didStatLocal {
|
||||
if info, err := r.statLocal(rev, hash); err == nil {
|
||||
if strings.HasPrefix(ref, "refs/tags/") {
|
||||
// Make sure tag exists, so it will be in localTags next time the go command is run.
|
||||
Run(r.dir, "git", "tag", strings.TrimPrefix(ref, "refs/tags/"), hash)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we know a specific commit we need, fetch it.
|
||||
if r.fetchLevel <= fetchSome && hash != "" && !r.local {
|
||||
r.fetchLevel = fetchSome
|
||||
var refspec string
|
||||
if ref != "" && ref != "head" {
|
||||
if ref != "" && ref != "HEAD" {
|
||||
// If we do know the ref name, save the mapping locally
|
||||
// so that (if it is a tag) it can show up in localTags
|
||||
// on a future call. Also, some servers refuse to allow
|
||||
|
@ -362,7 +378,7 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
|
|||
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||
// It uses version as info.Version.
|
||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||
out, err := Run(r.dir, "git", "log", "-n1", "--format=format:%H %ct %D", rev)
|
||||
out, err := Run(r.dir, "git", "log", "-n1", "--format=format:%H %ct %D", "--no-show-signature", rev)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown revision %s", rev)
|
||||
}
|
||||
|
@ -438,6 +454,166 @@ func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) {
|
||||
// Create space to hold results.
|
||||
files := make(map[string]*FileRev)
|
||||
for _, rev := range revs {
|
||||
f := &FileRev{Rev: rev}
|
||||
files[rev] = f
|
||||
}
|
||||
|
||||
// Collect locally-known revs.
|
||||
need, err := r.readFileRevs(revs, file, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(need) == 0 {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// Build list of known remote refs that might help.
|
||||
var redo []string
|
||||
r.refsOnce.Do(r.loadRefs)
|
||||
if r.refsErr != nil {
|
||||
return nil, r.refsErr
|
||||
}
|
||||
for _, tag := range need {
|
||||
if r.refs["refs/tags/"+tag] != "" {
|
||||
redo = append(redo, tag)
|
||||
}
|
||||
}
|
||||
if len(redo) == 0 {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// Protect r.fetchLevel and the "fetch more and more" sequence.
|
||||
// See stat method above.
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
var refs []string
|
||||
var protoFlag []string
|
||||
var unshallowFlag []string
|
||||
for _, tag := range redo {
|
||||
refs = append(refs, "refs/tags/"+tag+":refs/tags/"+tag)
|
||||
}
|
||||
if len(refs) > 1 {
|
||||
unshallowFlag = unshallow(r.dir)
|
||||
if len(unshallowFlag) > 0 {
|
||||
// To work around a protocol version 2 bug that breaks --unshallow,
|
||||
// add -c protocol.version=0.
|
||||
// TODO(rsc): The bug is believed to be server-side, meaning only
|
||||
// on Google's Git servers. Once the servers are fixed, drop the
|
||||
// protocol.version=0. See Google-internal bug b/110495752.
|
||||
protoFlag = []string{"-c", "protocol.version=0"}
|
||||
}
|
||||
}
|
||||
if _, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, refs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := r.readFileRevs(redo, file, files); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*FileRev) (missing []string, err error) {
|
||||
var stdin bytes.Buffer
|
||||
for _, tag := range tags {
|
||||
fmt.Fprintf(&stdin, "refs/tags/%s\n", tag)
|
||||
fmt.Fprintf(&stdin, "refs/tags/%s:%s\n", tag, file)
|
||||
}
|
||||
|
||||
data, err := RunWithStdin(r.dir, &stdin, "git", "cat-file", "--batch")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
next := func() (typ string, body []byte, ok bool) {
|
||||
var line string
|
||||
i := bytes.IndexByte(data, '\n')
|
||||
if i < 0 {
|
||||
return "", nil, false
|
||||
}
|
||||
line, data = string(bytes.TrimSpace(data[:i])), data[i+1:]
|
||||
if strings.HasSuffix(line, " missing") {
|
||||
return "missing", nil, true
|
||||
}
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 3 {
|
||||
return "", nil, false
|
||||
}
|
||||
n, err := strconv.Atoi(f[2])
|
||||
if err != nil || n > len(data) {
|
||||
return "", nil, false
|
||||
}
|
||||
body, data = data[:n], data[n:]
|
||||
if len(data) > 0 && data[0] == '\r' {
|
||||
data = data[1:]
|
||||
}
|
||||
if len(data) > 0 && data[0] == '\n' {
|
||||
data = data[1:]
|
||||
}
|
||||
return f[1], body, true
|
||||
}
|
||||
|
||||
badGit := func() ([]string, error) {
|
||||
return nil, fmt.Errorf("malformed output from git cat-file --batch")
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
commitType, _, ok := next()
|
||||
if !ok {
|
||||
return badGit()
|
||||
}
|
||||
fileType, fileData, ok := next()
|
||||
if !ok {
|
||||
return badGit()
|
||||
}
|
||||
f := fileMap[tag]
|
||||
f.Data = nil
|
||||
f.Err = nil
|
||||
switch commitType {
|
||||
default:
|
||||
f.Err = fmt.Errorf("unexpected non-commit type %q for rev %s", commitType, tag)
|
||||
|
||||
case "missing":
|
||||
// Note: f.Err must not satisfy os.IsNotExist. That's reserved for the file not existing in a valid commit.
|
||||
f.Err = fmt.Errorf("no such rev %s", tag)
|
||||
missing = append(missing, tag)
|
||||
|
||||
case "tag", "commit":
|
||||
switch fileType {
|
||||
default:
|
||||
f.Err = &os.PathError{Path: tag + ":" + file, Op: "read", Err: fmt.Errorf("unexpected non-blob type %q", fileType)}
|
||||
case "missing":
|
||||
f.Err = &os.PathError{Path: tag + ":" + file, Op: "read", Err: os.ErrNotExist}
|
||||
case "blob":
|
||||
f.Data = fileData
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(bytes.TrimSpace(data)) != 0 {
|
||||
return badGit()
|
||||
}
|
||||
|
||||
return missing, nil
|
||||
}
|
||||
|
||||
func (r *gitRepo) RecentTag(rev, prefix string) (tag string, err error) {
|
||||
_, err = r.Stat(rev)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := Run(r.dir, "git", "describe", "--first-parent", "--tags", "--always", "--abbrev=0", "--match", prefix+"v[0-9]*.[0-9]*.[0-9]*", "--tags", rev)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
|
||||
// TODO: Use maxSize or drop it.
|
||||
args := []string{}
|
||||
|
|
|
@ -7,6 +7,7 @@ package codehost
|
|||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
|
@ -22,6 +23,9 @@ import (
|
|||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// needed for initializing the test environment variables as testing.Short
|
||||
// and HasExternalNetwork
|
||||
flag.Parse()
|
||||
os.Exit(testMain(m))
|
||||
}
|
||||
|
||||
|
|
Двоичный файл не отображается.
|
@ -325,6 +325,14 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) {
|
||||
return nil, fmt.Errorf("ReadFileRevs not implemented")
|
||||
}
|
||||
|
||||
func (r *vcsRepo) RecentTag(rev, prefix string) (tag string, err error) {
|
||||
return "", fmt.Errorf("RecentTags not implemented")
|
||||
}
|
||||
|
||||
func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
|
||||
if rev == "latest" {
|
||||
rev = r.cmd.latest
|
||||
|
|
|
@ -6,15 +6,12 @@ package modfetch
|
|||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/modfile"
|
||||
|
@ -97,7 +94,9 @@ func (r *codeRepo) Versions(prefix string) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := []string{}
|
||||
var incompatible []string
|
||||
for _, tag := range tags {
|
||||
if !strings.HasPrefix(tag, p) {
|
||||
continue
|
||||
|
@ -106,11 +105,34 @@ func (r *codeRepo) Versions(prefix string) ([]string, error) {
|
|||
if r.codeDir != "" {
|
||||
v = v[len(r.codeDir)+1:]
|
||||
}
|
||||
if !semver.IsValid(v) || v != semver.Canonical(v) || IsPseudoVersion(v) || !module.MatchPathMajor(v, r.pathMajor) {
|
||||
if v == "" || v != module.CanonicalVersion(v) || IsPseudoVersion(v) {
|
||||
continue
|
||||
}
|
||||
if !module.MatchPathMajor(v, r.pathMajor) {
|
||||
if r.codeDir == "" && r.pathMajor == "" && semver.Major(v) > "v1" {
|
||||
incompatible = append(incompatible, v)
|
||||
}
|
||||
continue
|
||||
}
|
||||
list = append(list, v)
|
||||
}
|
||||
|
||||
if len(incompatible) > 0 {
|
||||
// Check for later versions that were created not following semantic import versioning,
|
||||
// as indicated by the absence of a go.mod file. Those versions can be addressed
|
||||
// by referring to them with a +incompatible suffix, as in v17.0.0+incompatible.
|
||||
files, err := r.code.ReadFileRevs(incompatible, "go.mod", codehost.MaxGoMod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rev := range incompatible {
|
||||
f := files[rev]
|
||||
if os.IsNotExist(f.Err) {
|
||||
list = append(list, rev+"+incompatible")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortVersions(list)
|
||||
return list, nil
|
||||
}
|
||||
|
@ -146,7 +168,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
}
|
||||
|
||||
// Determine version.
|
||||
if semver.IsValid(statVers) && statVers == semver.Canonical(statVers) && module.MatchPathMajor(statVers, r.pathMajor) {
|
||||
if module.CanonicalVersion(statVers) == statVers && module.MatchPathMajor(statVers, r.pathMajor) {
|
||||
// The original call was repo.Stat(statVers), and requestedVersion is OK, so use it.
|
||||
info2.Version = statVers
|
||||
} else {
|
||||
|
@ -157,27 +179,51 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
p = r.codeDir + "/"
|
||||
}
|
||||
|
||||
tagOK := func(v string) bool {
|
||||
// If this is a plain tag (no dir/ prefix)
|
||||
// and the module path is unversioned,
|
||||
// and if the underlying file tree has no go.mod,
|
||||
// then allow using the tag with a +incompatible suffix.
|
||||
canUseIncompatible := false
|
||||
if r.codeDir == "" && r.pathMajor == "" {
|
||||
_, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod)
|
||||
if errGoMod != nil {
|
||||
canUseIncompatible = true
|
||||
}
|
||||
}
|
||||
|
||||
tagToVersion := func(v string) string {
|
||||
if !strings.HasPrefix(v, p) {
|
||||
return false
|
||||
return ""
|
||||
}
|
||||
v = v[len(p):]
|
||||
return semver.IsValid(v) && v == semver.Canonical(v) && module.MatchPathMajor(v, r.pathMajor) && !IsPseudoVersion(v)
|
||||
if module.CanonicalVersion(v) != v || IsPseudoVersion(v) {
|
||||
return ""
|
||||
}
|
||||
if module.MatchPathMajor(v, r.pathMajor) {
|
||||
return v
|
||||
}
|
||||
if canUseIncompatible {
|
||||
return v + "+incompatible"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// If info.Version is OK, use it.
|
||||
if tagOK(info.Version) {
|
||||
info2.Version = info.Version[len(p):]
|
||||
if v := tagToVersion(info.Version); v != "" {
|
||||
info2.Version = v
|
||||
} else {
|
||||
// Otherwise look through all known tags for latest in semver ordering.
|
||||
for _, tag := range info.Tags {
|
||||
if tagOK(tag) && semver.Compare(info2.Version, tag[len(p):]) < 0 {
|
||||
info2.Version = tag[len(p):]
|
||||
if v := tagToVersion(tag); v != "" && semver.Compare(info2.Version, v) < 0 {
|
||||
info2.Version = v
|
||||
}
|
||||
}
|
||||
// Otherwise make a pseudo-version.
|
||||
if info2.Version == "" {
|
||||
info2.Version = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
|
||||
tag, _ := r.code.RecentTag(statVers, p)
|
||||
v = tagToVersion(tag)
|
||||
// TODO: Check that v is OK for r.pseudoMajor or else is OK for incompatible.
|
||||
info2.Version = PseudoVersion(r.pseudoMajor, v, info.Time, info.Short)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,9 +245,11 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
func (r *codeRepo) revToRev(rev string) string {
|
||||
if semver.IsValid(rev) {
|
||||
if IsPseudoVersion(rev) {
|
||||
i := strings.Index(rev, "-")
|
||||
j := strings.Index(rev[i+1:], "-")
|
||||
return rev[i+1+j+1:]
|
||||
r, _ := PseudoVersionRev(rev)
|
||||
return r
|
||||
}
|
||||
if semver.Build(rev) == "+incompatible" {
|
||||
rev = rev[:len(rev)-len("+incompatible")]
|
||||
}
|
||||
if r.codeDir == "" {
|
||||
return rev
|
||||
|
@ -548,71 +596,3 @@ func isVendoredPackage(name string) bool {
|
|||
}
|
||||
return strings.Contains(name[i:], "/")
|
||||
}
|
||||
|
||||
func PseudoVersion(major string, t time.Time, rev string) string {
|
||||
if major == "" {
|
||||
major = "v0"
|
||||
}
|
||||
return fmt.Sprintf("%s.0.0-%s-%s", major, t.UTC().Format("20060102150405"), rev)
|
||||
}
|
||||
|
||||
var ErrNotPseudoVersion = errors.New("not a pseudo-version")
|
||||
|
||||
/*
|
||||
func ParsePseudoVersion(repo Repo, version string) (rev string, err error) {
|
||||
major := semver.Major(version)
|
||||
if major == "" {
|
||||
return "", ErrNotPseudoVersion
|
||||
}
|
||||
majorPrefix := major + ".0.0-"
|
||||
if !strings.HasPrefix(version, majorPrefix) || !strings.Contains(version[len(majorPrefix):], "-") {
|
||||
return "", ErrNotPseudoVersion
|
||||
}
|
||||
versionSuffix := version[len(majorPrefix):]
|
||||
for i := 0; versionSuffix[i] != '-'; i++ {
|
||||
c := versionSuffix[i]
|
||||
if c < '0' || '9' < c {
|
||||
return "", ErrNotPseudoVersion
|
||||
}
|
||||
}
|
||||
rev = versionSuffix[strings.Index(versionSuffix, "-")+1:]
|
||||
if rev == "" {
|
||||
return "", ErrNotPseudoVersion
|
||||
}
|
||||
if proxyURL != "" {
|
||||
return version, nil
|
||||
}
|
||||
fullRev, t, err := repo.CommitInfo(rev)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unknown pseudo-version %s: loading %v: %v", version, rev, err)
|
||||
}
|
||||
v := PseudoVersion(major, t, repo.ShortRev(fullRev))
|
||||
if v != version {
|
||||
return "", fmt.Errorf("unknown pseudo-version %s: %v is %v", version, rev, v)
|
||||
}
|
||||
return fullRev, nil
|
||||
}
|
||||
*/
|
||||
|
||||
var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
|
||||
|
||||
// IsPseudoVersion reports whether v is a pseudo-version.
|
||||
func IsPseudoVersion(v string) bool {
|
||||
return pseudoVersionRE.MatchString(v)
|
||||
}
|
||||
|
||||
// PseudoVersionTime returns the time stamp of the pseudo-version v.
|
||||
// It returns an error if v is not a pseudo-version or if the time stamp
|
||||
// embedded in the pseudo-version is not a valid time.
|
||||
func PseudoVersionTime(v string) (time.Time, error) {
|
||||
if !IsPseudoVersion(v) {
|
||||
return time.Time{}, fmt.Errorf("not a pseudo-version")
|
||||
}
|
||||
i := strings.Index(v, "-") + 1
|
||||
j := i + strings.Index(v[i:], "-")
|
||||
t, err := time.Parse("20060102150405", v[i:j])
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("malformed pseudo-version %q", v)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
|
|
@ -88,9 +88,9 @@ var codeRepoTests = []struct {
|
|||
path: "github.com/rsc/vgotest1/v2",
|
||||
rev: "v2.0.0",
|
||||
version: "v2.0.0",
|
||||
name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
|
||||
short: "80d85c5d4d17",
|
||||
time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
|
||||
name: "45f53230a74ad275c7127e117ac46914c8126160",
|
||||
short: "45f53230a74a",
|
||||
time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
|
||||
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
|
||||
},
|
||||
{
|
||||
|
@ -121,11 +121,11 @@ var codeRepoTests = []struct {
|
|||
},
|
||||
{
|
||||
path: "github.com/rsc/vgotest1/v2",
|
||||
rev: "80d85c5",
|
||||
rev: "45f53230a",
|
||||
version: "v2.0.0",
|
||||
name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
|
||||
short: "80d85c5d4d17",
|
||||
time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
|
||||
name: "45f53230a74ad275c7127e117ac46914c8126160",
|
||||
short: "45f53230a74a",
|
||||
time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
|
||||
gomoderr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
|
||||
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
|
||||
},
|
||||
|
@ -237,7 +237,7 @@ var codeRepoTests = []struct {
|
|||
// redirect to googlesource
|
||||
path: "golang.org/x/text",
|
||||
rev: "4e4a3210bb",
|
||||
version: "v0.0.0-20180208041248-4e4a3210bb54",
|
||||
version: "v0.3.1-0.20180208041248-4e4a3210bb54",
|
||||
name: "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1",
|
||||
short: "4e4a3210bb54",
|
||||
time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC),
|
||||
|
@ -459,6 +459,7 @@ var hgmap = map[string]string{
|
|||
"2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46",
|
||||
"80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71",
|
||||
"1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459",
|
||||
"45f53230a74ad275c7127e117ac46914c8126160": "814fce58e83abd5bf2a13892e0b0e1198abefcd4",
|
||||
}
|
||||
|
||||
func remap(name string, m map[string]string) string {
|
||||
|
@ -486,10 +487,9 @@ var codeRepoVersionsTests = []struct {
|
|||
prefix string
|
||||
versions []string
|
||||
}{
|
||||
// TODO: Why do we allow a prefix here at all?
|
||||
{
|
||||
path: "github.com/rsc/vgotest1",
|
||||
versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0"},
|
||||
versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0", "v2.0.0+incompatible"},
|
||||
},
|
||||
{
|
||||
path: "github.com/rsc/vgotest1",
|
||||
|
@ -605,9 +605,15 @@ type fixedTagsRepo struct {
|
|||
func (ch *fixedTagsRepo) Tags(string) ([]string, error) { return ch.tags, nil }
|
||||
func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error) { panic("not impl") }
|
||||
func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
|
||||
func (ch *fixedTagsRepo) ReadFileRevs([]string, string, int64) (map[string]*codehost.FileRev, error) {
|
||||
panic("not impl")
|
||||
}
|
||||
func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
|
||||
panic("not impl")
|
||||
}
|
||||
func (ch *fixedTagsRepo) RecentTag(string, string) (string, error) {
|
||||
panic("not impl")
|
||||
}
|
||||
func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") }
|
||||
|
||||
func TestNonCanonicalSemver(t *testing.T) {
|
||||
|
|
|
@ -41,17 +41,22 @@ func Download(mod module.Version) (dir string, err error) {
|
|||
err error
|
||||
}
|
||||
c := downloadCache.Do(mod, func() interface{} {
|
||||
modpath := mod.Path + "@" + mod.Version
|
||||
dir = filepath.Join(SrcMod, modpath)
|
||||
dir, err := DownloadDir(mod)
|
||||
if err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
|
||||
zipfile := filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".zip")
|
||||
zipfile, err := CachePath(mod, "zip")
|
||||
if err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
if _, err := os.Stat(zipfile); err == nil {
|
||||
// Use it.
|
||||
// This should only happen if the mod/cache directory is preinitialized
|
||||
// or if src/mod/path was removed but not src/mod/cache/download.
|
||||
fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version)
|
||||
} else {
|
||||
if err := os.MkdirAll(filepath.Join(SrcMod, "cache/download", mod.Path, "@v"), 0777); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
|
||||
|
@ -59,6 +64,7 @@ func Download(mod module.Version) (dir string, err error) {
|
|||
return cached{"", err}
|
||||
}
|
||||
}
|
||||
modpath := mod.Path + "@" + mod.Version
|
||||
if err := Unzip(dir, zipfile, modpath, 0); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "-> %s\n", err)
|
||||
return cached{"", err}
|
||||
|
@ -201,7 +207,11 @@ func checkSum(mod module.Version) {
|
|||
}
|
||||
|
||||
// Do the file I/O before acquiring the go.sum lock.
|
||||
data, err := ioutil.ReadFile(filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".ziphash"))
|
||||
ziphash, err := CachePath(mod, "ziphash")
|
||||
if err != nil {
|
||||
base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
|
||||
}
|
||||
data, err := ioutil.ReadFile(ziphash)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
|
||||
|
@ -260,7 +270,11 @@ func Sum(mod module.Version) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".ziphash"))
|
||||
ziphash, err := CachePath(mod, "ziphash")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
data, err := ioutil.ReadFile(ziphash)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
@ -300,3 +314,21 @@ func WriteGoSum() {
|
|||
os.Remove(goSum.modverify)
|
||||
}
|
||||
}
|
||||
|
||||
// TrimGoSum trims go.sum to contain only the modules for which keep[m] is true.
|
||||
func TrimGoSum(keep map[module.Version]bool) {
|
||||
goSum.mu.Lock()
|
||||
defer goSum.mu.Unlock()
|
||||
if !initGoSum() {
|
||||
return
|
||||
}
|
||||
|
||||
for m := range goSum.m {
|
||||
// If we're keeping x@v we also keep x@v/go.mod.
|
||||
// Map x@v/go.mod back to x@v for the keep lookup.
|
||||
noGoMod := module.Version{Path: m.Path, Version: strings.TrimSuffix(m.Version, "/go.mod")}
|
||||
if !keep[m] && !keep[noGoMod] {
|
||||
delete(goSum.m, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,19 +14,87 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
)
|
||||
|
||||
var HelpGoproxy = &base.Command{
|
||||
UsageLine: "goproxy",
|
||||
Short: "module proxy protocol",
|
||||
Long: `
|
||||
The go command by default downloads modules from version control systems
|
||||
directly, just as 'go get' always has. If the GOPROXY environment variable
|
||||
is set to the URL of a module proxy, the go command will instead fetch
|
||||
all modules from that proxy. No matter the source of the modules, downloaded
|
||||
modules must match existing entries in go.sum (see 'go help modules' for
|
||||
discussion of verification).
|
||||
|
||||
A Go module proxy is any web server that can respond to GET requests for
|
||||
URLs of a specified form. The requests have no query parameters, so even
|
||||
a site serving from a fixed file system (including a file:/// URL)
|
||||
can be a module proxy.
|
||||
|
||||
The GET requests sent to a Go module proxy are:
|
||||
|
||||
GET $GOPROXY/<module>/@v/list returns a list of all known versions of the
|
||||
given module, one per line.
|
||||
|
||||
GET $GOPROXY/<module>/@v/<version>.info returns JSON-formatted metadata
|
||||
about that version of the given module.
|
||||
|
||||
GET $GOPROXY/<module>/@v/<version>.mod returns the go.mod file
|
||||
for that version of the given module.
|
||||
|
||||
GET $GOPROXY/<module>/@v/<version>.zip returns the zip archive
|
||||
for that version of the given module.
|
||||
|
||||
To avoid problems when serving from case-sensitive file systems,
|
||||
the <module> and <version> elements are case-encoded, replacing every
|
||||
uppercase letter with an exclamation mark followed by the correponding
|
||||
lower-case letter: github.com/Azure encodes as github.com/!azure.
|
||||
|
||||
The JSON-formatted metadata about a given module corresponds to
|
||||
this Go data structure, which may be expanded in the future:
|
||||
|
||||
type Info struct {
|
||||
Version string // version string
|
||||
Time time.Time // commit time
|
||||
}
|
||||
|
||||
The zip archive for a specific version of a given module is a
|
||||
standard zip file that contains the file tree corresponding
|
||||
to the module's source code and related files. The archive uses
|
||||
slash-separated paths, and every file path in the archive must
|
||||
begin with <module>@<version>/, where the module and version are
|
||||
substituted directly, not case-encoded. The root of the module
|
||||
file tree corresponds to the <module>@<version>/ prefix in the
|
||||
archive.
|
||||
|
||||
Even when downloading directly from version control systems,
|
||||
the go command synthesizes explicit info, mod, and zip files
|
||||
and stores them in its local cache, $GOPATH/src/mod/cache/download,
|
||||
the same as if it had downloaded them directly from a proxy.
|
||||
The cache layout is the same as the proxy URL space, so
|
||||
serving $GOPATH/src/mod/cache/download at (or copying it to)
|
||||
https://example.com/proxy would let other users access those
|
||||
cached module versions with GOPROXY=https://example.com/proxy.
|
||||
`,
|
||||
}
|
||||
|
||||
var proxyURL = os.Getenv("GOPROXY")
|
||||
|
||||
func lookupProxy(path string) (Repo, error) {
|
||||
if strings.Contains(proxyURL, ",") {
|
||||
return nil, fmt.Errorf("invalid $GOPROXY setting: cannot have comma")
|
||||
}
|
||||
u, err := url.Parse(proxyURL)
|
||||
if err != nil || u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "file" {
|
||||
// Don't echo $GOPROXY back in case it has user:password in it (sigh).
|
||||
return nil, fmt.Errorf("invalid $GOPROXY setting")
|
||||
return nil, fmt.Errorf("invalid $GOPROXY setting: malformed URL or invalid scheme (must be http, https, file)")
|
||||
}
|
||||
return newProxyRepo(u.String(), path), nil
|
||||
return newProxyRepo(u.String(), path)
|
||||
}
|
||||
|
||||
type proxyRepo struct {
|
||||
|
@ -34,8 +102,12 @@ type proxyRepo struct {
|
|||
path string
|
||||
}
|
||||
|
||||
func newProxyRepo(baseURL, path string) Repo {
|
||||
return &proxyRepo{strings.TrimSuffix(baseURL, "/") + "/" + pathEscape(path), path}
|
||||
func newProxyRepo(baseURL, path string) (Repo, error) {
|
||||
enc, err := module.EncodePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proxyRepo{strings.TrimSuffix(baseURL, "/") + "/" + pathEscape(enc), path}, nil
|
||||
}
|
||||
|
||||
func (p *proxyRepo) ModulePath() string {
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
// Pseudo-versions
|
||||
//
|
||||
// Code authors are expected to tag the revisions they want users to use,
|
||||
// including prereleases. However, not all authors tag versions at all,
|
||||
// and not all commits a user might want to try will have tags.
|
||||
// A pseudo-version is a version with a special form that allows us to
|
||||
// address an untagged commit and order that version with respect to
|
||||
// other versions we might encounter.
|
||||
//
|
||||
// A pseudo-version takes one of the general forms:
|
||||
//
|
||||
// (1) vX.0.0-yyyymmddhhmmss-abcdef123456
|
||||
// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456
|
||||
// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible
|
||||
// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456
|
||||
// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible
|
||||
//
|
||||
// If there is no recently tagged version with the right major version vX,
|
||||
// then form (1) is used, creating a space of pseudo-versions at the bottom
|
||||
// of the vX version range, less than any tagged version, including the unlikely v0.0.0.
|
||||
//
|
||||
// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible,
|
||||
// then the pseudo-version uses form (2) or (3), making it a prerelease for the next
|
||||
// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string
|
||||
// ensures that the pseudo-version compares less than possible future explicit prereleases
|
||||
// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1.
|
||||
//
|
||||
// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible,
|
||||
// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease.
|
||||
|
||||
package modfetch
|
||||
|
||||
import (
|
||||
"cmd/go/internal/semver"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PseudoVersion returns a pseudo-version for the given major version ("v1")
|
||||
// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
|
||||
// and revision identifier (usually a 12-byte commit hash prefix).
|
||||
func PseudoVersion(major, older string, t time.Time, rev string) string {
|
||||
if major == "" {
|
||||
major = "v0"
|
||||
}
|
||||
segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev)
|
||||
build := semver.Build(older)
|
||||
older = semver.Canonical(older)
|
||||
if older == "" {
|
||||
return major + ".0.0-" + segment // form (1)
|
||||
}
|
||||
if semver.Prerelease(older) != "" {
|
||||
return older + ".0." + segment + build // form (4), (5)
|
||||
}
|
||||
|
||||
// Form (2), (3).
|
||||
// Extract patch from vMAJOR.MINOR.PATCH
|
||||
v := older[:len(older)]
|
||||
i := strings.LastIndex(v, ".") + 1
|
||||
v, patch := v[:i], v[i:]
|
||||
|
||||
// Increment PATCH by adding 1 to decimal:
|
||||
// scan right to left turning 9s to 0s until you find a digit to increment.
|
||||
// (Number might exceed int64, but math/big is overkill.)
|
||||
digits := []byte(patch)
|
||||
for i = len(digits) - 1; i >= 0 && digits[i] == '9'; i-- {
|
||||
digits[i] = '0'
|
||||
}
|
||||
if i >= 0 {
|
||||
digits[i]++
|
||||
} else {
|
||||
// digits is all zeros
|
||||
digits[0] = '1'
|
||||
digits = append(digits, '0')
|
||||
}
|
||||
patch = string(digits)
|
||||
|
||||
// Reassemble.
|
||||
return v + patch + "-0." + segment + build
|
||||
}
|
||||
|
||||
var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`)
|
||||
|
||||
// IsPseudoVersion reports whether v is a pseudo-version.
|
||||
func IsPseudoVersion(v string) bool {
|
||||
return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v)
|
||||
}
|
||||
|
||||
// PseudoVersionTime returns the time stamp of the pseudo-version v.
|
||||
// It returns an error if v is not a pseudo-version or if the time stamp
|
||||
// embedded in the pseudo-version is not a valid time.
|
||||
func PseudoVersionTime(v string) (time.Time, error) {
|
||||
timestamp, _, err := parsePseudoVersion(v)
|
||||
t, err := time.Parse("20060102150405", timestamp)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("pseudo-version with malformed time %s: %q", timestamp, v)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// PseudoVersionRev returns the revision identifier of the pseudo-version v.
|
||||
// It returns an error if v is not a pseudo-version.
|
||||
func PseudoVersionRev(v string) (rev string, err error) {
|
||||
_, rev, err = parsePseudoVersion(v)
|
||||
return
|
||||
}
|
||||
|
||||
func parsePseudoVersion(v string) (timestamp, rev string, err error) {
|
||||
if !IsPseudoVersion(v) {
|
||||
return "", "", fmt.Errorf("malformed pseudo-version %q", v)
|
||||
}
|
||||
v = strings.TrimSuffix(v, "+incompatible")
|
||||
j := strings.LastIndex(v, "-")
|
||||
v, rev = v[:j], v[j+1:]
|
||||
i := strings.LastIndex(v, "-")
|
||||
if j := strings.LastIndex(v, "."); j > i {
|
||||
timestamp = v[j+1:]
|
||||
} else {
|
||||
timestamp = v[i+1:]
|
||||
}
|
||||
return timestamp, rev, nil
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
package modfetch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var pseudoTests = []struct {
|
||||
major string
|
||||
older string
|
||||
version string
|
||||
}{
|
||||
{"", "", "v0.0.0-20060102150405-hash"},
|
||||
{"v0", "", "v0.0.0-20060102150405-hash"},
|
||||
{"v1", "", "v1.0.0-20060102150405-hash"},
|
||||
{"v2", "", "v2.0.0-20060102150405-hash"},
|
||||
{"unused", "v0.0.0", "v0.0.1-0.20060102150405-hash"},
|
||||
{"unused", "v1.2.3", "v1.2.4-0.20060102150405-hash"},
|
||||
{"unused", "v1.2.99999999999999999", "v1.2.100000000000000000-0.20060102150405-hash"},
|
||||
{"unused", "v1.2.3-pre", "v1.2.3-pre.0.20060102150405-hash"},
|
||||
{"unused", "v1.3.0-pre", "v1.3.0-pre.0.20060102150405-hash"},
|
||||
}
|
||||
|
||||
var pseudoTime = time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)
|
||||
|
||||
func TestPseudoVersion(t *testing.T) {
|
||||
for _, tt := range pseudoTests {
|
||||
v := PseudoVersion(tt.major, tt.older, pseudoTime, "hash")
|
||||
if v != tt.version {
|
||||
t.Errorf("PseudoVersion(%q, %q, ...) = %v, want %v", tt.major, tt.older, v, tt.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPseudoVersion(t *testing.T) {
|
||||
for _, tt := range pseudoTests {
|
||||
if !IsPseudoVersion(tt.version) {
|
||||
t.Errorf("IsPseudoVersion(%q) = false, want true", tt.version)
|
||||
}
|
||||
if IsPseudoVersion(tt.older) {
|
||||
t.Errorf("IsPseudoVersion(%q) = true, want false", tt.older)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPseudoVersionTime(t *testing.T) {
|
||||
for _, tt := range pseudoTests {
|
||||
tm, err := PseudoVersionTime(tt.version)
|
||||
if tm != pseudoTime || err != nil {
|
||||
t.Errorf("PseudoVersionTime(%q) = %v, %v, want %v, nil", tt.version, tm.Format(time.RFC3339), err, pseudoTime.Format(time.RFC3339))
|
||||
}
|
||||
tm, err = PseudoVersionTime(tt.older)
|
||||
if tm != (time.Time{}) || err == nil {
|
||||
t.Errorf("PseudoVersionTime(%q) = %v, %v, want %v, error", tt.older, tm.Format(time.RFC3339), err, time.Time{}.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPseudoVersionRev(t *testing.T) {
|
||||
for _, tt := range pseudoTests {
|
||||
rev, err := PseudoVersionRev(tt.version)
|
||||
if rev != "hash" || err != nil {
|
||||
t.Errorf("PseudoVersionRev(%q) = %q, %v, want %q, nil", tt.older, rev, err, "hash")
|
||||
}
|
||||
rev, err = PseudoVersionRev(tt.older)
|
||||
if rev != "" || err == nil {
|
||||
t.Errorf("PseudoVersionRev(%q) = %q, %v, want %q, error", tt.older, rev, err, "")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -218,7 +218,7 @@ func lookup(path string) (r Repo, err error) {
|
|||
|
||||
if rr.VCS == "mod" {
|
||||
// Fetch module from proxy with base URL rr.Repo.
|
||||
return newProxyRepo(rr.Repo, path), nil
|
||||
return newProxyRepo(rr.Repo, path)
|
||||
}
|
||||
|
||||
code, err := lookupCodeRepo(rr)
|
||||
|
|
|
@ -10,11 +10,13 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/str"
|
||||
)
|
||||
|
||||
|
@ -48,7 +50,28 @@ func Unzip(dir, zipfile, prefix string, maxSize int64) error {
|
|||
return fmt.Errorf("unzip %v: %s", zipfile, err)
|
||||
}
|
||||
|
||||
// Check total size.
|
||||
foldPath := make(map[string]string)
|
||||
var checkFold func(string) error
|
||||
checkFold = func(name string) error {
|
||||
fold := str.ToFold(name)
|
||||
if foldPath[fold] == name {
|
||||
return nil
|
||||
}
|
||||
dir := path.Dir(name)
|
||||
if dir != "." {
|
||||
if err := checkFold(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if foldPath[fold] == "" {
|
||||
foldPath[fold] = name
|
||||
return nil
|
||||
}
|
||||
other := foldPath[fold]
|
||||
return fmt.Errorf("unzip %v: case-insensitive file name collision: %q and %q", zipfile, other, name)
|
||||
}
|
||||
|
||||
// Check total size, valid file names.
|
||||
var size int64
|
||||
for _, zf := range z.File {
|
||||
if !str.HasPathPrefix(zf.Name, prefix) {
|
||||
|
@ -57,7 +80,14 @@ func Unzip(dir, zipfile, prefix string, maxSize int64) error {
|
|||
if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") {
|
||||
continue
|
||||
}
|
||||
if filepath.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") {
|
||||
name := zf.Name[len(prefix)+1:]
|
||||
if err := module.CheckFilePath(name); err != nil {
|
||||
return fmt.Errorf("unzip %v: %v", zipfile, err)
|
||||
}
|
||||
if err := checkFold(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if path.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") {
|
||||
return fmt.Errorf("unzip %v: invalid file name %s", zipfile, zf.Name)
|
||||
}
|
||||
s := int64(zf.UncompressedSize64)
|
||||
|
@ -116,7 +146,7 @@ func Unzip(dir, zipfile, prefix string, maxSize int64) error {
|
|||
|
||||
// Run over list backward to chmod children before parents.
|
||||
for i := len(dirlist) - 1; i >= 0; i-- {
|
||||
os.Chmod(dir, 0555)
|
||||
os.Chmod(dirlist[i], 0555)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -167,13 +167,16 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
|
|||
fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err)
|
||||
return
|
||||
}
|
||||
v1, err := moduleMajorVersion(s)
|
||||
pathMajor, err := modulePathMajor(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
|
||||
return
|
||||
}
|
||||
if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
|
||||
fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
|
||||
if !module.MatchPathMajor(v, pathMajor) {
|
||||
if pathMajor == "" {
|
||||
pathMajor = "v0 or v1"
|
||||
}
|
||||
fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v)
|
||||
return
|
||||
}
|
||||
if verb == "require" {
|
||||
|
@ -202,7 +205,7 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
|
|||
fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
|
||||
return
|
||||
}
|
||||
v1, err := moduleMajorVersion(s)
|
||||
pathMajor, err := modulePathMajor(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
|
||||
return
|
||||
|
@ -215,8 +218,11 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
|
|||
fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
|
||||
return
|
||||
}
|
||||
if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
|
||||
fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
|
||||
if !module.MatchPathMajor(v, pathMajor) {
|
||||
if pathMajor == "" {
|
||||
pathMajor = "v0 or v1"
|
||||
}
|
||||
fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -364,39 +370,19 @@ func parseVersion(path string, s *string, fix VersionFixer) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
}
|
||||
if semver.IsValid(t) {
|
||||
*s = semver.Canonical(t)
|
||||
if v := module.CanonicalVersion(t); v != "" {
|
||||
*s = v
|
||||
return *s, nil
|
||||
}
|
||||
return "", fmt.Errorf("version must be of the form v1.2.3")
|
||||
}
|
||||
|
||||
func moduleMajorVersion(p string) (string, error) {
|
||||
if _, _, major, _, ok := ParseGopkgIn(p); ok {
|
||||
return major, nil
|
||||
func modulePathMajor(path string) (string, error) {
|
||||
_, major, ok := module.SplitPathVersion(path)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("invalid module path")
|
||||
}
|
||||
|
||||
start := strings.LastIndex(p, "/") + 1
|
||||
v := p[start:]
|
||||
if !isMajorVersion(v) {
|
||||
return "v1", nil
|
||||
}
|
||||
if v[1] == '0' || v == "v1" {
|
||||
return "", fmt.Errorf("module path has invalid version number %s", v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func isMajorVersion(v string) bool {
|
||||
if len(v) < 2 || v[0] != 'v' {
|
||||
return false
|
||||
}
|
||||
for i := 1; i < len(v); i++ {
|
||||
if v[i] < '0' || '9' < v[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return major, nil
|
||||
}
|
||||
|
||||
func (f *File) Format() ([]byte, error) {
|
||||
|
|
|
@ -356,16 +356,17 @@ func runGet(cmd *base.Command, args []string) {
|
|||
}
|
||||
lookup.Do(10, func(item interface{}) {
|
||||
t := item.(*task)
|
||||
if t.vers == "none" {
|
||||
// Wait for downgrade step.
|
||||
t.m = module.Version{Path: t.path, Version: "none"}
|
||||
return
|
||||
}
|
||||
m, err := getQuery(t.path, t.vers, t.forceModulePath)
|
||||
if err != nil {
|
||||
base.Errorf("go get %v: %v", t.arg, err)
|
||||
return
|
||||
}
|
||||
t.m = m
|
||||
if t.vers == "none" {
|
||||
// Wait for downgrade step.
|
||||
return
|
||||
}
|
||||
// If there is no -u, then we don't need to upgrade the
|
||||
// collected requirements separately from the overall
|
||||
// recalculation of the build list (modload.ReloadBuildList below),
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"cmd/go/internal/modinfo"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
@ -115,10 +114,9 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
|||
m.Version = q.Version
|
||||
m.Time = &q.Time
|
||||
}
|
||||
|
||||
if semver.IsValid(m.Version) {
|
||||
dir := filepath.Join(modfetch.SrcMod, m.Path+"@"+m.Version)
|
||||
if stat, err := os.Stat(dir); err == nil && stat.IsDir() {
|
||||
dir, err := modfetch.DownloadDir(module.Version{Path: m.Path, Version: m.Version})
|
||||
if err == nil {
|
||||
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
||||
m.Dir = dir
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,10 @@ Module support is enabled only when the current directory is outside
|
|||
GOPATH/src and itself contains a go.mod file or is below a directory
|
||||
containing a go.mod file.
|
||||
|
||||
In module-aware mode, GOPATH no longer defines the meaning of imports
|
||||
during a build, but it still stores downloaded dependencies (in GOPATH/src/mod)
|
||||
and installed commands (in GOPATH/bin, unless GOBIN is set).
|
||||
|
||||
Defining a module
|
||||
|
||||
A module is defined by a tree of Go source files with a go.mod file
|
||||
|
@ -68,7 +72,7 @@ of the module with path example.com/m, and it also declares that the module
|
|||
depends on specific versions of golang.org/x/text and gopkg.in/yaml.v2:
|
||||
|
||||
module example.com/m
|
||||
|
||||
|
||||
require (
|
||||
golang.org/x/text v0.3.0
|
||||
gopkg.in/yaml.v2 v2.1.0
|
||||
|
@ -176,13 +180,25 @@ the standard form for describing module versions, so that versions can be
|
|||
compared to determine which should be considered earlier or later than another.
|
||||
A module version like v1.2.3 is introduced by tagging a revision in the
|
||||
underlying source repository. Untagged revisions can be referred to
|
||||
using a "pseudo-version" of the form v0.0.0-yyyymmddhhmmss-abcdefabcdef,
|
||||
using a "pseudo-version" like v0.0.0-yyyymmddhhmmss-abcdefabcdef,
|
||||
where the time is the commit time in UTC and the final suffix is the prefix
|
||||
of the commit hash. The time portion ensures that two pseudo-versions can
|
||||
be compared to determine which happened later, the commit hash identifes
|
||||
the underlying commit, and the v0.0.0- prefix identifies the pseudo-version
|
||||
as a pre-release before version v0.0.0, so that the go command prefers any
|
||||
tagged release over any pseudo-version.
|
||||
the underlying commit, and the prefix (v0.0.0- in this example) is derived from
|
||||
the most recent tagged version in the commit graph before this commit.
|
||||
|
||||
There are three pseudo-version forms:
|
||||
|
||||
vX.0.0-yyyymmddhhmmss-abcdefabcdef is used when there is no earlier
|
||||
versioned commit with an appropriate major version before the target commit.
|
||||
(This was originally the only form, so some older go.mod files use this form
|
||||
even for commits that do follow tags.)
|
||||
|
||||
vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef is used when the most
|
||||
recent versioned commit before the target commit is vX.Y.Z-pre.
|
||||
|
||||
vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef is used when the most
|
||||
recent versioned commit before the target commit is vX.Y.Z.
|
||||
|
||||
Pseudo-versions never need to be typed by hand: the go command will accept
|
||||
the plain commit hash and translate it into a pseudo-version (or a tagged
|
||||
|
@ -233,7 +249,6 @@ For example, these commands are all valid:
|
|||
go get github.com/gorilla/mux@c856192 # records v0.0.0-20180517173623-c85619274f5d
|
||||
go get github.com/gorilla/mux@master # records current meaning of master
|
||||
|
||||
|
||||
Module compatibility and semantic versioning
|
||||
|
||||
The go command requires that modules use semantic versions and expects that
|
||||
|
@ -242,11 +257,11 @@ backwards-compatible replacement for v1.5.3, v1.4.0, and even v1.0.0.
|
|||
More generally the go command expects that packages follow the
|
||||
"import compatibility rule", which says:
|
||||
|
||||
"If an old package and a new package have the same import path,
|
||||
"If an old package and a new package have the same import path,
|
||||
the new package must be backwards compatible with the old package."
|
||||
|
||||
Because the go command assumes the import compatibility rule,
|
||||
a module definition can only set the minimum required version of one
|
||||
a module definition can only set the minimum required version of one
|
||||
of its dependencies: it cannot set a maximum or exclude selected versions.
|
||||
Still, the import compatibility rule is not a guarantee: it may be that
|
||||
v1.5.4 is buggy and not a backwards-compatible replacement for v1.5.3.
|
||||
|
@ -265,6 +280,11 @@ called "semantic import versioning". Pseudo-versions for modules with major
|
|||
version v2 and later begin with that major version instead of v0, as in
|
||||
v2.0.0-20180326061214-4fc5987536ef.
|
||||
|
||||
As a special case, module paths beginning with gopkg.in/ continue to use the
|
||||
conventions established on that system: the major version is always present,
|
||||
and it is preceded by a dot instead of a slash: gopkg.in/yaml.v1
|
||||
and gopkg.in/yaml.v2, not gopkg.in/yaml and gopkg.in/yaml/v2.
|
||||
|
||||
The go command treats modules with different module paths as unrelated:
|
||||
it makes no connection between example.com/m and example.com/m/v2.
|
||||
Modules with different major versions can be used together in a build
|
||||
|
@ -277,16 +297,35 @@ Major version v0 does not appear in the module path, because those
|
|||
versions are preparation for v1.0.0, and v1 does not appear in the
|
||||
module path either.
|
||||
|
||||
As a special case, for historical reasons, module paths beginning with
|
||||
gopkg.in/ continue to use the conventions established on that system:
|
||||
the major version is always present, and it is preceded by a dot
|
||||
instead of a slash: gopkg.in/yaml.v1 and gopkg.in/yaml.v2, not
|
||||
gopkg.in/yaml and gopkg.in/yaml/v2.
|
||||
Code written before the semantic import versioning convention
|
||||
was introduced may use major versions v2 and later to describe
|
||||
the same set of unversioned import paths as used in v0 and v1.
|
||||
To accommodate such code, if a source code repository has a
|
||||
v2.0.0 or later tag for a file tree with no go.mod, the version is
|
||||
considered to be part of the v1 module's available versions
|
||||
and is given an +incompatible suffix when converted to a module
|
||||
version, as in v2.0.0+incompatible. The +incompatible tag is also
|
||||
applied to pseudo-versions derived from such versions, as in
|
||||
v2.0.1-0.yyyymmddhhmmss-abcdefabcdef+incompatible.
|
||||
|
||||
See https://research.swtch.com/vgo-import and https://semver.org/
|
||||
for more information.
|
||||
In general, having a dependency in the build list (as reported by 'go list -m all')
|
||||
on a v0 version, pre-release version, pseudo-version, or +incompatible version
|
||||
is an indication that problems are more likely when upgrading that
|
||||
dependency, since there is no expectation of compatibility for those.
|
||||
|
||||
Module verification
|
||||
See https://research.swtch.com/vgo-import for more information about
|
||||
semantic import versioning, and see https://semver.org/ for more about
|
||||
semantic versioning.
|
||||
|
||||
Module code layout
|
||||
|
||||
For now, see https://research.swtch.com/vgo-module for information
|
||||
about how source code in version control systems is mapped to
|
||||
module file trees.
|
||||
|
||||
TODO: Add documentation to go command.
|
||||
|
||||
Module downloading and verification
|
||||
|
||||
The go command maintains, in the main module's root directory alongside
|
||||
go.mod, a file named go.sum containing the expected cryptographic checksums
|
||||
|
@ -302,6 +341,13 @@ each command invocation. The 'go mod -verify' command checks that
|
|||
the cached copies of module downloads still match both their recorded
|
||||
checksums and the entries in go.sum.
|
||||
|
||||
The go command can fetch modules from a proxy instead of connecting
|
||||
to source control systems directly, according to the setting of the GOPROXY
|
||||
environment variable.
|
||||
|
||||
See 'go help goproxy' for details about the proxy and also the format of
|
||||
the cached downloaded packages.
|
||||
|
||||
Modules and vendoring
|
||||
|
||||
When using modules, the go command completely ignores vendor directories.
|
||||
|
|
|
@ -17,8 +17,6 @@ import (
|
|||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/mvs"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/str"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -97,8 +95,8 @@ func Init() {
|
|||
}
|
||||
|
||||
// If this is testgo - the test binary during cmd/go tests -
|
||||
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting.
|
||||
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" {
|
||||
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting or this is 'go mod -init'.
|
||||
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" && !CmdModInit {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -135,25 +133,30 @@ func Init() {
|
|||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
inGOPATH := false
|
||||
for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
|
||||
if gopath == "" {
|
||||
continue
|
||||
}
|
||||
if search.InDir(cwd, filepath.Join(gopath, "src")) != "" {
|
||||
inGOPATH = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if inGOPATH && !MustUseModules && cfg.CmdName == "mod" {
|
||||
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
|
||||
}
|
||||
|
||||
if CmdModInit {
|
||||
// Running 'go mod -init': go.mod will be created in current directory.
|
||||
ModRoot = cwd
|
||||
} else {
|
||||
inGOPATH := false
|
||||
for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
|
||||
if gopath == "" {
|
||||
continue
|
||||
}
|
||||
if str.HasFilePathPrefix(cwd, filepath.Join(gopath, "src")) {
|
||||
inGOPATH = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if inGOPATH {
|
||||
if !MustUseModules {
|
||||
// No automatic enabling in GOPATH.
|
||||
return
|
||||
if inGOPATH && !MustUseModules {
|
||||
// No automatic enabling in GOPATH.
|
||||
if root, _ := FindModuleRoot(cwd, "", false); root != "" {
|
||||
cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
|
||||
}
|
||||
return
|
||||
}
|
||||
root, _ := FindModuleRoot(cwd, "", MustUseModules)
|
||||
if root == "" {
|
||||
|
@ -424,9 +427,11 @@ func FindModulePath(dir string) (string, error) {
|
|||
|
||||
// Look for path in GOPATH.
|
||||
for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
|
||||
src := filepath.Join(gpdir, "src") + string(filepath.Separator)
|
||||
if strings.HasPrefix(dir, src) {
|
||||
return filepath.ToSlash(dir[len(src):]), nil
|
||||
if gpdir == "" {
|
||||
continue
|
||||
}
|
||||
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
|
||||
return filepath.ToSlash(rel), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +518,7 @@ func fixVersion(path, vers string) (string, error) {
|
|||
if !ok {
|
||||
return "", fmt.Errorf("malformed module path: %s", path)
|
||||
}
|
||||
if semver.IsValid(vers) && vers == semver.Canonical(vers) && module.MatchPathMajor(vers, pathMajor) {
|
||||
if vers != "" && module.CanonicalVersion(vers) == vers && module.MatchPathMajor(vers, pathMajor) {
|
||||
return vers, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
|
|||
return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
|
||||
}
|
||||
var ok func(module.Version) bool
|
||||
var prefix string
|
||||
var preferOlder bool
|
||||
switch {
|
||||
case query == "latest":
|
||||
|
@ -95,9 +96,10 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
|
|||
ok = func(m module.Version) bool {
|
||||
return matchSemverPrefix(query, m.Version) && allowed(m)
|
||||
}
|
||||
prefix = query + "."
|
||||
|
||||
case semver.IsValid(query):
|
||||
vers := semver.Canonical(query)
|
||||
vers := module.CanonicalVersion(query)
|
||||
if !allowed(module.Version{Path: path, Version: vers}) {
|
||||
return nil, fmt.Errorf("%s@%s excluded", path, vers)
|
||||
}
|
||||
|
@ -120,7 +122,7 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versions, err := repo.Versions("")
|
||||
versions, err := repo.Versions(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ var queryTests = []struct {
|
|||
{path: queryRepo, query: ">v1.9.9", vers: "v1.9.10-pre1"},
|
||||
{path: queryRepo, query: ">v1.10.0", err: `no matching versions for query ">v1.10.0"`},
|
||||
{path: queryRepo, query: ">=v1.10.0", err: `no matching versions for query ">=v1.10.0"`},
|
||||
{path: queryRepo, query: "6cf84eb", vers: "v0.0.0-20180704023347-6cf84ebaea54"},
|
||||
{path: queryRepo, query: "6cf84eb", vers: "v0.0.2-0.20180704023347-6cf84ebaea54"},
|
||||
{path: queryRepo, query: "start", vers: "v0.0.0-20180704023101-5e9e31667ddf"},
|
||||
{path: queryRepo, query: "7a1b6bf", vers: "v0.1.0"},
|
||||
|
||||
|
|
|
@ -6,6 +6,17 @@
|
|||
// along with support code.
|
||||
package module
|
||||
|
||||
// IMPORTANT NOTE
|
||||
//
|
||||
// This file essentially defines the set of valid import paths for the go command.
|
||||
// There are many subtle considerations, including Unicode ambiguity,
|
||||
// security, network, and file system representations.
|
||||
//
|
||||
// This file also defines the set of valid module path and version combinations,
|
||||
// another topic with many subtle considerations.
|
||||
//
|
||||
// Changes to the semantics in this file require approval from rsc.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
@ -42,65 +53,77 @@ func Check(path, version string) error {
|
|||
if !semver.IsValid(version) {
|
||||
return fmt.Errorf("malformed semantic version %v", version)
|
||||
}
|
||||
vm := semver.Major(version)
|
||||
_, pathVersion, _ := SplitPathVersion(path)
|
||||
|
||||
if strings.HasPrefix(pathVersion, ".") {
|
||||
// Special-case gopkg.in path requirements.
|
||||
pathVersion = pathVersion[1:] // cut .
|
||||
if vm == pathVersion {
|
||||
return nil
|
||||
_, pathMajor, _ := SplitPathVersion(path)
|
||||
if !MatchPathMajor(version, pathMajor) {
|
||||
if pathMajor == "" {
|
||||
pathMajor = "v0 or v1"
|
||||
}
|
||||
} else {
|
||||
// Standard path requirements.
|
||||
if pathVersion != "" {
|
||||
pathVersion = pathVersion[1:] // cut /
|
||||
}
|
||||
if vm == "v0" || vm == "v1" {
|
||||
vm = ""
|
||||
}
|
||||
if vm == pathVersion {
|
||||
return nil
|
||||
}
|
||||
if pathVersion == "" {
|
||||
pathVersion = "v0 or v1"
|
||||
if pathMajor[0] == '.' { // .v1
|
||||
pathMajor = pathMajor[1:]
|
||||
}
|
||||
return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor)
|
||||
}
|
||||
return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// firstPathOK reports whether r can appear in the first element of a module path.
|
||||
// The first element of the path must be an LDH domain name, at least for now.
|
||||
// To avoid case ambiguity, the domain name must be entirely lower case.
|
||||
func firstPathOK(r rune) bool {
|
||||
return r == '-' || r == '.' ||
|
||||
'0' <= r && r <= '9' ||
|
||||
'A' <= r && r <= 'Z' ||
|
||||
'a' <= r && r <= 'z'
|
||||
}
|
||||
|
||||
// pathOK reports whether r can appear in a module path.
|
||||
// Paths must avoid potentially problematic ASCII punctuation
|
||||
// and control characters but otherwise can be any Unicode printable character,
|
||||
// as defined by Go's IsPrint.
|
||||
// pathOK reports whether r can appear in an import path element.
|
||||
// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~.
|
||||
// This matches what "go get" has historically recognized in import paths.
|
||||
// TODO(rsc): We would like to allow Unicode letters, but that requires additional
|
||||
// care in the safe encoding (see note below).
|
||||
func pathOK(r rune) bool {
|
||||
if r < utf8.RuneSelf {
|
||||
return r == '+' || r == ',' || r == '-' || r == '.' || r == '/' || r == '_' || r == '~' ||
|
||||
return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' ||
|
||||
'0' <= r && r <= '9' ||
|
||||
'A' <= r && r <= 'Z' ||
|
||||
'a' <= r && r <= 'z'
|
||||
}
|
||||
return unicode.IsPrint(r)
|
||||
return false
|
||||
}
|
||||
|
||||
// fileNameOK reports whether r can appear in a file name.
|
||||
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters.
|
||||
// If we expand the set of allowed characters here, we have to
|
||||
// work harder at detecting potential case-folding and normalization collisions.
|
||||
// See note about "safe encoding" below.
|
||||
func fileNameOK(r rune) bool {
|
||||
if r < utf8.RuneSelf {
|
||||
// Entire set of ASCII punctuation, from which we remove characters:
|
||||
// ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
|
||||
// We disallow some shell special characters: " ' * < > ? ` |
|
||||
// (Note that some of those are disallowed by the Windows file system as well.)
|
||||
// We also disallow path separators / : and \ (fileNameOK is only called on path element characters).
|
||||
// We allow spaces (U+0020) in file names.
|
||||
const allowed = "!#$%&()+,-.=@[]^_{}~ "
|
||||
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < len(allowed); i++ {
|
||||
if rune(allowed[i]) == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// It may be OK to add more ASCII punctuation here, but only carefully.
|
||||
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
|
||||
return unicode.IsLetter(r)
|
||||
}
|
||||
|
||||
// CheckPath checks that a module path is valid.
|
||||
func CheckPath(path string) error {
|
||||
if !utf8.ValidString(path) {
|
||||
return fmt.Errorf("malformed module path %q: invalid UTF-8", path)
|
||||
if err := checkPath(path, false); err != nil {
|
||||
return fmt.Errorf("malformed module path %q: %v", path, err)
|
||||
}
|
||||
if path == "" {
|
||||
return fmt.Errorf("malformed module path %q: empty string", path)
|
||||
}
|
||||
|
||||
i := strings.Index(path, "/")
|
||||
if i < 0 {
|
||||
i = len(path)
|
||||
|
@ -111,40 +134,139 @@ func CheckPath(path string) error {
|
|||
if !strings.Contains(path[:i], ".") {
|
||||
return fmt.Errorf("malformed module path %q: missing dot in first path element", path)
|
||||
}
|
||||
if path[i-1] == '.' {
|
||||
return fmt.Errorf("malformed module path %q: trailing dot in first path element", path)
|
||||
}
|
||||
if path[0] == '.' {
|
||||
return fmt.Errorf("malformed module path %q: leading dot in first path element", path)
|
||||
}
|
||||
if path[0] == '-' {
|
||||
return fmt.Errorf("malformed module path %q: leading dash in first path element", path)
|
||||
}
|
||||
if strings.Contains(path, "..") {
|
||||
return fmt.Errorf("malformed module path %q: double dot", path)
|
||||
}
|
||||
if strings.Contains(path, "//") {
|
||||
return fmt.Errorf("malformed module path %q: double slash", path)
|
||||
}
|
||||
for _, r := range path[:i] {
|
||||
if !firstPathOK(r) {
|
||||
return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r)
|
||||
}
|
||||
}
|
||||
if path[len(path)-1] == '/' {
|
||||
return fmt.Errorf("malformed module path %q: trailing slash", path)
|
||||
}
|
||||
for _, r := range path {
|
||||
if !pathOK(r) {
|
||||
return fmt.Errorf("malformed module path %q: invalid char %q", path, r)
|
||||
}
|
||||
}
|
||||
if _, _, ok := SplitPathVersion(path); !ok {
|
||||
return fmt.Errorf("malformed module path %q: invalid version %s", path, path[strings.LastIndex(path, "/")+1:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckImportPath checks that an import path is valid.
|
||||
func CheckImportPath(path string) error {
|
||||
if err := checkPath(path, false); err != nil {
|
||||
return fmt.Errorf("malformed import path %q: %v", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkPath checks that a general path is valid.
|
||||
// It returns an error describing why but not mentioning path.
|
||||
// Because these checks apply to both module paths and import paths,
|
||||
// the caller is expected to add the "malformed ___ path %q: " prefix.
|
||||
// fileName indicates whether the final element of the path is a file name
|
||||
// (as opposed to a directory name).
|
||||
func checkPath(path string, fileName bool) error {
|
||||
if !utf8.ValidString(path) {
|
||||
return fmt.Errorf("invalid UTF-8")
|
||||
}
|
||||
if path == "" {
|
||||
return fmt.Errorf("empty string")
|
||||
}
|
||||
if strings.Contains(path, "..") {
|
||||
return fmt.Errorf("double dot")
|
||||
}
|
||||
if strings.Contains(path, "//") {
|
||||
return fmt.Errorf("double slash")
|
||||
}
|
||||
if path[len(path)-1] == '/' {
|
||||
return fmt.Errorf("trailing slash")
|
||||
}
|
||||
elemStart := 0
|
||||
for i, r := range path {
|
||||
if r == '/' {
|
||||
if err := checkElem(path[elemStart:i], fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
elemStart = i + 1
|
||||
}
|
||||
}
|
||||
if err := checkElem(path[elemStart:], fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkElem checks whether an individual path element is valid.
|
||||
// fileName indicates whether the element is a file name (not a directory name).
|
||||
func checkElem(elem string, fileName bool) error {
|
||||
if elem == "" {
|
||||
return fmt.Errorf("empty path element")
|
||||
}
|
||||
if strings.Count(elem, ".") == len(elem) {
|
||||
return fmt.Errorf("invalid path element %q", elem)
|
||||
}
|
||||
if elem[0] == '.' && !fileName {
|
||||
return fmt.Errorf("leading dot in path element")
|
||||
}
|
||||
if elem[len(elem)-1] == '.' {
|
||||
return fmt.Errorf("trailing dot in path element")
|
||||
}
|
||||
charOK := pathOK
|
||||
if fileName {
|
||||
charOK = fileNameOK
|
||||
}
|
||||
for _, r := range elem {
|
||||
if !charOK(r) {
|
||||
return fmt.Errorf("invalid char %q", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Windows disallows a bunch of path elements, sadly.
|
||||
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||
short := elem
|
||||
if i := strings.Index(short, "."); i >= 0 {
|
||||
short = short[:i]
|
||||
}
|
||||
for _, bad := range badWindowsNames {
|
||||
if strings.EqualFold(bad, short) {
|
||||
return fmt.Errorf("disallowed path element %q", elem)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckFilePath checks whether a slash-separated file path is valid.
|
||||
func CheckFilePath(path string) error {
|
||||
if err := checkPath(path, true); err != nil {
|
||||
return fmt.Errorf("malformed file path %q: %v", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// badWindowsNames are the reserved file path elements on Windows.
|
||||
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||
var badWindowsNames = []string{
|
||||
"CON",
|
||||
"PRN",
|
||||
"AUX",
|
||||
"NUL",
|
||||
"COM1",
|
||||
"COM2",
|
||||
"COM3",
|
||||
"COM4",
|
||||
"COM5",
|
||||
"COM6",
|
||||
"COM7",
|
||||
"COM8",
|
||||
"COM9",
|
||||
"LPT1",
|
||||
"LPT2",
|
||||
"LPT3",
|
||||
"LPT4",
|
||||
"LPT5",
|
||||
"LPT6",
|
||||
"LPT7",
|
||||
"LPT8",
|
||||
"LPT9",
|
||||
}
|
||||
|
||||
// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path
|
||||
// and version is either empty or "/vN" for N >= 2.
|
||||
// As a special case, gopkg.in paths are recognized directly;
|
||||
|
@ -195,13 +317,28 @@ func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) {
|
|||
// MatchPathMajor reports whether the semantic version v
|
||||
// matches the path major version pathMajor.
|
||||
func MatchPathMajor(v, pathMajor string) bool {
|
||||
if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" {
|
||||
// Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1.
|
||||
// For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405.
|
||||
return true
|
||||
}
|
||||
m := semver.Major(v)
|
||||
if pathMajor == "" {
|
||||
return m == "v0" || m == "v1"
|
||||
return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible"
|
||||
}
|
||||
return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:]
|
||||
}
|
||||
|
||||
// CanonicalVersion returns the canonical form of the version string v.
|
||||
// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible".
|
||||
func CanonicalVersion(v string) string {
|
||||
cv := semver.Canonical(v)
|
||||
if semver.Build(v) == "+incompatible" {
|
||||
cv += "+incompatible"
|
||||
}
|
||||
return cv
|
||||
}
|
||||
|
||||
// Sort sorts the list by Path, breaking ties by comparing Versions.
|
||||
func Sort(list []Version) {
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
|
@ -228,3 +365,137 @@ func Sort(list []Version) {
|
|||
return fi < fj
|
||||
})
|
||||
}
|
||||
|
||||
// Safe encodings
|
||||
//
|
||||
// Module paths appear as substrings of file system paths
|
||||
// (in the download cache) and of web server URLs in the proxy protocol.
|
||||
// In general we cannot rely on file systems to be case-sensitive,
|
||||
// nor can we rely on web servers, since they read from file systems.
|
||||
// That is, we cannot rely on the file system to keep rsc.io/QUOTE
|
||||
// and rsc.io/quote separate. Windows and macOS don't.
|
||||
// Instead, we must never require two different casings of a file path.
|
||||
// Because we want the download cache to match the proxy protocol,
|
||||
// and because we want the proxy protocol to be possible to serve
|
||||
// from a tree of static files (which might be stored on a case-insensitive
|
||||
// file system), the proxy protocol must never require two different casings
|
||||
// of a URL path either.
|
||||
//
|
||||
// One possibility would be to make the safe encoding be the lowercase
|
||||
// hexadecimal encoding of the actual path bytes. This would avoid ever
|
||||
// needing different casings of a file path, but it would be fairly illegible
|
||||
// to most programmers when those paths appeared in the file system
|
||||
// (including in file paths in compiler errors and stack traces)
|
||||
// in web server logs, and so on. Instead, we want a safe encoding that
|
||||
// leaves most paths unaltered.
|
||||
//
|
||||
// The safe encoding is this:
|
||||
// replace every uppercase letter with an exclamation mark
|
||||
// followed by the letter's lowercase equivalent.
|
||||
//
|
||||
// For example,
|
||||
// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go.
|
||||
// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy
|
||||
// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus.
|
||||
//
|
||||
// Import paths that avoid upper-case letters are left unchanged.
|
||||
// Note that because import paths are ASCII-only and avoid various
|
||||
// problematic punctuation (like : < and >), the safe encoding is also ASCII-only
|
||||
// and avoids the same problematic punctuation.
|
||||
//
|
||||
// Import paths have never allowed exclamation marks, so there is no
|
||||
// need to define how to encode a literal !.
|
||||
//
|
||||
// Although paths are disallowed from using Unicode (see pathOK above),
|
||||
// the eventual plan is to allow Unicode letters as well, to assume that
|
||||
// file systems and URLs are Unicode-safe (storing UTF-8), and apply
|
||||
// the !-for-uppercase convention. Note however that not all runes that
|
||||
// are different but case-fold equivalent are an upper/lower pair.
|
||||
// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin)
|
||||
// are considered to case-fold to each other. When we do add Unicode
|
||||
// letters, we must not assume that upper/lower are the only case-equivalent pairs.
|
||||
// Perhaps the Kelvin symbol would be disallowed entirely, for example.
|
||||
// Or perhaps it would encode as "!!k", or perhaps as "(212A)".
|
||||
//
|
||||
// Also, it would be nice to allow Unicode marks as well as letters,
|
||||
// but marks include combining marks, and then we must deal not
|
||||
// only with case folding but also normalization: both U+00E9 ('é')
|
||||
// and U+0065 U+0301 ('e' followed by combining acute accent)
|
||||
// look the same on the page and are treated by some file systems
|
||||
// as the same path. If we do allow Unicode marks in paths, there
|
||||
// must be some kind of normalization to allow only one canonical
|
||||
// encoding of any character used in an import path.
|
||||
|
||||
// EncodePath returns the safe encoding of the given module path.
|
||||
// It fails if the module path is invalid.
|
||||
func EncodePath(path string) (encoding string, err error) {
|
||||
if err := CheckPath(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
haveUpper := false
|
||||
for _, r := range path {
|
||||
if r == '!' || r >= utf8.RuneSelf {
|
||||
// This should be disallowed by CheckPath, but diagnose anyway.
|
||||
// The correctness of the encoding loop below depends on it.
|
||||
return "", fmt.Errorf("internal error: inconsistency in EncodePath")
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
haveUpper = true
|
||||
}
|
||||
}
|
||||
|
||||
if !haveUpper {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
for _, r := range path {
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
buf = append(buf, '!', byte(r+'a'-'A'))
|
||||
} else {
|
||||
buf = append(buf, byte(r))
|
||||
}
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// DecodePath returns the module path of the given safe encoding.
|
||||
// It fails if the encoding is invalid.
|
||||
func DecodePath(encoding string) (path string, err error) {
|
||||
var buf []byte
|
||||
|
||||
bang := false
|
||||
for _, r := range encoding {
|
||||
if r >= utf8.RuneSelf {
|
||||
goto BadEncoding
|
||||
}
|
||||
if bang {
|
||||
bang = false
|
||||
if r < 'a' || 'z' < r {
|
||||
goto BadEncoding
|
||||
}
|
||||
buf = append(buf, byte(r+'A'-'a'))
|
||||
continue
|
||||
}
|
||||
if r == '!' {
|
||||
bang = true
|
||||
continue
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
goto BadEncoding
|
||||
}
|
||||
buf = append(buf, byte(r))
|
||||
}
|
||||
if bang {
|
||||
goto BadEncoding
|
||||
}
|
||||
path = string(buf)
|
||||
if err := CheckPath(path); err != nil {
|
||||
return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err)
|
||||
}
|
||||
return path, nil
|
||||
|
||||
BadEncoding:
|
||||
return "", fmt.Errorf("invalid module path encoding %q", encoding)
|
||||
}
|
||||
|
|
|
@ -37,10 +37,21 @@ var checkTests = []struct {
|
|||
{"gopkg.in/yaml.v1", "v2.1.5", false},
|
||||
{"gopkg.in/yaml.v1", "v3.0.0", false},
|
||||
|
||||
// For gopkg.in, .v1 means v1 only (not v0).
|
||||
// But early versions of vgo still generated v0 pseudo-versions for it.
|
||||
// Even though now we'd generate those as v1 pseudo-versions,
|
||||
// we accept the old pseudo-versions to avoid breaking existing go.mod files.
|
||||
// For example gopkg.in/yaml.v2@v2.2.1's go.mod requires check.v1 at a v0 pseudo-version.
|
||||
{"gopkg.in/check.v1", "v0.0.0", false},
|
||||
{"gopkg.in/check.v1", "v0.0.0-20160102150405-abcdef123456", true},
|
||||
|
||||
{"gopkg.in/yaml.v2", "v1.0.0", false},
|
||||
{"gopkg.in/yaml.v2", "v2.0.0", true},
|
||||
{"gopkg.in/yaml.v2", "v2.1.5", true},
|
||||
{"gopkg.in/yaml.v2", "v3.0.0", false},
|
||||
|
||||
{"rsc.io/quote", "v17.0.0", false},
|
||||
{"rsc.io/quote", "v17.0.0+incompatible", true},
|
||||
}
|
||||
|
||||
func TestCheck(t *testing.T) {
|
||||
|
@ -55,92 +66,120 @@ func TestCheck(t *testing.T) {
|
|||
}
|
||||
|
||||
var checkPathTests = []struct {
|
||||
path string
|
||||
ok bool
|
||||
path string
|
||||
ok bool
|
||||
importOK bool
|
||||
fileOK bool
|
||||
}{
|
||||
{"x.y/z", true},
|
||||
{"x.y", true},
|
||||
{"x.y/z", true, true, true},
|
||||
{"x.y", true, true, true},
|
||||
|
||||
{"", false},
|
||||
{"x.y/\xFFz", false},
|
||||
{"/x.y/z", false},
|
||||
{"x./z", false},
|
||||
{".x/z", false},
|
||||
{"-x/z", false},
|
||||
{"x..y/z", false},
|
||||
{"x.y/z/../../w", false},
|
||||
{"x.y//z", false},
|
||||
{"x.y/z//w", false},
|
||||
{"x.y/z/", false},
|
||||
{"", false, false, false},
|
||||
{"x.y/\xFFz", false, false, false},
|
||||
{"/x.y/z", false, false, false},
|
||||
{"x./z", false, false, false},
|
||||
{".x/z", false, false, true},
|
||||
{"-x/z", false, true, true},
|
||||
{"x..y/z", false, false, false},
|
||||
{"x.y/z/../../w", false, false, false},
|
||||
{"x.y//z", false, false, false},
|
||||
{"x.y/z//w", false, false, false},
|
||||
{"x.y/z/", false, false, false},
|
||||
|
||||
{"x.y/z/v0", false},
|
||||
{"x.y/z/v1", false},
|
||||
{"x.y/z/v2", true},
|
||||
{"x.y/z/v2.0", false},
|
||||
{"x.y/z/v0", false, true, true},
|
||||
{"x.y/z/v1", false, true, true},
|
||||
{"x.y/z/v2", true, true, true},
|
||||
{"x.y/z/v2.0", false, true, true},
|
||||
{"X.y/z", false, true, true},
|
||||
|
||||
{"!x.y/z", false},
|
||||
{"_x.y/z", false},
|
||||
{"x.y!/z", false},
|
||||
{"x.y\"/z", false},
|
||||
{"x.y#/z", false},
|
||||
{"x.y$/z", false},
|
||||
{"x.y%/z", false},
|
||||
{"x.y&/z", false},
|
||||
{"x.y'/z", false},
|
||||
{"x.y(/z", false},
|
||||
{"x.y)/z", false},
|
||||
{"x.y*/z", false},
|
||||
{"x.y+/z", false},
|
||||
{"x.y,/z", false},
|
||||
{"x.y-/z", true},
|
||||
{"x.y./zt", false},
|
||||
{"x.y:/z", false},
|
||||
{"x.y;/z", false},
|
||||
{"x.y</z", false},
|
||||
{"x.y=/z", false},
|
||||
{"x.y>/z", false},
|
||||
{"x.y?/z", false},
|
||||
{"x.y@/z", false},
|
||||
{"x.y[/z", false},
|
||||
{"x.y\\/z", false},
|
||||
{"x.y]/z", false},
|
||||
{"x.y^/z", false},
|
||||
{"x.y_/z", false},
|
||||
{"x.y`/z", false},
|
||||
{"x.y{/z", false},
|
||||
{"x.y}/z", false},
|
||||
{"x.y~/z", false},
|
||||
{"x.y/z!", false},
|
||||
{"x.y/z\"", false},
|
||||
{"x.y/z#", false},
|
||||
{"x.y/z$", false},
|
||||
{"x.y/z%", false},
|
||||
{"x.y/z&", false},
|
||||
{"x.y/z'", false},
|
||||
{"x.y/z(", false},
|
||||
{"x.y/z)", false},
|
||||
{"x.y/z*", false},
|
||||
{"x.y/z+", true},
|
||||
{"x.y/z,", true},
|
||||
{"x.y/z-", true},
|
||||
{"x.y/z.t", true},
|
||||
{"x.y/z/t", true},
|
||||
{"x.y/z:", false},
|
||||
{"x.y/z;", false},
|
||||
{"x.y/z<", false},
|
||||
{"x.y/z=", false},
|
||||
{"x.y/z>", false},
|
||||
{"x.y/z?", false},
|
||||
{"x.y/z@", false},
|
||||
{"x.y/z[", false},
|
||||
{"x.y/z\\", false},
|
||||
{"x.y/z]", false},
|
||||
{"x.y/z^", false},
|
||||
{"x.y/z_", true},
|
||||
{"x.y/z`", false},
|
||||
{"x.y/z{", false},
|
||||
{"x.y/z}", false},
|
||||
{"x.y/z~", true},
|
||||
{"!x.y/z", false, false, true},
|
||||
{"_x.y/z", false, true, true},
|
||||
{"x.y!/z", false, false, true},
|
||||
{"x.y\"/z", false, false, false},
|
||||
{"x.y#/z", false, false, true},
|
||||
{"x.y$/z", false, false, true},
|
||||
{"x.y%/z", false, false, true},
|
||||
{"x.y&/z", false, false, true},
|
||||
{"x.y'/z", false, false, false},
|
||||
{"x.y(/z", false, false, true},
|
||||
{"x.y)/z", false, false, true},
|
||||
{"x.y*/z", false, false, false},
|
||||
{"x.y+/z", false, true, true},
|
||||
{"x.y,/z", false, false, true},
|
||||
{"x.y-/z", true, true, true},
|
||||
{"x.y./zt", false, false, false},
|
||||
{"x.y:/z", false, false, false},
|
||||
{"x.y;/z", false, false, false},
|
||||
{"x.y</z", false, false, false},
|
||||
{"x.y=/z", false, false, true},
|
||||
{"x.y>/z", false, false, false},
|
||||
{"x.y?/z", false, false, false},
|
||||
{"x.y@/z", false, false, true},
|
||||
{"x.y[/z", false, false, true},
|
||||
{"x.y\\/z", false, false, false},
|
||||
{"x.y]/z", false, false, true},
|
||||
{"x.y^/z", false, false, true},
|
||||
{"x.y_/z", false, true, true},
|
||||
{"x.y`/z", false, false, false},
|
||||
{"x.y{/z", false, false, true},
|
||||
{"x.y}/z", false, false, true},
|
||||
{"x.y~/z", false, true, true},
|
||||
{"x.y/z!", false, false, true},
|
||||
{"x.y/z\"", false, false, false},
|
||||
{"x.y/z#", false, false, true},
|
||||
{"x.y/z$", false, false, true},
|
||||
{"x.y/z%", false, false, true},
|
||||
{"x.y/z&", false, false, true},
|
||||
{"x.y/z'", false, false, false},
|
||||
{"x.y/z(", false, false, true},
|
||||
{"x.y/z)", false, false, true},
|
||||
{"x.y/z*", false, false, false},
|
||||
{"x.y/z+", true, true, true},
|
||||
{"x.y/z,", false, false, true},
|
||||
{"x.y/z-", true, true, true},
|
||||
{"x.y/z.t", true, true, true},
|
||||
{"x.y/z/t", true, true, true},
|
||||
{"x.y/z:", false, false, false},
|
||||
{"x.y/z;", false, false, false},
|
||||
{"x.y/z<", false, false, false},
|
||||
{"x.y/z=", false, false, true},
|
||||
{"x.y/z>", false, false, false},
|
||||
{"x.y/z?", false, false, false},
|
||||
{"x.y/z@", false, false, true},
|
||||
{"x.y/z[", false, false, true},
|
||||
{"x.y/z\\", false, false, false},
|
||||
{"x.y/z]", false, false, true},
|
||||
{"x.y/z^", false, false, true},
|
||||
{"x.y/z_", true, true, true},
|
||||
{"x.y/z`", false, false, false},
|
||||
{"x.y/z{", false, false, true},
|
||||
{"x.y/z}", false, false, true},
|
||||
{"x.y/z~", true, true, true},
|
||||
{"x.y/x.foo", true, true, true},
|
||||
{"x.y/aux.foo", false, false, false},
|
||||
{"x.y/prn", false, false, false},
|
||||
{"x.y/prn2", true, true, true},
|
||||
{"x.y/com", true, true, true},
|
||||
{"x.y/com1", false, false, false},
|
||||
{"x.y/com1.txt", false, false, false},
|
||||
{"x.y/calm1", true, true, true},
|
||||
{"github.com/!123/logrus", false, false, true},
|
||||
|
||||
// TODO: CL 41822 allowed Unicode letters in old "go get"
|
||||
// without due consideration of the implications, and only on github.com (!).
|
||||
// For now, we disallow non-ASCII characters in module mode,
|
||||
// in both module paths and general import paths,
|
||||
// until we can get the implications right.
|
||||
// When we do, we'll enable them everywhere, not just for GitHub.
|
||||
{"github.com/user/unicode/испытание", false, false, true},
|
||||
|
||||
{"../x", false, false, false},
|
||||
{"./y", false, false, false},
|
||||
{"x:y", false, false, false},
|
||||
{`\temp\foo`, false, false, false},
|
||||
{".gitignore", false, false, true},
|
||||
{".github/ISSUE_TEMPLATE", false, false, true},
|
||||
{"x☺y", false, false, false},
|
||||
}
|
||||
|
||||
func TestCheckPath(t *testing.T) {
|
||||
|
@ -151,6 +190,20 @@ func TestCheckPath(t *testing.T) {
|
|||
} else if !tt.ok && err == nil {
|
||||
t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
|
||||
err = CheckImportPath(tt.path)
|
||||
if tt.importOK && err != nil {
|
||||
t.Errorf("CheckImportPath(%q) = %v, wanted nil error", tt.path, err)
|
||||
} else if !tt.importOK && err == nil {
|
||||
t.Errorf("CheckImportPath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
|
||||
err = CheckFilePath(tt.path)
|
||||
if tt.fileOK && err != nil {
|
||||
t.Errorf("CheckFilePath(%q) = %v, wanted nil error", tt.path, err)
|
||||
} else if !tt.fileOK && err == nil {
|
||||
t.Errorf("CheckFilePath(%q) succeeded, wanted error", tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,3 +235,84 @@ func TestSplitPathVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var encodeTests = []struct {
|
||||
path string
|
||||
enc string // empty means same as path
|
||||
}{
|
||||
{path: "ascii.com/abcdefghijklmnopqrstuvwxyz.-+/~_0123456789"},
|
||||
{path: "github.com/GoogleCloudPlatform/omega", enc: "github.com/!google!cloud!platform/omega"},
|
||||
}
|
||||
|
||||
func TestEncodePath(t *testing.T) {
|
||||
// Check invalid paths.
|
||||
for _, tt := range checkPathTests {
|
||||
if !tt.ok {
|
||||
_, err := EncodePath(tt.path)
|
||||
if err == nil {
|
||||
t.Errorf("EncodePath(%q): succeeded, want error (invalid path)", tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check encodings.
|
||||
for _, tt := range encodeTests {
|
||||
enc, err := EncodePath(tt.path)
|
||||
if err != nil {
|
||||
t.Errorf("EncodePath(%q): unexpected error: %v", tt.path, err)
|
||||
continue
|
||||
}
|
||||
want := tt.enc
|
||||
if want == "" {
|
||||
want = tt.path
|
||||
}
|
||||
if enc != want {
|
||||
t.Errorf("EncodePath(%q) = %q, want %q", tt.path, enc, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var badDecode = []string{
|
||||
"github.com/GoogleCloudPlatform/omega",
|
||||
"github.com/!google!cloud!platform!/omega",
|
||||
"github.com/!0google!cloud!platform/omega",
|
||||
"github.com/!_google!cloud!platform/omega",
|
||||
"github.com/!!google!cloud!platform/omega",
|
||||
"",
|
||||
}
|
||||
|
||||
func TestDecodePath(t *testing.T) {
|
||||
// Check invalid decodings.
|
||||
for _, bad := range badDecode {
|
||||
_, err := DecodePath(bad)
|
||||
if err == nil {
|
||||
t.Errorf("DecodePath(%q): succeeded, want error (invalid decoding)", bad)
|
||||
}
|
||||
}
|
||||
|
||||
// Check invalid paths (or maybe decodings).
|
||||
for _, tt := range checkPathTests {
|
||||
if !tt.ok {
|
||||
path, err := DecodePath(tt.path)
|
||||
if err == nil {
|
||||
t.Errorf("DecodePath(%q) = %q, want error (invalid path)", tt.path, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check encodings.
|
||||
for _, tt := range encodeTests {
|
||||
enc := tt.enc
|
||||
if enc == "" {
|
||||
enc = tt.path
|
||||
}
|
||||
path, err := DecodePath(enc)
|
||||
if err != nil {
|
||||
t.Errorf("DecodePath(%q): unexpected error: %v", enc, err)
|
||||
continue
|
||||
}
|
||||
if path != tt.path {
|
||||
t.Errorf("DecodePath(%q) = %q, want %q", enc, path, tt.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ func (w *Work) Do(n int, f func(item interface{})) {
|
|||
if n < 1 {
|
||||
panic("par.Work.Do: n < 1")
|
||||
}
|
||||
n = 1
|
||||
if w.running >= 1 {
|
||||
panic("par.Work.Do: already called Do")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package par
|
|||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWork(t *testing.T) {
|
||||
|
@ -30,6 +31,30 @@ func TestWork(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestWorkParallel(t *testing.T) {
|
||||
var w Work
|
||||
|
||||
for tries := 0; tries < 10; tries++ {
|
||||
const N = 100
|
||||
for i := 0; i < N; i++ {
|
||||
w.Add(i)
|
||||
}
|
||||
start := time.Now()
|
||||
var n int32
|
||||
w.Do(N, func(x interface{}) {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
atomic.AddInt32(&n, +1)
|
||||
})
|
||||
if n != N {
|
||||
t.Fatalf("par.Work.Do did not do all the work")
|
||||
}
|
||||
if time.Since(start) < N/2*time.Millisecond {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("par.Work.Do does not seem to be parallel")
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
var cache Cache
|
||||
|
||||
|
|
|
@ -437,3 +437,74 @@ func IsStandardImportPath(path string) bool {
|
|||
func IsRelativePath(pattern string) bool {
|
||||
return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".."
|
||||
}
|
||||
|
||||
// InDir checks whether path is in the file tree rooted at dir.
|
||||
// If so, InDir returns an equivalent path relative to dir.
|
||||
// If not, InDir returns an empty string.
|
||||
// InDir makes some effort to succeed even in the presence of symbolic links.
|
||||
// TODO(rsc): Replace internal/test.inDir with a call to this function for Go 1.12.
|
||||
func InDir(path, dir string) string {
|
||||
if rel := inDirLex(path, dir); rel != "" {
|
||||
return rel
|
||||
}
|
||||
xpath, err := filepath.EvalSymlinks(path)
|
||||
if err != nil || xpath == path {
|
||||
xpath = ""
|
||||
} else {
|
||||
if rel := inDirLex(xpath, dir); rel != "" {
|
||||
return rel
|
||||
}
|
||||
}
|
||||
|
||||
xdir, err := filepath.EvalSymlinks(dir)
|
||||
if err == nil && xdir != dir {
|
||||
if rel := inDirLex(path, xdir); rel != "" {
|
||||
return rel
|
||||
}
|
||||
if xpath != "" {
|
||||
if rel := inDirLex(xpath, xdir); rel != "" {
|
||||
return rel
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// inDirLex is like inDir but only checks the lexical form of the file names.
|
||||
// It does not consider symbolic links.
|
||||
// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
|
||||
// return the suffix. Most uses of str.HasFilePathPrefix should probably
|
||||
// be calling InDir instead.
|
||||
func inDirLex(path, dir string) string {
|
||||
pv := strings.ToUpper(filepath.VolumeName(path))
|
||||
dv := strings.ToUpper(filepath.VolumeName(dir))
|
||||
path = path[len(pv):]
|
||||
dir = dir[len(dv):]
|
||||
switch {
|
||||
default:
|
||||
return ""
|
||||
case pv != dv:
|
||||
return ""
|
||||
case len(path) == len(dir):
|
||||
if path == dir {
|
||||
return "."
|
||||
}
|
||||
return ""
|
||||
case dir == "":
|
||||
return path
|
||||
case len(path) > len(dir):
|
||||
if dir[len(dir)-1] == filepath.Separator {
|
||||
if path[:len(dir)] == dir {
|
||||
return path[len(dir):]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
|
||||
if len(path) == len(dir)+1 {
|
||||
return "."
|
||||
}
|
||||
return path[len(dir)+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func HasPathPrefix(s, prefix string) bool {
|
|||
return true
|
||||
}
|
||||
if len(s) > len(prefix) {
|
||||
if prefix != "" && prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
|
||||
if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
|
||||
return s[:len(prefix)] == prefix
|
||||
}
|
||||
}
|
||||
|
|
|
@ -650,7 +650,7 @@ func runTest(cmd *base.Command, args []string) {
|
|||
}
|
||||
|
||||
// Select for coverage all dependencies matching the testCoverPaths patterns.
|
||||
for _, p := range load.PackageList(pkgs) {
|
||||
for _, p := range load.TestPackageList(pkgs) {
|
||||
haveMatch := false
|
||||
for i := range testCoverPaths {
|
||||
if match[i](p) {
|
||||
|
@ -698,7 +698,7 @@ func runTest(cmd *base.Command, args []string) {
|
|||
coverFiles = append(coverFiles, p.GoFiles...)
|
||||
coverFiles = append(coverFiles, p.CgoFiles...)
|
||||
coverFiles = append(coverFiles, p.TestGoFiles...)
|
||||
p.Internal.CoverVars = declareCoverVars(p.ImportPath, coverFiles...)
|
||||
p.Internal.CoverVars = declareCoverVars(p, coverFiles...)
|
||||
if testCover && testCoverMode == "atomic" {
|
||||
ensureImport(p, "sync/atomic")
|
||||
}
|
||||
|
@ -716,13 +716,12 @@ func runTest(cmd *base.Command, args []string) {
|
|||
if err != nil {
|
||||
str := err.Error()
|
||||
str = strings.TrimPrefix(str, "\n")
|
||||
failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath)
|
||||
|
||||
if p.ImportPath != "" {
|
||||
base.Errorf("# %s\n%s\n%s", p.ImportPath, str, failed)
|
||||
base.Errorf("# %s\n%s", p.ImportPath, str)
|
||||
} else {
|
||||
base.Errorf("%s\n%s", str, failed)
|
||||
base.Errorf("%s", str)
|
||||
}
|
||||
fmt.Printf("FAIL\t%s [setup failed]\n", p.ImportPath)
|
||||
continue
|
||||
}
|
||||
builds = append(builds, buildTest)
|
||||
|
@ -781,6 +780,14 @@ var windowsBadWords = []string{
|
|||
}
|
||||
|
||||
func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) {
|
||||
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
|
||||
build := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
|
||||
run := &work.Action{Mode: "test run", Package: p, Deps: []*work.Action{build}}
|
||||
addTestVet(b, p, run, nil)
|
||||
print := &work.Action{Mode: "test print", Func: builderNoTest, Package: p, Deps: []*work.Action{run}}
|
||||
return build, run, print, nil
|
||||
}
|
||||
|
||||
// Build Package structs describing:
|
||||
// pmain - pkg.test binary
|
||||
// ptest - package + test files
|
||||
|
@ -958,7 +965,7 @@ func isTestFile(file string) bool {
|
|||
|
||||
// declareCoverVars attaches the required cover variables names
|
||||
// to the files, to be used when annotating the files.
|
||||
func declareCoverVars(importPath string, files ...string) map[string]*load.CoverVar {
|
||||
func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVar {
|
||||
coverVars := make(map[string]*load.CoverVar)
|
||||
coverIndex := 0
|
||||
// We create the cover counters as new top-level variables in the package.
|
||||
|
@ -967,14 +974,25 @@ func declareCoverVars(importPath string, files ...string) map[string]*load.Cover
|
|||
// so we append 12 hex digits from the SHA-256 of the import path.
|
||||
// The point is only to avoid accidents, not to defeat users determined to
|
||||
// break things.
|
||||
sum := sha256.Sum256([]byte(importPath))
|
||||
sum := sha256.Sum256([]byte(p.ImportPath))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
for _, file := range files {
|
||||
if isTestFile(file) {
|
||||
continue
|
||||
}
|
||||
// For a package that is "local" (imported via ./ import or command line, outside GOPATH),
|
||||
// we record the full path to the file name.
|
||||
// Otherwise we record the import path, then a forward slash, then the file name.
|
||||
// This makes profiles within GOPATH file system-independent.
|
||||
// These names appear in the cmd/cover HTML interface.
|
||||
var longFile string
|
||||
if p.Internal.Local {
|
||||
longFile = filepath.Join(p.Dir, file)
|
||||
} else {
|
||||
longFile = path.Join(p.ImportPath, file)
|
||||
}
|
||||
coverVars[file] = &load.CoverVar{
|
||||
File: filepath.Join(importPath, file),
|
||||
File: longFile,
|
||||
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
|
||||
}
|
||||
coverIndex++
|
||||
|
@ -1168,17 +1186,13 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
|
|||
|
||||
if err == nil {
|
||||
norun := ""
|
||||
res := "ok"
|
||||
if !testShowPass && !testJSON {
|
||||
buf.Reset()
|
||||
}
|
||||
if len(a.Package.TestGoFiles)+len(a.Package.XTestGoFiles) == 0 {
|
||||
res = "? "
|
||||
norun = " [no test files]"
|
||||
} else if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
|
||||
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
|
||||
norun = " [no tests to run]"
|
||||
}
|
||||
fmt.Fprintf(cmd.Stdout, "%s \t%s\t%s%s%s\n", res, a.Package.ImportPath, t, coveragePercentage(out), norun)
|
||||
fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
|
||||
c.saveOutput(a)
|
||||
} else {
|
||||
base.SetExitStatus(1)
|
||||
|
@ -1592,3 +1606,15 @@ func builderPrintTest(b *work.Builder, a *work.Action) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// builderNoTest is the action for testing a package with no test files.
|
||||
func builderNoTest(b *work.Builder, a *work.Action) error {
|
||||
var stdout io.Writer = os.Stdout
|
||||
if testJSON {
|
||||
json := test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
|
||||
defer json.Close()
|
||||
stdout = json
|
||||
}
|
||||
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", a.Package.ImportPath)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,9 +22,8 @@
|
|||
// The comment or file content ends at the next file marker line.
|
||||
// The file marker line must begin with the three-byte sequence "-- "
|
||||
// and end with the three-byte sequence " --", but the enclosed
|
||||
// file name can be surrounded by additional white space,
|
||||
// all of which is stripped. The file name may itself contain spaces,
|
||||
// which are preserved.
|
||||
// file name can be surrounding by additional white space,
|
||||
// all of which is stripped.
|
||||
//
|
||||
// If the txtar file is missing a trailing newline on the final line,
|
||||
// parsers should consider a final newline to be present anyway.
|
||||
|
@ -54,8 +53,7 @@ type File struct {
|
|||
// Format returns the serialized form of an Archive.
|
||||
// It is assumed that the Archive data structure is well-formed:
|
||||
// a.Comment and all a.File[i].Data contain no file marker lines,
|
||||
// and all a.File[i].Name are non-empty and well-formed
|
||||
// (no leading or trailing spaces, no newlines, and so on).
|
||||
// and all a.File[i].Name is non-empty.
|
||||
func Format(a *Archive) []byte {
|
||||
var buf bytes.Buffer
|
||||
buf.Write(fixNL(a.Comment))
|
||||
|
|
|
@ -13,8 +13,6 @@ import (
|
|||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
const version = "2018-02-20.1"
|
||||
|
||||
var CmdVersion = &base.Command{
|
||||
Run: runVersion,
|
||||
UsageLine: "version",
|
||||
|
@ -27,5 +25,5 @@ func runVersion(cmd *base.Command, args []string) {
|
|||
cmd.Usage()
|
||||
}
|
||||
|
||||
fmt.Printf("go version %s %s/%s go:%s\n", work.RuntimeVersion, runtime.GOOS, runtime.GOARCH, version)
|
||||
fmt.Printf("go version %s %s/%s vgo:%s\n", work.RuntimeVersion, runtime.GOOS, runtime.GOARCH, version)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package version
|
||||
|
||||
const version = "devel +88c76dcbab"
|
|
@ -213,7 +213,6 @@ const (
|
|||
)
|
||||
|
||||
func (b *Builder) Init() {
|
||||
var err error
|
||||
b.Print = func(a ...interface{}) (int, error) {
|
||||
return fmt.Fprint(os.Stderr, a...)
|
||||
}
|
||||
|
@ -225,14 +224,19 @@ func (b *Builder) Init() {
|
|||
if cfg.BuildN {
|
||||
b.WorkDir = "$WORK"
|
||||
} else {
|
||||
b.WorkDir, err = ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build")
|
||||
tmp, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build")
|
||||
if err != nil {
|
||||
base.Fatalf("%s", err)
|
||||
base.Fatalf("go: creating work dir: %v", err)
|
||||
}
|
||||
if !filepath.IsAbs(b.WorkDir) {
|
||||
os.RemoveAll(b.WorkDir)
|
||||
base.Fatalf("cmd/go: relative tmpdir not supported")
|
||||
if !filepath.IsAbs(tmp) {
|
||||
abs, err := filepath.Abs(tmp)
|
||||
if err != nil {
|
||||
os.RemoveAll(tmp)
|
||||
base.Fatalf("go: creating work dir: %v", err)
|
||||
}
|
||||
tmp = abs
|
||||
}
|
||||
b.WorkDir = tmp
|
||||
if cfg.BuildX || cfg.BuildWork {
|
||||
fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir)
|
||||
}
|
||||
|
@ -403,7 +407,16 @@ func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
|
|||
stk.Pop()
|
||||
aFmt := b.CompileAction(ModeBuild, depMode, p1)
|
||||
|
||||
deps := []*Action{a1, aFmt}
|
||||
var deps []*Action
|
||||
if a1.buggyInstall {
|
||||
// (*Builder).vet expects deps[0] to be the package
|
||||
// and deps[1] to be "fmt". If we see buggyInstall
|
||||
// here then a1 is an install of a shared library,
|
||||
// and the real package is a1.Deps[0].
|
||||
deps = []*Action{a1.Deps[0], aFmt, a1}
|
||||
} else {
|
||||
deps = []*Action{a1, aFmt}
|
||||
}
|
||||
for _, p1 := range load.PackageList(p.Internal.Imports) {
|
||||
deps = append(deps, b.vetAction(mode, depMode, p1))
|
||||
}
|
||||
|
@ -420,7 +433,7 @@ func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
|
|||
// Built-in packages like unsafe.
|
||||
return a
|
||||
}
|
||||
a1.needVet = true
|
||||
deps[0].needVet = true
|
||||
a.Func = (*Builder).vet
|
||||
return a
|
||||
})
|
||||
|
|
|
@ -196,10 +196,13 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
|||
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
|
||||
fmt.Fprintf(h, "import %q\n", p.ImportPath)
|
||||
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)
|
||||
if p.Internal.ForceLibrary {
|
||||
fmt.Fprintf(h, "forcelibrary\n")
|
||||
}
|
||||
if len(p.CgoFiles)+len(p.SwigFiles) > 0 {
|
||||
fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo"))
|
||||
cppflags, cflags, cxxflags, fflags, _, _ := b.CFlags(p)
|
||||
fmt.Fprintf(h, "CC=%q %q %q\n", b.ccExe(), cppflags, cflags)
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, _ := b.CFlags(p)
|
||||
fmt.Fprintf(h, "CC=%q %q %q %q\n", b.ccExe(), cppflags, cflags, ldflags)
|
||||
if len(p.CXXFiles)+len(p.SwigFiles) > 0 {
|
||||
fmt.Fprintf(h, "CXX=%q %q\n", b.cxxExe(), cxxflags)
|
||||
}
|
||||
|
@ -907,11 +910,6 @@ var VetTool string
|
|||
var VetFlags []string
|
||||
|
||||
func (b *Builder) vet(a *Action) error {
|
||||
b.toolID("vet")
|
||||
if oldVet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// a.Deps[0] is the build of the package being vetted.
|
||||
// a.Deps[1] is the build of the "fmt" package.
|
||||
|
||||
|
@ -985,7 +983,7 @@ func (b *Builder) vet(a *Action) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var env []string
|
||||
env := b.cCompilerEnv()
|
||||
if cfg.BuildToolchainName == "gccgo" {
|
||||
env = append(env, "GCCGO="+BuildToolchain.compiler())
|
||||
}
|
||||
|
@ -1370,7 +1368,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
|
|||
// so the built target is not in the a1.Objdir tree that b.cleanup(a1) removes.
|
||||
if a1.built == a.Target {
|
||||
a.built = a.Target
|
||||
b.cleanup(a1)
|
||||
if !a.buggyInstall {
|
||||
b.cleanup(a1)
|
||||
}
|
||||
// Whether we're smart enough to avoid a complete rebuild
|
||||
// depends on exactly what the staleness and rebuild algorithms
|
||||
// are, as well as potentially the state of the Go build cache.
|
||||
|
@ -1424,7 +1424,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
defer b.cleanup(a1)
|
||||
if !a.buggyInstall {
|
||||
defer b.cleanup(a1)
|
||||
}
|
||||
|
||||
return b.moveOrCopyFile(a.Target, a1.built, perm, false)
|
||||
}
|
||||
|
@ -1858,6 +1860,13 @@ func joinUnambiguously(a []string) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
// cCompilerEnv returns environment variables to set when running the
|
||||
// C compiler. This is needed to disable escape codes in clang error
|
||||
// messages that confuse tools like cgo.
|
||||
func (b *Builder) cCompilerEnv() []string {
|
||||
return []string{"TERM=dumb"}
|
||||
}
|
||||
|
||||
// mkdir makes the named directory.
|
||||
func (b *Builder) Mkdir(dir string) error {
|
||||
// Make Mkdir(a.Objdir) a no-op instead of an error when a.Objdir == "".
|
||||
|
@ -2005,7 +2014,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
|
|||
if !filepath.IsAbs(outfile) {
|
||||
outfile = filepath.Join(p.Dir, outfile)
|
||||
}
|
||||
output, err := b.runOut(filepath.Dir(file), nil, compiler, flags, "-o", outfile, "-c", filepath.Base(file))
|
||||
output, err := b.runOut(filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file))
|
||||
if len(output) > 0 {
|
||||
// On FreeBSD 11, when we pass -g to clang 3.8 it
|
||||
// invokes its internal assembler with -dwarf-version=2.
|
||||
|
@ -2045,7 +2054,7 @@ func (b *Builder) gccld(p *load.Package, objdir, out string, flags []string, obj
|
|||
} else {
|
||||
cmd = b.GccCmd(p.Dir, objdir)
|
||||
}
|
||||
return b.run(nil, p.Dir, p.ImportPath, nil, cmd, "-o", out, objs, flags)
|
||||
return b.run(nil, p.Dir, p.ImportPath, b.cCompilerEnv(), cmd, "-o", out, objs, flags)
|
||||
}
|
||||
|
||||
// Grab these before main helpfully overwrites them.
|
||||
|
@ -2340,7 +2349,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
|
|||
// along to the host linker. At this point in the code, cgoLDFLAGS
|
||||
// consists of the original $CGO_LDFLAGS (unchecked) and all the
|
||||
// flags put together from source code (checked).
|
||||
var cgoenv []string
|
||||
cgoenv := b.cCompilerEnv()
|
||||
if len(cgoLDFLAGS) > 0 {
|
||||
flags := make([]string, len(cgoLDFLAGS))
|
||||
for i, f := range cgoLDFLAGS {
|
||||
|
@ -2468,7 +2477,15 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
|
|||
// we need to use -pie for Linux/ARM to get accurate imported sym
|
||||
ldflags := cgoLDFLAGS
|
||||
if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" {
|
||||
ldflags = append(ldflags, "-pie")
|
||||
// -static -pie doesn't make sense, and causes link errors.
|
||||
// Issue 26197.
|
||||
n := make([]string, 0, len(ldflags))
|
||||
for _, flag := range ldflags {
|
||||
if flag != "-static" {
|
||||
n = append(n, flag)
|
||||
}
|
||||
}
|
||||
ldflags = append(n, "-pie")
|
||||
}
|
||||
if err := b.gccld(p, objdir, dynobj, ldflags, linkobj); err != nil {
|
||||
return err
|
||||
|
@ -2479,7 +2496,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
|
|||
if p.Standard && p.ImportPath == "runtime/cgo" {
|
||||
cgoflags = []string{"-dynlinker"} // record path to dynamic linker
|
||||
}
|
||||
return b.run(a, p.Dir, p.ImportPath, nil, cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
|
||||
return b.run(a, p.Dir, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
|
||||
}
|
||||
|
||||
// Run SWIG on all SWIG input files.
|
||||
|
|
|
@ -48,9 +48,9 @@ func instrumentInit() {
|
|||
platform := cfg.Goos + "/" + cfg.Goarch
|
||||
switch platform {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
|
||||
fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, freebsd/amd64, netbsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
|
||||
os.Exit(2)
|
||||
case "linux/amd64", "linux/ppc64le", "freebsd/amd64", "darwin/amd64", "windows/amd64":
|
||||
case "linux/amd64", "linux/ppc64le", "freebsd/amd64", "netbsd/amd64", "darwin/amd64", "windows/amd64":
|
||||
// race supported on these platforms
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ var validLinkerFlags = []*regexp.Regexp{
|
|||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?openmp(-simd)?`),
|
||||
re(`-fsanitize=([^@\-].*)`),
|
||||
re(`-flat_namespace`),
|
||||
re(`-g([^@\-].*)?`),
|
||||
re(`-headerpad_max_install_names`),
|
||||
re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/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 golang.org/x/vgo
|
||||
./go.latest help documentation | sed 's/^package main/package Main/' >alldocs.go
|
||||
gofmt -w alldocs.go
|
||||
rm go.latest
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -18,6 +18,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
|
@ -77,7 +78,12 @@ func readModList() {
|
|||
if i < 0 {
|
||||
continue
|
||||
}
|
||||
path := strings.Replace(name[:i], "_", "/", -1)
|
||||
enc := strings.Replace(name[:i], "_", "/", -1)
|
||||
path, err := module.DecodePath(enc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy_test: %v", err)
|
||||
continue
|
||||
}
|
||||
vers := name[i+1:]
|
||||
modList = append(modList, module.Version{Path: path, Version: vers})
|
||||
}
|
||||
|
@ -98,7 +104,13 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
path, file := path[:i], path[i+len("/@v/"):]
|
||||
enc, file := path[:i], path[i+len("/@v/"):]
|
||||
path, err := module.DecodePath(enc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if file == "list" {
|
||||
n := 0
|
||||
for _, m := range modList {
|
||||
|
@ -147,6 +159,7 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
a := readArchive(path, vers)
|
||||
if a == nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers)
|
||||
http.Error(w, "cannot load archive", 500)
|
||||
return
|
||||
}
|
||||
|
@ -188,6 +201,7 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}).(cached)
|
||||
|
||||
if c.err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err)
|
||||
http.Error(w, c.err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
@ -217,13 +231,21 @@ func findHash(m module.Version) string {
|
|||
|
||||
var archiveCache par.Cache
|
||||
|
||||
var cmdGoDir, _ = os.Getwd()
|
||||
|
||||
func readArchive(path, vers string) *txtar.Archive {
|
||||
prefix := strings.Replace(path, "/", "_", -1)
|
||||
enc, err := module.EncodePath(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := strings.Replace(enc, "/", "_", -1)
|
||||
name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+vers+".txt")
|
||||
a := archiveCache.Do(name, func() interface{} {
|
||||
a, err := txtar.ParseFile(name)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if testing.Verbose() || !os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
|
||||
}
|
||||
a = nil
|
||||
|
|
|
@ -0,0 +1,894 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
// Script-driven tests.
|
||||
// See testdata/script/README for an overview.
|
||||
|
||||
package Main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/txtar"
|
||||
)
|
||||
|
||||
// TestScript runs the tests in testdata/script/*.txt.
|
||||
func TestScript(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
if skipExternal {
|
||||
t.Skipf("skipping external tests on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
files, err := filepath.Glob("testdata/script/*.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
file := file
|
||||
name := strings.TrimSuffix(filepath.Base(file), ".txt")
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ts := &testScript{t: t, name: name, file: file}
|
||||
ts.setup()
|
||||
if !*testWork {
|
||||
defer removeAll(ts.workdir)
|
||||
}
|
||||
ts.run()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A testScript holds execution state for a single test script.
|
||||
type testScript struct {
|
||||
t *testing.T
|
||||
workdir string // temporary work dir ($WORK)
|
||||
log bytes.Buffer // test execution log (printed at end of test)
|
||||
mark int // offset of next log truncation
|
||||
cd string // current directory during test execution; initially $WORK/gopath/src
|
||||
name string // short name of test ("foo")
|
||||
file string // full file name ("testdata/script/foo.txt")
|
||||
lineno int // line number currently executing
|
||||
line string // line currently executing
|
||||
env []string // environment list (for os/exec)
|
||||
envMap map[string]string // environment mapping (matches env)
|
||||
stdout string // standard output from last 'go' command; for 'stdout' command
|
||||
stderr string // standard error from last 'go' command; for 'stderr' command
|
||||
stopped bool // test wants to stop early
|
||||
start time.Time // time phase started
|
||||
}
|
||||
|
||||
var extraEnvKeys = []string{
|
||||
"SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210
|
||||
}
|
||||
|
||||
// setup sets up the test execution temporary directory and environment.
|
||||
func (ts *testScript) setup() {
|
||||
StartProxy()
|
||||
ts.workdir = filepath.Join(testTmpDir, "script-"+ts.name)
|
||||
ts.check(os.MkdirAll(filepath.Join(ts.workdir, "tmp"), 0777))
|
||||
ts.check(os.MkdirAll(filepath.Join(ts.workdir, "gopath/src"), 0777))
|
||||
ts.cd = filepath.Join(ts.workdir, "gopath/src")
|
||||
ts.env = []string{
|
||||
"WORK=" + ts.workdir, // must be first for ts.abbrev
|
||||
"PATH=" + os.Getenv("PATH"),
|
||||
homeEnvName() + "=/no-home",
|
||||
"GOARCH=" + runtime.GOARCH,
|
||||
"GOCACHE=" + testGOCACHE,
|
||||
"GOOS=" + runtime.GOOS,
|
||||
"GOPATH=" + filepath.Join(ts.workdir, "gopath"),
|
||||
"GOPROXY=" + proxyURL,
|
||||
"GOROOT=" + testGOROOT,
|
||||
tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"),
|
||||
"devnull=" + os.DevNull,
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
ts.env = append(ts.env, "exe=.exe")
|
||||
} else {
|
||||
ts.env = append(ts.env, "exe=")
|
||||
}
|
||||
for _, key := range extraEnvKeys {
|
||||
if val := os.Getenv(key); val != "" {
|
||||
ts.env = append(ts.env, key+"="+val)
|
||||
}
|
||||
}
|
||||
|
||||
ts.envMap = make(map[string]string)
|
||||
for _, kv := range ts.env {
|
||||
if i := strings.Index(kv, "="); i >= 0 {
|
||||
ts.envMap[kv[:i]] = kv[i+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var execCache par.Cache
|
||||
|
||||
// run runs the test script.
|
||||
func (ts *testScript) run() {
|
||||
// Truncate log at end of last phase marker,
|
||||
// discarding details of successful phase.
|
||||
rewind := func() {
|
||||
if !testing.Verbose() {
|
||||
ts.log.Truncate(ts.mark)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert elapsed time for phase at end of phase marker
|
||||
markTime := func() {
|
||||
if ts.mark > 0 && !ts.start.IsZero() {
|
||||
afterMark := append([]byte{}, ts.log.Bytes()[ts.mark:]...)
|
||||
ts.log.Truncate(ts.mark - 1) // cut \n and afterMark
|
||||
fmt.Fprintf(&ts.log, " (%.3fs)\n", time.Since(ts.start).Seconds())
|
||||
ts.log.Write(afterMark)
|
||||
}
|
||||
ts.start = time.Time{}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
markTime()
|
||||
// Flush testScript log to testing.T log.
|
||||
ts.t.Log("\n" + ts.abbrev(ts.log.String()))
|
||||
}()
|
||||
|
||||
// Unpack archive.
|
||||
a, err := txtar.ParseFile(ts.file)
|
||||
ts.check(err)
|
||||
for _, f := range a.Files {
|
||||
name := ts.mkabs(ts.expand(f.Name))
|
||||
ts.check(os.MkdirAll(filepath.Dir(name), 0777))
|
||||
ts.check(ioutil.WriteFile(name, f.Data, 0666))
|
||||
}
|
||||
|
||||
// With -v or -testwork, start log with full environment.
|
||||
if *testWork || testing.Verbose() {
|
||||
// Display environment.
|
||||
ts.cmdEnv(false, nil)
|
||||
fmt.Fprintf(&ts.log, "\n")
|
||||
ts.mark = ts.log.Len()
|
||||
}
|
||||
|
||||
// Run script.
|
||||
// See testdata/script/README for documentation of script form.
|
||||
script := string(a.Comment)
|
||||
Script:
|
||||
for script != "" {
|
||||
// Extract next line.
|
||||
ts.lineno++
|
||||
var line string
|
||||
if i := strings.Index(script, "\n"); i >= 0 {
|
||||
line, script = script[:i], script[i+1:]
|
||||
} else {
|
||||
line, script = script, ""
|
||||
}
|
||||
|
||||
// # is a comment indicating the start of new phase.
|
||||
if strings.HasPrefix(line, "#") {
|
||||
// If there was a previous phase, it succeeded,
|
||||
// so rewind the log to delete its details (unless -v is in use).
|
||||
// If nothing has happened at all since the mark,
|
||||
// rewinding is a no-op and adding elapsed time
|
||||
// for doing nothing is meaningless, so don't.
|
||||
if ts.log.Len() > ts.mark {
|
||||
rewind()
|
||||
markTime()
|
||||
}
|
||||
// Print phase heading and mark start of phase output.
|
||||
fmt.Fprintf(&ts.log, "%s\n", line)
|
||||
ts.mark = ts.log.Len()
|
||||
ts.start = time.Now()
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse input line. Ignore blanks entirely.
|
||||
args := ts.parse(line)
|
||||
if len(args) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Echo command to log.
|
||||
fmt.Fprintf(&ts.log, "> %s\n", line)
|
||||
|
||||
// Command prefix [cond] means only run this command if cond is satisfied.
|
||||
for strings.HasPrefix(args[0], "[") && strings.HasSuffix(args[0], "]") {
|
||||
cond := args[0]
|
||||
cond = cond[1 : len(cond)-1]
|
||||
cond = strings.TrimSpace(cond)
|
||||
args = args[1:]
|
||||
if len(args) == 0 {
|
||||
ts.fatalf("missing command after condition")
|
||||
}
|
||||
want := true
|
||||
if strings.HasPrefix(cond, "!") {
|
||||
want = false
|
||||
cond = strings.TrimSpace(cond[1:])
|
||||
}
|
||||
// Known conds are: $GOOS, $GOARCH, runtime.Compiler, and 'short' (for testing.Short).
|
||||
//
|
||||
// NOTE: If you make changes here, update testdata/script/README too!
|
||||
//
|
||||
ok := false
|
||||
switch cond {
|
||||
case runtime.GOOS, runtime.GOARCH, runtime.Compiler:
|
||||
ok = true
|
||||
case "short":
|
||||
ok = testing.Short()
|
||||
case "cgo":
|
||||
ok = canCgo
|
||||
case "msan":
|
||||
ok = canMSan
|
||||
case "race":
|
||||
ok = canRace
|
||||
case "net":
|
||||
ok = testenv.HasExternalNetwork()
|
||||
case "link":
|
||||
ok = testenv.HasLink()
|
||||
case "symlink":
|
||||
ok = testenv.HasSymlink()
|
||||
default:
|
||||
if strings.HasPrefix(cond, "exec:") {
|
||||
prog := cond[len("exec:"):]
|
||||
ok = execCache.Do(prog, func() interface{} {
|
||||
_, err := exec.LookPath(prog)
|
||||
return err == nil
|
||||
}).(bool)
|
||||
break
|
||||
}
|
||||
if !imports.KnownArch[cond] && !imports.KnownOS[cond] && cond != "gc" && cond != "gccgo" {
|
||||
ts.fatalf("unknown condition %q", cond)
|
||||
}
|
||||
}
|
||||
if ok != want {
|
||||
// Don't run rest of line.
|
||||
continue Script
|
||||
}
|
||||
}
|
||||
|
||||
// Command prefix ! means negate the expectations about this command:
|
||||
// go command should fail, match should not be found, etc.
|
||||
neg := false
|
||||
if args[0] == "!" {
|
||||
neg = true
|
||||
args = args[1:]
|
||||
if len(args) == 0 {
|
||||
ts.fatalf("! on line by itself")
|
||||
}
|
||||
}
|
||||
|
||||
// Run command.
|
||||
cmd := scriptCmds[args[0]]
|
||||
if cmd == nil {
|
||||
ts.fatalf("unknown command %q", args[0])
|
||||
}
|
||||
cmd(ts, neg, args[1:])
|
||||
|
||||
// Command can ask script to stop early.
|
||||
if ts.stopped {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Final phase ended.
|
||||
rewind()
|
||||
markTime()
|
||||
fmt.Fprintf(&ts.log, "PASS\n")
|
||||
}
|
||||
|
||||
// scriptCmds are the script command implementations.
|
||||
// Keep list and the implementations below sorted by name.
|
||||
//
|
||||
// NOTE: If you make changes here, update testdata/script/README too!
|
||||
//
|
||||
var scriptCmds = map[string]func(*testScript, bool, []string){
|
||||
"addcrlf": (*testScript).cmdAddcrlf,
|
||||
"cd": (*testScript).cmdCd,
|
||||
"cmp": (*testScript).cmdCmp,
|
||||
"cp": (*testScript).cmdCp,
|
||||
"env": (*testScript).cmdEnv,
|
||||
"exec": (*testScript).cmdExec,
|
||||
"exists": (*testScript).cmdExists,
|
||||
"go": (*testScript).cmdGo,
|
||||
"grep": (*testScript).cmdGrep,
|
||||
"mkdir": (*testScript).cmdMkdir,
|
||||
"rm": (*testScript).cmdRm,
|
||||
"skip": (*testScript).cmdSkip,
|
||||
"stale": (*testScript).cmdStale,
|
||||
"stderr": (*testScript).cmdStderr,
|
||||
"stdout": (*testScript).cmdStdout,
|
||||
"stop": (*testScript).cmdStop,
|
||||
"symlink": (*testScript).cmdSymlink,
|
||||
}
|
||||
|
||||
// addcrlf adds CRLF line endings to the named files.
|
||||
func (ts *testScript) cmdAddcrlf(neg bool, args []string) {
|
||||
if len(args) == 0 {
|
||||
ts.fatalf("usage: addcrlf file...")
|
||||
}
|
||||
|
||||
for _, file := range args {
|
||||
file = ts.mkabs(file)
|
||||
data, err := ioutil.ReadFile(file)
|
||||
ts.check(err)
|
||||
ts.check(ioutil.WriteFile(file, bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1), 0666))
|
||||
}
|
||||
}
|
||||
|
||||
// cd changes to a different directory.
|
||||
func (ts *testScript) cmdCd(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! cd")
|
||||
}
|
||||
if len(args) != 1 {
|
||||
ts.fatalf("usage: cd dir")
|
||||
}
|
||||
|
||||
dir := args[0]
|
||||
if !filepath.IsAbs(dir) {
|
||||
dir = filepath.Join(ts.cd, dir)
|
||||
}
|
||||
info, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
ts.fatalf("directory %s does not exist", dir)
|
||||
}
|
||||
ts.check(err)
|
||||
if !info.IsDir() {
|
||||
ts.fatalf("%s is not a directory", dir)
|
||||
}
|
||||
ts.cd = dir
|
||||
fmt.Fprintf(&ts.log, "%s\n", ts.cd)
|
||||
}
|
||||
|
||||
// cmp compares two files.
|
||||
func (ts *testScript) cmdCmp(neg bool, args []string) {
|
||||
if neg {
|
||||
// It would be strange to say "this file can have any content except this precise byte sequence".
|
||||
ts.fatalf("unsupported: ! cmp")
|
||||
}
|
||||
if len(args) != 2 {
|
||||
ts.fatalf("usage: cmp file1 file2")
|
||||
}
|
||||
|
||||
name1, name2 := args[0], args[1]
|
||||
var text1, text2 string
|
||||
if name1 == "stdout" {
|
||||
text1 = ts.stdout
|
||||
} else if name1 == "stderr" {
|
||||
text1 = ts.stderr
|
||||
} else {
|
||||
data, err := ioutil.ReadFile(ts.mkabs(name1))
|
||||
ts.check(err)
|
||||
text1 = string(data)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(ts.mkabs(name2))
|
||||
ts.check(err)
|
||||
text2 = string(data)
|
||||
|
||||
if text1 == text2 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2))
|
||||
ts.fatalf("%s and %s differ", name1, name2)
|
||||
}
|
||||
|
||||
// cp copies files, maybe eventually directories.
|
||||
func (ts *testScript) cmdCp(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! cp")
|
||||
}
|
||||
if len(args) < 2 {
|
||||
ts.fatalf("usage: cp src... dst")
|
||||
}
|
||||
|
||||
dst := ts.mkabs(args[len(args)-1])
|
||||
info, err := os.Stat(dst)
|
||||
dstDir := err == nil && info.IsDir()
|
||||
if len(args) > 2 && !dstDir {
|
||||
ts.fatalf("cp: destination %s is not a directory", dst)
|
||||
}
|
||||
|
||||
for _, arg := range args[:len(args)-1] {
|
||||
src := ts.mkabs(arg)
|
||||
info, err := os.Stat(src)
|
||||
ts.check(err)
|
||||
data, err := ioutil.ReadFile(src)
|
||||
ts.check(err)
|
||||
targ := dst
|
||||
if dstDir {
|
||||
targ = filepath.Join(dst, filepath.Base(src))
|
||||
}
|
||||
ts.check(ioutil.WriteFile(targ, data, info.Mode()&0777))
|
||||
}
|
||||
}
|
||||
|
||||
// env displays or adds to the environment.
|
||||
func (ts *testScript) cmdEnv(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! env")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
printed := make(map[string]bool) // env list can have duplicates; only print effective value (from envMap) once
|
||||
for _, kv := range ts.env {
|
||||
k := kv[:strings.Index(kv, "=")]
|
||||
if !printed[k] {
|
||||
fmt.Fprintf(&ts.log, "%s=%s\n", k, ts.envMap[k])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, env := range args {
|
||||
i := strings.Index(env, "=")
|
||||
if i < 0 {
|
||||
// Display value instead of setting it.
|
||||
fmt.Fprintf(&ts.log, "%s=%s\n", env, ts.envMap[env])
|
||||
continue
|
||||
}
|
||||
ts.env = append(ts.env, env)
|
||||
ts.envMap[env[:i]] = env[i+1:]
|
||||
}
|
||||
}
|
||||
|
||||
// exec runs the given command.
|
||||
func (ts *testScript) cmdExec(neg bool, args []string) {
|
||||
if len(args) < 1 {
|
||||
ts.fatalf("usage: exec program [args...]")
|
||||
}
|
||||
var err error
|
||||
ts.stdout, ts.stderr, err = ts.exec(args[0], args[1:]...)
|
||||
if ts.stdout != "" {
|
||||
fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout)
|
||||
}
|
||||
if ts.stderr != "" {
|
||||
fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(&ts.log, "[%v]\n", err)
|
||||
if !neg {
|
||||
ts.fatalf("unexpected command failure")
|
||||
}
|
||||
} else {
|
||||
if neg {
|
||||
ts.fatalf("unexpected command success")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exists checks that the list of files exists.
|
||||
func (ts *testScript) cmdExists(neg bool, args []string) {
|
||||
var readonly bool
|
||||
if len(args) > 0 && args[0] == "-readonly" {
|
||||
readonly = true
|
||||
args = args[1:]
|
||||
}
|
||||
if len(args) == 0 {
|
||||
ts.fatalf("usage: exists [-readonly] file...")
|
||||
}
|
||||
|
||||
for _, file := range args {
|
||||
file = ts.mkabs(file)
|
||||
info, err := os.Stat(file)
|
||||
if err == nil && neg {
|
||||
what := "file"
|
||||
if info.IsDir() {
|
||||
what = "directory"
|
||||
}
|
||||
ts.fatalf("%s %s unexpectedly exists", what, file)
|
||||
}
|
||||
if err != nil && !neg {
|
||||
ts.fatalf("%s does not exist", file)
|
||||
}
|
||||
if err == nil && !neg && readonly && info.Mode()&0222 != 0 {
|
||||
ts.fatalf("%s exists but is writable", file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go runs the go command.
|
||||
func (ts *testScript) cmdGo(neg bool, args []string) {
|
||||
ts.cmdExec(neg, append([]string{testGo}, args...))
|
||||
}
|
||||
|
||||
// mkdir creates directories.
|
||||
func (ts *testScript) cmdMkdir(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! mkdir")
|
||||
}
|
||||
if len(args) < 1 {
|
||||
ts.fatalf("usage: mkdir dir...")
|
||||
}
|
||||
for _, arg := range args {
|
||||
ts.check(os.MkdirAll(ts.mkabs(arg), 0777))
|
||||
}
|
||||
}
|
||||
|
||||
// rm removes files or directories.
|
||||
func (ts *testScript) cmdRm(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! rm")
|
||||
}
|
||||
if len(args) < 1 {
|
||||
ts.fatalf("usage: rm file...")
|
||||
}
|
||||
for _, arg := range args {
|
||||
file := ts.mkabs(arg)
|
||||
removeAll(file) // does chmod and then attempts rm
|
||||
ts.check(os.RemoveAll(file)) // report error
|
||||
}
|
||||
}
|
||||
|
||||
// skip marks the test skipped.
|
||||
func (ts *testScript) cmdSkip(neg bool, args []string) {
|
||||
if len(args) > 1 {
|
||||
ts.fatalf("usage: skip [msg]")
|
||||
}
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! skip")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
ts.t.Skip(args[0])
|
||||
}
|
||||
ts.t.Skip()
|
||||
}
|
||||
|
||||
// stale checks that the named build targets are stale.
|
||||
func (ts *testScript) cmdStale(neg bool, args []string) {
|
||||
if len(args) == 0 {
|
||||
ts.fatalf("usage: stale target...")
|
||||
}
|
||||
tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{else}}"
|
||||
if neg {
|
||||
tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale{{end}}"
|
||||
} else {
|
||||
tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}"
|
||||
}
|
||||
tmpl += "{{end}}"
|
||||
goArgs := append([]string{"list", "-e", "-f=" + tmpl}, args...)
|
||||
stdout, stderr, err := ts.exec(testGo, goArgs...)
|
||||
if err != nil {
|
||||
ts.fatalf("go list: %v\n%s%s", err, stdout, stderr)
|
||||
}
|
||||
if stdout != "" {
|
||||
ts.fatalf("%s", stdout)
|
||||
}
|
||||
}
|
||||
|
||||
// stdout checks that the last go command standard output matches a regexp.
|
||||
func (ts *testScript) cmdStdout(neg bool, args []string) {
|
||||
scriptMatch(ts, neg, args, ts.stdout, "stdout")
|
||||
}
|
||||
|
||||
// stderr checks that the last go command standard output matches a regexp.
|
||||
func (ts *testScript) cmdStderr(neg bool, args []string) {
|
||||
scriptMatch(ts, neg, args, ts.stderr, "stderr")
|
||||
}
|
||||
|
||||
// grep checks that file content matches a regexp.
|
||||
// Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax.
|
||||
func (ts *testScript) cmdGrep(neg bool, args []string) {
|
||||
scriptMatch(ts, neg, args, "", "grep")
|
||||
}
|
||||
|
||||
// scriptMatch implements both stdout and stderr.
|
||||
func scriptMatch(ts *testScript, neg bool, args []string, text, name string) {
|
||||
n := 0
|
||||
if len(args) >= 1 && strings.HasPrefix(args[0], "-count=") {
|
||||
if neg {
|
||||
ts.fatalf("cannot use -count= with negated match")
|
||||
}
|
||||
var err error
|
||||
n, err = strconv.Atoi(args[0][len("-count="):])
|
||||
if err != nil {
|
||||
ts.fatalf("bad -count=: %v", err)
|
||||
}
|
||||
if n < 1 {
|
||||
ts.fatalf("bad -count=: must be at least 1")
|
||||
}
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
extraUsage := ""
|
||||
want := 1
|
||||
if name == "grep" {
|
||||
extraUsage = " file"
|
||||
want = 2
|
||||
}
|
||||
if len(args) != want {
|
||||
ts.fatalf("usage: %s [-count=N] 'pattern' file%s", name, extraUsage)
|
||||
}
|
||||
|
||||
pattern := args[0]
|
||||
re, err := regexp.Compile(`(?m)` + pattern)
|
||||
ts.check(err)
|
||||
|
||||
isGrep := name == "grep"
|
||||
if isGrep {
|
||||
name = args[1] // for error messages
|
||||
data, err := ioutil.ReadFile(ts.mkabs(args[1]))
|
||||
ts.check(err)
|
||||
text = string(data)
|
||||
}
|
||||
|
||||
if neg {
|
||||
if re.MatchString(text) {
|
||||
if isGrep {
|
||||
fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text)
|
||||
}
|
||||
ts.fatalf("unexpected match for %#q found in %s: %s", pattern, name, re.FindString(text))
|
||||
}
|
||||
} else {
|
||||
if !re.MatchString(text) {
|
||||
if isGrep {
|
||||
fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text)
|
||||
}
|
||||
ts.fatalf("no match for %#q found in %s", pattern, name)
|
||||
}
|
||||
if n > 0 {
|
||||
count := len(re.FindAllString(text, -1))
|
||||
if count != n {
|
||||
if isGrep {
|
||||
fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text)
|
||||
}
|
||||
ts.fatalf("have %d matches for %#q, want %d", count, pattern, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop stops execution of the test (marking it passed).
|
||||
func (ts *testScript) cmdStop(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! stop")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
ts.fatalf("usage: stop [msg]")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
fmt.Fprintf(&ts.log, "stop: %s\n", args[0])
|
||||
} else {
|
||||
fmt.Fprintf(&ts.log, "stop\n")
|
||||
}
|
||||
ts.stopped = true
|
||||
}
|
||||
|
||||
// symlink creates a symbolic link.
|
||||
func (ts *testScript) cmdSymlink(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! symlink")
|
||||
}
|
||||
if len(args) != 3 || args[1] != "->" {
|
||||
ts.fatalf("usage: symlink file -> target")
|
||||
}
|
||||
// Note that the link target args[2] is not interpreted with mkabs:
|
||||
// it will be interpreted relative to the directory file is in.
|
||||
ts.check(os.Symlink(args[2], ts.mkabs(args[0])))
|
||||
}
|
||||
|
||||
// Helpers for command implementations.
|
||||
|
||||
// abbrev abbreviates the actual work directory in the string s to the literal string "$WORK".
|
||||
func (ts *testScript) abbrev(s string) string {
|
||||
s = strings.Replace(s, ts.workdir, "$WORK", -1)
|
||||
if *testWork {
|
||||
// Expose actual $WORK value in environment dump on first line of work script,
|
||||
// so that the user can find out what directory -testwork left behind.
|
||||
s = "WORK=" + ts.workdir + "\n" + strings.TrimPrefix(s, "WORK=$WORK\n")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// check calls ts.fatalf if err != nil.
|
||||
func (ts *testScript) check(err error) {
|
||||
if err != nil {
|
||||
ts.fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// exec runs the given command line (an actual subprocess, not simulated)
|
||||
// in ts.cd with environment ts.env and then returns collected standard output and standard error.
|
||||
func (ts *testScript) exec(command string, args ...string) (stdout, stderr string, err error) {
|
||||
cmd := exec.Command(testGo, args...)
|
||||
cmd.Dir = ts.cd
|
||||
cmd.Env = append(ts.env, "PWD="+ts.cd)
|
||||
var stdoutBuf, stderrBuf strings.Builder
|
||||
cmd.Stdout = &stdoutBuf
|
||||
cmd.Stderr = &stderrBuf
|
||||
err = cmd.Run()
|
||||
return stdoutBuf.String(), stderrBuf.String(), err
|
||||
}
|
||||
|
||||
// expand applies environment variable expansion to the string s.
|
||||
func (ts *testScript) expand(s string) string {
|
||||
return os.Expand(s, func(key string) string { return ts.envMap[key] })
|
||||
}
|
||||
|
||||
// fatalf aborts the test with the given failure message.
|
||||
func (ts *testScript) fatalf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...))
|
||||
ts.t.FailNow()
|
||||
}
|
||||
|
||||
// mkabs interprets file relative to the test script's current directory
|
||||
// and returns the corresponding absolute path.
|
||||
func (ts *testScript) mkabs(file string) string {
|
||||
if filepath.IsAbs(file) {
|
||||
return file
|
||||
}
|
||||
return filepath.Join(ts.cd, file)
|
||||
}
|
||||
|
||||
// parse parses a single line as a list of space-separated arguments
|
||||
// subject to environment variable expansion (but not resplitting).
|
||||
// Single quotes around text disable splitting and expansion.
|
||||
// To embed a single quote, double it: 'Don''t communicate by sharing memory.'
|
||||
func (ts *testScript) parse(line string) []string {
|
||||
ts.line = line
|
||||
|
||||
var (
|
||||
args []string
|
||||
arg string // text of current arg so far (need to add line[start:i])
|
||||
start = -1 // if >= 0, position where current arg text chunk starts
|
||||
quoted = false // currently processing quoted text
|
||||
)
|
||||
for i := 0; ; i++ {
|
||||
if !quoted && (i >= len(line) || line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '#') {
|
||||
// Found arg-separating space.
|
||||
if start >= 0 {
|
||||
arg += ts.expand(line[start:i])
|
||||
args = append(args, arg)
|
||||
start = -1
|
||||
arg = ""
|
||||
}
|
||||
if i >= len(line) || line[i] == '#' {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if i >= len(line) {
|
||||
ts.fatalf("unterminated quoted argument")
|
||||
}
|
||||
if line[i] == '\'' {
|
||||
if !quoted {
|
||||
// starting a quoted chunk
|
||||
if start >= 0 {
|
||||
arg += ts.expand(line[start:i])
|
||||
}
|
||||
start = i + 1
|
||||
quoted = true
|
||||
continue
|
||||
}
|
||||
// 'foo''bar' means foo'bar, like in rc shell and Pascal.
|
||||
if i+1 < len(line) && line[i+1] == '\'' {
|
||||
arg += line[start:i]
|
||||
start = i + 1
|
||||
i++ // skip over second ' before next iteration
|
||||
continue
|
||||
}
|
||||
// ending a quoted chunk
|
||||
arg += line[start:i]
|
||||
start = i + 1
|
||||
quoted = false
|
||||
continue
|
||||
}
|
||||
// found character worth saving; make sure we're saving
|
||||
if start < 0 {
|
||||
start = i
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// diff returns a formatted diff of the two texts,
|
||||
// showing the entire text and the minimum line-level
|
||||
// additions and removals to turn text1 into text2.
|
||||
// (That is, lines only in text1 appear with a leading -,
|
||||
// and lines only in text2 appear with a leading +.)
|
||||
func diff(text1, text2 string) string {
|
||||
if text1 != "" && !strings.HasSuffix(text1, "\n") {
|
||||
text1 += "(missing final newline)"
|
||||
}
|
||||
lines1 := strings.Split(text1, "\n")
|
||||
lines1 = lines1[:len(lines1)-1] // remove empty string after final line
|
||||
if text2 != "" && !strings.HasSuffix(text2, "\n") {
|
||||
text2 += "(missing final newline)"
|
||||
}
|
||||
lines2 := strings.Split(text2, "\n")
|
||||
lines2 = lines2[:len(lines2)-1] // remove empty string after final line
|
||||
|
||||
// Naive dynamic programming algorithm for edit distance.
|
||||
// https://en.wikipedia.org/wiki/Wagner–Fischer_algorithm
|
||||
// dist[i][j] = edit distance between lines1[:len(lines1)-i] and lines2[:len(lines2)-j]
|
||||
// (The reversed indices make following the minimum cost path
|
||||
// visit lines in the same order as in the text.)
|
||||
dist := make([][]int, len(lines1)+1)
|
||||
for i := range dist {
|
||||
dist[i] = make([]int, len(lines2)+1)
|
||||
if i == 0 {
|
||||
for j := range dist[0] {
|
||||
dist[0][j] = j
|
||||
}
|
||||
continue
|
||||
}
|
||||
for j := range dist[i] {
|
||||
if j == 0 {
|
||||
dist[i][0] = i
|
||||
continue
|
||||
}
|
||||
cost := dist[i][j-1] + 1
|
||||
if cost > dist[i-1][j]+1 {
|
||||
cost = dist[i-1][j] + 1
|
||||
}
|
||||
if lines1[len(lines1)-i] == lines2[len(lines2)-j] {
|
||||
if cost > dist[i-1][j-1] {
|
||||
cost = dist[i-1][j-1]
|
||||
}
|
||||
}
|
||||
dist[i][j] = cost
|
||||
}
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
i, j := len(lines1), len(lines2)
|
||||
for i > 0 || j > 0 {
|
||||
cost := dist[i][j]
|
||||
if i > 0 && j > 0 && cost == dist[i-1][j-1] && lines1[len(lines1)-i] == lines2[len(lines2)-j] {
|
||||
fmt.Fprintf(&buf, " %s\n", lines1[len(lines1)-i])
|
||||
i--
|
||||
j--
|
||||
} else if i > 0 && cost == dist[i-1][j]+1 {
|
||||
fmt.Fprintf(&buf, "-%s\n", lines1[len(lines1)-i])
|
||||
i--
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "+%s\n", lines2[len(lines2)-j])
|
||||
j--
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var diffTests = []struct {
|
||||
text1 string
|
||||
text2 string
|
||||
diff string
|
||||
}{
|
||||
{"a b c", "a b d e f", "a b -c +d +e +f"},
|
||||
{"", "a b c", "+a +b +c"},
|
||||
{"a b c", "", "-a -b -c"},
|
||||
{"a b c", "d e f", "-a -b -c +d +e +f"},
|
||||
{"a b c d e f", "a b d e f", "a b -c d e f"},
|
||||
{"a b c e f", "a b c d e f", "a b c +d e f"},
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
for _, tt := range diffTests {
|
||||
// Turn spaces into \n.
|
||||
text1 := strings.Replace(tt.text1, " ", "\n", -1)
|
||||
if text1 != "" {
|
||||
text1 += "\n"
|
||||
}
|
||||
text2 := strings.Replace(tt.text2, " ", "\n", -1)
|
||||
if text2 != "" {
|
||||
text2 += "\n"
|
||||
}
|
||||
out := diff(text1, text2)
|
||||
// Cut final \n, cut spaces, turn remaining \n into spaces.
|
||||
out = strings.Replace(strings.Replace(strings.TrimSuffix(out, "\n"), " ", "", -1), "\n", " ", -1)
|
||||
if out != tt.diff {
|
||||
t.Errorf("diff(%q, %q) = %q, want %q", text1, text2, out, tt.diff)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
env GO111MODULE=on
|
||||
|
||||
# sync removes unused y, but everything else is used
|
||||
go mod -sync -v
|
||||
stderr '^unused y.1'
|
||||
! stderr '^unused [^y]'
|
||||
|
||||
go list -m all
|
||||
! stdout '^y'
|
||||
stdout '^w.1 v1.2.0'
|
||||
stdout '^z.1 v1.2.0'
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
require (
|
||||
x.1 v1.0.0
|
||||
y.1 v1.0.0
|
||||
w.1 v1.2.0
|
||||
)
|
||||
|
||||
replace x.1 v1.0.0 => ../x
|
||||
replace y.1 v1.0.0 => ../y
|
||||
replace z.1 v1.1.0 => ../z
|
||||
replace z.1 v1.2.0 => ../z
|
||||
replace w.1 => ../w
|
||||
|
||||
-- m.go --
|
||||
package m
|
||||
|
||||
import _ "x.1"
|
||||
import _ "z.1/sub"
|
||||
|
||||
-- w/go.mod --
|
||||
module w
|
||||
|
||||
-- w/w.go --
|
||||
package w
|
||||
|
||||
-- x/go.mod --
|
||||
module x
|
||||
require w.1 v1.1.0
|
||||
require z.1 v1.1.0
|
||||
|
||||
-- x/x.go --
|
||||
package x
|
||||
import _ "w.1"
|
||||
|
||||
-- y/go.mod --
|
||||
module y
|
||||
require z.1 v1.2.0
|
||||
|
||||
-- z/go.mod --
|
||||
module z
|
||||
|
||||
-- z/sub/sub.go --
|
||||
package sub
|
|
@ -0,0 +1,88 @@
|
|||
rsc.io/quote@v2.0.0 && cp mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt mod/rsc.io_quote_v2.0.0.txt
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/QUOTE
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
-- .info --
|
||||
{"Version":"v1.5.2","Name":"","Short":"","Time":"2018-07-15T16:25:34Z"}
|
||||
-- go.mod --
|
||||
module rsc.io/QUOTE
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
-- QUOTE/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// PACKAGE QUOTE COLLECTS LOUD SAYINGS.
|
||||
package QUOTE
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"rsc.io/quote"
|
||||
)
|
||||
|
||||
// HELLO RETURNS A GREETING.
|
||||
func HELLO() string {
|
||||
return strings.ToUpper(quote.Hello())
|
||||
}
|
||||
|
||||
// GLASS RETURNS A USEFUL PHRASE FOR WORLD TRAVELERS.
|
||||
func GLASS() string {
|
||||
return strings.ToUpper(quote.GLASS())
|
||||
}
|
||||
|
||||
// GO RETURNS A GO PROVERB.
|
||||
func GO() string {
|
||||
return strings.ToUpper(quote.GO())
|
||||
}
|
||||
|
||||
// OPT RETURNS AN OPTIMIZATION TRUTH.
|
||||
func OPT() string {
|
||||
return strings.ToUpper(quote.OPT())
|
||||
}
|
||||
-- QUOTE/quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package QUOTE
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHELLO(t *testing.T) {
|
||||
hello := "HELLO, WORLD"
|
||||
if out := HELLO(); out != hello {
|
||||
t.Errorf("HELLO() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGLASS(t *testing.T) {
|
||||
glass := "I CAN EAT GLASS AND IT DOESN'T HURT ME."
|
||||
if out := GLASS(); out != glass {
|
||||
t.Errorf("GLASS() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGO(t *testing.T) {
|
||||
go1 := "DON'T COMMUNICATE BY SHARING MEMORY, SHARE MEMORY BY COMMUNICATING."
|
||||
if out := GO(); out != go1 {
|
||||
t.Errorf("GO() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOPT(t *testing.T) {
|
||||
opt := "IF A PROGRAM IS TOO SLOW, IT MUST HAVE A LOOP."
|
||||
if out := OPT(); out != opt {
|
||||
t.Errorf("OPT() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
rsc.io/badfile1 v1.0.0
|
||||
written by hand
|
||||
this is part of the badfile test but is a valid zip file.
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/badfile1
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module rsc.io/badfile1
|
||||
-- α.go --
|
||||
package α
|
||||
-- .gitignore --
|
||||
-- x/y/z/.gitignore --
|
|
@ -0,0 +1,12 @@
|
|||
rsc.io/badfile1 v1.0.0
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/badfile2
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module rsc.io/badfile2
|
||||
-- ☺.go --
|
||||
package smiley
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
rsc.io/badfile3 v1.0.0
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/badfile3
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module rsc.io/badfile3
|
||||
-- x?y.go --
|
||||
package x
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
rsc.io/badfile4 v1.0.0
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/badfile4
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module rsc.io/badfile4
|
||||
-- x/Y.go --
|
||||
package x
|
||||
-- x/y.go --
|
||||
package x
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
rsc.io/badfile5 v1.0.0
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/badfile5
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module rsc.io/badfile5
|
||||
-- x/y/z/w.go --
|
||||
package z
|
||||
-- x/Y/zz/ww.go --
|
||||
package zz
|
|
@ -0,0 +1,11 @@
|
|||
rsc.io/breaker v1.0.0
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/breaker
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- breaker.go --
|
||||
package breaker
|
||||
|
||||
const X = 1
|
|
@ -0,0 +1,11 @@
|
|||
rsc.io/breaker v2.0.0+incompatible
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/breaker
|
||||
-- .info --
|
||||
{"Version":"v2.0.0+incompatible", "Name": "7307b307f4f0dde421900f8e5126fadac1e13aed", "Short": "7307b307f4f0"}
|
||||
-- breaker.go --
|
||||
package breaker
|
||||
|
||||
const XX = 2
|
|
@ -0,0 +1,11 @@
|
|||
rsc.io/breaker v2.0.0+incompatible
|
||||
written by hand
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/breaker
|
||||
-- .info --
|
||||
{"Version":"v2.0.0+incompatible", "Name": "7307b307f4f0dde421900f8e5126fadac1e13aed", "Short": "7307b307f4f0"}
|
||||
-- breaker.go --
|
||||
package breaker
|
||||
|
||||
const XX = 2
|
|
@ -1,166 +0,0 @@
|
|||
generated by: go run savedir.go .
|
||||
|
||||
-- LICENSE --
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-- README.md --
|
||||
This package collects pithy sayings.
|
||||
|
||||
It's part of a demonstration of
|
||||
[package versioning in Go](https://research.swtch.com/vgo1).
|
||||
-- buggy/buggy_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package buggy
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Fatal("buggy!")
|
||||
}
|
||||
-- go.mod --
|
||||
module rsc.io/quote
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
-- quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/quote/v2"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return quote.HelloV2()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return quote.GlassV2()
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return quote.GoV2()
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func Opt() string {
|
||||
// Wisdom from ken.
|
||||
return quote.OptV2()
|
||||
}
|
||||
-- quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package quote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory, share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpt(t *testing.T) {
|
||||
opt := "If a program is too slow, it must have a loop."
|
||||
if out := Opt(); out != opt {
|
||||
t.Errorf("Opt() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
-- v3/go.mod --
|
||||
module rsc.io/quote/v3
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- v3/go.sum --
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
|
||||
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
-- v3/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func HelloV3() string {
|
||||
return sampler.Hello()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func GlassV3() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return "I can eat glass and it doesn't hurt me."
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func GoV3() string {
|
||||
return "Don't communicate by sharing memory, share memory by communicating."
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func OptV3() string {
|
||||
// Wisdom from ken.
|
||||
return "If a program is too slow, it must have a loop."
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
generated by: go run savedir.go .
|
||||
|
||||
-- LICENSE --
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-- README.md --
|
||||
This package collects pithy sayings.
|
||||
|
||||
It's part of a demonstration of
|
||||
[package versioning in Go](https://research.swtch.com/vgo1).
|
||||
-- buggy/buggy_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package buggy
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Fatal("buggy!")
|
||||
}
|
||||
-- go.mod --
|
||||
module rsc.io/quote
|
||||
|
||||
require (
|
||||
rsc.io/quote/v3 v3.0.0
|
||||
rsc.io/sampler v1.3.0
|
||||
)
|
||||
-- quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/quote/v3"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return quote.HelloV3()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return quote.GlassV3()
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return quote.GoV3()
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func Opt() string {
|
||||
// Wisdom from ken.
|
||||
return quote.OptV3()
|
||||
}
|
||||
-- quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package quote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory, share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpt(t *testing.T) {
|
||||
opt := "If a program is too slow, it must have a loop."
|
||||
if out := Opt(); out != opt {
|
||||
t.Errorf("Opt() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
-- v3/go.mod --
|
||||
module rsc.io/quote/v3
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- v3/go.sum --
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
|
||||
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
-- v3/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func HelloV3() string {
|
||||
return sampler.Hello()
|
|
@ -1,148 +0,0 @@
|
|||
generated by: go run savedir.go .
|
||||
|
||||
-- LICENSE --
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-- README.md --
|
||||
This package collects pithy sayings.
|
||||
|
||||
It's part of a demonstration of
|
||||
[package versioning in Go](https://research.swtch.com/vgo1).
|
||||
-- buggy/buggy_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package buggy
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Fatal("buggy!")
|
||||
}
|
||||
-- go.mod --
|
||||
module rsc.io/quote
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
-- quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/quote/v3"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return quote.HelloV3()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return quote.GlassV3()
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return quote.GoV3()
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func Opt() string {
|
||||
// Wisdom from ken.
|
||||
return quote.OptV3()
|
||||
}
|
||||
-- quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package quote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory, share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpt(t *testing.T) {
|
||||
opt := "If a program is too slow, it must have a loop."
|
||||
if out := Opt(); out != opt {
|
||||
t.Errorf("Opt() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
-- v3/go.mod --
|
||||
module rsc.io/quote/v3
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- v3/go.sum --
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
|
||||
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
-- v3/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func HelloV3() string {
|
||||
return sampler.Hello()
|
|
@ -1,169 +0,0 @@
|
|||
generated by: go run savedir.go .
|
||||
|
||||
-- LICENSE --
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-- README.md --
|
||||
This package collects pithy sayings.
|
||||
|
||||
It's part of a demonstration of
|
||||
[package versioning in Go](https://research.swtch.com/vgo1).
|
||||
-- buggy/buggy_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package buggy
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Fatal("buggy!")
|
||||
}
|
||||
-- go.mod --
|
||||
module rsc.io/quote
|
||||
|
||||
require (
|
||||
rsc.io/quote/v2 v2.0.1
|
||||
rsc.io/sampler v1.3.0
|
||||
)
|
||||
-- quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/quote/v2"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return quote.HelloV2()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return quote.GlassV2()
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return quote.GoV2()
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func Opt() string {
|
||||
// Wisdom from ken.
|
||||
return quote.OptV2()
|
||||
}
|
||||
-- quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package quote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory, share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpt(t *testing.T) {
|
||||
opt := "If a program is too slow, it must have a loop."
|
||||
if out := Opt(); out != opt {
|
||||
t.Errorf("Opt() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
-- v3/go.mod --
|
||||
module rsc.io/quote/v3
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- v3/go.sum --
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
|
||||
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
-- v3/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func HelloV3() string {
|
||||
return sampler.Hello()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func GlassV3() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return "I can eat glass and it doesn't hurt me."
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func GoV3() string {
|
||||
return "Don't communicate by sharing memory, share memory by communicating."
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func OptV3() string {
|
||||
// Wisdom from ken.
|
||||
return "If a program is too slow, it must have a loop."
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
generated by: go run savedir.go .
|
||||
|
||||
-- LICENSE --
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-- README.md --
|
||||
This package collects pithy sayings.
|
||||
|
||||
It's part of a demonstration of
|
||||
[package versioning in Go](https://research.swtch.com/vgo1).
|
||||
-- buggy/buggy_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package buggy
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Fatal("buggy!")
|
||||
}
|
||||
-- go.mod --
|
||||
module rsc.io/quote
|
||||
|
||||
require (
|
||||
rsc.io/quote/v2 v2.0.1
|
||||
rsc.io/sampler v1.3.0
|
||||
)
|
||||
-- quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/quote/v2"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return quote.HelloV2()
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return quote.GlassV2()
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return quote.GoV2()
|
||||
}
|
||||
|
||||
// Opt returns an optimization truth.
|
||||
func Opt() string {
|
||||
// Wisdom from ken.
|
||||
return quote.OptV2()
|
||||
}
|
||||
-- quote_test.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
package quote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory, share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpt(t *testing.T) {
|
||||
opt := "If a program is too slow, it must have a loop."
|
||||
if out := Opt(); out != opt {
|
||||
t.Errorf("Opt() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
-- v3/go.mod --
|
||||
module rsc.io/quote/v3
|
||||
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- v3/go.sum --
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
|
||||
rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
-- v3/quote.go --
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func HelloV3() string {
|
||||
return sampler.Hello()
|
|
@ -0,0 +1,261 @@
|
|||
This directory holds test scripts *.txt run during 'go test cmd/go'.
|
||||
To run a specific script foo.txt
|
||||
|
||||
go test cmd/go -run=Script/^foo$
|
||||
|
||||
In general script files should have short names: a few words, not whole sentences.
|
||||
The first word should be the general category of behavior being tested,
|
||||
often the name of a go subcommand (list, build, test, ...) or concept (vendor, pattern).
|
||||
|
||||
Each script is a text archive (go doc cmd/go/internal/txtar).
|
||||
The script begins with an actual command script to run
|
||||
followed by the content of zero or more supporting files to
|
||||
create in the script's temporary file system before it starts executing.
|
||||
|
||||
As an example, run_hello.txt says:
|
||||
|
||||
# hello world
|
||||
go run hello.go
|
||||
stderr 'hello world'
|
||||
! stdout .
|
||||
|
||||
-- hello.go --
|
||||
package main
|
||||
func main() { println("hello world") }
|
||||
|
||||
Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
|
||||
Scripts also have access to these other environment variables:
|
||||
|
||||
GOARCH=<target GOARCH>
|
||||
GOCACHE=<actual GOCACHE being used outside the test>
|
||||
GOOS=<target GOOS>
|
||||
GOPATH=$WORK/gopath
|
||||
GOPROXY=<local module proxy serving from cmd/go/testdata/mod>
|
||||
GOROOT=<actual GOROOT>
|
||||
HOME=/no-home
|
||||
PATH=<actual PATH>
|
||||
TMPDIR=$WORK/tmp
|
||||
devnull=<value of os.DevNull>
|
||||
|
||||
The environment variable $exe (lowercase) is an empty string on most systems, ".exe" on Windows.
|
||||
|
||||
The scripts supporting files are unpacked relative to $GOPATH/src (aka $WORK/gopath/src)
|
||||
and then the script begins execution in that directory as well. Thus the example above runs
|
||||
in $WORK/gopath/src with GOPATH=$WORK/gopath and $WORK/gopath/src/hello.go
|
||||
containing the listed contents.
|
||||
|
||||
The lines at the top of the script are a sequence of commands to be executed
|
||||
by a tiny script engine in ../../script_test.go (not the system shell).
|
||||
The script stops and the overall test fails if any particular command fails.
|
||||
|
||||
Each line is parsed into a sequence of space-separated command words,
|
||||
with environment variable expansion and # marking an end-of-line comment.
|
||||
Adding single quotes around text keeps spaces in that text from being treated
|
||||
as word separators and also disables environment variable expansion.
|
||||
Inside a single-quoted block of text, a repeated single quote indicates
|
||||
a literal single quote, as in:
|
||||
|
||||
'Don''t communicate by sharing memory.'
|
||||
|
||||
A line beginning with # is a comment and conventionally explains what is
|
||||
being done or tested at the start of a new phase in the script.
|
||||
|
||||
The command prefix ! indicates that the command on the rest of the line
|
||||
(typically go or a matching predicate) must fail, not succeed. Only certain
|
||||
commands support this prefix. They are indicated below by [!] in the synopsis.
|
||||
|
||||
The command prefix [cond] indicates that the command on the rest of the line
|
||||
should only run when the condition is satisfied. The available conditions are:
|
||||
|
||||
- GOOS and GOARCH values, like [386], [windows], and so on.
|
||||
- Compiler names, like [gccgo], [gc].
|
||||
- Test environment details:
|
||||
- [short] for testing.Short()
|
||||
- [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used
|
||||
- [net] for whether the external network can be used
|
||||
- [link] for testenv.HasLink()
|
||||
- [symlink] for testenv.HasSymlink()
|
||||
- [exec:prog] for whether prog is available for execution (found by exec.LookPath)
|
||||
|
||||
A condition can be negated: [!short] means to run the rest of the line
|
||||
when testing.Short() is false.
|
||||
|
||||
The commands are:
|
||||
|
||||
- cd dir
|
||||
Change to the given directory for future commands.
|
||||
|
||||
- cmp file1 file2
|
||||
Check that the named files have the same content.
|
||||
By convention, file1 is the actual data and file2 the expected data.
|
||||
File1 can be "stdout" or "stderr" to use the standard output or standard error
|
||||
from the most recent exec or go command.
|
||||
(If the files have differing content, the failure prints a diff.)
|
||||
|
||||
- cp src... dst
|
||||
Copy the listed files to the target file or existing directory.
|
||||
|
||||
- env [key=value...]
|
||||
With no arguments, print the environment (useful for debugging).
|
||||
Otherwise add the listed key=value pairs to the environment.
|
||||
|
||||
- [!] exec program [args...]
|
||||
Run the given executable program with the arguments.
|
||||
It must (or must not) succeed.
|
||||
Note that 'exec' does not terminate the script (unlike in Unix shells).
|
||||
|
||||
- [!] exists [-readonly] file...
|
||||
Each of the listed files or directories must (or must not) exist.
|
||||
If -readonly is given, the files or directories must be unwritable.
|
||||
|
||||
- [!] go args...
|
||||
Run the (test copy of the) go command with the given arguments.
|
||||
It must (or must not) succeed.
|
||||
|
||||
- [!] grep [-count=N] pattern file
|
||||
The file's content must (or must not) match the regular expression pattern.
|
||||
For positive matches, -count=N specifies an exact number of matches to require.
|
||||
|
||||
- mkdir path...
|
||||
Create the listed directories, if they do not already exists.
|
||||
|
||||
- rm file...
|
||||
Remove the listed files or directories.
|
||||
|
||||
- skip [message]
|
||||
Mark the test skipped, including the message if given.
|
||||
|
||||
- [!] stale path...
|
||||
The packages named by the path arguments must (or must not)
|
||||
be reported as "stale" by the go command.
|
||||
|
||||
- [!] stderr [-count=N] pattern
|
||||
Apply the grep command (see above) to the standard error
|
||||
from the most recent exec or go command.
|
||||
|
||||
- [!] stdout [-count=N] pattern
|
||||
Apply the grep command (see above) to the standard output
|
||||
from the most recent exec or go command.
|
||||
|
||||
- stop [message]
|
||||
Stop the test early (marking it as passing), including the message if given.
|
||||
|
||||
- symlink file -> target
|
||||
Create file as a symlink to target. The -> (like in ls -l output) is required.
|
||||
|
||||
When TestScript runs a script and the script fails, by default TestScript shows
|
||||
the execution of the most recent phase of the script (since the last # comment)
|
||||
and only shows the # comments for earlier phases. For example, here is a
|
||||
multi-phase script with a bug in it:
|
||||
|
||||
# GOPATH with p1 in d2, p2 in d2
|
||||
env GOPATH=$WORK/d1:$WORK/d2
|
||||
|
||||
# build & install p1
|
||||
env
|
||||
go install -i p1
|
||||
! stale p1
|
||||
! stale p2
|
||||
|
||||
# modify p2 - p1 should appear stale
|
||||
cp $WORK/p2x.go $WORK/d2/src/p2/p2.go
|
||||
stale p1 p2
|
||||
|
||||
# build & install p1 again
|
||||
go install -i p11
|
||||
! stale p1
|
||||
! stale p2
|
||||
|
||||
-- $WORK/d1/src/p1/p1.go --
|
||||
package p1
|
||||
import "p2"
|
||||
func F() { p2.F() }
|
||||
-- $WORK/d2/src/p2/p2.go --
|
||||
package p2
|
||||
func F() {}
|
||||
-- $WORK/p2x.go --
|
||||
package p2
|
||||
func F() {}
|
||||
func G() {}
|
||||
|
||||
The bug is that the final phase installs p11 instead of p1. The test failure looks like:
|
||||
|
||||
$ go test -run=Script
|
||||
--- FAIL: TestScript (3.75s)
|
||||
--- FAIL: TestScript/install_rebuild_gopath (0.16s)
|
||||
script_test.go:223:
|
||||
# GOPATH with p1 in d2, p2 in d2 (0.000s)
|
||||
# build & install p1 (0.087s)
|
||||
# modify p2 - p1 should appear stale (0.029s)
|
||||
# build & install p1 again (0.022s)
|
||||
> go install -i p11
|
||||
[stderr]
|
||||
can't load package: package p11: cannot find package "p11" in any of:
|
||||
/Users/rsc/go/src/p11 (from $GOROOT)
|
||||
$WORK/d1/src/p11 (from $GOPATH)
|
||||
$WORK/d2/src/p11
|
||||
[exit status 1]
|
||||
FAIL: unexpected go command failure
|
||||
|
||||
script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
|
||||
|
||||
FAIL
|
||||
exit status 1
|
||||
FAIL cmd/go 4.875s
|
||||
$
|
||||
|
||||
Note that the commands in earlier phases have been hidden, so that the relevant
|
||||
commands are more easily found, and the elapsed time for a completed phase
|
||||
is shown next to the phase heading. To see the entire execution, use "go test -v",
|
||||
which also adds an initial environment dump to the beginning of the log.
|
||||
|
||||
Note also that in reported output, the actual name of the per-script temporary directory
|
||||
has been consistently replaced with the literal string $WORK.
|
||||
|
||||
The cmd/go test flag -testwork (which must appear on the "go test" command line after
|
||||
standard test flags) causes each test to log the name of its $WORK directory and other
|
||||
environment variable settings and also to leave that directory behind when it exits,
|
||||
for manual debugging of failing tests:
|
||||
|
||||
$ go test -run=Script -work
|
||||
--- FAIL: TestScript (3.75s)
|
||||
--- FAIL: TestScript/install_rebuild_gopath (0.16s)
|
||||
script_test.go:223:
|
||||
WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
|
||||
GOARCH=
|
||||
GOCACHE=/Users/rsc/Library/Caches/go-build
|
||||
GOOS=
|
||||
GOPATH=$WORK/gopath
|
||||
GOROOT=/Users/rsc/go
|
||||
HOME=/no-home
|
||||
TMPDIR=$WORK/tmp
|
||||
exe=
|
||||
|
||||
# GOPATH with p1 in d2, p2 in d2 (0.000s)
|
||||
# build & install p1 (0.085s)
|
||||
# modify p2 - p1 should appear stale (0.030s)
|
||||
# build & install p1 again (0.019s)
|
||||
> go install -i p11
|
||||
[stderr]
|
||||
can't load package: package p11: cannot find package "p11" in any of:
|
||||
/Users/rsc/go/src/p11 (from $GOROOT)
|
||||
$WORK/d1/src/p11 (from $GOPATH)
|
||||
$WORK/d2/src/p11
|
||||
[exit status 1]
|
||||
FAIL: unexpected go command failure
|
||||
|
||||
script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
|
||||
|
||||
FAIL
|
||||
exit status 1
|
||||
FAIL cmd/go 4.875s
|
||||
$
|
||||
|
||||
$ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
|
||||
$ cd $WORK/d1/src/p1
|
||||
$ cat p1.go
|
||||
package p1
|
||||
import "p2"
|
||||
func F() { p2.F() }
|
||||
$
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Build should use GOTMPDIR if set.
|
||||
env GOTMPDIR=$WORK/my-favorite-tmpdir
|
||||
env GOCACHE=off
|
||||
mkdir $GOTMPDIR
|
||||
go build -work hello.go
|
||||
stderr ^WORK=.*my-favorite-tmpdir
|
||||
|
||||
-- hello.go --
|
||||
package main
|
||||
func main() { println("hello") }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Set up fresh GOCACHE.
|
||||
env GOCACHE=$WORK/gocache
|
||||
mkdir $GOCACHE
|
||||
|
||||
# Building trivial non-main package should run compiler the first time.
|
||||
go build -x lib.go
|
||||
stderr '(compile|gccgo)( |\.exe).*lib\.go'
|
||||
|
||||
# ... but not again ...
|
||||
go build -x lib.go
|
||||
! stderr '(compile|gccgo)( |\.exe).*lib\.go'
|
||||
|
||||
# ... unless we use -a.
|
||||
go build -a -x lib.go
|
||||
stderr '(compile|gccgo)( |\.exe)'
|
||||
|
||||
-- lib.go --
|
||||
package lib
|
|
@ -0,0 +1,23 @@
|
|||
# Set up fresh GOCACHE.
|
||||
env GOCACHE=$WORK/gocache
|
||||
mkdir $GOCACHE
|
||||
|
||||
# Building a main package should run the compiler and linker ...
|
||||
go build -o $devnull -x main.go
|
||||
stderr '(compile|gccgo)( |\.exe).*main\.go'
|
||||
stderr '(link|gccgo)( |\.exe)'
|
||||
|
||||
# ... and then the linker again ...
|
||||
go build -o $devnull -x main.go
|
||||
! stderr '(compile|gccgo)( |\.exe).*main\.go'
|
||||
stderr '(link|gccgo)( |\.exe)'
|
||||
|
||||
# ... but the output binary can serve as a cache.
|
||||
go build -o main$exe -x main.go
|
||||
stderr '(link|gccgo)( |\.exe)'
|
||||
go build -o main$exe -x main.go
|
||||
! stderr '(link|gccgo)( |\.exe)'
|
||||
|
||||
-- main.go --
|
||||
package main
|
||||
func main() {}
|
|
@ -0,0 +1,19 @@
|
|||
[!gc] skip
|
||||
|
||||
# Set up fresh GOCACHE.
|
||||
env GOCACHE=$WORK/gocache
|
||||
mkdir $GOCACHE
|
||||
|
||||
# Building a trivial non-main package should run compiler the first time.
|
||||
go build -x -gcflags=-m lib.go
|
||||
stderr 'compile( |\.exe)'
|
||||
stderr 'lib.go:2.* can inline f'
|
||||
|
||||
# ... but not the second, even though it still prints the compiler output.
|
||||
go build -x -gcflags=-m lib.go
|
||||
! stderr 'compile( |\.exe)'
|
||||
stderr 'lib.go:2.* can inline f'
|
||||
|
||||
-- lib.go --
|
||||
package p
|
||||
func f(x *int) *int { return x }
|
|
@ -0,0 +1,6 @@
|
|||
# look for short, relative file:line in error message
|
||||
! go run ../../gopath/x/y/z/err.go
|
||||
stderr ^..[\\/]x[\\/]y[\\/]z[\\/]err.go:
|
||||
|
||||
-- ../x/y/z/err.go --
|
||||
package main; import "bar"
|
|
@ -0,0 +1,22 @@
|
|||
# 'go install' with no arguments should clean up after go build
|
||||
cd mycmd
|
||||
go build
|
||||
exists mycmd$exe
|
||||
go install
|
||||
! exists mycmd$exe
|
||||
|
||||
# 'go install mycmd' does not clean up, even in the mycmd directory
|
||||
go build
|
||||
exists mycmd$exe
|
||||
go install mycmd
|
||||
exists mycmd$exe
|
||||
|
||||
# 'go install mycmd' should not clean up in an unrelated current directory either
|
||||
cd ..
|
||||
cp mycmd/mycmd$exe mycmd$exe
|
||||
go install mycmd
|
||||
exists mycmd$exe
|
||||
|
||||
-- mycmd/main.go --
|
||||
package main
|
||||
func main() {}
|
|
@ -0,0 +1,23 @@
|
|||
cd mycmd
|
||||
go build mycmd
|
||||
|
||||
# cross-compile install with implicit GOBIN=$GOPATH/bin can make subdirectory
|
||||
env GOARCH=386
|
||||
[386] env GOARCH=amd64
|
||||
env GOOS=linux
|
||||
go install mycmd
|
||||
exists $GOPATH/bin/linux_$GOARCH/mycmd
|
||||
|
||||
# cross-compile install with explicit GOBIN cannot make subdirectory
|
||||
env GOBIN=$WORK/bin
|
||||
! go install mycmd
|
||||
! exists $GOBIN/linux_$GOARCH
|
||||
|
||||
# installing standard command should still work
|
||||
# (should also be mtime update only if cmd/pack is up-to-date).
|
||||
! stale cmd/pack
|
||||
[!short] go install cmd/pack
|
||||
|
||||
-- mycmd/x.go --
|
||||
package main
|
||||
func main() {}
|
|
@ -0,0 +1,29 @@
|
|||
# GOPATH with p1 in d1, p2 in d2
|
||||
[!windows] env GOPATH=$WORK/d1:$WORK/d2
|
||||
[windows] env GOPATH=$WORK/d1;$WORK/d2
|
||||
|
||||
# build & install p1
|
||||
go install -i p1
|
||||
! stale p1 p2
|
||||
|
||||
# modify p2 - p1 should appear stale
|
||||
cp $WORK/p2x.go $WORK/d2/src/p2/p2.go
|
||||
stale p1 p2
|
||||
|
||||
# build & install p1 again
|
||||
go install -i p1
|
||||
! stale p1 p2
|
||||
|
||||
-- $WORK/d1/src/p1/p1.go --
|
||||
package p1
|
||||
import "p2"
|
||||
func F() { p2.F() }
|
||||
|
||||
-- $WORK/d2/src/p2/p2.go --
|
||||
package p2
|
||||
func F() {}
|
||||
|
||||
-- $WORK/p2x.go --
|
||||
package p2
|
||||
func F() {}
|
||||
func G() {}
|
|
@ -0,0 +1,42 @@
|
|||
# go command should detect package staleness as source file set changes
|
||||
go install mypkg
|
||||
! stale mypkg
|
||||
|
||||
# z.go was not compiled; removing it should NOT make mypkg stale
|
||||
rm mypkg/z.go
|
||||
! stale mypkg
|
||||
|
||||
# y.go was compiled; removing it should make mypkg stale
|
||||
rm mypkg/y.go
|
||||
stale mypkg
|
||||
|
||||
# go command should detect executable staleness too
|
||||
go install mycmd
|
||||
! stale mycmd
|
||||
rm mycmd/z.go
|
||||
! stale mycmd
|
||||
rm mycmd/y.go
|
||||
stale mycmd
|
||||
|
||||
-- mypkg/x.go --
|
||||
package mypkg
|
||||
|
||||
-- mypkg/y.go --
|
||||
package mypkg
|
||||
|
||||
-- mypkg/z.go --
|
||||
// +build missingtag
|
||||
|
||||
package mypkg
|
||||
|
||||
-- mycmd/x.go --
|
||||
package main
|
||||
func main() {}
|
||||
|
||||
-- mycmd/y.go --
|
||||
package main
|
||||
|
||||
-- mycmd/z.go --
|
||||
// +build missingtag
|
||||
|
||||
package main
|
|
@ -0,0 +1,7 @@
|
|||
# check for linker name in error message about linker crash
|
||||
[!gc] skip
|
||||
! go build -ldflags=-crash_for_testing x.go
|
||||
stderr [\\/]tool[\\/].*[\\/]link
|
||||
|
||||
-- x.go --
|
||||
package main; func main() {}
|
|
@ -0,0 +1,12 @@
|
|||
[!gc] skip
|
||||
|
||||
# listing GOROOT should only find standard packages
|
||||
cd $GOROOT/src
|
||||
go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' ./...
|
||||
! stdout .
|
||||
# TODO: ignore _/blah/go/src in output
|
||||
|
||||
# our vendored packages should be reported as standard
|
||||
go list std cmd
|
||||
stdout golang_org/x/net/http2/hpack
|
||||
stdout cmd/vendor/golang\.org/x/arch/x86/x86asm
|
|
@ -0,0 +1,8 @@
|
|||
# go list supports -tags
|
||||
go list -tags=thetag ./my...
|
||||
stdout mypkg
|
||||
|
||||
-- mypkg/x.go --
|
||||
// +build thetag
|
||||
|
||||
package mypkg
|
|
@ -0,0 +1,21 @@
|
|||
env GO111MODULE=on
|
||||
|
||||
# explicit get should report errors about bad names
|
||||
! go get appengine
|
||||
stderr 'cannot find module providing package appengine'
|
||||
! go get x/y.z
|
||||
stderr 'cannot find module providing package x/y.z'
|
||||
|
||||
# build should skip over appengine imports
|
||||
! go build
|
||||
! stderr appengine
|
||||
stderr 'cannot find module providing package nonexistent.rsc.io'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
|
||||
-- x.go --
|
||||
package x
|
||||
|
||||
import _ "appengine"
|
||||
import _ "nonexistent.rsc.io" // domain does not exist
|
|
@ -0,0 +1,11 @@
|
|||
env GO111MODULE=on
|
||||
|
||||
! go get rsc.io/badfile1 rsc.io/badfile2 rsc.io/badfile3 rsc.io/badfile4 rsc.io/badfile5
|
||||
! stderr 'unzip.*badfile1'
|
||||
stderr 'unzip.*badfile2[\\/]@v[\\/]v1.0.0.zip:.*malformed file path "☺.go": invalid char ''☺'''
|
||||
stderr 'unzip.*badfile3[\\/]@v[\\/]v1.0.0.zip: malformed file path "x\?y.go": invalid char ''\?'''
|
||||
stderr 'unzip.*badfile4[\\/]@v[\\/]v1.0.0.zip: case-insensitive file name collision: "x/Y.go" and "x/y.go"'
|
||||
stderr 'unzip.*badfile5[\\/]@v[\\/]v1.0.0.zip: case-insensitive file name collision: "x/y" and "x/Y"'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
|
@ -0,0 +1,30 @@
|
|||
# Test that build tags are used.
|
||||
# golang.org/issue/24053.
|
||||
|
||||
env GO111MODULE=on
|
||||
|
||||
cd x
|
||||
! go list -f {{.GoFiles}}
|
||||
stderr 'build constraints exclude all Go files'
|
||||
|
||||
go list -f {{.GoFiles}} -tags tag1
|
||||
stdout '\[x.go\]'
|
||||
|
||||
go list -f {{.GoFiles}} -tags tag2
|
||||
stdout '\[y\.go\]'
|
||||
|
||||
go list -f {{.GoFiles}} -tags 'tag1 tag2'
|
||||
stdout '\[x\.go y\.go\]'
|
||||
|
||||
-- x/go.mod --
|
||||
module x
|
||||
|
||||
-- x/x.go --
|
||||
// +build tag1
|
||||
|
||||
package y
|
||||
|
||||
-- x/y.go --
|
||||
// +build tag2
|
||||
|
||||
package y
|
|
@ -0,0 +1,13 @@
|
|||
env GO111MODULE=on
|
||||
|
||||
go get rsc.io/QUOTE
|
||||
go list -m all
|
||||
stdout '^rsc.io/quote v1.5.2'
|
||||
stdout '^rsc.io/QUOTE v1.5.2'
|
||||
|
||||
go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE
|
||||
stdout 'DEPS.*rsc.io/quote'
|
||||
stdout 'DIR.*!q!u!o!t!e'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче