├── .gitignore ├── testdata ├── help.txt ├── failing_run.txt ├── version.txt ├── mod │ ├── example.com_good_v2_v2.0.0.txt │ ├── github.com_gobin-testrepos_food_v1.0.0.txt │ ├── github.com_gobin-testrepos_foodsubst_v1.0.0.txt │ ├── github.com_gobin-testrepos_non-main_v1.0.0.txt │ ├── gopkg.in_src-d_go-kallax.v1_v1.3.5.txt │ ├── github.com_gobin-testrepos_non-module_v1.0.0.txt │ ├── example.com_fail_v1.0.0.txt │ ├── github.com_gobin-testrepos_simple-main_v1.1.0.txt │ ├── github.com_gobin-testrepos_simple-main-directory-replace_v1.0.0.txt │ └── github.com_gobin-testrepos_simple-main_v1.0.0.txt ├── replace_directory.txt ├── replace.txt ├── replace-main-mod.txt ├── major_version.txt ├── download.txt ├── print-non-main.txt ├── print-main-module-non-module.txt ├── run_alt_goos_goarch.txt ├── run.txt ├── goflags-tags-precedence.txt ├── install-self.txt ├── print-global-non-module.txt ├── main-module-change.txt ├── mod_readonly.txt ├── go111module.txt ├── gopkg.in.txt ├── print-main-module-simple-main.txt ├── install-global-simple-main.txt ├── latest-global.txt ├── print-global-simple-main.txt └── install-main-module-simple-main.txt ├── .gitattributes ├── go.mod ├── vendor ├── github.com │ └── rogpeppe │ │ └── go-internal │ │ ├── testscript │ │ ├── envvarname.go │ │ ├── envvarname_windows.go │ │ ├── exe.go │ │ ├── cover.go │ │ ├── doc.go │ │ └── cmd.go │ │ ├── internal │ │ ├── os │ │ │ └── execpath │ │ │ │ ├── exec.go │ │ │ │ ├── lp_js.go │ │ │ │ ├── lp_plan9.go │ │ │ │ ├── lp_unix.go │ │ │ │ └── lp_windows.go │ │ └── textutil │ │ │ ├── doc.go │ │ │ └── diff.go │ │ ├── testenv │ │ ├── testenv_cgo.go │ │ ├── testenv_notwin.go │ │ ├── testenv_windows.go │ │ └── testenv.go │ │ ├── goproxytest │ │ ├── allhex.go │ │ ├── pseudo.go │ │ └── proxy.go │ │ ├── LICENSE │ │ ├── imports │ │ ├── scan.go │ │ ├── build.go │ │ └── read.go │ │ ├── par │ │ └── work.go │ │ ├── gotooltest │ │ └── setup.go │ │ ├── cmd │ │ └── txtar-addmod │ │ │ └── addmod.go │ │ ├── txtar │ │ └── archive.go │ │ ├── semver │ │ └── semver.go │ │ └── module │ │ └── module.go ├── modules.txt └── gopkg.in │ └── errgo.v2 │ ├── errors │ ├── itoa.go │ ├── interface.go │ ├── struct.go │ └── errors.go │ ├── fmt │ └── errors │ │ ├── errors.go │ │ └── alias.go │ └── LICENSE ├── tools.go ├── exitcode_plan9.go ├── exitcode_posix.go ├── os.go ├── .github └── workflows │ ├── verify_commit.yml │ └── test.yml ├── go.sum ├── LICENSE ├── .readme.sh ├── script_test.go ├── help.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.bin 2 | /release 3 | /gobin 4 | -------------------------------------------------------------------------------- /testdata/help.txt: -------------------------------------------------------------------------------- 1 | ! gobin -help 2 | stderr 'gobin' 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /vendor/* linguist-vendored 2 | /vendor/* linguist-generated=true 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/myitcv/gobin 2 | 3 | require github.com/rogpeppe/go-internal v1.5.2 4 | 5 | go 1.11 6 | -------------------------------------------------------------------------------- /testdata/failing_run.txt: -------------------------------------------------------------------------------- 1 | ! gobin -run example.com/fail 2 | stdout '^This will fail$' 3 | stderr '^It''s bad$' 4 | 5 | 6 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/envvarname.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package testscript 4 | 5 | func envvarname(k string) string { 6 | return k 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/envvarname_windows.go: -------------------------------------------------------------------------------- 1 | package testscript 2 | 3 | import "strings" 4 | 5 | func envvarname(k string) string { 6 | return strings.ToLower(k) 7 | } 8 | -------------------------------------------------------------------------------- /testdata/version.txt: -------------------------------------------------------------------------------- 1 | # Exercise the -v flag 2 | env HOME=$WORK/home 3 | gobin -v github.com/gobin-testrepos/simple-main@v1.0.0 4 | stdout '^github.com/gobin-testrepos/simple-main v1.0.0$' 5 | ! stderr .+ 6 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/os/execpath/exec.go: -------------------------------------------------------------------------------- 1 | package execpath 2 | 3 | import "os/exec" 4 | 5 | type Error = exec.Error 6 | 7 | // ErrNotFound is the error resulting if a path search failed to find an executable file. 8 | var ErrNotFound = exec.ErrNotFound 9 | -------------------------------------------------------------------------------- /testdata/mod/example.com_good_v2_v2.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module example.com/good/v2 3 | 4 | -- .info -- 5 | {"Version":"v2.0.0","Time":"2018-10-22T18:45:39Z"} 6 | 7 | -- go.mod -- 8 | module example.com/good/v2 9 | 10 | -- main.go -- 11 | package main 12 | 13 | func main() {} 14 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_food_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module github.com/gobin-testrepos/food 3 | -- .info -- 4 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} 5 | -- go.mod -- 6 | module github.com/gobin-testrepos/food 7 | -- main.go -- 8 | package food 9 | 10 | const MainCourse = "fish" 11 | -------------------------------------------------------------------------------- /testdata/replace_directory.txt: -------------------------------------------------------------------------------- 1 | # Verify that directory-target replace statements in the main package's module 2 | # are ignored. 3 | 4 | env HOME=$WORK/home 5 | gobin -run github.com/gobin-testrepos/simple-main-directory-replace@v1.0.0 6 | stdout '^Simple module-based main v1.0.0$' 7 | stdout '^Today we will eat fish$' 8 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_foodsubst_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module github.com/gobin-testrepos/food 3 | -- .info -- 4 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} 5 | -- go.mod -- 6 | module github.com/gobin-testrepos/food 7 | -- main.go -- 8 | package food 9 | 10 | const MainCourse = "chips" 11 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testenv/testenv_cgo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build cgo 6 | 7 | package testenv 8 | 9 | func init() { 10 | haveCGO = true 11 | } 12 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_non-main_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | module github.com/gobin-testrepos/non-main@v1.0.0 2 | 3 | -- .mod -- 4 | module github.com/gobin-testrepos/non-main 5 | -- .info -- 6 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:57Z"} 7 | -- go.mod -- 8 | module github.com/gobin-testrepos/non-main 9 | -- main.go -- 10 | package tools 11 | 12 | -------------------------------------------------------------------------------- /testdata/replace.txt: -------------------------------------------------------------------------------- 1 | # Verify that replace statements in the main package's module are 2 | # applied when the main package is installed (note, this is not 3 | # the expectation with -m) 4 | 5 | env HOME=$WORK/home 6 | gobin -run github.com/gobin-testrepos/simple-main@v1.0.0 7 | stdout '^Simple module-based main v1.0.0$' 8 | stdout '^Today we will eat chips$' 9 | -------------------------------------------------------------------------------- /testdata/mod/gopkg.in_src-d_go-kallax.v1_v1.3.5.txt: -------------------------------------------------------------------------------- 1 | module gopkg.in/src-d/go-kallax.v1@v1.3.5 2 | 3 | -- .mod -- 4 | module gopkg.in/src-d/go-kallax.v1 5 | -- .info -- 6 | {"Version":"v1.3.5","Time":"2018-06-07T08:58:58Z"} 7 | 8 | -- generator/cli/kallax/main.go -- 9 | package main 10 | 11 | import "fmt" 12 | 13 | func main() { 14 | fmt.Println("This is kallax") 15 | } 16 | -------------------------------------------------------------------------------- /testdata/replace-main-mod.txt: -------------------------------------------------------------------------------- 1 | # When we supply -m, then we will _not_ get any replace statements that 2 | # are present in the main package's module's go.mod 3 | 4 | cd repo 5 | gobin -m -run github.com/gobin-testrepos/simple-main@v1.0.0 6 | stdout '^Simple module-based main v1.0.0$' 7 | stdout '^Today we will eat fish$' 8 | 9 | -- repo/go.mod -- 10 | module example.com/repo 11 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_non-module_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | module github.com/gobin-testrepos/non-module@v1.0.0 2 | 3 | -- .mod -- 4 | module github.com/gobin-testrepos/non-module 5 | -- .info -- 6 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:28Z"} 7 | -- main.go -- 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | fmt.Println("I am not a module") 14 | } 15 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/rogpeppe/go-internal/cmd/txtar-addmod" 7 | // 8 | // Temporarily remove this dependency to break the circular module 9 | // requirement (https://github.com/golang/go/issues/29773) 10 | // 11 | // _ "myitcv.io/cmd/egrunner" 12 | // _ "myitcv.io/cmd/githubcli" 13 | // _ "myitcv.io/cmd/mdreplace" 14 | ) 15 | -------------------------------------------------------------------------------- /testdata/major_version.txt: -------------------------------------------------------------------------------- 1 | # A test to ensure that where a major version (>=2) exists 2 | # at the end of the main package path that we end up with 3 | # the right binary name 4 | 5 | gobin example.com/good/v2 6 | stdout '^Installed example\.com/good/v2@v2\.0\.0 to '${WORK@R}'[/\\]gopath[/\\]bin[/\\]good'$exe$ 7 | ! stderr .+ 8 | 9 | gobin -p example.com/good/v2 10 | stdout [/\\]good$exe$ 11 | ! stderr .+ 12 | -------------------------------------------------------------------------------- /testdata/download.txt: -------------------------------------------------------------------------------- 1 | gobin -d github.com/gobin-testrepos/simple-main@v1.0.0 2 | ! stdout .+ 3 | ! stderr .+ 4 | [linux] exists $HOME/.cache/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 5 | [darwin] exists $HOME/Library/Caches/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 6 | [windows] exists $LOCALAPPDATA/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main.exe 7 | 8 | -------------------------------------------------------------------------------- /testdata/mod/example.com_fail_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module example.com/fail 3 | 4 | -- .info -- 5 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} 6 | 7 | -- go.mod -- 8 | module example.com/fail 9 | 10 | -- main.go -- 11 | package main 12 | 13 | import "fmt" 14 | import "os" 15 | 16 | func main() { 17 | fmt.Println("This will fail") 18 | fmt.Fprintln(os.Stderr, "It's bad") 19 | os.Exit(42) 20 | } 21 | -------------------------------------------------------------------------------- /testdata/print-non-main.txt: -------------------------------------------------------------------------------- 1 | env HOME=$WORK/home 2 | ! gobin -p github.com/gobin-testrepos/non-main 3 | ! stdout .+ 4 | stderr '^github.com/gobin-testrepos/non-main@v1.0.0: not a main package$' 5 | 6 | cd repo 7 | ! gobin -m -p github.com/gobin-testrepos/non-main 8 | ! stdout .+ 9 | stderr '^github.com/gobin-testrepos/non-main@v1.0.0: not a main package$' 10 | 11 | -- repo/go.mod -- 12 | module example.com/repo 13 | -------------------------------------------------------------------------------- /exitcode_plan9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // ExitCode returns the exit code of the exited process, or -1 9 | // if the process hasn't exited or was terminated by a signal. 10 | func ExitCode(p *os.ProcessState) int { 11 | // return -1 if the process hasn't started. 12 | if p == nil { 13 | return -1 14 | } 15 | return p.Sys().(syscall.WaitStatus).ExitStatus() 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/textutil/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package textutil contains text processing utilities. 6 | // 7 | // This package came to life as a result of refactoring code common to 8 | // internal packages we have factored out of the Go repo. 9 | package textutil 10 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_simple-main_v1.1.0.txt: -------------------------------------------------------------------------------- 1 | module github.com/gobin-testrepos/simple-main@v1.1.0 2 | 3 | -- .mod -- 4 | module github.com/gobin-testrepos/simple-main 5 | -- .info -- 6 | {"Version":"v1.1.0","Time":"2018-10-22T18:45:39Z"} 7 | -- go.mod -- 8 | module github.com/gobin-testrepos/simple-main 9 | -- main.go -- 10 | package main 11 | 12 | import "fmt" 13 | 14 | func main() { 15 | fmt.Println("Simple module-based main v1.1.0") 16 | } 17 | -------------------------------------------------------------------------------- /exitcode_posix.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | // ExitCode returns the exit code of the exited process, or -1 11 | // if the process hasn't exited or was terminated by a signal. 12 | func ExitCode(p *os.ProcessState) int { 13 | // return -1 if the process hasn't started. 14 | if p == nil { 15 | return -1 16 | } 17 | return p.Sys().(syscall.WaitStatus).ExitStatus() 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testenv/testenv_notwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !windows 6 | 7 | package testenv 8 | 9 | import ( 10 | "runtime" 11 | ) 12 | 13 | func hasSymlink() (ok bool, reason string) { 14 | switch runtime.GOOS { 15 | case "android", "nacl", "plan9": 16 | return false, "" 17 | } 18 | 19 | return true, "" 20 | } 21 | -------------------------------------------------------------------------------- /testdata/print-main-module-non-module.txt: -------------------------------------------------------------------------------- 1 | cd repo 2 | gobin -m -p github.com/gobin-testrepos/non-module 3 | stdout ^${WORK@R}[/\\]repo[/\\].gobincache[/\\]github.com[/\\]gobin-testrepos[/\\]non-module[/\\]@v[/\\]v1.0.0[/\\]non-module$exe'$' 4 | ! stderr .+ 5 | 6 | exec $WORK/repo/.gobincache/github.com/gobin-testrepos/non-module/@v/v1.0.0/non-module$exe 7 | stdout '^I am not a module$' 8 | ! stderr .+ 9 | 10 | -- repo/go.mod -- 11 | module example.com/repo 12 | 13 | require github.com/gobin-testrepos/non-module v1.0.0 14 | -------------------------------------------------------------------------------- /testdata/run_alt_goos_goarch.txt: -------------------------------------------------------------------------------- 1 | # Test that the value of GOOS and GOARCH does not affect -run 2 | 3 | # Set values that we will never test with 4 | env GOOS=solaris 5 | env GOARCH=amd64 6 | 7 | gobin -m -run mod.com/p 8 | stdout '^GOOS is solaris, GOARCH is amd64$' 9 | 10 | -- go.mod -- 11 | module mod.com 12 | 13 | -- p/main.go -- 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | ) 20 | 21 | func main() { 22 | fmt.Printf("GOOS is %v, GOARCH is %v\n", os.Getenv("GOOS"), os.Getenv("GOARCH")) 23 | } 24 | -------------------------------------------------------------------------------- /testdata/run.txt: -------------------------------------------------------------------------------- 1 | # Various -run tests 2 | 3 | # Ensure os.Args[0] is set correctly and that Stdin is correctly 4 | # attached 5 | stdin input 6 | gobin -m -run example.com/blah 7 | cmpenv stdout output.golden 8 | 9 | -- go.mod -- 10 | module example.com/blah 11 | 12 | -- main.go -- 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "io" 18 | "os" 19 | ) 20 | 21 | func main() { 22 | fmt.Println(os.Args[0]) 23 | if _, err := io.Copy(os.Stdout, os.Stdin); err != nil { 24 | panic(err) 25 | } 26 | } 27 | -- input -- 28 | This is stdin 29 | -- output.golden -- 30 | blah$exe 31 | This is stdin 32 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/goproxytest/allhex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package goproxytest 6 | 7 | // This code was taken from src/cmd/go/internal/modfetch/codehost. 8 | 9 | // allHex reports whether the revision rev is entirely lower-case hexadecimal digits. 10 | func allHex(rev string) bool { 11 | for i := 0; i < len(rev); i++ { 12 | c := rev[i] 13 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' { 14 | continue 15 | } 16 | return false 17 | } 18 | return true 19 | } 20 | -------------------------------------------------------------------------------- /testdata/goflags-tags-precedence.txt: -------------------------------------------------------------------------------- 1 | # Test that we have correct precedence handling of GOFLAGS and -tags 2 | gobin -m -run . 3 | stdout '^Not blah$' 4 | gobin -m -run -tags blah . 5 | stdout '^Blah$' 6 | env GOFLAGS=-tags=blah 7 | gobin -m -run . 8 | stdout '^Blah$' 9 | gobin -m -run -tags '' . 10 | stdout '^Not blah$' 11 | 12 | -- go.mod -- 13 | module mod.com 14 | 15 | -- not_blah.go -- 16 | // +build !blah 17 | 18 | package main 19 | 20 | import "fmt" 21 | 22 | func main() { 23 | fmt.Println("Not blah") 24 | } 25 | -- blah.go -- 26 | // +build blah 27 | 28 | package main 29 | 30 | import "fmt" 31 | 32 | func main() { 33 | fmt.Println("Blah") 34 | } 35 | -------------------------------------------------------------------------------- /testdata/install-self.txt: -------------------------------------------------------------------------------- 1 | skip 'Skipping because of https://github.com/golang/go/issues/26241' 2 | 3 | env GOPROXY=file://$TESTGOPATH/pkg/mod/cache/download 4 | env GOBIN=$WORK/install 5 | env PATH=$GOBIN:$PATH 6 | 7 | env GOPATH=$TESTGOPATH 8 | cd $GOBINMODPATH 9 | go install 10 | exists $GOBIN/gobin 11 | exec which gobin 12 | stdout ^$GOBIN/gobin'$' 13 | 14 | cd $WORK/repo 15 | 16 | # Temporarily remove this whilst we break the circular module dependency 17 | # https://github.com/golang/go/issues/29773 18 | # exec go mod edit -replace=myitcv.io=$GOBINMODPATH 19 | 20 | exec gobin -m github.com/myitcv/gobin 21 | 22 | -- repo/go.mod -- 23 | module mod 24 | 25 | require github.com/myitcv/gobin v0.0.0 26 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/rogpeppe/go-internal v1.5.2 2 | github.com/rogpeppe/go-internal/cmd/txtar-addmod 3 | github.com/rogpeppe/go-internal/goproxytest 4 | github.com/rogpeppe/go-internal/gotooltest 5 | github.com/rogpeppe/go-internal/imports 6 | github.com/rogpeppe/go-internal/internal/os/execpath 7 | github.com/rogpeppe/go-internal/internal/textutil 8 | github.com/rogpeppe/go-internal/module 9 | github.com/rogpeppe/go-internal/par 10 | github.com/rogpeppe/go-internal/semver 11 | github.com/rogpeppe/go-internal/testenv 12 | github.com/rogpeppe/go-internal/testscript 13 | github.com/rogpeppe/go-internal/txtar 14 | # gopkg.in/errgo.v2 v2.1.0 15 | gopkg.in/errgo.v2/errors 16 | gopkg.in/errgo.v2/fmt/errors 17 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/errors/itoa.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | // itoa converts val to a decimal string. It allows this package to 4 | // avoid a dependency on strconv. Adapted from net/parse.go 5 | func appendInt(buf []byte, val int) []byte { 6 | if val < 0 { 7 | buf = append(buf, '-') 8 | return appendUint(buf, uint(-val)) 9 | } 10 | return appendUint(buf, uint(val)) 11 | } 12 | 13 | func appendUint(p []byte, val uint) []byte { 14 | var buf [20]byte // big enough for 64bit value base 10 15 | i := len(buf) - 1 16 | for val >= 10 { 17 | q := val / 10 18 | buf[i] = byte('0' + val - q*10) 19 | i-- 20 | val = q 21 | } 22 | // val < 10 23 | buf[i] = byte('0' + val) 24 | return append(p, buf[i:]...) 25 | } 26 | -------------------------------------------------------------------------------- /os.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "runtime" 7 | ) 8 | 9 | // os.UserHomeDir() is implemented in 10 | // https://go-review.googlesource.com/c/go/+/139418 so implement here for now 11 | func userHomeDir() (string, error) { 12 | env, enverr := "HOME", "$HOME" 13 | switch runtime.GOOS { 14 | case "windows": 15 | env, enverr = "USERPROFILE", "%userprofile%" 16 | case "plan9": 17 | env, enverr = "home", "$home" 18 | case "nacl", "android": 19 | return "/", nil 20 | case "darwin": 21 | if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { 22 | return "/", nil 23 | } 24 | } 25 | if v := os.Getenv(env); v != "" { 26 | return v, nil 27 | } 28 | return "", errors.New(enverr + " is not defined") 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/goproxytest/pseudo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package goproxytest 6 | 7 | import ( 8 | "regexp" 9 | "strings" 10 | 11 | "github.com/rogpeppe/go-internal/semver" 12 | ) 13 | 14 | // This code was taken from cmd/go/internal/modfetch/pseudo.go 15 | 16 | var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`) 17 | 18 | // isPseudoVersion reports whether v is a pseudo-version. 19 | func isPseudoVersion(v string) bool { 20 | return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) 21 | } 22 | -------------------------------------------------------------------------------- /testdata/print-global-non-module.txt: -------------------------------------------------------------------------------- 1 | gobin -p github.com/gobin-testrepos/non-module@v1.0.0 2 | [linux] stdout ^$HOME\Q/.cache/gobin/github.com/gobin-testrepos/non-module/@v/v1.0.0/non-module\E'$' 3 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/github.com/gobin-testrepos/non-module/@v/v1.0.0/non-module\E'$' 4 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\github.com\gobin-testrepos\non-module\@v\v1.0.0\non-module.exe\E'$' 5 | ! stderr .+ 6 | 7 | [linux] exec $HOME/.cache/gobin/github.com/gobin-testrepos/non-module/@v/v1.0.0/non-module 8 | [darwin] exec $HOME/Library/Caches/gobin/github.com/gobin-testrepos/non-module/@v/v1.0.0/non-module 9 | [windows] exec $LOCALAPPDATA\gobin\github.com\gobin-testrepos\non-module\@v\v1.0.0\non-module.exe 10 | stdout '^I am not a module$' 11 | ! stderr .+ 12 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/os/execpath/lp_js.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build js,wasm 6 | 7 | package execpath 8 | 9 | // Look searches for an executable named file, using getenv to look up 10 | // environment variables. If getenv is nil, os.Getenv will be used. If file 11 | // contains a slash, it is tried directly and getenv will not be called. The 12 | // result may be an absolute path or a path relative to the current directory. 13 | func Look(file string, getenv func(string) string) (string, error) { 14 | // Wasm can not execute processes, so act as if there are no executables at all. 15 | return "", &Error{file, ErrNotFound} 16 | } 17 | -------------------------------------------------------------------------------- /testdata/main-module-change.txt: -------------------------------------------------------------------------------- 1 | # Specifically test the logic that ensures -m packages are always 2 | # installed. Per the code comment, we always install as a means of 3 | # establishing whether the target is current. If we instead chose 4 | # to perform these checks ourself, we'd do just the same amount of 5 | # work. 6 | 7 | # Before 8 | cp msg.go.before msg.go 9 | gobin -m -run . 10 | stdout ^before$ 11 | ! stderr .+ 12 | 13 | # After 14 | cp msg.go.after msg.go 15 | gobin -m -run . 16 | stdout ^after$ 17 | ! stderr .+ 18 | 19 | -- go.mod -- 20 | module mod.com 21 | -- main.go -- 22 | package main 23 | 24 | import "fmt" 25 | 26 | func main() { 27 | fmt.Println(msg) 28 | } 29 | -- msg.go.before -- 30 | package main 31 | 32 | var msg = "before" 33 | -- msg.go.after -- 34 | package main 35 | 36 | var msg = "after" 37 | -------------------------------------------------------------------------------- /testdata/mod_readonly.txt: -------------------------------------------------------------------------------- 1 | # Check that -mod=readonly works. 2 | # 3 | # TODO: add a test that verifies a pre-existing value in GOFLAGS is preserved 4 | # and indeed appended to. 5 | 6 | cd repo 7 | ! gobin -mod=readonly github.com/gobin-testrepos/simple-main 8 | ! stdout .+ 9 | [go1.11] [!go1.12] stderr 'import lookup disabled by -mod=readonly' 10 | [go1.12] [!go1.13] stderr 'can''t load package: package github.com/gobin-testrepos/simple-main: unknown import path "github.com/gobin-testrepos/simple-main": import lookup disabled by -mod=readonly' 11 | [go1.13] [!go1.14] stderr 'updates to go.mod needed, disabled by -mod=readonly' 12 | [go1.14] stderr 'cannot find module providing package github\.com/gobin-testrepos/simple-main: import lookup disabled by -mod=readonly' 13 | 14 | -- repo/go.mod -- 15 | module example.com/repo 16 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_simple-main-directory-replace_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module github.com/gobin-testrepos/simple-main-directory-replace 3 | 4 | require github.com/gobin-testrepos/food v1.0.0 5 | 6 | replace github.com/gobin-testrepos/food => /road/to/nowhere 7 | -- .info -- 8 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} 9 | 10 | -- go.mod -- 11 | module github.com/gobin-testrepos/simple-main-directory-replace 12 | 13 | require github.com/gobin-testrepos/food v1.0.0 14 | 15 | replace github.com/gobin-testrepos/food => /road/to/nowhere 16 | 17 | -- main.go -- 18 | package main 19 | 20 | import "fmt" 21 | 22 | import "github.com/gobin-testrepos/food" 23 | 24 | func main() { 25 | fmt.Println("Simple module-based main v1.0.0") 26 | fmt.Printf("Today we will eat %v\n", food.MainCourse) 27 | } 28 | -------------------------------------------------------------------------------- /testdata/mod/github.com_gobin-testrepos_simple-main_v1.0.0.txt: -------------------------------------------------------------------------------- 1 | -- .mod -- 2 | module github.com/gobin-testrepos/simple-main 3 | 4 | require github.com/gobin-testrepos/food v1.0.0 5 | 6 | replace github.com/gobin-testrepos/food => github.com/gobin-testrepos/foodsubst v1.0.0 7 | -- .info -- 8 | {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} 9 | 10 | -- go.mod -- 11 | module github.com/gobin-testrepos/simple-main 12 | 13 | require github.com/gobin-testrepos/food v1.0.0 14 | 15 | replace github.com/gobin-testrepos/food => github.com/gobin-testrepos/foodsubst v1.0.0 16 | 17 | -- main.go -- 18 | package main 19 | 20 | import "fmt" 21 | 22 | import "github.com/gobin-testrepos/food" 23 | 24 | func main() { 25 | fmt.Println("Simple module-based main v1.0.0") 26 | fmt.Printf("Today we will eat %v\n", food.MainCourse) 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/verify_commit.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - '**' 8 | 9 | env: 10 | GOPROXY: https://proxy.golang.org 11 | 12 | name: Verify commit is clean 13 | jobs: 14 | test: 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | go-version: [1.14.1] 19 | platform: [ubuntu-latest] 20 | runs-on: ${{ matrix.platform }} 21 | steps: 22 | - name: Install Go 23 | uses: actions/setup-go@9fbc767707c286e568c92927bbf57d76b73e0892 24 | with: 25 | go-version: ${{ matrix.go-version }} 26 | - name: Checkout code 27 | uses: actions/checkout@01aecccf739ca6ff86c0539fbc67a7a5007bbc81 28 | - run: go mod vendor 29 | - run: go mod tidy 30 | - name: Check git is clean 31 | run: test -z "$(git status --porcelain)" || (git status; git diff; false) 32 | 33 | -------------------------------------------------------------------------------- /testdata/go111module.txt: -------------------------------------------------------------------------------- 1 | # Verify that gobin behaves correctly irrespective of the user's setting of GO111MODULE 2 | 3 | env GO111MODULE=off 4 | gobin -p github.com/gobin-testrepos/simple-main@v1.0.0 5 | [linux] stdout ^$HOME\Q/.cache/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main\E'$' 6 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main\E'$' 7 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\github.com\gobin-testrepos\simple-main\@v\v1.0.0\simple-main.exe\E'$' 8 | ! stderr .+ 9 | 10 | [linux] exec $HOME/.cache/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 11 | [darwin] exec $HOME/Library/Caches/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 12 | [windows] exec $LOCALAPPDATA/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main.exe 13 | stdout '^Simple module-based main v1.0.0$' 14 | ! stderr .+ 15 | -------------------------------------------------------------------------------- /testdata/gopkg.in.txt: -------------------------------------------------------------------------------- 1 | # Test that we have the correct logic for gopkg.in paths 2 | # per https://github.com/myitcv/gobin/issues/65 3 | 4 | gobin -p gopkg.in/src-d/go-kallax.v1/generator/cli/kallax 5 | [linux] stdout ^$HOME\Q/.cache/gobin/gopkg.in/src-d/go-kallax.v1/@v/v1.3.5/generator/cli/kallax/kallax\E'$' 6 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/gopkg.in/src-d/go-kallax.v1/@v/v1.3.5/generator/cli/kallax/kallax\E'$' 7 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\gopkg.in\src-d\go-kallax.v1\@v\v1.3.5\generator\cli\kallax\kallax.exe\E'$' 8 | ! stderr .+ 9 | 10 | [linux] exec $HOME/.cache/gobin/gopkg.in/src-d/go-kallax.v1/@v/v1.3.5/generator/cli/kallax/kallax 11 | [darwin] exec $HOME/Library/Caches/gobin/gopkg.in/src-d/go-kallax.v1/@v/v1.3.5/generator/cli/kallax/kallax 12 | [windows] exec $LOCALAPPDATA/gobin/gopkg.in\src-d\go-kallax.v1\@v\v1.3.5\generator\cli\kallax\kallax.exe 13 | stdout '^This is kallax$' 14 | ! stderr .+ 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 2 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 3 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 4 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 5 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 6 | github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= 7 | github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 8 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 9 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= 11 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 12 | -------------------------------------------------------------------------------- /testdata/print-main-module-simple-main.txt: -------------------------------------------------------------------------------- 1 | cd repo 2 | 3 | # no -tags 4 | gobin -m -p github.com/gobin-testrepos/simple-main 5 | stdout ^${WORK@R}[/\\]repo[/\\].gobincache[/\\]github.com[/\\]gobin-testrepos[/\\]simple-main[/\\]@v[/\\]v1.0.0[/\\]simple-main$exe'$' 6 | ! stderr .+ 7 | 8 | # with -tags 9 | gobin -m -tags blah -p github.com/gobin-testrepos/simple-main 10 | stdout ^${WORK@R}[/\\]repo[/\\].gobincache[/\\][0-9a-f]{64}[/\\]simple-main$exe'$' 11 | ! stderr .+ 12 | 13 | # with GOFLAGS=-tags set 14 | env GOFLAGS=-tags=blah 15 | gobin -m -tags blah -p github.com/gobin-testrepos/simple-main 16 | stdout ^${WORK@R}[/\\]repo[/\\].gobincache[/\\][0-9a-f]{64}[/\\]simple-main$exe'$' 17 | ! stderr .+ 18 | env GOFLAGS= 19 | 20 | # run as a check 21 | exec $WORK/repo/.gobincache/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main$exe 22 | stdout '^Simple module-based main v1.0.0$' 23 | ! stderr .+ 24 | 25 | -- repo/go.mod -- 26 | module example.com/repo 27 | 28 | require github.com/gobin-testrepos/simple-main v1.0.0 29 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/fmt/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors is the same as gopkg.in/errgo.v2/errors except that 2 | // it adds convenience functions that use the fmt package to format 3 | // error messages. 4 | package errors 5 | 6 | import ( 7 | "fmt" 8 | 9 | "gopkg.in/errgo.v2/errors" 10 | ) 11 | 12 | // Newf is like New except it formats the message with a fmt 13 | // format specifier. 14 | func Newf(format string, a ...interface{}) error { 15 | err := errors.New(fmt.Sprintf(format, a...)) 16 | errors.SetLocation(err, 1) 17 | return err 18 | } 19 | 20 | // Notef is like Note except it formats the message with a fmt 21 | // format specifier. 22 | func Notef(err error, shouldPreserveCause func(error) bool, format string, a ...interface{}) error { 23 | err = Note(err, shouldPreserveCause, fmt.Sprintf(format, a...)) 24 | errors.SetLocation(err, 1) 25 | return err 26 | } 27 | 28 | // Becausef is like Because except it formats the message with a fmt 29 | // format specifier. 30 | func Becausef(err, cause error, format string, a ...interface{}) error { 31 | err = Because(err, cause, fmt.Sprintf(format, a...)) 32 | errors.SetLocation(err, 1) 33 | return err 34 | } 35 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/errors/interface.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | // Causer is the type of an error that may provide an error cause for 4 | // error diagnosis. Cause may return nil if there is no cause (for 5 | // example because the cause has been masked). 6 | type Causer interface { 7 | Cause() error 8 | } 9 | 10 | // Wrapper is the type of an error that wraps another error. It is 11 | // exposed so that external types may implement it, but should in 12 | // general not be used otherwise. 13 | type Wrapper interface { 14 | // Message returns the top level error message, 15 | // not including the message from the underlying 16 | // error. 17 | Message() string 18 | 19 | // Underlying returns the underlying error, or nil 20 | // if there is none. 21 | Underlying() error 22 | } 23 | 24 | // Deprecated: Locationer is the old name for Locator, 25 | // kept for backward compatibility only. 26 | type Locationer = Locator 27 | 28 | // Locator can be implemented by any error type that wants to expose 29 | // the source location of an error. 30 | type Locator interface { 31 | // Location returns the name of the file and the line number 32 | // associated with an error. 33 | Location() (file string, line int) 34 | } 35 | -------------------------------------------------------------------------------- /testdata/install-global-simple-main.txt: -------------------------------------------------------------------------------- 1 | # No GOPATH 2 | env GOPATH= 3 | gobin github.com/gobin-testrepos/simple-main@v1.0.0 4 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/home/go/bin/simple-main\E'$' 5 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\home\go\bin\simple-main.exe\E'$' 6 | ! stderr .+ 7 | exists $HOME/go/bin/simple-main$exe 8 | 9 | # GOPATH set 10 | env GOPATH=$WORK/asdf 11 | gobin github.com/gobin-testrepos/simple-main@v1.0.0 12 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/asdf/bin/simple-main\E'$' 13 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\asdf\bin\simple-main.exe\E'$' 14 | ! stderr .+ 15 | exists $WORK/asdf/bin/simple-main$exe 16 | 17 | # GOBIN set 18 | env GOBIN=$WORK/gobin 19 | gobin github.com/gobin-testrepos/simple-main@v1.0.0 20 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/gobin/simple-main\E'$' 21 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\gobin\simple-main.exe\E'$' 22 | ! stderr .+ 23 | exists $WORK/gobin/simple-main$exe 24 | 25 | -------------------------------------------------------------------------------- /testdata/latest-global.txt: -------------------------------------------------------------------------------- 1 | # Install specific version 2 | gobin github.com/gobin-testrepos/simple-main@v1.0.0 3 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/gopath/bin/simple-main\E'$' 4 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\gopath\bin\simple-main.exe\E'$' 5 | ! stderr .+ 6 | 7 | # Verify we get that same version back via latest... 8 | # without having to hit the network 9 | env PREVGOPROXY=$GOPROXY 10 | env GOPROXY= 11 | gobin -nonet github.com/gobin-testrepos/simple-main@latest 12 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/gopath/bin/simple-main\E'$' 13 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\gopath\bin\simple-main.exe\E'$' 14 | ! stderr .+ 15 | 16 | # Upgrade latest to verify we get the later version 17 | env GOPROXY=$PREVGOPROXY 18 | gobin -u github.com/gobin-testrepos/simple-main@latest 19 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.1.0 to \E'${WORK@R}\Q/gopath/bin/simple-main\E'$' 20 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.1.0 to \E'${WORK@R}\Q\gopath\bin\simple-main.exe\E'$' 21 | ! stderr .+ 22 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testenv/testenv_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package testenv 6 | 7 | import ( 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "sync" 12 | "syscall" 13 | ) 14 | 15 | var symlinkOnce sync.Once 16 | var winSymlinkErr error 17 | 18 | func initWinHasSymlink() { 19 | tmpdir, err := ioutil.TempDir("", "symtest") 20 | if err != nil { 21 | panic("failed to create temp directory: " + err.Error()) 22 | } 23 | defer os.RemoveAll(tmpdir) 24 | 25 | err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) 26 | if err != nil { 27 | err = err.(*os.LinkError).Err 28 | switch err { 29 | case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: 30 | winSymlinkErr = err 31 | } 32 | } 33 | } 34 | 35 | func hasSymlink() (ok bool, reason string) { 36 | symlinkOnce.Do(initWinHasSymlink) 37 | 38 | switch winSymlinkErr { 39 | case nil: 40 | return true, "" 41 | case syscall.EWINDOWS: 42 | return false, ": symlinks are not supported on your version of Windows" 43 | case syscall.ERROR_PRIVILEGE_NOT_HELD: 44 | return false, ": you don't have enough privileges to create symlinks" 45 | } 46 | 47 | return false, "" 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Paul Jolly . All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/os/execpath/lp_plan9.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package execpath 6 | 7 | import ( 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | func findExecutable(file string) error { 14 | d, err := os.Stat(file) 15 | if err != nil { 16 | return err 17 | } 18 | if m := d.Mode(); !m.IsDir() && m&0111 != 0 { 19 | return nil 20 | } 21 | return os.ErrPermission 22 | } 23 | 24 | // Look searches for an executable named file, using getenv to look up 25 | // environment variables. If getenv is nil, os.Getenv will be used. If file 26 | // contains a slash, it is tried directly and getenv will not be called. The 27 | // result may be an absolute path or a path relative to the current directory. 28 | func Look(file string, getenv func(string) string) (string, error) { 29 | if getenv == nil { 30 | getenv = os.Getenv 31 | } 32 | 33 | // skip the path lookup for these prefixes 34 | skip := []string{"/", "#", "./", "../"} 35 | 36 | for _, p := range skip { 37 | if strings.HasPrefix(file, p) { 38 | err := findExecutable(file) 39 | if err == nil { 40 | return file, nil 41 | } 42 | return "", &Error{file, err} 43 | } 44 | } 45 | 46 | path := getenv("path") 47 | for _, dir := range filepath.SplitList(path) { 48 | path := filepath.Join(dir, file) 49 | if err := findExecutable(path); err == nil { 50 | return path, nil 51 | } 52 | } 53 | return "", &Error{file, ErrNotFound} 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2013, Roger Peppe 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of this project nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /testdata/print-global-simple-main.txt: -------------------------------------------------------------------------------- 1 | # check printed path 2 | gobin -p github.com/gobin-testrepos/simple-main@v1.0.0 3 | [linux] stdout ^$HOME\Q/.cache/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main\E'$' 4 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main\E'$' 5 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\github.com\gobin-testrepos\simple-main\@v\v1.0.0\simple-main.exe\E'$' 6 | ! stderr .+ 7 | 8 | # with -tags 9 | gobin -tags blah -p github.com/gobin-testrepos/simple-main@v1.0.0 10 | [linux] stdout ^$HOME\Q/.cache/gobin/\E[0-9a-f]{64}\Q/simple-main\E'$' 11 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/\E[0-9a-f]{64}\Q/simple-main\E'$' 12 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\\E[0-9a-f]{64}\Q\simple-main.exe\E'$' 13 | ! stderr .+ 14 | 15 | # with GOFLAGS=-tags set 16 | env GOFLAGS=-tags=blah 17 | gobin -tags blah -p github.com/gobin-testrepos/simple-main@v1.0.0 18 | [linux] stdout ^$HOME\Q/.cache/gobin/\E[0-9a-f]{64}\Q/simple-main\E'$' 19 | [darwin] stdout ^$HOME\Q/Library/Caches/gobin/\E[0-9a-f]{64}\Q/simple-main\E'$' 20 | [windows] stdout ^${LOCALAPPDATA@R}\Q\gobin\\E[0-9a-f]{64}\Q\simple-main.exe\E'$' 21 | ! stderr .+ 22 | env GOFLAGS= 23 | 24 | # run as a check 25 | [linux] exec $HOME/.cache/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 26 | [darwin] exec $HOME/Library/Caches/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main 27 | [windows] exec $LOCALAPPDATA/gobin/github.com/gobin-testrepos/simple-main/@v/v1.0.0/simple-main.exe 28 | stdout '^Simple module-based main v1.0.0$' 29 | ! stderr .+ 30 | -------------------------------------------------------------------------------- /testdata/install-main-module-simple-main.txt: -------------------------------------------------------------------------------- 1 | # Test that gobin -m works for various permutations of GOPATH and GOBIN being set 2 | 3 | cd repo 4 | 5 | # By "resetting" the go.mod below we are ensuring that the go command has work to do, i.e. the get is not already satisfied. 6 | 7 | # No GOPATH 8 | env GOPATH= 9 | cp go.mod.orig go.mod 10 | gobin -m github.com/gobin-testrepos/simple-main@v1.0.0 11 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${HOME@R}\Q/go/bin/simple-main\E'$' 12 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${HOME@R}\Q\go\bin\simple-main.exe\E'$' 13 | ! stderr .+ 14 | exists $HOME/go/bin/simple-main$exe 15 | 16 | # GOPATH set 17 | env GOPATH=$WORK/asdf 18 | cp go.mod.orig go.mod 19 | gobin -m github.com/gobin-testrepos/simple-main@v1.0.0 20 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/asdf/bin/simple-main\E'$' 21 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\asdf\bin\simple-main.exe\E'$' 22 | ! stderr .+ 23 | exists $WORK/asdf/bin/simple-main$exe 24 | 25 | # GOBIN set 26 | mkdir bin 27 | env GOBIN=$WORK/repo/bin 28 | cp go.mod.orig go.mod 29 | gobin -m github.com/gobin-testrepos/simple-main@v1.0.0 30 | [!windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q/repo/bin/simple-main\E'$' 31 | [windows] stdout '^\QInstalled github.com/gobin-testrepos/simple-main@v1.0.0 to \E'${WORK@R}\Q\repo\bin\simple-main.exe\E'$' 32 | ! stderr .+ 33 | exists $WORK/repo/bin/simple-main$exe 34 | 35 | -- repo/go.mod.orig -- 36 | module example.com/repo 37 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - '**' 8 | 9 | env: 10 | GOPROXY: https://proxy.golang.org 11 | 12 | # At the time of writing (2020-04-07) the default git config 13 | # on action runners includes core.autocrlf=true. Therefore we 14 | # override that below before checkout out the code. 15 | # 16 | # See also https://github.com/actions/checkout/issues/135 17 | 18 | name: Test 19 | jobs: 20 | test: 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | go-version: [1.13.9, 1.14.1] 25 | platform: [ubuntu-latest, macos-latest, windows-latest] 26 | runs-on: ${{ matrix.platform }} 27 | steps: 28 | - name: Install Go 29 | uses: actions/setup-go@9fbc767707c286e568c92927bbf57d76b73e0892 30 | with: 31 | go-version: ${{ matrix.go-version }} 32 | - name: Fix git config 33 | run: git config --global core.autocrlf false 34 | - run: mkdir -p gopath/src/github.com/myitcv/gobin 35 | - name: Checkout code 36 | uses: actions/checkout@01aecccf739ca6ff86c0539fbc67a7a5007bbc81 37 | with: 38 | path: gopath/src/github.com/myitcv/gobin 39 | - name: Test 40 | run: go test ./... 41 | working-directory: gopath/src/github.com/myitcv/gobin 42 | - name: Adjust GOPATH for non-module install 43 | run: echo "::set-env name=GOPATH::$PWD/gopath" 44 | - name: Adjust GO111MODULE for non-module install 45 | run: echo "::set-env name=GO111MODULE::off" 46 | - name: Non-module install test 47 | run: go install 48 | working-directory: gopath/src/github.com/myitcv/gobin 49 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/os/execpath/lp_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris 6 | 7 | package execpath 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | ) 14 | 15 | func findExecutable(file string) error { 16 | d, err := os.Stat(file) 17 | if err != nil { 18 | return err 19 | } 20 | if m := d.Mode(); !m.IsDir() && m&0111 != 0 { 21 | return nil 22 | } 23 | return os.ErrPermission 24 | } 25 | 26 | // Look searches for an executable named file, using getenv to look up 27 | // environment variables. If getenv is nil, os.Getenv will be used. If file 28 | // contains a slash, it is tried directly and getenv will not be called. The 29 | // result may be an absolute path or a path relative to the current directory. 30 | func Look(file string, getenv func(string) string) (string, error) { 31 | if getenv == nil { 32 | getenv = os.Getenv 33 | } 34 | 35 | // NOTE(rsc): I wish we could use the Plan 9 behavior here 36 | // (only bypass the path if file begins with / or ./ or ../) 37 | // but that would not match all the Unix shells. 38 | 39 | if strings.Contains(file, "/") { 40 | err := findExecutable(file) 41 | if err == nil { 42 | return file, nil 43 | } 44 | return "", &Error{file, err} 45 | } 46 | path := getenv("PATH") 47 | for _, dir := range filepath.SplitList(path) { 48 | if dir == "" { 49 | // Unix shell semantics: path element "" means "." 50 | dir = "." 51 | } 52 | path := filepath.Join(dir, file) 53 | if err := findExecutable(path); err == nil { 54 | return path, nil 55 | } 56 | } 57 | return "", &Error{file, ErrNotFound} 58 | } 59 | -------------------------------------------------------------------------------- /.readme.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # **START** 4 | 5 | # block: get 6 | GO111MODULE=off go get -u github.com/myitcv/gobin 7 | 8 | # actually under the hood we want to install the "current" local version 9 | # else we won't be able to take advantage of changes until they are merged 10 | pushd /self > /dev/null 11 | GOBIN=$GOPATH/bin GOPATH=/gopath go install 12 | popd > /dev/null 13 | 14 | # block: fix path 15 | export PATH=$(go env GOPATH)/bin:$PATH 16 | which gobin 17 | 18 | # ==================================== 19 | # global examples 20 | 21 | # behind the scenes fix the version of gohack we install 22 | gobin github.com/rogpeppe/gohack@v1.0.0 23 | 24 | # block: gohack 25 | gobin github.com/rogpeppe/gohack 26 | 27 | # block: gohack latest 28 | gobin github.com/rogpeppe/gohack@latest 29 | 30 | # block: gohack v1.0.0 31 | gobin github.com/rogpeppe/gohack@v1.0.0 32 | 33 | # block: gohack print 34 | gobin -p github.com/rogpeppe/gohack@v1.0.0 35 | 36 | # block: gohack run 37 | gobin -run github.com/rogpeppe/gohack@v1.0.0 -help 38 | assert "$? -eq 2" $LINENO 39 | 40 | # ==================================== 41 | # main-module examples 42 | 43 | mkdir hello 44 | cd hello 45 | go mod init example.com/hello 46 | cat < tools.go 47 | // +build tools 48 | 49 | package tools 50 | 51 | import ( 52 | _ "golang.org/x/tools/cmd/stringer" 53 | ) 54 | EOD 55 | gofmt -w tools.go 56 | 57 | # block: module 58 | cat go.mod 59 | 60 | # block: tools 61 | cat tools.go 62 | 63 | # behind the scenes fix the version of gohack we install 64 | gobin -m -p golang.org/x/tools/cmd/stringer@v0.0.0-20181102223251-96e9e165b75e 65 | 66 | # block: tools version 67 | gobin -m -p golang.org/x/tools/cmd/stringer 68 | 69 | # block: stringer help 70 | gobin -m -run golang.org/x/tools/cmd/stringer -help 71 | assert "$? -eq 2" $LINENO 72 | 73 | cat < main.go 74 | package main 75 | 76 | import "fmt" 77 | 78 | //go:generate gobin -m -run golang.org/x/tools/cmd/stringer -type=Pill 79 | 80 | type Pill int 81 | 82 | const ( 83 | Placebo Pill = iota 84 | Aspirin 85 | Ibuprofen 86 | Paracetamol 87 | Acetaminophen = Paracetamol 88 | ) 89 | 90 | func main() { 91 | fmt.Printf("For headaches, take %v\n", Ibuprofen) 92 | } 93 | EOD 94 | 95 | # block: use in go generate 96 | cat main.go 97 | 98 | # block: go generate and run 99 | go generate 100 | go run . 101 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/os/execpath/lp_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package execpath 6 | 7 | import ( 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | func chkStat(file string) error { 14 | d, err := os.Stat(file) 15 | if err != nil { 16 | return err 17 | } 18 | if d.IsDir() { 19 | return os.ErrPermission 20 | } 21 | return nil 22 | } 23 | 24 | func hasExt(file string) bool { 25 | i := strings.LastIndex(file, ".") 26 | if i < 0 { 27 | return false 28 | } 29 | return strings.LastIndexAny(file, `:\/`) < i 30 | } 31 | 32 | func findExecutable(file string, exts []string) (string, error) { 33 | if len(exts) == 0 { 34 | return file, chkStat(file) 35 | } 36 | if hasExt(file) { 37 | if chkStat(file) == nil { 38 | return file, nil 39 | } 40 | } 41 | for _, e := range exts { 42 | if f := file + e; chkStat(f) == nil { 43 | return f, nil 44 | } 45 | } 46 | return "", os.ErrNotExist 47 | } 48 | 49 | // Look searches for an executable named file, using getenv to look up 50 | // environment variables. If getenv is nil, os.Getenv will be used. If file 51 | // contains a slash, it is tried directly and getenv will not be called. The 52 | // result may be an absolute path or a path relative to the current directory. 53 | // Look also uses PATHEXT environment variable to match 54 | // a suitable candidate. 55 | func Look(file string, getenv func(string) string) (string, error) { 56 | if getenv == nil { 57 | getenv = os.Getenv 58 | } 59 | var exts []string 60 | x := getenv(`PATHEXT`) 61 | if x != "" { 62 | for _, e := range strings.Split(strings.ToLower(x), `;`) { 63 | if e == "" { 64 | continue 65 | } 66 | if e[0] != '.' { 67 | e = "." + e 68 | } 69 | exts = append(exts, e) 70 | } 71 | } else { 72 | exts = []string{".com", ".exe", ".bat", ".cmd"} 73 | } 74 | 75 | if strings.ContainsAny(file, `:\/`) { 76 | if f, err := findExecutable(file, exts); err == nil { 77 | return f, nil 78 | } else { 79 | return "", &Error{file, err} 80 | } 81 | } 82 | if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { 83 | return f, nil 84 | } 85 | path := getenv("path") 86 | for _, dir := range filepath.SplitList(path) { 87 | if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { 88 | return f, nil 89 | } 90 | } 91 | return "", &Error{file, ErrNotFound} 92 | } 93 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/internal/textutil/diff.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package textutil 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // Diff returns a formatted diff of the two texts, 13 | // showing the entire text and the minimum line-level 14 | // additions and removals to turn text1 into text2. 15 | // (That is, lines only in text1 appear with a leading -, 16 | // and lines only in text2 appear with a leading +.) 17 | func Diff(text1, text2 string) string { 18 | if text1 != "" && !strings.HasSuffix(text1, "\n") { 19 | text1 += "(missing final newline)" 20 | } 21 | lines1 := strings.Split(text1, "\n") 22 | lines1 = lines1[:len(lines1)-1] // remove empty string after final line 23 | if text2 != "" && !strings.HasSuffix(text2, "\n") { 24 | text2 += "(missing final newline)" 25 | } 26 | lines2 := strings.Split(text2, "\n") 27 | lines2 = lines2[:len(lines2)-1] // remove empty string after final line 28 | 29 | // Naive dynamic programming algorithm for edit distance. 30 | // https://en.wikipedia.org/wiki/Wagner–Fischer_algorithm 31 | // dist[i][j] = edit distance between lines1[:len(lines1)-i] and lines2[:len(lines2)-j] 32 | // (The reversed indices make following the minimum cost path 33 | // visit lines in the same order as in the text.) 34 | dist := make([][]int, len(lines1)+1) 35 | for i := range dist { 36 | dist[i] = make([]int, len(lines2)+1) 37 | if i == 0 { 38 | for j := range dist[0] { 39 | dist[0][j] = j 40 | } 41 | continue 42 | } 43 | for j := range dist[i] { 44 | if j == 0 { 45 | dist[i][0] = i 46 | continue 47 | } 48 | cost := dist[i][j-1] + 1 49 | if cost > dist[i-1][j]+1 { 50 | cost = dist[i-1][j] + 1 51 | } 52 | if lines1[len(lines1)-i] == lines2[len(lines2)-j] { 53 | if cost > dist[i-1][j-1] { 54 | cost = dist[i-1][j-1] 55 | } 56 | } 57 | dist[i][j] = cost 58 | } 59 | } 60 | 61 | var buf strings.Builder 62 | i, j := len(lines1), len(lines2) 63 | for i > 0 || j > 0 { 64 | cost := dist[i][j] 65 | if i > 0 && j > 0 && cost == dist[i-1][j-1] && lines1[len(lines1)-i] == lines2[len(lines2)-j] { 66 | fmt.Fprintf(&buf, " %s\n", lines1[len(lines1)-i]) 67 | i-- 68 | j-- 69 | } else if i > 0 && cost == dist[i-1][j]+1 { 70 | fmt.Fprintf(&buf, "-%s\n", lines1[len(lines1)-i]) 71 | i-- 72 | } else { 73 | fmt.Fprintf(&buf, "+%s\n", lines2[len(lines2)-j]) 74 | j-- 75 | } 76 | } 77 | return buf.String() 78 | } 79 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/imports/scan.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package imports 6 | 7 | import ( 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "path/filepath" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) { 18 | infos, err := ioutil.ReadDir(dir) 19 | if err != nil { 20 | return nil, nil, err 21 | } 22 | var files []string 23 | for _, info := range infos { 24 | name := info.Name() 25 | if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) { 26 | files = append(files, filepath.Join(dir, name)) 27 | } 28 | } 29 | return scanFiles(files, tags, false) 30 | } 31 | 32 | func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) { 33 | return scanFiles(files, tags, true) 34 | } 35 | 36 | func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) { 37 | imports := make(map[string]bool) 38 | testImports := make(map[string]bool) 39 | numFiles := 0 40 | Files: 41 | for _, name := range files { 42 | r, err := os.Open(name) 43 | if err != nil { 44 | return nil, nil, err 45 | } 46 | var list []string 47 | data, err := ReadImports(r, false, &list) 48 | r.Close() 49 | if err != nil { 50 | return nil, nil, fmt.Errorf("reading %s: %v", name, err) 51 | } 52 | 53 | // import "C" is implicit requirement of cgo tag. 54 | // When listing files on the command line (explicitFiles=true) 55 | // we do not apply build tag filtering but we still do apply 56 | // cgo filtering, so no explicitFiles check here. 57 | // Why? Because we always have, and it's not worth breaking 58 | // that behavior now. 59 | for _, path := range list { 60 | if path == `"C"` && !tags["cgo"] && !tags["*"] { 61 | continue Files 62 | } 63 | } 64 | 65 | if !explicitFiles && !ShouldBuild(data, tags) { 66 | continue 67 | } 68 | numFiles++ 69 | m := imports 70 | if strings.HasSuffix(name, "_test.go") { 71 | m = testImports 72 | } 73 | for _, p := range list { 74 | q, err := strconv.Unquote(p) 75 | if err != nil { 76 | continue 77 | } 78 | m[q] = true 79 | } 80 | } 81 | if numFiles == 0 { 82 | return nil, nil, ErrNoGo 83 | } 84 | return keys(imports), keys(testImports), nil 85 | } 86 | 87 | var ErrNoGo = fmt.Errorf("no Go source files") 88 | 89 | func keys(m map[string]bool) []string { 90 | var list []string 91 | for k := range m { 92 | list = append(list, k) 93 | } 94 | sort.Strings(list) 95 | return list 96 | } 97 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/errors/struct.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "runtime" 4 | 5 | // errorInfo holds a description of an error along with information about 6 | // where the error was created. 7 | // 8 | // It may be embedded in custom error types to add 9 | // extra information that this errors package can 10 | // understand. 11 | type errorInfo struct { 12 | // message holds the text of the error message. It may be empty 13 | // if underlying is set. 14 | message string 15 | 16 | // cause holds the cause of the error as returned 17 | // by the Cause method. 18 | cause error 19 | 20 | // underlying holds the underlying error, if any. 21 | underlying error 22 | 23 | // callerPC holds the program counter of the calling 24 | // code that created the error. For some reason, a single 25 | // caller PC is not enough to allow CallersFrames 26 | // to extract a single source location. 27 | callerPC [2]uintptr 28 | } 29 | 30 | func newError(underlying, cause error, msg string) error { 31 | err := &errorInfo{ 32 | underlying: underlying, 33 | cause: cause, 34 | message: msg, 35 | } 36 | // Skip two frames as we're interested in the caller of the 37 | // caller of newError. 38 | err.setLocation(2) 39 | return err 40 | } 41 | 42 | // Underlying returns the underlying error if any. 43 | func (e *errorInfo) Underlying() error { 44 | return e.underlying 45 | } 46 | 47 | // Cause implements Causer. 48 | func (e *errorInfo) Cause() error { 49 | return e.cause 50 | } 51 | 52 | // Message returns the top level error message. 53 | func (e *errorInfo) Message() string { 54 | return e.message 55 | } 56 | 57 | // Error implements error.Error. 58 | func (e *errorInfo) Error() string { 59 | switch { 60 | case e.message == "" && e.underlying == nil: 61 | return "" 62 | case e.message == "": 63 | return e.underlying.Error() 64 | case e.underlying == nil: 65 | return e.message 66 | } 67 | return e.message + ": " + e.underlying.Error() 68 | } 69 | 70 | // GoString returns the details of the receiving error message, so that 71 | // printing an error with %#v will produce useful information. 72 | func (e *errorInfo) GoString() string { 73 | return Details(e) 74 | } 75 | 76 | func (e *errorInfo) setLocation(callDepth int) { 77 | // Technically this might not be correct in the future 78 | // because with mid-stack inlining, the skip count 79 | // might not directly correspond to the number of 80 | // frames to skip. 81 | // 82 | // If this fails, we'll leave the location as is, 83 | // which seems reasonable. 84 | var callerPC [2]uintptr 85 | if runtime.Callers(callDepth+2, callerPC[:]) == len(callerPC) { 86 | e.callerPC = callerPC 87 | } 88 | } 89 | 90 | // Location implements Locator. 91 | func (e *errorInfo) Location() (file string, line int) { 92 | frames := runtime.CallersFrames(e.callerPC[:]) 93 | frame, ok := frames.Next() 94 | if ok { 95 | return frame.File, frame.Line 96 | } 97 | return "", 0 98 | } 99 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/fmt/errors/alias.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "gopkg.in/errgo.v2/errors" 5 | ) 6 | 7 | type ( 8 | Causer = errors.Causer 9 | Wrapper = errors.Wrapper 10 | Locator = errors.Locator 11 | ) 12 | 13 | // Deprecated: Locationer is the old name for Locator, 14 | // kept for backward compatibility only. 15 | type Locationer = errors.Locationer 16 | 17 | // New returns a new error with the given error message and no cause. It 18 | // is a drop-in replacement for errors.New from the standard library. 19 | func New(s string) error { 20 | err := errors.New(s) 21 | errors.SetLocation(err, 1) 22 | return err 23 | } 24 | 25 | // Note returns a new error that wraps the given error 26 | // and holds information about the caller. It preserves the cause if 27 | // shouldPreserveCause is non-nil and shouldPreserveCause(err) is true. If msg is 28 | // non-empty, it is used to prefix the returned error's string value. 29 | // 30 | // If err is nil, Note returns nil without calling shouldPreserveCause. 31 | func Note(err error, shouldPreserveCause func(error) bool, msg string) error { 32 | err = errors.Note(err, shouldPreserveCause, msg) 33 | errors.SetLocation(err, 1) 34 | return err 35 | } 36 | 37 | // Because returns a new error that wraps err and has the given cause, 38 | // and adds the given message. 39 | // 40 | // If err is nil and msg is empty, the returned error's message will be 41 | // the same as cause's. This is equivalent to calling errors.Note(cause, 42 | // errors.Is(cause), "") and is useful for annotating an global error 43 | // value with source location information. 44 | // 45 | // Because returns a nil error if all of err, cause and msg are 46 | // zero. 47 | func Because(err, cause error, msg string) error { 48 | err = errors.Because(err, cause, msg) 49 | errors.SetLocation(err, 1) 50 | return err 51 | } 52 | 53 | // Wrap returns a new error that wraps the given error and holds 54 | // information about the caller. It is equivalent to Note(err, nil, ""). 55 | // Note that this means that the returned error has no cause. 56 | func Wrap(err error) error { 57 | err = errors.Wrap(err) 58 | errors.SetLocation(err, 1) 59 | return err 60 | } 61 | 62 | // Cause returns the cause of the given error. If err does not 63 | // implement Causer or its Cause method returns nil, it returns err itself. 64 | // 65 | // Cause is the usual way to diagnose errors that may have 66 | // been wrapped. 67 | func Cause(err error) error { 68 | return errors.Cause(err) 69 | } 70 | 71 | // SetLocation sets the location of the error to the file and line number 72 | // of the code that is running callDepth frames above 73 | // the caller. It does nothing if the error was not created 74 | // by this package. SetLocation should only be called 75 | // mmediately after an error has been created, before 76 | // it is shared with other code which could access it 77 | // concurrently. 78 | func SetLocation(err error, callDepth int) { 79 | errors.SetLocation(err, callDepth+1) 80 | } 81 | 82 | // Details returns information about the stack of 83 | // underlying errors wrapped by err, in the format: 84 | // 85 | // [{filename:99: error one} {otherfile:55: cause of error one}] 86 | // 87 | // The details are found by type-asserting the error to 88 | // the Locator, Causer and Wrapper interfaces. 89 | // Details of the underlying stack are found by 90 | // recursively calling Underlying when the 91 | // underlying error implements Wrapper. 92 | func Details(err error) string { 93 | return errors.Details(err) 94 | } 95 | 96 | // Is returns a function that returns whether the 97 | // an error is equal to the given error. 98 | // It is intended to be used as a "shouldPreserveCause" argument 99 | // to Note. For example: 100 | // 101 | // return errgo.Note(err, errgo.Is(http.ErrNoCookie), "") 102 | // 103 | // would return an error with an http.ErrNoCookie cause 104 | // only if that was err's cause. 105 | func Is(err error) func(error) bool { 106 | return errors.Is(err) 107 | } 108 | 109 | // Any returns true. It can be used as an argument to Note 110 | // to allow any cause to pass through to the wrapped 111 | // error. 112 | func Any(err error) bool { 113 | return errors.Any(err) 114 | } 115 | -------------------------------------------------------------------------------- /script_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "runtime" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/rogpeppe/go-internal/goproxytest" 13 | "github.com/rogpeppe/go-internal/gotooltest" 14 | "github.com/rogpeppe/go-internal/testscript" 15 | ) 16 | 17 | var ( 18 | proxyURL string 19 | ) 20 | 21 | func TestMain(m *testing.M) { 22 | os.Exit(testscript.RunMain(gobinMain{m}, map[string]func() int{ 23 | "gobin": main1, 24 | })) 25 | } 26 | 27 | type gobinMain struct { 28 | m *testing.M 29 | } 30 | 31 | func (m gobinMain) Run() int { 32 | // Start the Go proxy server running for all tests. 33 | srv, err := goproxytest.NewServer("testdata/mod", "") 34 | if err != nil { 35 | fmt.Fprintf(os.Stderr, "cannot start proxy: %v", err) 36 | return 1 37 | } 38 | proxyURL = srv.URL 39 | 40 | return m.m.Run() 41 | } 42 | 43 | func TestExitCode(t *testing.T) { 44 | var err error 45 | self, err := os.Executable() 46 | if err != nil { 47 | t.Fatalf("failed to determine os.Executable: %v", err) 48 | } 49 | 50 | temp, err := ioutil.TempDir("", "gobin-exitcode-test") 51 | if err != nil { 52 | t.Fatalf("failed to create temp directory for home: %v", err) 53 | } 54 | defer os.RemoveAll(temp) 55 | 56 | cmd := exec.Command(self, "-run", "example.com/fail") 57 | cmd.Env = os.Environ() 58 | cmd.Env = append(cmd.Env, homeEnv(temp)...) 59 | cmd.Env = append(cmd.Env, 60 | "GONOSUMDB=*", 61 | "GOPROXY="+proxyURL, 62 | "TESTSCRIPT_COMMAND=gobin", 63 | 64 | // We zero GOPATH here so that we definitely use the default 65 | // location of $HOME (with the new $HOME) 66 | "GOPATH=", 67 | ) 68 | 69 | out, err := cmd.CombinedOutput() 70 | fmt.Printf(">> %s\n", out) 71 | if err == nil { 72 | t.Fatalf("unexpected success") 73 | } 74 | ee, ok := err.(*exec.ExitError) 75 | if !ok { 76 | t.Fatalf("expected *exec.ExitError; got %T: %v", err, err) 77 | } 78 | want := 42 79 | if got := ExitCode(ee.ProcessState); want != got { 80 | t.Fatalf("expected exit code %v; got %v; output was: %s", want, got, out) 81 | } 82 | } 83 | 84 | func TestScripts(t *testing.T) { 85 | var ( 86 | pathToMod string // local path to this module 87 | modTestGOPATH string // GOPATH set when running tests in this module 88 | ) 89 | 90 | // set pathToMod 91 | wd, err := os.Getwd() 92 | if err != nil { 93 | t.Fatalf("failed to get working directory for test: %v", err) 94 | } 95 | pathToMod = wd 96 | 97 | // set modTestGOPATH 98 | cmd := exec.Command("go", "env", "GOPATH") 99 | out, err := cmd.Output() 100 | if err != nil { 101 | var stderr []byte 102 | if err, ok := err.(*exec.ExitError); ok { 103 | stderr = err.Stderr 104 | } 105 | t.Fatalf("failed to get GOPATH for test: %v\n%s", err, stderr) 106 | } 107 | modTestGOPATH = strings.TrimSpace(string(out)) 108 | 109 | ucd, err := os.UserCacheDir() 110 | if err != nil { 111 | t.Fatalf("failed to get os.UserCacheDir: %v", err) 112 | } 113 | 114 | p := testscript.Params{ 115 | Dir: "testdata", 116 | Setup: func(e *testscript.Env) error { 117 | // TODO feels like this could be a method on testscript.Env? 118 | getEnv := func(s string) string { 119 | cmp := s + "=" 120 | for i := len(e.Vars) - 1; i >= 0; i-- { 121 | v := e.Vars[i] 122 | if strings.HasPrefix(v, cmp) { 123 | return strings.TrimPrefix(v, cmp) 124 | } 125 | } 126 | return "" 127 | } 128 | 129 | wd := getEnv("WORK") 130 | 131 | e.Vars = append(e.Vars, 132 | "TESTGOPATH="+modTestGOPATH, 133 | "GOBINMODPATH="+pathToMod, 134 | "GONOSUMDB=*", 135 | "GOPROXY="+proxyURL, 136 | "USERCACHEDIR="+ucd, 137 | ) 138 | 139 | e.Vars = append(e.Vars, homeEnv(wd)...) 140 | 141 | return nil 142 | }, 143 | } 144 | if err := gotooltest.Setup(&p); err != nil { 145 | t.Fatal(err) 146 | } 147 | testscript.Run(t, p) 148 | } 149 | 150 | func homeEnv(base string) []string { 151 | if runtime.GOOS == "windows" { 152 | return []string{ 153 | "USERPROFILE=" + base + "\\home", 154 | "LOCALAPPDATA=" + base + "\\appdata", 155 | "HOME=" + base + "\\home", // match USERPROFILE 156 | } 157 | } else { 158 | return []string{"HOME=" + base + "/home"} 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/par/work.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package par implements parallel execution helpers. 6 | package par 7 | 8 | import ( 9 | "math/rand" 10 | "sync" 11 | "sync/atomic" 12 | ) 13 | 14 | // Work manages a set of work items to be executed in parallel, at most once each. 15 | // The items in the set must all be valid map keys. 16 | type Work struct { 17 | f func(interface{}) // function to run for each item 18 | running int // total number of runners 19 | 20 | mu sync.Mutex 21 | added map[interface{}]bool // items added to set 22 | todo []interface{} // items yet to be run 23 | wait sync.Cond // wait when todo is empty 24 | waiting int // number of runners waiting for todo 25 | } 26 | 27 | func (w *Work) init() { 28 | if w.added == nil { 29 | w.added = make(map[interface{}]bool) 30 | } 31 | } 32 | 33 | // Add adds item to the work set, if it hasn't already been added. 34 | func (w *Work) Add(item interface{}) { 35 | w.mu.Lock() 36 | w.init() 37 | if !w.added[item] { 38 | w.added[item] = true 39 | w.todo = append(w.todo, item) 40 | if w.waiting > 0 { 41 | w.wait.Signal() 42 | } 43 | } 44 | w.mu.Unlock() 45 | } 46 | 47 | // Do runs f in parallel on items from the work set, 48 | // with at most n invocations of f running at a time. 49 | // It returns when everything added to the work set has been processed. 50 | // At least one item should have been added to the work set 51 | // before calling Do (or else Do returns immediately), 52 | // but it is allowed for f(item) to add new items to the set. 53 | // Do should only be used once on a given Work. 54 | func (w *Work) Do(n int, f func(item interface{})) { 55 | if n < 1 { 56 | panic("par.Work.Do: n < 1") 57 | } 58 | if w.running >= 1 { 59 | panic("par.Work.Do: already called Do") 60 | } 61 | 62 | w.running = n 63 | w.f = f 64 | w.wait.L = &w.mu 65 | 66 | for i := 0; i < n-1; i++ { 67 | go w.runner() 68 | } 69 | w.runner() 70 | } 71 | 72 | // runner executes work in w until both nothing is left to do 73 | // and all the runners are waiting for work. 74 | // (Then all the runners return.) 75 | func (w *Work) runner() { 76 | for { 77 | // Wait for something to do. 78 | w.mu.Lock() 79 | for len(w.todo) == 0 { 80 | w.waiting++ 81 | if w.waiting == w.running { 82 | // All done. 83 | w.wait.Broadcast() 84 | w.mu.Unlock() 85 | return 86 | } 87 | w.wait.Wait() 88 | w.waiting-- 89 | } 90 | 91 | // Pick something to do at random, 92 | // to eliminate pathological contention 93 | // in case items added at about the same time 94 | // are most likely to contend. 95 | i := rand.Intn(len(w.todo)) 96 | item := w.todo[i] 97 | w.todo[i] = w.todo[len(w.todo)-1] 98 | w.todo = w.todo[:len(w.todo)-1] 99 | w.mu.Unlock() 100 | 101 | w.f(item) 102 | } 103 | } 104 | 105 | // Cache runs an action once per key and caches the result. 106 | type Cache struct { 107 | m sync.Map 108 | } 109 | 110 | type cacheEntry struct { 111 | done uint32 112 | mu sync.Mutex 113 | result interface{} 114 | } 115 | 116 | // Do calls the function f if and only if Do is being called for the first time with this key. 117 | // No call to Do with a given key returns until the one call to f returns. 118 | // Do returns the value returned by the one call to f. 119 | func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { 120 | entryIface, ok := c.m.Load(key) 121 | if !ok { 122 | entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) 123 | } 124 | e := entryIface.(*cacheEntry) 125 | if atomic.LoadUint32(&e.done) == 0 { 126 | e.mu.Lock() 127 | if atomic.LoadUint32(&e.done) == 0 { 128 | e.result = f() 129 | atomic.StoreUint32(&e.done, 1) 130 | } 131 | e.mu.Unlock() 132 | } 133 | return e.result 134 | } 135 | 136 | // Get returns the cached result associated with key. 137 | // It returns nil if there is no such result. 138 | // If the result for key is being computed, Get does not wait for the computation to finish. 139 | func (c *Cache) Get(key interface{}) interface{} { 140 | entryIface, ok := c.m.Load(key) 141 | if !ok { 142 | return nil 143 | } 144 | e := entryIface.(*cacheEntry) 145 | if atomic.LoadUint32(&e.done) == 0 { 146 | return nil 147 | } 148 | return e.result 149 | } 150 | -------------------------------------------------------------------------------- /help.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | ) 8 | 9 | func mainUsage(f io.Writer) { 10 | t := template.Must(template.New("").Parse(mainHelpTemplate)) 11 | if err := t.Execute(f, nil); err != nil { 12 | fmt.Fprintf(f, "cannot write usage output: %v", err) 13 | } 14 | } 15 | 16 | var mainHelpTemplate = ` 17 | The gobin command installs/runs main packages. 18 | 19 | Usage: 20 | gobin [-m] [-run|-p|-v|-d] [-u|-nonet] [-tags 'tag list'] packages [run arguments...] 21 | 22 | The gobin command builds, installs, and possibly runs an executable binary for 23 | each of the named main packages. 24 | 25 | The packages argument to gobin is similar to that of the go get command (in 26 | module aware mode) with the additional constraint that the list of packages 27 | must be main packages. Each argument takes the form $main_pkg[@$version]. 28 | 29 | By default, gobin will use the main package's module to resolve its 30 | dependencies, unless the -m flag is specified, in which case dependencies will 31 | be resolved using the main module (as given by go env GOMOD). 32 | 33 | The -mod flag provides additional control over updating and use of go.mod when 34 | using the main module to resolve dependencies. If the -mod flag is provided it 35 | implies -m. With -mod=readonly, gobin is disallowed from any implicit updating 36 | of go.mod. Instead, it fails when any changes to go.mod are needed. With 37 | -mod=vendor, gobin assumes that the vendor directory holds the correct copies 38 | of dependencies and ignores the dependency descriptions in go.mod 39 | 40 | This means that gobin $package@v1.2.3 is a repeatable way to install an exact 41 | version of a binary (assuming it has an associated go.mod file). 42 | 43 | The version "latest" matches the latest available tagged version for the module 44 | containing the main package. If gobin is able to resolve "latest" within the 45 | module download cache it will use that version. Otherwise, gobin will make a 46 | network request to resolve "latest". The -u flag forces gobin to check the 47 | network for the latest tagged version. If the -nonet flag is provided, gobin 48 | will only check the module download cache. Hence, the -u and -nonet flags are 49 | mutually exclusive. 50 | 51 | Versions that take the form of a revision identifier (a branch name, for 52 | example) can only be resolved with a network request and hence are incompatible 53 | with -nonet. 54 | 55 | If no version is specified for a main package, gobin behaves differently 56 | depending on whether the -m flag is provided. If the -m flag is not provided, 57 | gobin $module is equivalent to gobin $module@latest. If the -m flag is 58 | provided, gobin attempts to resolve the current version via the main module's 59 | go.mod; if this resolution fails, "latest" is assumed as the version. 60 | 61 | By default, gobin installs the main packages to $GOBIN (or $GOPATH/bin if GOBIN 62 | is not set, which defaults to $HOME/go/bin if GOPATH is not set). 63 | 64 | The -run flag takes exactly one main package argument and runs that package. 65 | It is similar therefore to go run. Any arguments after the single main package 66 | will be passed to the main package as command line arguments. 67 | 68 | The -p flag prints the gobin cache path for each of the packages' executables 69 | once versions have been resolved. 70 | 71 | The -v flag prints the module path and version for each of the packages. Each 72 | line in the output has two space-separated fields: a module path and a version. 73 | 74 | The -d flag instructs gobin to stop after installing the packages to the gobin 75 | cache; that is, it instructs gobin not to install, run or print the packages. 76 | 77 | The -run, -p, -v and -d flags are mutually exclusive. 78 | 79 | The -tags flag is identical to the cmd/go build flag (see go help build). It is 80 | a space-separated list of build tags to consider satisfied during the build. 81 | Alternatively, GOFLAGS can be set to include a value for -tags (see go help 82 | environment). 83 | 84 | It is an error for a non-main package to be provided as a package argument. 85 | 86 | 87 | Cache directories 88 | ================= 89 | 90 | gobin maintains a cache of executables, separate from any executables that may 91 | be installed to $GOBIN. 92 | 93 | By default, gobin uses the directories gobin/$module@$version/$main_pkg under 94 | your user cache directory. See the documentation for os.UserCacheDir for 95 | OS-specific details on how to configure its location. 96 | 97 | When the -m flag is provided, gobin uses the directories 98 | .gobincache/$module@$version/$main_pkg under the directory containing the main 99 | module's go.mod. 100 | 101 | `[1:] 102 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/gotooltest/setup.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gotooltest implements functionality useful for testing 6 | // tools that use the go command. 7 | package gotooltest 8 | 9 | import ( 10 | "bytes" 11 | "encoding/json" 12 | "fmt" 13 | "go/build" 14 | "os/exec" 15 | "path/filepath" 16 | "regexp" 17 | "runtime" 18 | "strings" 19 | "sync" 20 | 21 | "github.com/rogpeppe/go-internal/testscript" 22 | ) 23 | 24 | var ( 25 | goVersionRegex = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) 26 | 27 | goEnv struct { 28 | GOROOT string 29 | GOCACHE string 30 | GOPROXY string 31 | goversion string 32 | releaseTags []string 33 | once sync.Once 34 | err error 35 | } 36 | ) 37 | 38 | // initGoEnv initialises goEnv. It should only be called using goEnv.once.Do, 39 | // as in Setup. 40 | func initGoEnv() error { 41 | var err error 42 | 43 | run := func(args ...string) (*bytes.Buffer, *bytes.Buffer, error) { 44 | var stdout, stderr bytes.Buffer 45 | cmd := exec.Command(args[0], args[1:]...) 46 | cmd.Stdout = &stdout 47 | cmd.Stderr = &stderr 48 | return &stdout, &stderr, cmd.Run() 49 | } 50 | 51 | lout, stderr, err := run("go", "list", "-f={{context.ReleaseTags}}", "runtime") 52 | if err != nil { 53 | return fmt.Errorf("failed to determine release tags from go command: %v\n%v", err, stderr.String()) 54 | } 55 | tagStr := strings.TrimSpace(lout.String()) 56 | tagStr = strings.Trim(tagStr, "[]") 57 | goEnv.releaseTags = strings.Split(tagStr, " ") 58 | 59 | eout, stderr, err := run("go", "env", "-json", 60 | "GOROOT", 61 | "GOCACHE", 62 | "GOPROXY", 63 | ) 64 | if err != nil { 65 | return fmt.Errorf("failed to determine environment from go command: %v\n%v", err, stderr) 66 | } 67 | if err := json.Unmarshal(eout.Bytes(), &goEnv); err != nil { 68 | return fmt.Errorf("failed to unmarshal GOROOT and GOCACHE tags from go command out: %v\n%v", err, eout) 69 | } 70 | 71 | version := goEnv.releaseTags[len(goEnv.releaseTags)-1] 72 | if !goVersionRegex.MatchString(version) { 73 | return fmt.Errorf("invalid go version %q", version) 74 | } 75 | goEnv.goversion = version[2:] 76 | 77 | return nil 78 | } 79 | 80 | // Setup sets up the given test environment for tests that use the go 81 | // command. It adds support for go tags to p.Condition and adds the go 82 | // command to p.Cmds. It also wraps p.Setup to set up the environment 83 | // variables for running the go command appropriately. 84 | // 85 | // It checks go command can run, but not that it can build or run 86 | // binaries. 87 | func Setup(p *testscript.Params) error { 88 | goEnv.once.Do(func() { 89 | goEnv.err = initGoEnv() 90 | }) 91 | if goEnv.err != nil { 92 | return goEnv.err 93 | } 94 | 95 | origSetup := p.Setup 96 | p.Setup = func(e *testscript.Env) error { 97 | e.Vars = goEnviron(e.Vars) 98 | if origSetup != nil { 99 | return origSetup(e) 100 | } 101 | return nil 102 | } 103 | if p.Cmds == nil { 104 | p.Cmds = make(map[string]func(ts *testscript.TestScript, neg bool, args []string)) 105 | } 106 | p.Cmds["go"] = cmdGo 107 | origCondition := p.Condition 108 | p.Condition = func(cond string) (bool, error) { 109 | if cond == "gc" || cond == "gccgo" { 110 | // TODO this reflects the compiler that the current 111 | // binary was built with but not necessarily the compiler 112 | // that will be used. 113 | return cond == runtime.Compiler, nil 114 | } 115 | if goVersionRegex.MatchString(cond) { 116 | for _, v := range build.Default.ReleaseTags { 117 | if cond == v { 118 | return true, nil 119 | } 120 | } 121 | return false, nil 122 | } 123 | if origCondition == nil { 124 | return false, fmt.Errorf("unknown condition %q", cond) 125 | } 126 | return origCondition(cond) 127 | } 128 | return nil 129 | } 130 | 131 | func goEnviron(env0 []string) []string { 132 | env := environ(env0) 133 | workdir := env.get("WORK") 134 | return append(env, []string{ 135 | "GOPATH=" + filepath.Join(workdir, "gopath"), 136 | "CCACHE_DISABLE=1", // ccache breaks with non-existent HOME 137 | "GOARCH=" + runtime.GOARCH, 138 | "GOOS=" + runtime.GOOS, 139 | "GOROOT=" + goEnv.GOROOT, 140 | "GOCACHE=" + goEnv.GOCACHE, 141 | "GOPROXY=" + goEnv.GOPROXY, 142 | "goversion=" + goEnv.goversion, 143 | }...) 144 | } 145 | 146 | func cmdGo(ts *testscript.TestScript, neg bool, args []string) { 147 | if len(args) < 1 { 148 | ts.Fatalf("usage: go subcommand ...") 149 | } 150 | err := ts.Exec("go", args...) 151 | if err != nil { 152 | ts.Logf("[%v]\n", err) 153 | if !neg { 154 | ts.Fatalf("unexpected go command failure") 155 | } 156 | } else { 157 | if neg { 158 | ts.Fatalf("unexpected go command success") 159 | } 160 | } 161 | } 162 | 163 | type environ []string 164 | 165 | func (e0 *environ) get(name string) string { 166 | e := *e0 167 | for i := len(e) - 1; i >= 0; i-- { 168 | v := e[i] 169 | if len(v) <= len(name) { 170 | continue 171 | } 172 | if strings.HasPrefix(v, name) && v[len(name)] == '=' { 173 | return v[len(name)+1:] 174 | } 175 | } 176 | return "" 177 | } 178 | 179 | func (e *environ) set(name, val string) { 180 | *e = append(*e, name+"="+val) 181 | } 182 | 183 | func (e *environ) unset(name string) { 184 | // TODO actually remove the name from the environment. 185 | e.set(name, "") 186 | } 187 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/cmd/txtar-addmod/addmod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The txtar-addmod command adds a module as a txtar archive to the a testdata module directory 6 | // as understood by the goproxytest package (see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest). 7 | // 8 | // Usage: 9 | // 10 | // txtar-addmod dir path@version... 11 | // 12 | // where dir is the directory to add the module to. 13 | // 14 | // In general, it's intended to be used only for very small modules - we do not want to check 15 | // very large files into testdata/mod. 16 | // 17 | // It is acceptable to edit the archive afterward to remove or shorten files. 18 | // 19 | package main 20 | 21 | import ( 22 | "bytes" 23 | "flag" 24 | "fmt" 25 | "io/ioutil" 26 | "log" 27 | "os" 28 | "os/exec" 29 | "path/filepath" 30 | "strings" 31 | 32 | "github.com/rogpeppe/go-internal/module" 33 | "github.com/rogpeppe/go-internal/txtar" 34 | ) 35 | 36 | func usage() { 37 | fmt.Fprintf(os.Stderr, "usage: txtar-addmod dir path@version...\n") 38 | flag.PrintDefaults() 39 | 40 | fmt.Fprintf(os.Stderr, ` 41 | The txtar-addmod command adds a module as a txtar archive to the 42 | testdata module directory as understood by the goproxytest package 43 | (see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest). 44 | 45 | The dir argument names to directory to add the module to. If dir is "-", 46 | the result will instead be written to the standard output in a form 47 | suitable for embedding directly into a testscript txtar file, with each 48 | file prefixed with the ".gomodproxy" directory. 49 | 50 | In general, txtar-addmod is intended to be used only for very small 51 | modules - we do not want to check very large files into testdata/mod. 52 | 53 | It is acceptable to edit the archive afterward to remove or shorten files. 54 | `) 55 | os.Exit(2) 56 | } 57 | 58 | var tmpdir string 59 | 60 | func fatalf(format string, args ...interface{}) { 61 | os.RemoveAll(tmpdir) 62 | log.Fatalf(format, args...) 63 | } 64 | 65 | const goCmd = "go" 66 | 67 | func main() { 68 | os.Exit(main1()) 69 | } 70 | 71 | var allFiles = flag.Bool("all", false, "include all source files") 72 | 73 | func main1() int { 74 | flag.Usage = usage 75 | flag.Parse() 76 | if flag.NArg() < 2 { 77 | usage() 78 | } 79 | targetDir := flag.Arg(0) 80 | modules := flag.Args()[1:] 81 | 82 | log.SetPrefix("txtar-addmod: ") 83 | log.SetFlags(0) 84 | 85 | var err error 86 | tmpdir, err = ioutil.TempDir("", "txtar-addmod-") 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | run := func(command string, args ...string) string { 92 | cmd := exec.Command(command, args...) 93 | cmd.Dir = tmpdir 94 | var stderr bytes.Buffer 95 | cmd.Stderr = &stderr 96 | out, err := cmd.Output() 97 | if err != nil { 98 | fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes()) 99 | } 100 | return string(out) 101 | } 102 | 103 | gopath := strings.TrimSpace(run("go", "env", "GOPATH")) 104 | if gopath == "" { 105 | fatalf("cannot find GOPATH") 106 | } 107 | 108 | exitCode := 0 109 | for _, arg := range modules { 110 | if err := ioutil.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0666); err != nil { 111 | fatalf("%v", err) 112 | } 113 | run(goCmd, "get", "-d", arg) 114 | path := arg 115 | if i := strings.Index(path, "@"); i >= 0 { 116 | path = path[:i] 117 | } 118 | out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path) 119 | f := strings.Fields(out) 120 | if len(f) != 3 { 121 | log.Printf("go list -m %s: unexpected output %q", arg, out) 122 | exitCode = 1 123 | continue 124 | } 125 | path, vers, dir := f[0], f[1], f[2] 126 | 127 | encpath, err := module.EncodePath(path) 128 | if err != nil { 129 | log.Printf("failed to encode path %q: %v", path, err) 130 | continue 131 | } 132 | path = encpath 133 | 134 | mod, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".mod")) 135 | if err != nil { 136 | log.Printf("%s: %v", arg, err) 137 | exitCode = 1 138 | continue 139 | } 140 | info, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".info")) 141 | if err != nil { 142 | log.Printf("%s: %v", arg, err) 143 | exitCode = 1 144 | continue 145 | } 146 | 147 | a := new(txtar.Archive) 148 | title := arg 149 | if !strings.Contains(arg, "@") { 150 | title += "@" + vers 151 | } 152 | dir = filepath.Clean(dir) 153 | modDir := strings.Replace(path, "/", "_", -1) + "_" + vers 154 | filePrefix := "" 155 | if targetDir == "-" { 156 | filePrefix = ".gomodproxy/" + modDir + "/" 157 | } else { 158 | // No comment if we're writing to stdout. 159 | a.Comment = []byte(fmt.Sprintf("module %s\n\n", title)) 160 | } 161 | a.Files = []txtar.File{ 162 | {Name: filePrefix + ".mod", Data: mod}, 163 | {Name: filePrefix + ".info", Data: info}, 164 | } 165 | err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 166 | if !info.Mode().IsRegular() { 167 | return nil 168 | } 169 | // TODO: skip dirs like "testdata" or "_foo" unless -all 170 | // is given? 171 | name := info.Name() 172 | switch { 173 | case *allFiles: 174 | case name == "go.mod": 175 | case strings.HasSuffix(name, ".go"): 176 | default: 177 | // the name is not in the whitelist, and we're 178 | // not including all files via -all 179 | return nil 180 | } 181 | data, err := ioutil.ReadFile(path) 182 | if err != nil { 183 | return err 184 | } 185 | a.Files = append(a.Files, txtar.File{ 186 | Name: filePrefix + strings.TrimPrefix(path, dir+string(filepath.Separator)), 187 | Data: data, 188 | }) 189 | return nil 190 | }) 191 | if err != nil { 192 | log.Printf("%s: %v", arg, err) 193 | exitCode = 1 194 | continue 195 | } 196 | 197 | data := txtar.Format(a) 198 | if targetDir == "-" { 199 | if _, err := os.Stdout.Write(data); err != nil { 200 | log.Printf("cannot write output: %v", err) 201 | exitCode = 1 202 | break 203 | } 204 | } else { 205 | if err := ioutil.WriteFile(filepath.Join(targetDir, modDir+".txt"), data, 0666); err != nil { 206 | log.Printf("%s: %v", arg, err) 207 | exitCode = 1 208 | continue 209 | } 210 | } 211 | } 212 | os.RemoveAll(tmpdir) 213 | return exitCode 214 | } 215 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/imports/build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Copied from Go distribution src/go/build/build.go, syslist.go 6 | 7 | package imports 8 | 9 | import ( 10 | "bytes" 11 | "strings" 12 | "unicode" 13 | ) 14 | 15 | var slashslash = []byte("//") 16 | 17 | // ShouldBuild reports whether it is okay to use this file, 18 | // The rule is that in the file's leading run of // comments 19 | // and blank lines, which must be followed by a blank line 20 | // (to avoid including a Go package clause doc comment), 21 | // lines beginning with '// +build' are taken as build directives. 22 | // 23 | // The file is accepted only if each such line lists something 24 | // matching the file. For example: 25 | // 26 | // // +build windows linux 27 | // 28 | // marks the file as applicable only on Windows and Linux. 29 | // 30 | // If tags["*"] is true, then ShouldBuild will consider every 31 | // build tag except "ignore" to be both true and false for 32 | // the purpose of satisfying build tags, in order to estimate 33 | // (conservatively) whether a file could ever possibly be used 34 | // in any build. 35 | // 36 | func ShouldBuild(content []byte, tags map[string]bool) bool { 37 | // Pass 1. Identify leading run of // comments and blank lines, 38 | // which must be followed by a blank line. 39 | end := 0 40 | p := content 41 | for len(p) > 0 { 42 | line := p 43 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 44 | line, p = line[:i], p[i+1:] 45 | } else { 46 | p = p[len(p):] 47 | } 48 | line = bytes.TrimSpace(line) 49 | if len(line) == 0 { // Blank line 50 | end = len(content) - len(p) 51 | continue 52 | } 53 | if !bytes.HasPrefix(line, slashslash) { // Not comment line 54 | break 55 | } 56 | } 57 | content = content[:end] 58 | 59 | // Pass 2. Process each line in the run. 60 | p = content 61 | allok := true 62 | for len(p) > 0 { 63 | line := p 64 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 65 | line, p = line[:i], p[i+1:] 66 | } else { 67 | p = p[len(p):] 68 | } 69 | line = bytes.TrimSpace(line) 70 | if !bytes.HasPrefix(line, slashslash) { 71 | continue 72 | } 73 | line = bytes.TrimSpace(line[len(slashslash):]) 74 | if len(line) > 0 && line[0] == '+' { 75 | // Looks like a comment +line. 76 | f := strings.Fields(string(line)) 77 | if f[0] == "+build" { 78 | ok := false 79 | for _, tok := range f[1:] { 80 | if matchTags(tok, tags) { 81 | ok = true 82 | } 83 | } 84 | if !ok { 85 | allok = false 86 | } 87 | } 88 | } 89 | } 90 | 91 | return allok 92 | } 93 | 94 | // matchTags reports whether the name is one of: 95 | // 96 | // tag (if tags[tag] is true) 97 | // !tag (if tags[tag] is false) 98 | // a comma-separated list of any of these 99 | // 100 | func matchTags(name string, tags map[string]bool) bool { 101 | if name == "" { 102 | return false 103 | } 104 | if i := strings.Index(name, ","); i >= 0 { 105 | // comma-separated list 106 | ok1 := matchTags(name[:i], tags) 107 | ok2 := matchTags(name[i+1:], tags) 108 | return ok1 && ok2 109 | } 110 | if strings.HasPrefix(name, "!!") { // bad syntax, reject always 111 | return false 112 | } 113 | if strings.HasPrefix(name, "!") { // negation 114 | return len(name) > 1 && matchTag(name[1:], tags, false) 115 | } 116 | return matchTag(name, tags, true) 117 | } 118 | 119 | // matchTag reports whether the tag name is valid and satisfied by tags[name]==want. 120 | func matchTag(name string, tags map[string]bool, want bool) bool { 121 | // Tags must be letters, digits, underscores or dots. 122 | // Unlike in Go identifiers, all digits are fine (e.g., "386"). 123 | for _, c := range name { 124 | if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 125 | return false 126 | } 127 | } 128 | 129 | if tags["*"] && name != "" && name != "ignore" { 130 | // Special case for gathering all possible imports: 131 | // if we put * in the tags map then all tags 132 | // except "ignore" are considered both present and not 133 | // (so we return true no matter how 'want' is set). 134 | return true 135 | } 136 | 137 | have := tags[name] 138 | if name == "linux" { 139 | have = have || tags["android"] 140 | } 141 | return have == want 142 | } 143 | 144 | // MatchFile returns false if the name contains a $GOOS or $GOARCH 145 | // suffix which does not match the current system. 146 | // The recognized name formats are: 147 | // 148 | // name_$(GOOS).* 149 | // name_$(GOARCH).* 150 | // name_$(GOOS)_$(GOARCH).* 151 | // name_$(GOOS)_test.* 152 | // name_$(GOARCH)_test.* 153 | // name_$(GOOS)_$(GOARCH)_test.* 154 | // 155 | // An exception: if GOOS=android, then files with GOOS=linux are also matched. 156 | // 157 | // If tags["*"] is true, then MatchFile will consider all possible 158 | // GOOS and GOARCH to be available and will consequently 159 | // always return true. 160 | func MatchFile(name string, tags map[string]bool) bool { 161 | if tags["*"] { 162 | return true 163 | } 164 | if dot := strings.Index(name, "."); dot != -1 { 165 | name = name[:dot] 166 | } 167 | 168 | // Before Go 1.4, a file called "linux.go" would be equivalent to having a 169 | // build tag "linux" in that file. For Go 1.4 and beyond, we require this 170 | // auto-tagging to apply only to files with a non-empty prefix, so 171 | // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 172 | // systems, such as android, to arrive without breaking existing code with 173 | // innocuous source code in "android.go". The easiest fix: cut everything 174 | // in the name before the initial _. 175 | i := strings.Index(name, "_") 176 | if i < 0 { 177 | return true 178 | } 179 | name = name[i:] // ignore everything before first _ 180 | 181 | l := strings.Split(name, "_") 182 | if n := len(l); n > 0 && l[n-1] == "test" { 183 | l = l[:n-1] 184 | } 185 | n := len(l) 186 | if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] { 187 | return tags[l[n-2]] && tags[l[n-1]] 188 | } 189 | if n >= 1 && KnownOS[l[n-1]] { 190 | return tags[l[n-1]] 191 | } 192 | if n >= 1 && KnownArch[l[n-1]] { 193 | return tags[l[n-1]] 194 | } 195 | return true 196 | } 197 | 198 | var KnownOS = make(map[string]bool) 199 | var KnownArch = make(map[string]bool) 200 | 201 | func init() { 202 | for _, v := range strings.Fields(goosList) { 203 | KnownOS[v] = true 204 | } 205 | for _, v := range strings.Fields(goarchList) { 206 | KnownArch[v] = true 207 | } 208 | } 209 | 210 | const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos " 211 | const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm " 212 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/imports/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Copied from Go distribution src/go/build/read.go. 6 | 7 | package imports 8 | 9 | import ( 10 | "bufio" 11 | "errors" 12 | "io" 13 | "unicode/utf8" 14 | ) 15 | 16 | type importReader struct { 17 | b *bufio.Reader 18 | buf []byte 19 | peek byte 20 | err error 21 | eof bool 22 | nerr int 23 | } 24 | 25 | func isIdent(c byte) bool { 26 | return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf 27 | } 28 | 29 | var ( 30 | errSyntax = errors.New("syntax error") 31 | errNUL = errors.New("unexpected NUL in input") 32 | ) 33 | 34 | // syntaxError records a syntax error, but only if an I/O error has not already been recorded. 35 | func (r *importReader) syntaxError() { 36 | if r.err == nil { 37 | r.err = errSyntax 38 | } 39 | } 40 | 41 | // readByte reads the next byte from the input, saves it in buf, and returns it. 42 | // If an error occurs, readByte records the error in r.err and returns 0. 43 | func (r *importReader) readByte() byte { 44 | c, err := r.b.ReadByte() 45 | if err == nil { 46 | r.buf = append(r.buf, c) 47 | if c == 0 { 48 | err = errNUL 49 | } 50 | } 51 | if err != nil { 52 | if err == io.EOF { 53 | r.eof = true 54 | } else if r.err == nil { 55 | r.err = err 56 | } 57 | c = 0 58 | } 59 | return c 60 | } 61 | 62 | // peekByte returns the next byte from the input reader but does not advance beyond it. 63 | // If skipSpace is set, peekByte skips leading spaces and comments. 64 | func (r *importReader) peekByte(skipSpace bool) byte { 65 | if r.err != nil { 66 | if r.nerr++; r.nerr > 10000 { 67 | panic("go/build: import reader looping") 68 | } 69 | return 0 70 | } 71 | 72 | // Use r.peek as first input byte. 73 | // Don't just return r.peek here: it might have been left by peekByte(false) 74 | // and this might be peekByte(true). 75 | c := r.peek 76 | if c == 0 { 77 | c = r.readByte() 78 | } 79 | for r.err == nil && !r.eof { 80 | if skipSpace { 81 | // For the purposes of this reader, semicolons are never necessary to 82 | // understand the input and are treated as spaces. 83 | switch c { 84 | case ' ', '\f', '\t', '\r', '\n', ';': 85 | c = r.readByte() 86 | continue 87 | 88 | case '/': 89 | c = r.readByte() 90 | if c == '/' { 91 | for c != '\n' && r.err == nil && !r.eof { 92 | c = r.readByte() 93 | } 94 | } else if c == '*' { 95 | var c1 byte 96 | for (c != '*' || c1 != '/') && r.err == nil { 97 | if r.eof { 98 | r.syntaxError() 99 | } 100 | c, c1 = c1, r.readByte() 101 | } 102 | } else { 103 | r.syntaxError() 104 | } 105 | c = r.readByte() 106 | continue 107 | } 108 | } 109 | break 110 | } 111 | r.peek = c 112 | return r.peek 113 | } 114 | 115 | // nextByte is like peekByte but advances beyond the returned byte. 116 | func (r *importReader) nextByte(skipSpace bool) byte { 117 | c := r.peekByte(skipSpace) 118 | r.peek = 0 119 | return c 120 | } 121 | 122 | // readKeyword reads the given keyword from the input. 123 | // If the keyword is not present, readKeyword records a syntax error. 124 | func (r *importReader) readKeyword(kw string) { 125 | r.peekByte(true) 126 | for i := 0; i < len(kw); i++ { 127 | if r.nextByte(false) != kw[i] { 128 | r.syntaxError() 129 | return 130 | } 131 | } 132 | if isIdent(r.peekByte(false)) { 133 | r.syntaxError() 134 | } 135 | } 136 | 137 | // readIdent reads an identifier from the input. 138 | // If an identifier is not present, readIdent records a syntax error. 139 | func (r *importReader) readIdent() { 140 | c := r.peekByte(true) 141 | if !isIdent(c) { 142 | r.syntaxError() 143 | return 144 | } 145 | for isIdent(r.peekByte(false)) { 146 | r.peek = 0 147 | } 148 | } 149 | 150 | // readString reads a quoted string literal from the input. 151 | // If an identifier is not present, readString records a syntax error. 152 | func (r *importReader) readString(save *[]string) { 153 | switch r.nextByte(true) { 154 | case '`': 155 | start := len(r.buf) - 1 156 | for r.err == nil { 157 | if r.nextByte(false) == '`' { 158 | if save != nil { 159 | *save = append(*save, string(r.buf[start:])) 160 | } 161 | break 162 | } 163 | if r.eof { 164 | r.syntaxError() 165 | } 166 | } 167 | case '"': 168 | start := len(r.buf) - 1 169 | for r.err == nil { 170 | c := r.nextByte(false) 171 | if c == '"' { 172 | if save != nil { 173 | *save = append(*save, string(r.buf[start:])) 174 | } 175 | break 176 | } 177 | if r.eof || c == '\n' { 178 | r.syntaxError() 179 | } 180 | if c == '\\' { 181 | r.nextByte(false) 182 | } 183 | } 184 | default: 185 | r.syntaxError() 186 | } 187 | } 188 | 189 | // readImport reads an import clause - optional identifier followed by quoted string - 190 | // from the input. 191 | func (r *importReader) readImport(imports *[]string) { 192 | c := r.peekByte(true) 193 | if c == '.' { 194 | r.peek = 0 195 | } else if isIdent(c) { 196 | r.readIdent() 197 | } 198 | r.readString(imports) 199 | } 200 | 201 | // ReadComments is like ioutil.ReadAll, except that it only reads the leading 202 | // block of comments in the file. 203 | func ReadComments(f io.Reader) ([]byte, error) { 204 | r := &importReader{b: bufio.NewReader(f)} 205 | r.peekByte(true) 206 | if r.err == nil && !r.eof { 207 | // Didn't reach EOF, so must have found a non-space byte. Remove it. 208 | r.buf = r.buf[:len(r.buf)-1] 209 | } 210 | return r.buf, r.err 211 | } 212 | 213 | // ReadImports is like ioutil.ReadAll, except that it expects a Go file as input 214 | // and stops reading the input once the imports have completed. 215 | func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) { 216 | r := &importReader{b: bufio.NewReader(f)} 217 | 218 | r.readKeyword("package") 219 | r.readIdent() 220 | for r.peekByte(true) == 'i' { 221 | r.readKeyword("import") 222 | if r.peekByte(true) == '(' { 223 | r.nextByte(false) 224 | for r.peekByte(true) != ')' && r.err == nil { 225 | r.readImport(imports) 226 | } 227 | r.nextByte(false) 228 | } else { 229 | r.readImport(imports) 230 | } 231 | } 232 | 233 | // If we stopped successfully before EOF, we read a byte that told us we were done. 234 | // Return all but that last byte, which would cause a syntax error if we let it through. 235 | if r.err == nil && !r.eof { 236 | return r.buf[:len(r.buf)-1], nil 237 | } 238 | 239 | // If we stopped for a syntax error, consume the whole file so that 240 | // we are sure we don't change the errors that go/parser returns. 241 | if r.err == errSyntax && !reportSyntaxError { 242 | r.err = nil 243 | for r.err == nil && !r.eof { 244 | r.readByte() 245 | } 246 | } 247 | 248 | return r.buf, r.err 249 | } 250 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/exe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package testscript 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | "sync/atomic" 14 | "testing" 15 | ) 16 | 17 | var profileId int32 = 0 18 | 19 | // TestingM is implemented by *testing.M. It's defined as an interface 20 | // to allow testscript to co-exist with other testing frameworks 21 | // that might also wish to call M.Run. 22 | type TestingM interface { 23 | Run() int 24 | } 25 | 26 | var ignoreMissedCoverage = false 27 | 28 | // IgnoreMissedCoverage causes any missed coverage information 29 | // (for example when a function passed to RunMain 30 | // calls os.Exit, for example) to be ignored. 31 | // This function should be called before calling RunMain. 32 | func IgnoreMissedCoverage() { 33 | ignoreMissedCoverage = true 34 | } 35 | 36 | // RunMain should be called within a TestMain function to allow 37 | // subcommands to be run in the testscript context. 38 | // 39 | // The commands map holds the set of command names, each 40 | // with an associated run function which should return the 41 | // code to pass to os.Exit. It's OK for a command function to 42 | // exit itself, but this may result in loss of coverage information. 43 | // 44 | // When Run is called, these commands will be available as 45 | // testscript commands; note that these commands behave like 46 | // commands run with the "exec" command: they set stdout 47 | // and stderr, and can be run in the background by passing "&" 48 | // as a final argument. 49 | // 50 | // This function returns an exit code to pass to os.Exit, after calling m.Run. 51 | func RunMain(m TestingM, commands map[string]func() int) (exitCode int) { 52 | goCoverProfileMerge() 53 | cmdName := os.Getenv("TESTSCRIPT_COMMAND") 54 | if cmdName == "" { 55 | defer func() { 56 | if err := finalizeCoverProfile(); err != nil { 57 | log.Printf("cannot merge cover profiles: %v", err) 58 | exitCode = 2 59 | } 60 | }() 61 | // We're not in a subcommand. 62 | for name := range commands { 63 | name := name 64 | scriptCmds[name] = func(ts *TestScript, neg bool, args []string) { 65 | path, err := os.Executable() 66 | if err != nil { 67 | ts.Fatalf("cannot determine path to test binary: %v", err) 68 | } 69 | id := atomic.AddInt32(&profileId, 1) - 1 70 | oldEnvLen := len(ts.env) 71 | cprof := coverFilename(id) 72 | ts.env = append(ts.env, 73 | "TESTSCRIPT_COMMAND="+name, 74 | "TESTSCRIPT_COVERPROFILE="+cprof, 75 | ) 76 | ts.cmdExec(neg, append([]string{path}, args...)) 77 | ts.env = ts.env[0:oldEnvLen] 78 | if cprof == "" { 79 | return 80 | } 81 | f, err := os.Open(cprof) 82 | if err != nil { 83 | if ignoreMissedCoverage { 84 | return 85 | } 86 | ts.Fatalf("command %s (args %q) failed to generate coverage information", name, args) 87 | return 88 | } 89 | coverChan <- f 90 | } 91 | } 92 | return m.Run() 93 | } 94 | mainf := commands[cmdName] 95 | if mainf == nil { 96 | log.Printf("unknown command name %q", cmdName) 97 | return 2 98 | } 99 | // The command being registered is being invoked, so run it, then exit. 100 | os.Args[0] = cmdName 101 | cprof := os.Getenv("TESTSCRIPT_COVERPROFILE") 102 | if cprof == "" { 103 | // No coverage, act as normal. 104 | return mainf() 105 | } 106 | return runCoverSubcommand(cprof, mainf) 107 | } 108 | 109 | // runCoverSubcommand runs the given function, then writes any generated 110 | // coverage information to the cprof file. 111 | // This is called inside a separately run executable. 112 | func runCoverSubcommand(cprof string, mainf func() int) (exitCode int) { 113 | // Change the error handling mode to PanicOnError 114 | // so that in the common case of calling flag.Parse in main we'll 115 | // be able to catch the panic instead of just exiting. 116 | flag.CommandLine.Init(flag.CommandLine.Name(), flag.PanicOnError) 117 | defer func() { 118 | panicErr := recover() 119 | if _, ok := panicErr.(error); ok { 120 | // The flag package will already have printed this error, assuming, 121 | // that is, that the error was created in the flag package. 122 | // TODO check the stack to be sure it was actually raised by the flag package. 123 | exitCode = 2 124 | panicErr = nil 125 | } 126 | // Set os.Args so that flag.Parse will tell testing the correct 127 | // coverprofile setting. Unfortunately this isn't sufficient because 128 | // the testing oackage explicitly avoids calling flag.Parse again 129 | // if flag.Parsed returns true, so we the coverprofile value directly 130 | // too. 131 | os.Args = []string{os.Args[0], "-test.coverprofile=" + cprof} 132 | setCoverProfile(cprof) 133 | 134 | // Suppress the chatty coverage and test report. 135 | devNull, err := os.Open(os.DevNull) 136 | if err != nil { 137 | panic(err) 138 | } 139 | os.Stdout = devNull 140 | os.Stderr = devNull 141 | 142 | // Run MainStart (recursively, but it we should be ok) with no tests 143 | // so that it writes the coverage profile. 144 | m := testing.MainStart(nopTestDeps{}, nil, nil, nil) 145 | if code := m.Run(); code != 0 && exitCode == 0 { 146 | exitCode = code 147 | } 148 | if _, err := os.Stat(cprof); err != nil { 149 | log.Printf("failed to write coverage profile %q", cprof) 150 | } 151 | if panicErr != nil { 152 | // The error didn't originate from the flag package (we know that 153 | // flag.PanicOnError causes an error value that implements error), 154 | // so carry on panicking. 155 | panic(panicErr) 156 | } 157 | }() 158 | return mainf() 159 | } 160 | 161 | func coverFilename(id int32) string { 162 | if cprof := coverProfile(); cprof != "" { 163 | return fmt.Sprintf("%s_%d", cprof, id) 164 | } 165 | return "" 166 | } 167 | 168 | func coverProfileFlag() flag.Getter { 169 | f := flag.CommandLine.Lookup("test.coverprofile") 170 | if f == nil { 171 | // We've imported testing so it definitely should be there. 172 | panic("cannot find test.coverprofile flag") 173 | } 174 | return f.Value.(flag.Getter) 175 | } 176 | 177 | func coverProfile() string { 178 | return coverProfileFlag().Get().(string) 179 | } 180 | 181 | func setCoverProfile(cprof string) { 182 | coverProfileFlag().Set(cprof) 183 | } 184 | 185 | type nopTestDeps struct{} 186 | 187 | func (nopTestDeps) MatchString(pat, str string) (result bool, err error) { 188 | return false, nil 189 | } 190 | 191 | func (nopTestDeps) StartCPUProfile(w io.Writer) error { 192 | return nil 193 | } 194 | 195 | func (nopTestDeps) StopCPUProfile() {} 196 | 197 | func (nopTestDeps) WriteProfileTo(name string, w io.Writer, debug int) error { 198 | return nil 199 | } 200 | func (nopTestDeps) ImportPath() string { 201 | return "" 202 | } 203 | func (nopTestDeps) StartTestLog(w io.Writer) {} 204 | 205 | func (nopTestDeps) StopTestLog() error { 206 | return nil 207 | } 208 | 209 | // Note: WriteHeapProfile is needed for Go 1.10 but not Go 1.11. 210 | func (nopTestDeps) WriteHeapProfile(io.Writer) error { 211 | // Not needed for Go 1.10. 212 | return nil 213 | } 214 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/cover.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package testscript 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | "regexp" 14 | "strconv" 15 | "strings" 16 | "sync/atomic" 17 | "testing" 18 | 19 | "gopkg.in/errgo.v2/fmt/errors" 20 | ) 21 | 22 | // mergeCoverProfile merges the coverage information in f into 23 | // cover. It assumes that the coverage information in f is 24 | // always produced from the same binary for every call. 25 | func mergeCoverProfile(cover *testing.Cover, r io.Reader) error { 26 | scanner, err := newProfileScanner(r) 27 | if err != nil { 28 | return errors.Wrap(err) 29 | } 30 | if scanner.Mode() != testing.CoverMode() { 31 | return errors.Newf("unexpected coverage mode in subcommand") 32 | } 33 | if cover.Mode == "" { 34 | cover.Mode = scanner.Mode() 35 | } 36 | isCount := cover.Mode == "count" 37 | if cover.Counters == nil { 38 | cover.Counters = make(map[string][]uint32) 39 | cover.Blocks = make(map[string][]testing.CoverBlock) 40 | } 41 | 42 | // Note that we rely on the fact that the coverage is written 43 | // out file-by-file, with all blocks for a file in sequence. 44 | var ( 45 | filename string 46 | blockId uint32 47 | counters []uint32 48 | blocks []testing.CoverBlock 49 | ) 50 | flush := func() { 51 | if len(counters) > 0 { 52 | cover.Counters[filename] = counters 53 | cover.Blocks[filename] = blocks 54 | } 55 | } 56 | for scanner.Scan() { 57 | block := scanner.Block() 58 | if scanner.Filename() != filename { 59 | flush() 60 | filename = scanner.Filename() 61 | counters = cover.Counters[filename] 62 | blocks = cover.Blocks[filename] 63 | blockId = 0 64 | } else { 65 | blockId++ 66 | } 67 | if int(blockId) >= len(counters) { 68 | counters = append(counters, block.Count) 69 | blocks = append(blocks, block.CoverBlock) 70 | continue 71 | } 72 | // TODO check that block.CoverBlock == blocks[blockId] ? 73 | if isCount { 74 | counters[blockId] += block.Count 75 | } else { 76 | counters[blockId] |= block.Count 77 | } 78 | } 79 | flush() 80 | if scanner.Err() != nil { 81 | return errors.Notef(err, nil, "error scanning profile") 82 | } 83 | return nil 84 | } 85 | 86 | var ( 87 | coverChan chan *os.File 88 | coverDone chan testing.Cover 89 | ) 90 | 91 | func goCoverProfileMerge() { 92 | if coverChan != nil { 93 | panic("RunMain called twice!") 94 | } 95 | coverChan = make(chan *os.File) 96 | coverDone = make(chan testing.Cover) 97 | go mergeCoverProfiles() 98 | } 99 | 100 | func mergeCoverProfiles() { 101 | var cover testing.Cover 102 | for f := range coverChan { 103 | if err := mergeCoverProfile(&cover, f); err != nil { 104 | log.Printf("cannot merge coverage profile from %v: %v", f.Name(), err) 105 | } 106 | f.Close() 107 | os.Remove(f.Name()) 108 | } 109 | coverDone <- cover 110 | } 111 | 112 | func finalizeCoverProfile() error { 113 | cprof := coverProfile() 114 | if cprof == "" { 115 | return nil 116 | } 117 | f, err := os.Open(cprof) 118 | if err != nil { 119 | return errors.Notef(err, nil, "cannot open existing cover profile") 120 | } 121 | coverChan <- f 122 | close(coverChan) 123 | cover := <-coverDone 124 | f, err = os.Create(cprof) 125 | if err != nil { 126 | return errors.Notef(err, nil, "cannot create cover profile") 127 | } 128 | defer f.Close() 129 | w := bufio.NewWriter(f) 130 | if err := writeCoverProfile1(w, cover); err != nil { 131 | return errors.Wrap(err) 132 | } 133 | if err := w.Flush(); err != nil { 134 | return errors.Wrap(err) 135 | } 136 | if err := f.Close(); err != nil { 137 | return errors.Wrap(err) 138 | } 139 | return nil 140 | } 141 | 142 | func writeCoverProfile1(w io.Writer, cover testing.Cover) error { 143 | fmt.Fprintf(w, "mode: %s\n", cover.Mode) 144 | var active, total int64 145 | var count uint32 146 | for name, counts := range cover.Counters { 147 | blocks := cover.Blocks[name] 148 | for i := range counts { 149 | stmts := int64(blocks[i].Stmts) 150 | total += stmts 151 | count = atomic.LoadUint32(&counts[i]) // For -mode=atomic. 152 | if count > 0 { 153 | active += stmts 154 | } 155 | _, err := fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name, 156 | blocks[i].Line0, blocks[i].Col0, 157 | blocks[i].Line1, blocks[i].Col1, 158 | stmts, 159 | count, 160 | ) 161 | if err != nil { 162 | return errors.Wrap(err) 163 | } 164 | } 165 | } 166 | if total == 0 { 167 | total = 1 168 | } 169 | fmt.Printf("total coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) 170 | return nil 171 | } 172 | 173 | type profileScanner struct { 174 | mode string 175 | err error 176 | scanner *bufio.Scanner 177 | filename string 178 | block coverBlock 179 | } 180 | 181 | type coverBlock struct { 182 | testing.CoverBlock 183 | Count uint32 184 | } 185 | 186 | var profileLineRe = regexp.MustCompile(`^(.+):([0-9]+)\.([0-9]+),([0-9]+)\.([0-9]+) ([0-9]+) ([0-9]+)$`) 187 | 188 | func toInt(s string) int { 189 | i, err := strconv.Atoi(s) 190 | if err != nil { 191 | panic(err) 192 | } 193 | return i 194 | } 195 | 196 | func newProfileScanner(r io.Reader) (*profileScanner, error) { 197 | s := &profileScanner{ 198 | scanner: bufio.NewScanner(r), 199 | } 200 | // First line is "mode: foo", where foo is "set", "count", or "atomic". 201 | // Rest of file is in the format 202 | // encoding/base64/base64.go:34.44,37.40 3 1 203 | // where the fields are: name.go:line.column,line.column numberOfStatements count 204 | if !s.scanner.Scan() { 205 | return nil, errors.Newf("no lines found in profile: %v", s.Err()) 206 | } 207 | line := s.scanner.Text() 208 | mode := strings.TrimPrefix(line, "mode: ") 209 | if len(mode) == len(line) { 210 | return nil, fmt.Errorf("bad mode line %q", line) 211 | } 212 | s.mode = mode 213 | return s, nil 214 | } 215 | 216 | // Mode returns the profile's coverage mode (one of "atomic", "count: 217 | // or "set"). 218 | func (s *profileScanner) Mode() string { 219 | return s.mode 220 | } 221 | 222 | // Err returns any error encountered when scanning a profile. 223 | func (s *profileScanner) Err() error { 224 | if s.err == io.EOF { 225 | return nil 226 | } 227 | return s.err 228 | } 229 | 230 | // Block returns the most recently scanned profile block, or the zero 231 | // block if Scan has not been called or has returned false. 232 | func (s *profileScanner) Block() coverBlock { 233 | if s.err == nil { 234 | return s.block 235 | } 236 | return coverBlock{} 237 | } 238 | 239 | // Filename returns the filename of the most recently scanned profile 240 | // block, or the empty string if Scan has not been called or has 241 | // returned false. 242 | func (s *profileScanner) Filename() string { 243 | if s.err == nil { 244 | return s.filename 245 | } 246 | return "" 247 | } 248 | 249 | // Scan scans the next line in a coverage profile and reports whether 250 | // a line was found. 251 | func (s *profileScanner) Scan() bool { 252 | if s.err != nil { 253 | return false 254 | } 255 | if !s.scanner.Scan() { 256 | s.err = io.EOF 257 | return false 258 | } 259 | m := profileLineRe.FindStringSubmatch(s.scanner.Text()) 260 | if m == nil { 261 | s.err = errors.Newf("line %q doesn't match expected format %v", m, profileLineRe) 262 | return false 263 | } 264 | s.filename = m[1] 265 | s.block = coverBlock{ 266 | CoverBlock: testing.CoverBlock{ 267 | Line0: uint32(toInt(m[2])), 268 | Col0: uint16(toInt(m[3])), 269 | Line1: uint32(toInt(m[4])), 270 | Col1: uint16(toInt(m[5])), 271 | Stmts: uint16(toInt(m[6])), 272 | }, 273 | Count: uint32(toInt(m[7])), 274 | } 275 | return true 276 | } 277 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/txtar/archive.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package txtar implements a trivial text-based file archive format. 6 | // 7 | // The goals for the format are: 8 | // 9 | // - be trivial enough to create and edit by hand. 10 | // - be able to store trees of text files describing go command test cases. 11 | // - diff nicely in git history and code reviews. 12 | // 13 | // Non-goals include being a completely general archive format, 14 | // storing binary data, storing file modes, storing special files like 15 | // symbolic links, and so on. 16 | // 17 | // Txtar format 18 | // 19 | // A txtar archive is zero or more comment lines and then a sequence of file entries. 20 | // Each file entry begins with a file marker line of the form "-- FILENAME --" 21 | // and is followed by zero or more file content lines making up the file data. 22 | // The comment or file content ends at the next file marker line. 23 | // The file marker line must begin with the three-byte sequence "-- " 24 | // and end with the three-byte sequence " --", but the enclosed 25 | // file name can be surrounding by additional white space, 26 | // all of which is stripped. 27 | // 28 | // If the txtar file is missing a trailing newline on the final line, 29 | // parsers should consider a final newline to be present anyway. 30 | // 31 | // There are no possible syntax errors in a txtar archive. 32 | package txtar 33 | 34 | import ( 35 | "bytes" 36 | "errors" 37 | "fmt" 38 | "io/ioutil" 39 | "os" 40 | "path/filepath" 41 | "strings" 42 | "unicode/utf8" 43 | ) 44 | 45 | // An Archive is a collection of files. 46 | type Archive struct { 47 | Comment []byte 48 | Files []File 49 | } 50 | 51 | // A File is a single file in an archive. 52 | type File struct { 53 | Name string // name of file ("foo/bar.txt") 54 | Data []byte // text content of file 55 | } 56 | 57 | // Format returns the serialized form of an Archive. 58 | // It is assumed that the Archive data structure is well-formed: 59 | // a.Comment and all a.File[i].Data contain no file marker lines, 60 | // and all a.File[i].Name is non-empty. 61 | func Format(a *Archive) []byte { 62 | var buf bytes.Buffer 63 | buf.Write(fixNL(a.Comment)) 64 | for _, f := range a.Files { 65 | fmt.Fprintf(&buf, "-- %s --\n", f.Name) 66 | buf.Write(fixNL(f.Data)) 67 | } 68 | return buf.Bytes() 69 | } 70 | 71 | // ParseFile parses the named file as an archive. 72 | func ParseFile(file string) (*Archive, error) { 73 | data, err := ioutil.ReadFile(file) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return Parse(data), nil 78 | } 79 | 80 | // Parse parses the serialized form of an Archive. 81 | // The returned Archive holds slices of data. 82 | func Parse(data []byte) *Archive { 83 | a := new(Archive) 84 | var name string 85 | a.Comment, name, data = findFileMarker(data) 86 | for name != "" { 87 | f := File{name, nil} 88 | f.Data, name, data = findFileMarker(data) 89 | a.Files = append(a.Files, f) 90 | } 91 | return a 92 | } 93 | 94 | // NeedsQuote reports whether the given data needs to 95 | // be quoted before it's included as a txtar file. 96 | func NeedsQuote(data []byte) bool { 97 | _, _, after := findFileMarker(data) 98 | return after != nil 99 | } 100 | 101 | // Quote quotes the data so that it can be safely stored in a txtar 102 | // file. This copes with files that contain lines that look like txtar 103 | // separators. 104 | // 105 | // The original data can be recovered with Unquote. It returns an error 106 | // if the data cannot be quoted (for example because it has no final 107 | // newline or it holds unprintable characters) 108 | func Quote(data []byte) ([]byte, error) { 109 | if len(data) == 0 { 110 | return nil, nil 111 | } 112 | if data[len(data)-1] != '\n' { 113 | return nil, errors.New("data has no final newline") 114 | } 115 | if !utf8.Valid(data) { 116 | return nil, fmt.Errorf("data contains non-UTF-8 characters") 117 | } 118 | var nd []byte 119 | prev := byte('\n') 120 | for _, b := range data { 121 | if prev == '\n' { 122 | nd = append(nd, '>') 123 | } 124 | nd = append(nd, b) 125 | prev = b 126 | } 127 | return nd, nil 128 | } 129 | 130 | // Unquote unquotes data as quoted by Quote. 131 | func Unquote(data []byte) ([]byte, error) { 132 | if len(data) == 0 { 133 | return nil, nil 134 | } 135 | if data[0] != '>' || data[len(data)-1] != '\n' { 136 | return nil, errors.New("data does not appear to be quoted") 137 | } 138 | data = bytes.Replace(data, []byte("\n>"), []byte("\n"), -1) 139 | data = bytes.TrimPrefix(data, []byte(">")) 140 | return data, nil 141 | } 142 | 143 | var ( 144 | newlineMarker = []byte("\n-- ") 145 | marker = []byte("-- ") 146 | markerEnd = []byte(" --") 147 | ) 148 | 149 | // findFileMarker finds the next file marker in data, 150 | // extracts the file name, and returns the data before the marker, 151 | // the file name, and the data after the marker. 152 | // If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil. 153 | func findFileMarker(data []byte) (before []byte, name string, after []byte) { 154 | var i int 155 | for { 156 | if name, after = isMarker(data[i:]); name != "" { 157 | return data[:i], name, after 158 | } 159 | j := bytes.Index(data[i:], newlineMarker) 160 | if j < 0 { 161 | return fixNL(data), "", nil 162 | } 163 | i += j + 1 // positioned at start of new possible marker 164 | } 165 | } 166 | 167 | // isMarker checks whether data begins with a file marker line. 168 | // If so, it returns the name from the line and the data after the line. 169 | // Otherwise it returns name == "" with an unspecified after. 170 | func isMarker(data []byte) (name string, after []byte) { 171 | if !bytes.HasPrefix(data, marker) { 172 | return "", nil 173 | } 174 | if i := bytes.IndexByte(data, '\n'); i >= 0 { 175 | data, after = data[:i], data[i+1:] 176 | if data[i-1] == '\r' { 177 | data = data[:len(data)-1] 178 | } 179 | } 180 | if !bytes.HasSuffix(data, markerEnd) { 181 | return "", nil 182 | } 183 | return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after 184 | } 185 | 186 | // If data is empty or ends in \n, fixNL returns data. 187 | // Otherwise fixNL returns a new slice consisting of data with a final \n added. 188 | func fixNL(data []byte) []byte { 189 | if len(data) == 0 || data[len(data)-1] == '\n' { 190 | return data 191 | } 192 | d := make([]byte, len(data)+1) 193 | copy(d, data) 194 | d[len(data)] = '\n' 195 | return d 196 | } 197 | 198 | // Write writes each File in an Archive to the given directory, returning any 199 | // errors encountered. An error is also returned in the event a file would be 200 | // written outside of dir. 201 | func Write(a *Archive, dir string) error { 202 | for _, f := range a.Files { 203 | fp := filepath.Clean(filepath.FromSlash(f.Name)) 204 | if isAbs(fp) || strings.HasPrefix(fp, ".."+string(filepath.Separator)) { 205 | return fmt.Errorf("%q: outside parent directory", f.Name) 206 | } 207 | fp = filepath.Join(dir, fp) 208 | 209 | if err := os.MkdirAll(filepath.Dir(fp), 0777); err != nil { 210 | return err 211 | } 212 | // Avoid overwriting existing files by using O_EXCL. 213 | out, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) 214 | if err != nil { 215 | return err 216 | } 217 | 218 | _, err = out.Write(f.Data) 219 | cerr := out.Close() 220 | if err != nil { 221 | return err 222 | } 223 | if cerr != nil { 224 | return cerr 225 | } 226 | } 227 | return nil 228 | } 229 | 230 | func isAbs(p string) bool { 231 | // Note: under Windows, filepath.IsAbs(`\foo`) returns false, 232 | // so we need to check for that case specifically. 233 | return filepath.IsAbs(p) || strings.HasPrefix(p, string(filepath.Separator)) 234 | } 235 | -------------------------------------------------------------------------------- /vendor/gopkg.in/errgo.v2/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Roger Peppe. 2 | // See LICENCE file for details. 3 | 4 | // The errors package provides a way to create and diagnose errors. It 5 | // is compatible with the usual Go error idioms but adds a way to wrap 6 | // errors so that they record source location information while 7 | // retaining a consistent way for code to inspect errors to find out 8 | // particular problems. 9 | // 10 | // An error created by this package holds three values, any one of 11 | // which (but not all) may be zero 12 | // 13 | // - an error message. This is used as part of the result of the Error method. 14 | // - an underlying error. This holds the error that the error was created in response to. 15 | // - an error cause. See below. 16 | // 17 | // Error Causes 18 | // 19 | // The "cause" of an error is something that code can use to diagnose 20 | // what an error means and take action accordingly. 21 | // 22 | // For example, if you use os.Remove to remove a file that is not there, 23 | // it returns an error with a cause that satisfies os.IsNotExist. If you 24 | // use filepath.Match to try to match a malformed pattern, it returns an 25 | // error with a cause that equals filepath.ErrBadPattern. 26 | // 27 | // When the errors package wraps an error value with another one, the 28 | // new error hides the cause by default. This is to prevent unintended 29 | // dependencies between unrelated parts of the code base. When the cause 30 | // is hidden, a caller can't write code that relies on the type or value 31 | // of a particular cause which may well only be an implementation detail 32 | // and subject to future change - a potentially API-breaking change if 33 | // the cause is not hidden. 34 | // 35 | // The Note function can be used to preserve an existing error cause. 36 | // The Because function can be used to associate an error with an 37 | // existing cause value. 38 | // 39 | // Error wrapping 40 | // 41 | // When an error is returned that "wraps" another one, the new error 42 | // records the source code location of the caller. This means that it is 43 | // possible to extract this information later (by calling Details, for 44 | // example). It is good practice to wrap errors whenever returning an 45 | // error returned by a lower level function, so that the path taken by 46 | // the error is recorded. This can make debugging significantly easier 47 | // when there are many places an error could have come from. 48 | package errors 49 | 50 | // New returns a new error with the given error message and no cause. It 51 | // is a drop-in replacement for errors.New from the standard library. 52 | func New(s string) error { 53 | return newError(nil, nil, s) 54 | } 55 | 56 | // Note returns a new error that wraps the given error. It preserves the cause if 57 | // shouldPreserveCause is non-nil and shouldPreserveCause(err) is true. If msg is 58 | // non-empty, it is used to prefix the returned error's Error value. 59 | // 60 | // If err is nil, Note returns nil and does not call shouldPreserveCause. 61 | func Note(err error, shouldPreserveCause func(error) bool, msg string) error { 62 | if err == nil { 63 | return nil 64 | } 65 | if shouldPreserveCause == nil { 66 | return newError(err, nil, msg) 67 | } 68 | if cause := Cause(err); shouldPreserveCause(cause) { 69 | return newError(err, cause, msg) 70 | } 71 | return newError(err, nil, msg) 72 | } 73 | 74 | // Because returns a new error that wraps err and has the given cause, 75 | // and adds the given message. 76 | // 77 | // If err is nil and msg is empty, the returned error's message will be 78 | // the same as cause's. This is equivalent to calling errors.Note(cause, 79 | // errors.Is(cause), "") and is useful for annotating an global error 80 | // value with source location information. 81 | // 82 | // Because returns a nil error if all of err, cause and msg are 83 | // zero. 84 | func Because(err, cause error, msg string) error { 85 | if err == nil && msg == "" { 86 | if cause == nil { 87 | return nil 88 | } 89 | msg = cause.Error() 90 | } 91 | return newError(err, cause, msg) 92 | } 93 | 94 | // Wrap returns a new error that wraps the given error and holds 95 | // information about the caller. It is equivalent to Note(err, nil, ""). 96 | // Note that this means that the returned error has no cause. 97 | func Wrap(err error) error { 98 | if err == nil { 99 | return nil 100 | } 101 | return newError(err, nil, "") 102 | } 103 | 104 | // Cause returns the cause of the given error. If err does not 105 | // implement Causer or its Cause method returns nil, it returns err itself. 106 | // 107 | // Cause is the usual way to diagnose errors that may have 108 | // been wrapped. 109 | func Cause(err error) error { 110 | if err, ok := err.(Causer); ok { 111 | if cause := err.Cause(); cause != nil { 112 | return cause 113 | } 114 | } 115 | return err 116 | } 117 | 118 | // SetLocation sets the location of the error to the file and line 119 | // number of the code that is running callDepth frames above the caller. 120 | // It does nothing if the error was not created by this package. 121 | // SetLocation should only be called immediately after an error has been 122 | // created, before it is shared with other code which could access it 123 | // concurrently. 124 | // 125 | // This function is helpful when a helper function is used to 126 | // create an error and the caller of the helper function is 127 | // the important thing, not the location in the helper function itself. 128 | func SetLocation(err error, callDepth int) { 129 | if err, ok := err.(*errorInfo); ok { 130 | err.setLocation(callDepth + 1) 131 | } 132 | } 133 | 134 | // Details returns information about the stack of underlying errors 135 | // wrapped by err, in the format: 136 | // 137 | // [ 138 | // {filename:99: error one} 139 | // {otherfile:55: cause of error one} 140 | // ] 141 | // 142 | // The details are found by type-asserting the error to the Locator, 143 | // Causer and Wrapper interfaces. Details of the underlying stack are 144 | // found by recursively calling Underlying when the underlying error 145 | // implements Wrapper. 146 | func Details(err error) string { 147 | if err == nil { 148 | return "[]" 149 | } 150 | s := make([]byte, 0, 30) 151 | s = append(s, '[') 152 | for { 153 | s = append(s, "\n\t{"...) 154 | if err, ok := err.(Locator); ok { 155 | file, line := err.Location() 156 | if file != "" { 157 | s = append(s, file...) 158 | s = append(s, ':') 159 | s = appendInt(s, line) 160 | s = append(s, ": "...) 161 | } 162 | } 163 | if cerr, ok := err.(Wrapper); ok { 164 | s = append(s, cerr.Message()...) 165 | err = cerr.Underlying() 166 | } else { 167 | s = append(s, err.Error()...) 168 | err = nil 169 | } 170 | s = append(s, '}') 171 | if err == nil { 172 | break 173 | } 174 | } 175 | s = append(s, "\n]"...) 176 | return string(s) 177 | } 178 | 179 | // Is returns a function that returns whether the an error is equal to 180 | // the given error. It is intended to be used as a "shouldPreserveCause" 181 | // argument to Note. For example: 182 | // 183 | // return errgo.Note(err, errgo.Is(http.ErrNoCookie), "") 184 | // 185 | // would return an error with an http.ErrNoCookie cause 186 | // only if that was err's cause. 187 | func Is(err error) func(error) bool { 188 | return func(err1 error) bool { 189 | return err == err1 190 | } 191 | } 192 | 193 | // Any returns true. It can be used as an argument to Note 194 | // to allow any cause to pass through to the wrapped 195 | // error. 196 | func Any(error) bool { 197 | return true 198 | } 199 | 200 | // OneOf returns a function suitable for passing to Note 201 | // that reports whether an error matches any of the 202 | // given predicates. 203 | func OneOf(predicates ...func(error) bool) func(error) bool { 204 | return func(err error) bool { 205 | for _, predicate := range predicates { 206 | if predicate(err) { 207 | return true 208 | } 209 | } 210 | return false 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testenv/testenv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package testenv provides information about what functionality 6 | // is available in different testing environments run by the Go team. 7 | // 8 | // It is an internal package because these details are specific 9 | // to the Go team's test setup (on build.golang.org) and not 10 | // fundamental to tests in general. 11 | package testenv 12 | 13 | import ( 14 | "errors" 15 | "flag" 16 | "os" 17 | "os/exec" 18 | "path/filepath" 19 | "runtime" 20 | "strconv" 21 | "strings" 22 | "testing" 23 | ) 24 | 25 | // Builder reports the name of the builder running this test 26 | // (for example, "linux-amd64" or "windows-386-gce"). 27 | // If the test is not running on the build infrastructure, 28 | // Builder returns the empty string. 29 | func Builder() string { 30 | return os.Getenv("GO_BUILDER_NAME") 31 | } 32 | 33 | // HasGoBuild reports whether the current system can build programs with ``go build'' 34 | // and then run them with os.StartProcess or exec.Command. 35 | func HasGoBuild() bool { 36 | if os.Getenv("GO_GCFLAGS") != "" { 37 | // It's too much work to require every caller of the go command 38 | // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). 39 | // For now, if $GO_GCFLAGS is set, report that we simply can't 40 | // run go build. 41 | return false 42 | } 43 | switch runtime.GOOS { 44 | case "android", "nacl", "js": 45 | return false 46 | case "darwin": 47 | if strings.HasPrefix(runtime.GOARCH, "arm") { 48 | return false 49 | } 50 | } 51 | return true 52 | } 53 | 54 | // MustHaveGoBuild checks that the current system can build programs with ``go build'' 55 | // and then run them with os.StartProcess or exec.Command. 56 | // If not, MustHaveGoBuild calls t.Skip with an explanation. 57 | func MustHaveGoBuild(t testing.TB) { 58 | if os.Getenv("GO_GCFLAGS") != "" { 59 | t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") 60 | } 61 | if !HasGoBuild() { 62 | t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 63 | } 64 | } 65 | 66 | // HasGoRun reports whether the current system can run programs with ``go run.'' 67 | func HasGoRun() bool { 68 | // For now, having go run and having go build are the same. 69 | return HasGoBuild() 70 | } 71 | 72 | // MustHaveGoRun checks that the current system can run programs with ``go run.'' 73 | // If not, MustHaveGoRun calls t.Skip with an explanation. 74 | func MustHaveGoRun(t testing.TB) { 75 | if !HasGoRun() { 76 | t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 77 | } 78 | } 79 | 80 | // GoToolPath reports the path to the Go tool. 81 | // It is a convenience wrapper around GoTool. 82 | // If the tool is unavailable GoToolPath calls t.Skip. 83 | // If the tool should be available and isn't, GoToolPath calls t.Fatal. 84 | func GoToolPath(t testing.TB) string { 85 | MustHaveGoBuild(t) 86 | path, err := GoTool() 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | return path 91 | } 92 | 93 | // GoTool reports the path to the Go tool. 94 | func GoTool() (string, error) { 95 | if !HasGoBuild() { 96 | return "", errors.New("platform cannot run go tool") 97 | } 98 | var exeSuffix string 99 | if runtime.GOOS == "windows" { 100 | exeSuffix = ".exe" 101 | } 102 | path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) 103 | if _, err := os.Stat(path); err == nil { 104 | return path, nil 105 | } 106 | goBin, err := exec.LookPath("go" + exeSuffix) 107 | if err != nil { 108 | return "", errors.New("cannot find go tool: " + err.Error()) 109 | } 110 | return goBin, nil 111 | } 112 | 113 | // HasExec reports whether the current system can start new processes 114 | // using os.StartProcess or (more commonly) exec.Command. 115 | func HasExec() bool { 116 | switch runtime.GOOS { 117 | case "nacl", "js": 118 | return false 119 | case "darwin": 120 | if strings.HasPrefix(runtime.GOARCH, "arm") { 121 | return false 122 | } 123 | } 124 | return true 125 | } 126 | 127 | // HasSrc reports whether the entire source tree is available under GOROOT. 128 | func HasSrc() bool { 129 | switch runtime.GOOS { 130 | case "nacl": 131 | return false 132 | case "darwin": 133 | if strings.HasPrefix(runtime.GOARCH, "arm") { 134 | return false 135 | } 136 | } 137 | return true 138 | } 139 | 140 | // MustHaveExec checks that the current system can start new processes 141 | // using os.StartProcess or (more commonly) exec.Command. 142 | // If not, MustHaveExec calls t.Skip with an explanation. 143 | func MustHaveExec(t testing.TB) { 144 | if !HasExec() { 145 | t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH) 146 | } 147 | } 148 | 149 | // HasExternalNetwork reports whether the current system can use 150 | // external (non-localhost) networks. 151 | func HasExternalNetwork() bool { 152 | return !testing.Short() && runtime.GOOS != "nacl" && runtime.GOOS != "js" 153 | } 154 | 155 | // MustHaveExternalNetwork checks that the current system can use 156 | // external (non-localhost) networks. 157 | // If not, MustHaveExternalNetwork calls t.Skip with an explanation. 158 | func MustHaveExternalNetwork(t testing.TB) { 159 | if runtime.GOOS == "nacl" || runtime.GOOS == "js" { 160 | t.Skipf("skipping test: no external network on %s", runtime.GOOS) 161 | } 162 | if testing.Short() { 163 | t.Skipf("skipping test: no external network in -short mode") 164 | } 165 | } 166 | 167 | var haveCGO bool 168 | 169 | // HasCGO reports whether the current system can use cgo. 170 | func HasCGO() bool { 171 | return haveCGO 172 | } 173 | 174 | // MustHaveCGO calls t.Skip if cgo is not available. 175 | func MustHaveCGO(t testing.TB) { 176 | if !haveCGO { 177 | t.Skipf("skipping test: no cgo") 178 | } 179 | } 180 | 181 | // HasSymlink reports whether the current system can use os.Symlink. 182 | func HasSymlink() bool { 183 | ok, _ := hasSymlink() 184 | return ok 185 | } 186 | 187 | // MustHaveSymlink reports whether the current system can use os.Symlink. 188 | // If not, MustHaveSymlink calls t.Skip with an explanation. 189 | func MustHaveSymlink(t testing.TB) { 190 | ok, reason := hasSymlink() 191 | if !ok { 192 | t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) 193 | } 194 | } 195 | 196 | // HasLink reports whether the current system can use os.Link. 197 | func HasLink() bool { 198 | // From Android release M (Marshmallow), hard linking files is blocked 199 | // and an attempt to call link() on a file will return EACCES. 200 | // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150 201 | return runtime.GOOS != "plan9" && runtime.GOOS != "android" 202 | } 203 | 204 | // MustHaveLink reports whether the current system can use os.Link. 205 | // If not, MustHaveLink calls t.Skip with an explanation. 206 | func MustHaveLink(t testing.TB) { 207 | if !HasLink() { 208 | t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) 209 | } 210 | } 211 | 212 | var flaky = flag.Bool("flaky", false, "run known-flaky tests too") 213 | 214 | func SkipFlaky(t testing.TB, issue int) { 215 | t.Helper() 216 | if !*flaky { 217 | t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) 218 | } 219 | } 220 | 221 | func SkipFlakyNet(t testing.TB) { 222 | t.Helper() 223 | if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { 224 | t.Skip("skipping test on builder known to have frequent network failures") 225 | } 226 | } 227 | 228 | // CleanCmdEnv will fill cmd.Env with the environment, excluding certain 229 | // variables that could modify the behavior of the Go tools such as 230 | // GODEBUG and GOTRACEBACK. 231 | func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd { 232 | if cmd.Env != nil { 233 | panic("environment already set") 234 | } 235 | for _, env := range os.Environ() { 236 | // Exclude GODEBUG from the environment to prevent its output 237 | // from breaking tests that are trying to parse other command output. 238 | if strings.HasPrefix(env, "GODEBUG=") { 239 | continue 240 | } 241 | // Exclude GOTRACEBACK for the same reason. 242 | if strings.HasPrefix(env, "GOTRACEBACK=") { 243 | continue 244 | } 245 | cmd.Env = append(cmd.Env, env) 246 | } 247 | return cmd 248 | } 249 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/goproxytest/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package goproxytest serves Go modules from a proxy server designed to run on 7 | localhost during tests, both to make tests avoid requiring specific network 8 | servers and also to make them significantly faster. 9 | 10 | Each module archive is either a file named path_vers.txt or a directory named 11 | path_vers, where slashes in path have been replaced with underscores. The 12 | archive or directory must contain two files ".info" and ".mod", to be served as 13 | the info and mod files in the proxy protocol (see 14 | https://research.swtch.com/vgo-module). The remaining files are served as the 15 | content of the module zip file. The path@vers prefix required of files in the 16 | zip file is added automatically by the proxy: the files in the archive have 17 | names without the prefix, like plain "go.mod", "x.go", and so on. 18 | 19 | See ../cmd/txtar-addmod and ../cmd/txtar-c for tools generate txtar 20 | files, although it's fine to write them by hand. 21 | */ 22 | package goproxytest 23 | 24 | import ( 25 | "archive/zip" 26 | "bytes" 27 | "encoding/json" 28 | "fmt" 29 | "io/ioutil" 30 | "log" 31 | "net" 32 | "net/http" 33 | "os" 34 | "path/filepath" 35 | "strings" 36 | 37 | "github.com/rogpeppe/go-internal/module" 38 | "github.com/rogpeppe/go-internal/par" 39 | "github.com/rogpeppe/go-internal/semver" 40 | "github.com/rogpeppe/go-internal/txtar" 41 | ) 42 | 43 | type Server struct { 44 | server *http.Server 45 | URL string 46 | dir string 47 | modList []module.Version 48 | zipCache par.Cache 49 | archiveCache par.Cache 50 | } 51 | 52 | // StartProxy starts the Go module proxy listening on the given 53 | // network address. It serves modules taken from the given directory 54 | // name. If addr is empty, it will listen on an arbitrary 55 | // localhost port. If dir is empty, "testmod" will be used. 56 | // 57 | // The returned Server should be closed after use. 58 | func NewServer(dir, addr string) (*Server, error) { 59 | var srv Server 60 | if addr == "" { 61 | addr = "localhost:0" 62 | } 63 | if dir == "" { 64 | dir = "testmod" 65 | } 66 | srv.dir = dir 67 | if err := srv.readModList(); err != nil { 68 | return nil, fmt.Errorf("cannot read modules: %v", err) 69 | } 70 | l, err := net.Listen("tcp", addr) 71 | if err != nil { 72 | return nil, fmt.Errorf("cannot listen on %q: %v", addr, err) 73 | } 74 | srv.server = &http.Server{ 75 | Handler: http.HandlerFunc(srv.handler), 76 | } 77 | addr = l.Addr().String() 78 | srv.URL = "http://" + addr + "/mod" 79 | go func() { 80 | if err := srv.server.Serve(l); err != nil && err != http.ErrServerClosed { 81 | log.Printf("go proxy: http.Serve: %v", err) 82 | } 83 | }() 84 | return &srv, nil 85 | } 86 | 87 | // Close shuts down the proxy. 88 | func (srv *Server) Close() { 89 | srv.server.Close() 90 | } 91 | 92 | func (srv *Server) readModList() error { 93 | infos, err := ioutil.ReadDir(srv.dir) 94 | if err != nil { 95 | return err 96 | } 97 | for _, info := range infos { 98 | name := info.Name() 99 | if !strings.HasSuffix(name, ".txt") && !info.IsDir() { 100 | continue 101 | } 102 | name = strings.TrimSuffix(name, ".txt") 103 | i := strings.LastIndex(name, "_v") 104 | if i < 0 { 105 | continue 106 | } 107 | encPath := strings.Replace(name[:i], "_", "/", -1) 108 | path, err := module.DecodePath(encPath) 109 | if err != nil { 110 | return fmt.Errorf("cannot decode module path in %q: %v", name, err) 111 | } 112 | encVers := name[i+1:] 113 | vers, err := module.DecodeVersion(encVers) 114 | if err != nil { 115 | return fmt.Errorf("cannot decode module version in %q: %v", name, err) 116 | } 117 | srv.modList = append(srv.modList, module.Version{Path: path, Version: vers}) 118 | } 119 | return nil 120 | } 121 | 122 | // handler serves the Go module proxy protocol. 123 | // See the proxy section of https://research.swtch.com/vgo-module. 124 | func (srv *Server) handler(w http.ResponseWriter, r *http.Request) { 125 | if !strings.HasPrefix(r.URL.Path, "/mod/") { 126 | http.NotFound(w, r) 127 | return 128 | } 129 | path := strings.TrimPrefix(r.URL.Path, "/mod/") 130 | i := strings.Index(path, "/@v/") 131 | if i < 0 { 132 | http.NotFound(w, r) 133 | return 134 | } 135 | enc, file := path[:i], path[i+len("/@v/"):] 136 | path, err := module.DecodePath(enc) 137 | if err != nil { 138 | fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 139 | http.NotFound(w, r) 140 | return 141 | } 142 | if file == "list" { 143 | n := 0 144 | for _, m := range srv.modList { 145 | if m.Path == path && !isPseudoVersion(m.Version) { 146 | if err := module.Check(m.Path, m.Version); err == nil { 147 | fmt.Fprintf(w, "%s\n", m.Version) 148 | n++ 149 | } 150 | } 151 | } 152 | if n == 0 { 153 | http.NotFound(w, r) 154 | } 155 | return 156 | } 157 | 158 | i = strings.LastIndex(file, ".") 159 | if i < 0 { 160 | http.NotFound(w, r) 161 | return 162 | } 163 | encVers, ext := file[:i], file[i+1:] 164 | vers, err := module.DecodeVersion(encVers) 165 | if err != nil { 166 | fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 167 | http.NotFound(w, r) 168 | return 169 | } 170 | 171 | if allHex(vers) { 172 | var best string 173 | // Convert commit hash (only) to known version. 174 | // Use latest version in semver priority, to match similar logic 175 | // in the repo-based module server (see modfetch.(*codeRepo).convert). 176 | for _, m := range srv.modList { 177 | if m.Path == path && semver.Compare(best, m.Version) < 0 { 178 | var hash string 179 | if isPseudoVersion(m.Version) { 180 | hash = m.Version[strings.LastIndex(m.Version, "-")+1:] 181 | } else { 182 | hash = srv.findHash(m) 183 | } 184 | if strings.HasPrefix(hash, vers) || strings.HasPrefix(vers, hash) { 185 | best = m.Version 186 | } 187 | } 188 | } 189 | if best != "" { 190 | vers = best 191 | } 192 | } 193 | 194 | a := srv.readArchive(path, vers) 195 | if a == nil { 196 | // As of https://go-review.googlesource.com/c/go/+/189517, cmd/go 197 | // resolves all paths. i.e. for github.com/hello/world, cmd/go attempts 198 | // to resolve github.com, github.com/hello and github.com/hello/world. 199 | // cmd/go expects a 404/410 response if there is nothing there. Hence we 200 | // cannot return with a 500. 201 | fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers) 202 | http.NotFound(w, r) 203 | return 204 | } 205 | 206 | switch ext { 207 | case "info", "mod": 208 | want := "." + ext 209 | for _, f := range a.Files { 210 | if f.Name == want { 211 | w.Write(f.Data) 212 | return 213 | } 214 | } 215 | 216 | case "zip": 217 | type cached struct { 218 | zip []byte 219 | err error 220 | } 221 | c := srv.zipCache.Do(a, func() interface{} { 222 | var buf bytes.Buffer 223 | z := zip.NewWriter(&buf) 224 | for _, f := range a.Files { 225 | if strings.HasPrefix(f.Name, ".") { 226 | continue 227 | } 228 | zf, err := z.Create(path + "@" + vers + "/" + f.Name) 229 | if err != nil { 230 | return cached{nil, err} 231 | } 232 | if _, err := zf.Write(f.Data); err != nil { 233 | return cached{nil, err} 234 | } 235 | } 236 | if err := z.Close(); err != nil { 237 | return cached{nil, err} 238 | } 239 | return cached{buf.Bytes(), nil} 240 | }).(cached) 241 | 242 | if c.err != nil { 243 | fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err) 244 | http.Error(w, c.err.Error(), 500) 245 | return 246 | } 247 | w.Write(c.zip) 248 | return 249 | 250 | } 251 | http.NotFound(w, r) 252 | } 253 | 254 | func (srv *Server) findHash(m module.Version) string { 255 | a := srv.readArchive(m.Path, m.Version) 256 | if a == nil { 257 | return "" 258 | } 259 | var data []byte 260 | for _, f := range a.Files { 261 | if f.Name == ".info" { 262 | data = f.Data 263 | break 264 | } 265 | } 266 | var info struct{ Short string } 267 | json.Unmarshal(data, &info) 268 | return info.Short 269 | } 270 | 271 | func (srv *Server) readArchive(path, vers string) *txtar.Archive { 272 | enc, err := module.EncodePath(path) 273 | if err != nil { 274 | fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 275 | return nil 276 | } 277 | encVers, err := module.EncodeVersion(vers) 278 | if err != nil { 279 | fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 280 | return nil 281 | } 282 | 283 | prefix := strings.Replace(enc, "/", "_", -1) 284 | name := filepath.Join(srv.dir, prefix+"_"+encVers+".txt") 285 | a := srv.archiveCache.Do(name, func() interface{} { 286 | a, err := txtar.ParseFile(name) 287 | if os.IsNotExist(err) { 288 | // we fallback to trying a directory 289 | name = strings.TrimSuffix(name, ".txt") 290 | 291 | a = new(txtar.Archive) 292 | 293 | err = filepath.Walk(name, func(path string, info os.FileInfo, err error) error { 294 | if err != nil { 295 | return err 296 | } 297 | if path == name && !info.IsDir() { 298 | return fmt.Errorf("expected a directory root") 299 | } 300 | if info.IsDir() { 301 | return nil 302 | } 303 | arpath := filepath.ToSlash(strings.TrimPrefix(path, name+string(os.PathSeparator))) 304 | data, err := ioutil.ReadFile(path) 305 | if err != nil { 306 | return err 307 | } 308 | a.Files = append(a.Files, txtar.File{ 309 | Name: arpath, 310 | Data: data, 311 | }) 312 | return nil 313 | }) 314 | } 315 | if err != nil { 316 | if !os.IsNotExist(err) { 317 | fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 318 | } 319 | a = nil 320 | } 321 | return a 322 | }).(*txtar.Archive) 323 | return a 324 | } 325 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/semver/semver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package semver implements comparison of semantic version strings. 6 | // In this package, semantic version strings must begin with a leading "v", 7 | // as in "v1.0.0". 8 | // 9 | // The general form of a semantic version string accepted by this package is 10 | // 11 | // vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] 12 | // 13 | // where square brackets indicate optional parts of the syntax; 14 | // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; 15 | // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers 16 | // using only alphanumeric characters and hyphens; and 17 | // all-numeric PRERELEASE identifiers must not have leading zeros. 18 | // 19 | // This package follows Semantic Versioning 2.0.0 (see semver.org) 20 | // with two exceptions. First, it requires the "v" prefix. Second, it recognizes 21 | // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) 22 | // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. 23 | package semver 24 | 25 | // parsed returns the parsed form of a semantic version string. 26 | type parsed struct { 27 | major string 28 | minor string 29 | patch string 30 | short string 31 | prerelease string 32 | build string 33 | err string 34 | } 35 | 36 | // IsValid reports whether v is a valid semantic version string. 37 | func IsValid(v string) bool { 38 | _, ok := parse(v) 39 | return ok 40 | } 41 | 42 | // Canonical returns the canonical formatting of the semantic version v. 43 | // It fills in any missing .MINOR or .PATCH and discards build metadata. 44 | // Two semantic versions compare equal only if their canonical formattings 45 | // are identical strings. 46 | // The canonical invalid semantic version is the empty string. 47 | func Canonical(v string) string { 48 | p, ok := parse(v) 49 | if !ok { 50 | return "" 51 | } 52 | if p.build != "" { 53 | return v[:len(v)-len(p.build)] 54 | } 55 | if p.short != "" { 56 | return v + p.short 57 | } 58 | return v 59 | } 60 | 61 | // Major returns the major version prefix of the semantic version v. 62 | // For example, Major("v2.1.0") == "v2". 63 | // If v is an invalid semantic version string, Major returns the empty string. 64 | func Major(v string) string { 65 | pv, ok := parse(v) 66 | if !ok { 67 | return "" 68 | } 69 | return v[:1+len(pv.major)] 70 | } 71 | 72 | // MajorMinor returns the major.minor version prefix of the semantic version v. 73 | // For example, MajorMinor("v2.1.0") == "v2.1". 74 | // If v is an invalid semantic version string, MajorMinor returns the empty string. 75 | func MajorMinor(v string) string { 76 | pv, ok := parse(v) 77 | if !ok { 78 | return "" 79 | } 80 | i := 1 + len(pv.major) 81 | if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { 82 | return v[:j] 83 | } 84 | return v[:i] + "." + pv.minor 85 | } 86 | 87 | // Prerelease returns the prerelease suffix of the semantic version v. 88 | // For example, Prerelease("v2.1.0-pre+meta") == "-pre". 89 | // If v is an invalid semantic version string, Prerelease returns the empty string. 90 | func Prerelease(v string) string { 91 | pv, ok := parse(v) 92 | if !ok { 93 | return "" 94 | } 95 | return pv.prerelease 96 | } 97 | 98 | // Build returns the build suffix of the semantic version v. 99 | // For example, Build("v2.1.0+meta") == "+meta". 100 | // If v is an invalid semantic version string, Build returns the empty string. 101 | func Build(v string) string { 102 | pv, ok := parse(v) 103 | if !ok { 104 | return "" 105 | } 106 | return pv.build 107 | } 108 | 109 | // Compare returns an integer comparing two versions according to 110 | // according to semantic version precedence. 111 | // The result will be 0 if v == w, -1 if v < w, or +1 if v > w. 112 | // 113 | // An invalid semantic version string is considered less than a valid one. 114 | // All invalid semantic version strings compare equal to each other. 115 | func Compare(v, w string) int { 116 | pv, ok1 := parse(v) 117 | pw, ok2 := parse(w) 118 | if !ok1 && !ok2 { 119 | return 0 120 | } 121 | if !ok1 { 122 | return -1 123 | } 124 | if !ok2 { 125 | return +1 126 | } 127 | if c := compareInt(pv.major, pw.major); c != 0 { 128 | return c 129 | } 130 | if c := compareInt(pv.minor, pw.minor); c != 0 { 131 | return c 132 | } 133 | if c := compareInt(pv.patch, pw.patch); c != 0 { 134 | return c 135 | } 136 | return comparePrerelease(pv.prerelease, pw.prerelease) 137 | } 138 | 139 | // Max canonicalizes its arguments and then returns the version string 140 | // that compares greater. 141 | func Max(v, w string) string { 142 | v = Canonical(v) 143 | w = Canonical(w) 144 | if Compare(v, w) > 0 { 145 | return v 146 | } 147 | return w 148 | } 149 | 150 | func parse(v string) (p parsed, ok bool) { 151 | if v == "" || v[0] != 'v' { 152 | p.err = "missing v prefix" 153 | return 154 | } 155 | p.major, v, ok = parseInt(v[1:]) 156 | if !ok { 157 | p.err = "bad major version" 158 | return 159 | } 160 | if v == "" { 161 | p.minor = "0" 162 | p.patch = "0" 163 | p.short = ".0.0" 164 | return 165 | } 166 | if v[0] != '.' { 167 | p.err = "bad minor prefix" 168 | ok = false 169 | return 170 | } 171 | p.minor, v, ok = parseInt(v[1:]) 172 | if !ok { 173 | p.err = "bad minor version" 174 | return 175 | } 176 | if v == "" { 177 | p.patch = "0" 178 | p.short = ".0" 179 | return 180 | } 181 | if v[0] != '.' { 182 | p.err = "bad patch prefix" 183 | ok = false 184 | return 185 | } 186 | p.patch, v, ok = parseInt(v[1:]) 187 | if !ok { 188 | p.err = "bad patch version" 189 | return 190 | } 191 | if len(v) > 0 && v[0] == '-' { 192 | p.prerelease, v, ok = parsePrerelease(v) 193 | if !ok { 194 | p.err = "bad prerelease" 195 | return 196 | } 197 | } 198 | if len(v) > 0 && v[0] == '+' { 199 | p.build, v, ok = parseBuild(v) 200 | if !ok { 201 | p.err = "bad build" 202 | return 203 | } 204 | } 205 | if v != "" { 206 | p.err = "junk on end" 207 | ok = false 208 | return 209 | } 210 | ok = true 211 | return 212 | } 213 | 214 | func parseInt(v string) (t, rest string, ok bool) { 215 | if v == "" { 216 | return 217 | } 218 | if v[0] < '0' || '9' < v[0] { 219 | return 220 | } 221 | i := 1 222 | for i < len(v) && '0' <= v[i] && v[i] <= '9' { 223 | i++ 224 | } 225 | if v[0] == '0' && i != 1 { 226 | return 227 | } 228 | return v[:i], v[i:], true 229 | } 230 | 231 | func parsePrerelease(v string) (t, rest string, ok bool) { 232 | // "A pre-release version MAY be denoted by appending a hyphen and 233 | // a series of dot separated identifiers immediately following the patch version. 234 | // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. 235 | // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." 236 | if v == "" || v[0] != '-' { 237 | return 238 | } 239 | i := 1 240 | start := 1 241 | for i < len(v) && v[i] != '+' { 242 | if !isIdentChar(v[i]) && v[i] != '.' { 243 | return 244 | } 245 | if v[i] == '.' { 246 | if start == i || isBadNum(v[start:i]) { 247 | return 248 | } 249 | start = i + 1 250 | } 251 | i++ 252 | } 253 | if start == i || isBadNum(v[start:i]) { 254 | return 255 | } 256 | return v[:i], v[i:], true 257 | } 258 | 259 | func parseBuild(v string) (t, rest string, ok bool) { 260 | if v == "" || v[0] != '+' { 261 | return 262 | } 263 | i := 1 264 | start := 1 265 | for i < len(v) { 266 | if !isIdentChar(v[i]) { 267 | return 268 | } 269 | if v[i] == '.' { 270 | if start == i { 271 | return 272 | } 273 | start = i + 1 274 | } 275 | i++ 276 | } 277 | if start == i { 278 | return 279 | } 280 | return v[:i], v[i:], true 281 | } 282 | 283 | func isIdentChar(c byte) bool { 284 | return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' 285 | } 286 | 287 | func isBadNum(v string) bool { 288 | i := 0 289 | for i < len(v) && '0' <= v[i] && v[i] <= '9' { 290 | i++ 291 | } 292 | return i == len(v) && i > 1 && v[0] == '0' 293 | } 294 | 295 | func isNum(v string) bool { 296 | i := 0 297 | for i < len(v) && '0' <= v[i] && v[i] <= '9' { 298 | i++ 299 | } 300 | return i == len(v) 301 | } 302 | 303 | func compareInt(x, y string) int { 304 | if x == y { 305 | return 0 306 | } 307 | if len(x) < len(y) { 308 | return -1 309 | } 310 | if len(x) > len(y) { 311 | return +1 312 | } 313 | if x < y { 314 | return -1 315 | } else { 316 | return +1 317 | } 318 | } 319 | 320 | func comparePrerelease(x, y string) int { 321 | // "When major, minor, and patch are equal, a pre-release version has 322 | // lower precedence than a normal version. 323 | // Example: 1.0.0-alpha < 1.0.0. 324 | // Precedence for two pre-release versions with the same major, minor, 325 | // and patch version MUST be determined by comparing each dot separated 326 | // identifier from left to right until a difference is found as follows: 327 | // identifiers consisting of only digits are compared numerically and 328 | // identifiers with letters or hyphens are compared lexically in ASCII 329 | // sort order. Numeric identifiers always have lower precedence than 330 | // non-numeric identifiers. A larger set of pre-release fields has a 331 | // higher precedence than a smaller set, if all of the preceding 332 | // identifiers are equal. 333 | // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 334 | // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." 335 | if x == y { 336 | return 0 337 | } 338 | if x == "" { 339 | return +1 340 | } 341 | if y == "" { 342 | return -1 343 | } 344 | for x != "" && y != "" { 345 | x = x[1:] // skip - or . 346 | y = y[1:] // skip - or . 347 | var dx, dy string 348 | dx, x = nextIdent(x) 349 | dy, y = nextIdent(y) 350 | if dx != dy { 351 | ix := isNum(dx) 352 | iy := isNum(dy) 353 | if ix != iy { 354 | if ix { 355 | return -1 356 | } else { 357 | return +1 358 | } 359 | } 360 | if ix { 361 | if len(dx) < len(dy) { 362 | return -1 363 | } 364 | if len(dx) > len(dy) { 365 | return +1 366 | } 367 | } 368 | if dx < dy { 369 | return -1 370 | } else { 371 | return +1 372 | } 373 | } 374 | } 375 | if x == "" { 376 | return -1 377 | } else { 378 | return +1 379 | } 380 | } 381 | 382 | func nextIdent(x string) (dx, rest string) { 383 | i := 0 384 | for i < len(x) && x[i] != '.' { 385 | i++ 386 | } 387 | return x[:i], x[i:] 388 | } 389 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 19 | ### `gobin` 20 | 21 | The gobin command installs/runs main packages. 22 | 23 | See the [FAQ](https://github.com/myitcv/gobin/wiki/FAQ) for more details. 24 | 25 | ### `gobin` is deprecated as of Go 1.16 26 | 27 | Go 1.16 supports [`go install $pkg@$version`](https://golang.org/doc/go1.16#go-command) to install commands without 28 | affecting the main module. This is the default and most popular mode of operation for `gobin`. 29 | 30 | A proposal to support [`go run $pkg@$version`](https://github.com/golang/go/issues/42088) was accepted in January 31 | 2021, and should hopefully land in Go 1.17. This will cover the `gobin -run` use case. 32 | 33 | Hence we have decided to archive this project. 34 | 35 | 36 | 37 | 119 | 120 | ### Installation 121 | 122 | ``` 123 | $ GO111MODULE=off go get -u github.com/myitcv/gobin 124 | ``` 125 | 126 | or download a binary from [the latest release](https://github.com/myitcv/gobin/releases). 127 | 128 | Update your `PATH` and verify we can find `gobin` in our new `PATH`: 129 | 130 | ``` 131 | $ export PATH=$(go env GOPATH)/bin:$PATH 132 | $ which gobin 133 | /home/gopher/gopath/bin/gobin 134 | ``` 135 | 136 | ### Examples 137 | 138 | Install `gohack`: 139 | 140 | ``` 141 | $ gobin github.com/rogpeppe/gohack 142 | Installed github.com/rogpeppe/gohack@v1.0.0 to /home/gopher/gopath/bin/gohack 143 | ``` 144 | 145 | Install a specific version of `gohack`: 146 | 147 | ``` 148 | $ gobin github.com/rogpeppe/gohack@v1.0.0 149 | Installed github.com/rogpeppe/gohack@v1.0.0 to /home/gopher/gopath/bin/gohack 150 | ``` 151 | 152 | Print the `gobin` cache location of a specific `gohack` version: 153 | 154 | ``` 155 | $ gobin -p github.com/rogpeppe/gohack@v1.0.0 156 | /home/gopher/.cache/gobin/github.com/rogpeppe/gohack/@v/v1.0.0/github.com/rogpeppe/gohack/gohack 157 | ``` 158 | 159 | Run a specific `gohack` version: 160 | 161 | ``` 162 | $ gobin -run github.com/rogpeppe/gohack@v1.0.0 -help 163 | The gohack command checks out Go module dependencies 164 | into a directory where they can be edited, and adjusts 165 | the go.mod file appropriately. 166 | ... 167 | ``` 168 | 169 | ### Examples: using `-m` 170 | 171 | Define a module: 172 | 173 | ``` 174 | $ cat go.mod 175 | module example.com/hello 176 | ``` 177 | 178 | Add a [tool dependency](https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md): 179 | 180 | ```go 181 | $ cat tools.go 182 | // +build tools 183 | 184 | package tools 185 | 186 | import ( 187 | _ "golang.org/x/tools/cmd/stringer" 188 | ) 189 | ``` 190 | 191 | Review the version of `stringer` being used: 192 | 193 | ``` 194 | $ gobin -m -p golang.org/x/tools/cmd/stringer 195 | /home/gopher/hello/.gobincache/golang.org/x/tools/@v/v0.0.0-20181102223251-96e9e165b75e/golang.org/x/tools/cmd/stringer/stringer 196 | ``` 197 | 198 | Check the help for `stringer`: 199 | 200 | ``` 201 | $ gobin -m -run golang.org/x/tools/cmd/stringer -help 202 | Usage of stringer: 203 | stringer [flags] -type T [directory] 204 | stringer [flags] -type T files... # Must be a single package 205 | For more information, see: 206 | ... 207 | ``` 208 | 209 | Use `stringer` via a `go:generate` directive: 210 | 211 | ```go 212 | $ cat main.go 213 | package main 214 | 215 | import "fmt" 216 | 217 | //go:generate gobin -m -run golang.org/x/tools/cmd/stringer -type=Pill 218 | 219 | type Pill int 220 | 221 | const ( 222 | Placebo Pill = iota 223 | Aspirin 224 | Ibuprofen 225 | Paracetamol 226 | Acetaminophen = Paracetamol 227 | ) 228 | 229 | func main() { 230 | fmt.Printf("For headaches, take %v\n", Ibuprofen) 231 | } 232 | ``` 233 | 234 | Generate and run as a "test": 235 | 236 | ``` 237 | $ go generate 238 | $ go run . 239 | For headaches, take Ibuprofen 240 | ``` 241 | 242 | 243 | 244 | 245 | ### Usage 246 | 247 | 253 | 254 | ``` 255 | The gobin command installs/runs main packages. 256 | 257 | Usage: 258 | gobin [-m] [-run|-p|-v|-d] [-u|-nonet] [-tags 'tag list'] packages [run arguments...] 259 | 260 | The gobin command builds, installs, and possibly runs an executable binary for 261 | each of the named main packages. 262 | 263 | The packages argument to gobin is similar to that of the go get command (in 264 | module aware mode) with the additional constraint that the list of packages 265 | must be main packages. Each argument takes the form $main_pkg[@$version]. 266 | 267 | By default, gobin will use the main package's module to resolve its 268 | dependencies, unless the -m flag is specified, in which case dependencies will 269 | be resolved using the main module (as given by go env GOMOD). 270 | 271 | The -mod flag provides additional control over updating and use of go.mod when 272 | using the main module to resolve dependencies. If the -mod flag is provided it 273 | implies -m. With -mod=readonly, gobin is disallowed from any implicit updating 274 | of go.mod. Instead, it fails when any changes to go.mod are needed. With 275 | -mod=vendor, gobin assumes that the vendor directory holds the correct copies 276 | of dependencies and ignores the dependency descriptions in go.mod 277 | 278 | This means that gobin $package@v1.2.3 is a repeatable way to install an exact 279 | version of a binary (assuming it has an associated go.mod file). 280 | 281 | The version "latest" matches the latest available tagged version for the module 282 | containing the main package. If gobin is able to resolve "latest" within the 283 | module download cache it will use that version. Otherwise, gobin will make a 284 | network request to resolve "latest". The -u flag forces gobin to check the 285 | network for the latest tagged version. If the -nonet flag is provided, gobin 286 | will only check the module download cache. Hence, the -u and -nonet flags are 287 | mutually exclusive. 288 | 289 | Versions that take the form of a revision identifier (a branch name, for 290 | example) can only be resolved with a network request and hence are incompatible 291 | with -nonet. 292 | 293 | If no version is specified for a main package, gobin behaves differently 294 | depending on whether the -m flag is provided. If the -m flag is not provided, 295 | gobin $module is equivalent to gobin $module@latest. If the -m flag is 296 | provided, gobin attempts to resolve the current version via the main module's 297 | go.mod; if this resolution fails, "latest" is assumed as the version. 298 | 299 | By default, gobin installs the main packages to $GOBIN (or $GOPATH/bin if GOBIN 300 | is not set, which defaults to $HOME/go/bin if GOPATH is not set). 301 | 302 | The -run flag takes exactly one main package argument and runs that package. 303 | It is similar therefore to go run. Any arguments after the single main package 304 | will be passed to the main package as command line arguments. 305 | 306 | The -p flag prints the gobin cache path for each of the packages' executables 307 | once versions have been resolved. 308 | 309 | The -v flag prints the module path and version for each of the packages. Each 310 | line in the output has two space-separated fields: a module path and a version. 311 | 312 | The -d flag instructs gobin to stop after installing the packages to the gobin 313 | cache; that is, it instructs gobin not to install, run or print the packages. 314 | 315 | The -run, -p, -v and -d flags are mutually exclusive. 316 | 317 | The -tags flag is identical to the cmd/go build flag (see go help build). It is 318 | a space-separated list of build tags to consider satisfied during the build. 319 | Alternatively, GOFLAGS can be set to include a value for -tags (see go help 320 | environment). 321 | 322 | It is an error for a non-main package to be provided as a package argument. 323 | 324 | 325 | Cache directories 326 | ================= 327 | 328 | gobin maintains a cache of executables, separate from any executables that may 329 | be installed to $GOBIN. 330 | 331 | By default, gobin uses the directories gobin/$module@$version/$main_pkg under 332 | your user cache directory. See the documentation for os.UserCacheDir for 333 | OS-specific details on how to configure its location. 334 | 335 | When the -m flag is provided, gobin uses the directories 336 | .gobincache/$module@$version/$main_pkg under the directory containing the main 337 | module's go.mod. 338 | 339 | ``` 340 | 341 | 342 | 343 | ### Credits 344 | 345 | * [mvdan](https://github.com/mvdan) 346 | * [rogpeppe](https://github.com/rogpeppe) 347 | 348 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package testscript provides support for defining filesystem-based tests by 7 | creating scripts in a directory. 8 | 9 | To invoke the tests, call testscript.Run. For example: 10 | 11 | func TestFoo(t *testing.T) { 12 | testscript.Run(t, testscript.Params{ 13 | Dir: "testdata", 14 | }) 15 | } 16 | 17 | A testscript directory holds test scripts *.txt run during 'go test'. 18 | Each script defines a subtest; the exact set of allowable commands in a 19 | script are defined by the parameters passed to the Run function. 20 | To run a specific script foo.txt 21 | 22 | go test cmd/go -run=TestName/^foo$ 23 | 24 | where TestName is the name of the test that Run is called from. 25 | 26 | To define an executable command (or several) that can be run as part of the script, 27 | call RunMain with the functions that implement the command's functionality. 28 | The command functions will be called in a separate process, so are 29 | free to mutate global variables without polluting the top level test binary. 30 | 31 | func TestMain(m *testing.M) { 32 | os.Exit(testscript.RunMain(m, map[string] func() int{ 33 | "testscript": testscriptMain, 34 | })) 35 | } 36 | 37 | In general script files should have short names: a few words, not whole sentences. 38 | The first word should be the general category of behavior being tested, 39 | often the name of a subcommand to be tested or a concept (vendor, pattern). 40 | 41 | Each script is a text archive (go doc github.com/rogpeppe/go-internal/txtar). 42 | The script begins with an actual command script to run 43 | followed by the content of zero or more supporting files to 44 | create in the script's temporary file system before it starts executing. 45 | 46 | As an example: 47 | 48 | # hello world 49 | exec cat hello.text 50 | stdout 'hello world\n' 51 | ! stderr . 52 | 53 | -- hello.text -- 54 | hello world 55 | 56 | Each script runs in a fresh temporary work directory tree, available to scripts as $WORK. 57 | Scripts also have access to these other environment variables: 58 | 59 | HOME=/no-home 60 | PATH= 61 | TMPDIR=$WORK/tmp 62 | devnull= 63 | goversion= 64 | 65 | The environment variable $exe (lowercase) is an empty string on most 66 | systems, ".exe" on Windows. 67 | 68 | The script's supporting files are unpacked relative to $WORK 69 | and then the script begins execution in that 70 | directory as well. Thus the example above runs in $WORK 71 | with $WORK/hello.txt containing the listed contents. 72 | 73 | The lines at the top of the script are a sequence of commands to be 74 | executed by a small script engine in the testscript package (not the system 75 | shell). The script stops and the overall test fails if any particular 76 | command fails. 77 | 78 | Each line is parsed into a sequence of space-separated command words, 79 | with environment variable expansion and # marking an end-of-line comment. 80 | Adding single quotes around text keeps spaces in that text from being 81 | treated as word separators and also disables environment variable 82 | expansion. Inside a single-quoted block of text, a repeated single 83 | quote indicates a literal single quote, as in: 84 | 85 | 'Don''t communicate by sharing memory.' 86 | 87 | A line beginning with # is a comment and conventionally explains what is 88 | being done or tested at the start of a new phase in the script. 89 | 90 | A special form of environment variable syntax can be used to quote 91 | regexp metacharacters inside environment variables. The "@R" suffix 92 | is special, and indicates that the variable should be quoted. 93 | 94 | ${VAR@R} 95 | 96 | The command prefix ! indicates that the command on the rest of the line 97 | (typically go or a matching predicate) must fail, not succeed. Only certain 98 | commands support this prefix. They are indicated below by [!] in the synopsis. 99 | 100 | The command prefix [cond] indicates that the command on the rest of the line 101 | should only run when the condition is satisfied. The predefined conditions are: 102 | 103 | - [short] for testing.Short() 104 | - [net] for whether the external network can be used 105 | - [link] for whether the OS has hard link support 106 | - [symlink] for whether the OS has symbolic link support 107 | - [exec:prog] for whether prog is available for execution (found by exec.LookPath) 108 | 109 | A condition can be negated: [!short] means to run the rest of the line 110 | when testing.Short() is false. 111 | 112 | Additional conditions can be added by passing a function to Params.Condition. 113 | 114 | The predefined commands are: 115 | 116 | - cd dir 117 | Change to the given directory for future commands. 118 | 119 | - chmod mode file 120 | 121 | Change the permissions of file or directory to the given octal mode (000 to 777). 122 | 123 | - cmp file1 file2 124 | Check that the named files have the same content. 125 | By convention, file1 is the actual data and file2 the expected data. 126 | File1 can be "stdout" or "stderr" to use the standard output or standard error 127 | from the most recent exec or wait command. 128 | (If the files have differing content, the failure prints a diff.) 129 | 130 | - cmpenv file1 file2 131 | Like cmp, but environment variables in file2 are substituted before the 132 | comparison. For example, $GOOS is replaced by the target GOOS. 133 | 134 | - cp src... dst 135 | Copy the listed files to the target file or existing directory. 136 | src can include "stdout" or "stderr" to use the standard output or standard error 137 | from the most recent exec or go command. 138 | 139 | - env [key=value...] 140 | With no arguments, print the environment (useful for debugging). 141 | Otherwise add the listed key=value pairs to the environment. 142 | 143 | - [!] exec program [args...] [&] 144 | Run the given executable program with the arguments. 145 | It must (or must not) succeed. 146 | Note that 'exec' does not terminate the script (unlike in Unix shells). 147 | 148 | If the last token is '&', the program executes in the background. The standard 149 | output and standard error of the previous command is cleared, but the output 150 | of the background process is buffered — and checking of its exit status is 151 | delayed — until the next call to 'wait', 'skip', or 'stop' or the end of the 152 | test. At the end of the test, any remaining background processes are 153 | terminated using os.Interrupt (if supported) or os.Kill. 154 | 155 | Standard input can be provided using the stdin command; this will be 156 | cleared after exec has been called. 157 | 158 | - [!] exists [-readonly] file... 159 | Each of the listed files or directories must (or must not) exist. 160 | If -readonly is given, the files or directories must be unwritable. 161 | 162 | - [!] grep [-count=N] pattern file 163 | The file's content must (or must not) match the regular expression pattern. 164 | For positive matches, -count=N specifies an exact number of matches to require. 165 | 166 | - mkdir path... 167 | Create the listed directories, if they do not already exists. 168 | 169 | - unquote file... 170 | Rewrite each file by replacing any leading ">" characters from 171 | each line. This enables a file to contain substrings that look like 172 | txtar file markers. 173 | See also https://godoc.org/github.com/rogpeppe/go-internal/txtar#Unquote 174 | 175 | - rm file... 176 | Remove the listed files or directories. 177 | 178 | - skip [message] 179 | Mark the test skipped, including the message if given. 180 | 181 | - stdin file 182 | Set the standard input for the next exec command to the contents of the given file. 183 | 184 | - [!] stderr [-count=N] pattern 185 | Apply the grep command (see above) to the standard error 186 | from the most recent exec or wait command. 187 | 188 | - [!] stdout [-count=N] pattern 189 | Apply the grep command (see above) to the standard output 190 | from the most recent exec or wait command. 191 | 192 | - stop [message] 193 | Stop the test early (marking it as passing), including the message if given. 194 | 195 | - symlink file -> target 196 | Create file as a symlink to target. The -> (like in ls -l output) is required. 197 | 198 | - wait 199 | Wait for all 'exec' and 'go' commands started in the background (with the '&' 200 | token) to exit, and display success or failure status for them. 201 | After a call to wait, the 'stderr' and 'stdout' commands will apply to the 202 | concatenation of the corresponding streams of the background commands, 203 | in the order in which those commands were started. 204 | 205 | When TestScript runs a script and the script fails, by default TestScript shows 206 | the execution of the most recent phase of the script (since the last # comment) 207 | and only shows the # comments for earlier phases. For example, here is a 208 | multi-phase script with a bug in it (TODO: make this example less go-command 209 | specific): 210 | 211 | # GOPATH with p1 in d2, p2 in d2 212 | env GOPATH=$WORK/d1${:}$WORK/d2 213 | 214 | # build & install p1 215 | env 216 | go install -i p1 217 | ! stale p1 218 | ! stale p2 219 | 220 | # modify p2 - p1 should appear stale 221 | cp $WORK/p2x.go $WORK/d2/src/p2/p2.go 222 | stale p1 p2 223 | 224 | # build & install p1 again 225 | go install -i p11 226 | ! stale p1 227 | ! stale p2 228 | 229 | -- $WORK/d1/src/p1/p1.go -- 230 | package p1 231 | import "p2" 232 | func F() { p2.F() } 233 | -- $WORK/d2/src/p2/p2.go -- 234 | package p2 235 | func F() {} 236 | -- $WORK/p2x.go -- 237 | package p2 238 | func F() {} 239 | func G() {} 240 | 241 | The bug is that the final phase installs p11 instead of p1. The test failure looks like: 242 | 243 | $ go test -run=Script 244 | --- FAIL: TestScript (3.75s) 245 | --- FAIL: TestScript/install_rebuild_gopath (0.16s) 246 | script_test.go:223: 247 | # GOPATH with p1 in d2, p2 in d2 (0.000s) 248 | # build & install p1 (0.087s) 249 | # modify p2 - p1 should appear stale (0.029s) 250 | # build & install p1 again (0.022s) 251 | > go install -i p11 252 | [stderr] 253 | can't load package: package p11: cannot find package "p11" in any of: 254 | /Users/rsc/go/src/p11 (from $GOROOT) 255 | $WORK/d1/src/p11 (from $GOPATH) 256 | $WORK/d2/src/p11 257 | [exit status 1] 258 | FAIL: unexpected go command failure 259 | 260 | script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src 261 | 262 | FAIL 263 | exit status 1 264 | FAIL cmd/go 4.875s 265 | $ 266 | 267 | Note that the commands in earlier phases have been hidden, so that the relevant 268 | commands are more easily found, and the elapsed time for a completed phase 269 | is shown next to the phase heading. To see the entire execution, use "go test -v", 270 | which also adds an initial environment dump to the beginning of the log. 271 | 272 | Note also that in reported output, the actual name of the per-script temporary directory 273 | has been consistently replaced with the literal string $WORK. 274 | 275 | If Params.TestWork is true, it causes each test to log the name of its $WORK directory and other 276 | environment variable settings and also to leave that directory behind when it exits, 277 | for manual debugging of failing tests: 278 | 279 | $ go test -run=Script -work 280 | --- FAIL: TestScript (3.75s) 281 | --- FAIL: TestScript/install_rebuild_gopath (0.16s) 282 | script_test.go:223: 283 | WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath 284 | GOARCH= 285 | GOCACHE=/Users/rsc/Library/Caches/go-build 286 | GOOS= 287 | GOPATH=$WORK/gopath 288 | GOROOT=/Users/rsc/go 289 | HOME=/no-home 290 | TMPDIR=$WORK/tmp 291 | exe= 292 | 293 | # GOPATH with p1 in d2, p2 in d2 (0.000s) 294 | # build & install p1 (0.085s) 295 | # modify p2 - p1 should appear stale (0.030s) 296 | # build & install p1 again (0.019s) 297 | > go install -i p11 298 | [stderr] 299 | can't load package: package p11: cannot find package "p11" in any of: 300 | /Users/rsc/go/src/p11 (from $GOROOT) 301 | $WORK/d1/src/p11 (from $GOPATH) 302 | $WORK/d2/src/p11 303 | [exit status 1] 304 | FAIL: unexpected go command failure 305 | 306 | script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src 307 | 308 | FAIL 309 | exit status 1 310 | FAIL cmd/go 4.875s 311 | $ 312 | 313 | $ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath 314 | $ cd $WORK/d1/src/p1 315 | $ cat p1.go 316 | package p1 317 | import "p2" 318 | func F() { p2.F() } 319 | $ 320 | */ 321 | package testscript 322 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/testscript/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package testscript 6 | 7 | import ( 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "regexp" 14 | "strconv" 15 | "strings" 16 | 17 | "github.com/rogpeppe/go-internal/internal/textutil" 18 | "github.com/rogpeppe/go-internal/txtar" 19 | ) 20 | 21 | // scriptCmds are the script command implementations. 22 | // Keep list and the implementations below sorted by name. 23 | // 24 | // NOTE: If you make changes here, update doc.go. 25 | // 26 | var scriptCmds = map[string]func(*TestScript, bool, []string){ 27 | "cd": (*TestScript).cmdCd, 28 | "chmod": (*TestScript).cmdChmod, 29 | "cmp": (*TestScript).cmdCmp, 30 | "cmpenv": (*TestScript).cmdCmpenv, 31 | "cp": (*TestScript).cmdCp, 32 | "env": (*TestScript).cmdEnv, 33 | "exec": (*TestScript).cmdExec, 34 | "exists": (*TestScript).cmdExists, 35 | "grep": (*TestScript).cmdGrep, 36 | "mkdir": (*TestScript).cmdMkdir, 37 | "rm": (*TestScript).cmdRm, 38 | "unquote": (*TestScript).cmdUnquote, 39 | "skip": (*TestScript).cmdSkip, 40 | "stdin": (*TestScript).cmdStdin, 41 | "stderr": (*TestScript).cmdStderr, 42 | "stdout": (*TestScript).cmdStdout, 43 | "stop": (*TestScript).cmdStop, 44 | "symlink": (*TestScript).cmdSymlink, 45 | "wait": (*TestScript).cmdWait, 46 | } 47 | 48 | // cd changes to a different directory. 49 | func (ts *TestScript) cmdCd(neg bool, args []string) { 50 | if neg { 51 | ts.Fatalf("unsupported: ! cd") 52 | } 53 | if len(args) != 1 { 54 | ts.Fatalf("usage: cd dir") 55 | } 56 | 57 | dir := args[0] 58 | if !filepath.IsAbs(dir) { 59 | dir = filepath.Join(ts.cd, dir) 60 | } 61 | info, err := os.Stat(dir) 62 | if os.IsNotExist(err) { 63 | ts.Fatalf("directory %s does not exist", dir) 64 | } 65 | ts.Check(err) 66 | if !info.IsDir() { 67 | ts.Fatalf("%s is not a directory", dir) 68 | } 69 | ts.cd = dir 70 | ts.Logf("%s\n", ts.cd) 71 | } 72 | 73 | func (ts *TestScript) cmdChmod(neg bool, args []string) { 74 | if len(args) != 2 { 75 | ts.Fatalf("usage: chmod mode file") 76 | } 77 | mode, err := strconv.ParseInt(args[0], 8, 32) 78 | if err != nil { 79 | ts.Fatalf("bad file mode %q: %v", args[0], err) 80 | } 81 | if mode > 0777 { 82 | ts.Fatalf("unsupported file mode %.3o", mode) 83 | } 84 | err = os.Chmod(ts.MkAbs(args[1]), os.FileMode(mode)) 85 | if neg { 86 | if err == nil { 87 | ts.Fatalf("unexpected chmod success") 88 | } 89 | return 90 | } 91 | if err != nil { 92 | ts.Fatalf("unexpected chmod failure: %v", err) 93 | } 94 | } 95 | 96 | // cmp compares two files. 97 | func (ts *TestScript) cmdCmp(neg bool, args []string) { 98 | if neg { 99 | // It would be strange to say "this file can have any content except this precise byte sequence". 100 | ts.Fatalf("unsupported: ! cmp") 101 | } 102 | if len(args) != 2 { 103 | ts.Fatalf("usage: cmp file1 file2") 104 | } 105 | 106 | ts.doCmdCmp(args, false) 107 | } 108 | 109 | // cmpenv compares two files with environment variable substitution. 110 | func (ts *TestScript) cmdCmpenv(neg bool, args []string) { 111 | if neg { 112 | ts.Fatalf("unsupported: ! cmpenv") 113 | } 114 | if len(args) != 2 { 115 | ts.Fatalf("usage: cmpenv file1 file2") 116 | } 117 | ts.doCmdCmp(args, true) 118 | } 119 | 120 | func (ts *TestScript) doCmdCmp(args []string, env bool) { 121 | name1, name2 := args[0], args[1] 122 | text1 := ts.ReadFile(name1) 123 | 124 | absName2 := ts.MkAbs(name2) 125 | data, err := ioutil.ReadFile(absName2) 126 | ts.Check(err) 127 | text2 := string(data) 128 | if env { 129 | text2 = ts.expand(text2) 130 | } 131 | if text1 == text2 { 132 | return 133 | } 134 | if ts.params.UpdateScripts && !env && (args[0] == "stdout" || args[0] == "stderr") { 135 | if scriptFile, ok := ts.scriptFiles[absName2]; ok { 136 | ts.scriptUpdates[scriptFile] = text1 137 | return 138 | } 139 | // The file being compared against isn't in the txtar archive, so don't 140 | // update the script. 141 | } 142 | 143 | ts.Logf("[diff -%s +%s]\n%s\n", name1, name2, textutil.Diff(text1, text2)) 144 | ts.Fatalf("%s and %s differ", name1, name2) 145 | } 146 | 147 | // cp copies files, maybe eventually directories. 148 | func (ts *TestScript) cmdCp(neg bool, args []string) { 149 | if neg { 150 | ts.Fatalf("unsupported: ! cp") 151 | } 152 | if len(args) < 2 { 153 | ts.Fatalf("usage: cp src... dst") 154 | } 155 | 156 | dst := ts.MkAbs(args[len(args)-1]) 157 | info, err := os.Stat(dst) 158 | dstDir := err == nil && info.IsDir() 159 | if len(args) > 2 && !dstDir { 160 | ts.Fatalf("cp: destination %s is not a directory", dst) 161 | } 162 | 163 | for _, arg := range args[:len(args)-1] { 164 | var ( 165 | src string 166 | data []byte 167 | mode os.FileMode 168 | ) 169 | switch arg { 170 | case "stdout": 171 | src = arg 172 | data = []byte(ts.stdout) 173 | mode = 0666 174 | case "stderr": 175 | src = arg 176 | data = []byte(ts.stderr) 177 | mode = 0666 178 | default: 179 | src = ts.MkAbs(arg) 180 | info, err := os.Stat(src) 181 | ts.Check(err) 182 | mode = info.Mode() & 0777 183 | data, err = ioutil.ReadFile(src) 184 | ts.Check(err) 185 | } 186 | targ := dst 187 | if dstDir { 188 | targ = filepath.Join(dst, filepath.Base(src)) 189 | } 190 | ts.Check(ioutil.WriteFile(targ, data, mode)) 191 | } 192 | } 193 | 194 | // env displays or adds to the environment. 195 | func (ts *TestScript) cmdEnv(neg bool, args []string) { 196 | if neg { 197 | ts.Fatalf("unsupported: ! env") 198 | } 199 | if len(args) == 0 { 200 | printed := make(map[string]bool) // env list can have duplicates; only print effective value (from envMap) once 201 | for _, kv := range ts.env { 202 | k := envvarname(kv[:strings.Index(kv, "=")]) 203 | if !printed[k] { 204 | printed[k] = true 205 | ts.Logf("%s=%s\n", k, ts.envMap[k]) 206 | } 207 | } 208 | return 209 | } 210 | for _, env := range args { 211 | i := strings.Index(env, "=") 212 | if i < 0 { 213 | // Display value instead of setting it. 214 | ts.Logf("%s=%s\n", env, ts.Getenv(env)) 215 | continue 216 | } 217 | ts.Setenv(env[:i], env[i+1:]) 218 | } 219 | } 220 | 221 | // exec runs the given command. 222 | func (ts *TestScript) cmdExec(neg bool, args []string) { 223 | if len(args) < 1 || (len(args) == 1 && args[0] == "&") { 224 | ts.Fatalf("usage: exec program [args...] [&]") 225 | } 226 | 227 | var err error 228 | if len(args) > 0 && args[len(args)-1] == "&" { 229 | var cmd *exec.Cmd 230 | cmd, err = ts.execBackground(args[0], args[1:len(args)-1]...) 231 | if err == nil { 232 | wait := make(chan struct{}) 233 | go func() { 234 | ctxWait(ts.ctxt, cmd) 235 | close(wait) 236 | }() 237 | ts.background = append(ts.background, backgroundCmd{cmd, wait, neg}) 238 | } 239 | ts.stdout, ts.stderr = "", "" 240 | } else { 241 | ts.stdout, ts.stderr, err = ts.exec(args[0], args[1:]...) 242 | if ts.stdout != "" { 243 | fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout) 244 | } 245 | if ts.stderr != "" { 246 | fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr) 247 | } 248 | if err == nil && neg { 249 | ts.Fatalf("unexpected command success") 250 | } 251 | } 252 | 253 | if err != nil { 254 | fmt.Fprintf(&ts.log, "[%v]\n", err) 255 | if ts.ctxt.Err() != nil { 256 | ts.Fatalf("test timed out while running command") 257 | } else if !neg { 258 | ts.Fatalf("unexpected command failure") 259 | } 260 | } 261 | } 262 | 263 | // exists checks that the list of files exists. 264 | func (ts *TestScript) cmdExists(neg bool, args []string) { 265 | var readonly bool 266 | if len(args) > 0 && args[0] == "-readonly" { 267 | readonly = true 268 | args = args[1:] 269 | } 270 | if len(args) == 0 { 271 | ts.Fatalf("usage: exists [-readonly] file...") 272 | } 273 | 274 | for _, file := range args { 275 | file = ts.MkAbs(file) 276 | info, err := os.Stat(file) 277 | if err == nil && neg { 278 | what := "file" 279 | if info.IsDir() { 280 | what = "directory" 281 | } 282 | ts.Fatalf("%s %s unexpectedly exists", what, file) 283 | } 284 | if err != nil && !neg { 285 | ts.Fatalf("%s does not exist", file) 286 | } 287 | if err == nil && !neg && readonly && info.Mode()&0222 != 0 { 288 | ts.Fatalf("%s exists but is writable", file) 289 | } 290 | } 291 | } 292 | 293 | // mkdir creates directories. 294 | func (ts *TestScript) cmdMkdir(neg bool, args []string) { 295 | if neg { 296 | ts.Fatalf("unsupported: ! mkdir") 297 | } 298 | if len(args) < 1 { 299 | ts.Fatalf("usage: mkdir dir...") 300 | } 301 | for _, arg := range args { 302 | ts.Check(os.MkdirAll(ts.MkAbs(arg), 0777)) 303 | } 304 | } 305 | 306 | // unquote unquotes files. 307 | func (ts *TestScript) cmdUnquote(neg bool, args []string) { 308 | if neg { 309 | ts.Fatalf("unsupported: ! unquote") 310 | } 311 | for _, arg := range args { 312 | file := ts.MkAbs(arg) 313 | data, err := ioutil.ReadFile(file) 314 | ts.Check(err) 315 | data, err = txtar.Unquote(data) 316 | ts.Check(err) 317 | err = ioutil.WriteFile(file, data, 0666) 318 | ts.Check(err) 319 | } 320 | } 321 | 322 | // rm removes files or directories. 323 | func (ts *TestScript) cmdRm(neg bool, args []string) { 324 | if neg { 325 | ts.Fatalf("unsupported: ! rm") 326 | } 327 | if len(args) < 1 { 328 | ts.Fatalf("usage: rm file...") 329 | } 330 | for _, arg := range args { 331 | file := ts.MkAbs(arg) 332 | removeAll(file) // does chmod and then attempts rm 333 | ts.Check(os.RemoveAll(file)) // report error 334 | } 335 | } 336 | 337 | // skip marks the test skipped. 338 | func (ts *TestScript) cmdSkip(neg bool, args []string) { 339 | if len(args) > 1 { 340 | ts.Fatalf("usage: skip [msg]") 341 | } 342 | if neg { 343 | ts.Fatalf("unsupported: ! skip") 344 | } 345 | 346 | // Before we mark the test as skipped, shut down any background processes and 347 | // make sure they have returned the correct status. 348 | for _, bg := range ts.background { 349 | interruptProcess(bg.cmd.Process) 350 | } 351 | ts.cmdWait(false, nil) 352 | 353 | if len(args) == 1 { 354 | ts.t.Skip(args[0]) 355 | } 356 | ts.t.Skip() 357 | } 358 | 359 | func (ts *TestScript) cmdStdin(neg bool, args []string) { 360 | if neg { 361 | ts.Fatalf("unsupported: ! stdin") 362 | } 363 | if len(args) != 1 { 364 | ts.Fatalf("usage: stdin filename") 365 | } 366 | data, err := ioutil.ReadFile(ts.MkAbs(args[0])) 367 | ts.Check(err) 368 | ts.stdin = string(data) 369 | } 370 | 371 | // stdout checks that the last go command standard output matches a regexp. 372 | func (ts *TestScript) cmdStdout(neg bool, args []string) { 373 | scriptMatch(ts, neg, args, ts.stdout, "stdout") 374 | } 375 | 376 | // stderr checks that the last go command standard output matches a regexp. 377 | func (ts *TestScript) cmdStderr(neg bool, args []string) { 378 | scriptMatch(ts, neg, args, ts.stderr, "stderr") 379 | } 380 | 381 | // grep checks that file content matches a regexp. 382 | // Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax. 383 | func (ts *TestScript) cmdGrep(neg bool, args []string) { 384 | scriptMatch(ts, neg, args, "", "grep") 385 | } 386 | 387 | // stop stops execution of the test (marking it passed). 388 | func (ts *TestScript) cmdStop(neg bool, args []string) { 389 | if neg { 390 | ts.Fatalf("unsupported: ! stop") 391 | } 392 | if len(args) > 1 { 393 | ts.Fatalf("usage: stop [msg]") 394 | } 395 | if len(args) == 1 { 396 | ts.Logf("stop: %s\n", args[0]) 397 | } else { 398 | ts.Logf("stop\n") 399 | } 400 | ts.stopped = true 401 | } 402 | 403 | // symlink creates a symbolic link. 404 | func (ts *TestScript) cmdSymlink(neg bool, args []string) { 405 | if neg { 406 | ts.Fatalf("unsupported: ! symlink") 407 | } 408 | if len(args) != 3 || args[1] != "->" { 409 | ts.Fatalf("usage: symlink file -> target") 410 | } 411 | // Note that the link target args[2] is not interpreted with MkAbs: 412 | // it will be interpreted relative to the directory file is in. 413 | ts.Check(os.Symlink(args[2], ts.MkAbs(args[0]))) 414 | } 415 | 416 | // Tait waits for background commands to exit, setting stderr and stdout to their result. 417 | func (ts *TestScript) cmdWait(neg bool, args []string) { 418 | if neg { 419 | ts.Fatalf("unsupported: ! wait") 420 | } 421 | if len(args) > 0 { 422 | ts.Fatalf("usage: wait") 423 | } 424 | 425 | var stdouts, stderrs []string 426 | for _, bg := range ts.background { 427 | <-bg.wait 428 | 429 | args := append([]string{filepath.Base(bg.cmd.Args[0])}, bg.cmd.Args[1:]...) 430 | fmt.Fprintf(&ts.log, "[background] %s: %v\n", strings.Join(args, " "), bg.cmd.ProcessState) 431 | 432 | cmdStdout := bg.cmd.Stdout.(*strings.Builder).String() 433 | if cmdStdout != "" { 434 | fmt.Fprintf(&ts.log, "[stdout]\n%s", cmdStdout) 435 | stdouts = append(stdouts, cmdStdout) 436 | } 437 | 438 | cmdStderr := bg.cmd.Stderr.(*strings.Builder).String() 439 | if cmdStderr != "" { 440 | fmt.Fprintf(&ts.log, "[stderr]\n%s", cmdStderr) 441 | stderrs = append(stderrs, cmdStderr) 442 | } 443 | 444 | if bg.cmd.ProcessState.Success() { 445 | if bg.neg { 446 | ts.Fatalf("unexpected command success") 447 | } 448 | } else { 449 | if ts.ctxt.Err() != nil { 450 | ts.Fatalf("test timed out while running command") 451 | } else if !bg.neg { 452 | ts.Fatalf("unexpected command failure") 453 | } 454 | } 455 | } 456 | 457 | ts.stdout = strings.Join(stdouts, "") 458 | ts.stderr = strings.Join(stderrs, "") 459 | ts.background = nil 460 | } 461 | 462 | // scriptMatch implements both stdout and stderr. 463 | func scriptMatch(ts *TestScript, neg bool, args []string, text, name string) { 464 | n := 0 465 | if len(args) >= 1 && strings.HasPrefix(args[0], "-count=") { 466 | if neg { 467 | ts.Fatalf("cannot use -count= with negated match") 468 | } 469 | var err error 470 | n, err = strconv.Atoi(args[0][len("-count="):]) 471 | if err != nil { 472 | ts.Fatalf("bad -count=: %v", err) 473 | } 474 | if n < 1 { 475 | ts.Fatalf("bad -count=: must be at least 1") 476 | } 477 | args = args[1:] 478 | } 479 | 480 | extraUsage := "" 481 | want := 1 482 | if name == "grep" { 483 | extraUsage = " file" 484 | want = 2 485 | } 486 | if len(args) != want { 487 | ts.Fatalf("usage: %s [-count=N] 'pattern'%s", name, extraUsage) 488 | } 489 | 490 | pattern := args[0] 491 | re, err := regexp.Compile(`(?m)` + pattern) 492 | ts.Check(err) 493 | 494 | isGrep := name == "grep" 495 | if isGrep { 496 | name = args[1] // for error messages 497 | data, err := ioutil.ReadFile(ts.MkAbs(args[1])) 498 | ts.Check(err) 499 | text = string(data) 500 | } 501 | 502 | if neg { 503 | if re.MatchString(text) { 504 | if isGrep { 505 | ts.Logf("[%s]\n%s\n", name, text) 506 | } 507 | ts.Fatalf("unexpected match for %#q found in %s: %s", pattern, name, re.FindString(text)) 508 | } 509 | } else { 510 | if !re.MatchString(text) { 511 | if isGrep { 512 | ts.Logf("[%s]\n%s\n", name, text) 513 | } 514 | ts.Fatalf("no match for %#q found in %s", pattern, name) 515 | } 516 | if n > 0 { 517 | count := len(re.FindAllString(text, -1)) 518 | if count != n { 519 | if isGrep { 520 | ts.Logf("[%s]\n%s\n", name, text) 521 | } 522 | ts.Fatalf("have %d matches for %#q, want %d", count, pattern, n) 523 | } 524 | } 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /vendor/github.com/rogpeppe/go-internal/module/module.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package module defines the module.Version type 6 | // along with support code. 7 | package module 8 | 9 | // IMPORTANT NOTE 10 | // 11 | // This file essentially defines the set of valid import paths for the go command. 12 | // There are many subtle considerations, including Unicode ambiguity, 13 | // security, network, and file system representations. 14 | // 15 | // This file also defines the set of valid module path and version combinations, 16 | // another topic with many subtle considerations. 17 | // 18 | // Changes to the semantics in this file require approval from rsc. 19 | 20 | import ( 21 | "fmt" 22 | "sort" 23 | "strings" 24 | "unicode" 25 | "unicode/utf8" 26 | 27 | "github.com/rogpeppe/go-internal/semver" 28 | ) 29 | 30 | // A Version is defined by a module path and version pair. 31 | type Version struct { 32 | Path string 33 | 34 | // Version is usually a semantic version in canonical form. 35 | // There are two exceptions to this general rule. 36 | // First, the top-level target of a build has no specific version 37 | // and uses Version = "". 38 | // Second, during MVS calculations the version "none" is used 39 | // to represent the decision to take no version of a given module. 40 | Version string `json:",omitempty"` 41 | } 42 | 43 | // Check checks that a given module path, version pair is valid. 44 | // In addition to the path being a valid module path 45 | // and the version being a valid semantic version, 46 | // the two must correspond. 47 | // For example, the path "yaml/v2" only corresponds to 48 | // semantic versions beginning with "v2.". 49 | func Check(path, version string) error { 50 | if err := CheckPath(path); err != nil { 51 | return err 52 | } 53 | if !semver.IsValid(version) { 54 | return fmt.Errorf("malformed semantic version %v", version) 55 | } 56 | _, pathMajor, _ := SplitPathVersion(path) 57 | if !MatchPathMajor(version, pathMajor) { 58 | if pathMajor == "" { 59 | pathMajor = "v0 or v1" 60 | } 61 | if pathMajor[0] == '.' { // .v1 62 | pathMajor = pathMajor[1:] 63 | } 64 | return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor) 65 | } 66 | return nil 67 | } 68 | 69 | // firstPathOK reports whether r can appear in the first element of a module path. 70 | // The first element of the path must be an LDH domain name, at least for now. 71 | // To avoid case ambiguity, the domain name must be entirely lower case. 72 | func firstPathOK(r rune) bool { 73 | return r == '-' || r == '.' || 74 | '0' <= r && r <= '9' || 75 | 'a' <= r && r <= 'z' 76 | } 77 | 78 | // pathOK reports whether r can appear in an import path element. 79 | // Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. 80 | // This matches what "go get" has historically recognized in import paths. 81 | // TODO(rsc): We would like to allow Unicode letters, but that requires additional 82 | // care in the safe encoding (see note below). 83 | func pathOK(r rune) bool { 84 | if r < utf8.RuneSelf { 85 | return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || 86 | '0' <= r && r <= '9' || 87 | 'A' <= r && r <= 'Z' || 88 | 'a' <= r && r <= 'z' 89 | } 90 | return false 91 | } 92 | 93 | // fileNameOK reports whether r can appear in a file name. 94 | // For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. 95 | // If we expand the set of allowed characters here, we have to 96 | // work harder at detecting potential case-folding and normalization collisions. 97 | // See note about "safe encoding" below. 98 | func fileNameOK(r rune) bool { 99 | if r < utf8.RuneSelf { 100 | // Entire set of ASCII punctuation, from which we remove characters: 101 | // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ 102 | // We disallow some shell special characters: " ' * < > ? ` | 103 | // (Note that some of those are disallowed by the Windows file system as well.) 104 | // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). 105 | // We allow spaces (U+0020) in file names. 106 | const allowed = "!#$%&()+,-.=@[]^_{}~ " 107 | if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { 108 | return true 109 | } 110 | for i := 0; i < len(allowed); i++ { 111 | if rune(allowed[i]) == r { 112 | return true 113 | } 114 | } 115 | return false 116 | } 117 | // It may be OK to add more ASCII punctuation here, but only carefully. 118 | // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. 119 | return unicode.IsLetter(r) 120 | } 121 | 122 | // CheckPath checks that a module path is valid. 123 | func CheckPath(path string) error { 124 | if err := checkPath(path, false); err != nil { 125 | return fmt.Errorf("malformed module path %q: %v", path, err) 126 | } 127 | i := strings.Index(path, "/") 128 | if i < 0 { 129 | i = len(path) 130 | } 131 | if i == 0 { 132 | return fmt.Errorf("malformed module path %q: leading slash", path) 133 | } 134 | if !strings.Contains(path[:i], ".") { 135 | return fmt.Errorf("malformed module path %q: missing dot in first path element", path) 136 | } 137 | if path[0] == '-' { 138 | return fmt.Errorf("malformed module path %q: leading dash in first path element", path) 139 | } 140 | for _, r := range path[:i] { 141 | if !firstPathOK(r) { 142 | return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r) 143 | } 144 | } 145 | if _, _, ok := SplitPathVersion(path); !ok { 146 | return fmt.Errorf("malformed module path %q: invalid version", path) 147 | } 148 | return nil 149 | } 150 | 151 | // CheckImportPath checks that an import path is valid. 152 | func CheckImportPath(path string) error { 153 | if err := checkPath(path, false); err != nil { 154 | return fmt.Errorf("malformed import path %q: %v", path, err) 155 | } 156 | return nil 157 | } 158 | 159 | // checkPath checks that a general path is valid. 160 | // It returns an error describing why but not mentioning path. 161 | // Because these checks apply to both module paths and import paths, 162 | // the caller is expected to add the "malformed ___ path %q: " prefix. 163 | // fileName indicates whether the final element of the path is a file name 164 | // (as opposed to a directory name). 165 | func checkPath(path string, fileName bool) error { 166 | if !utf8.ValidString(path) { 167 | return fmt.Errorf("invalid UTF-8") 168 | } 169 | if path == "" { 170 | return fmt.Errorf("empty string") 171 | } 172 | if strings.Contains(path, "..") { 173 | return fmt.Errorf("double dot") 174 | } 175 | if strings.Contains(path, "//") { 176 | return fmt.Errorf("double slash") 177 | } 178 | if path[len(path)-1] == '/' { 179 | return fmt.Errorf("trailing slash") 180 | } 181 | elemStart := 0 182 | for i, r := range path { 183 | if r == '/' { 184 | if err := checkElem(path[elemStart:i], fileName); err != nil { 185 | return err 186 | } 187 | elemStart = i + 1 188 | } 189 | } 190 | if err := checkElem(path[elemStart:], fileName); err != nil { 191 | return err 192 | } 193 | return nil 194 | } 195 | 196 | // checkElem checks whether an individual path element is valid. 197 | // fileName indicates whether the element is a file name (not a directory name). 198 | func checkElem(elem string, fileName bool) error { 199 | if elem == "" { 200 | return fmt.Errorf("empty path element") 201 | } 202 | if strings.Count(elem, ".") == len(elem) { 203 | return fmt.Errorf("invalid path element %q", elem) 204 | } 205 | if elem[0] == '.' && !fileName { 206 | return fmt.Errorf("leading dot in path element") 207 | } 208 | if elem[len(elem)-1] == '.' { 209 | return fmt.Errorf("trailing dot in path element") 210 | } 211 | charOK := pathOK 212 | if fileName { 213 | charOK = fileNameOK 214 | } 215 | for _, r := range elem { 216 | if !charOK(r) { 217 | return fmt.Errorf("invalid char %q", r) 218 | } 219 | } 220 | 221 | // Windows disallows a bunch of path elements, sadly. 222 | // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file 223 | short := elem 224 | if i := strings.Index(short, "."); i >= 0 { 225 | short = short[:i] 226 | } 227 | for _, bad := range badWindowsNames { 228 | if strings.EqualFold(bad, short) { 229 | return fmt.Errorf("disallowed path element %q", elem) 230 | } 231 | } 232 | return nil 233 | } 234 | 235 | // CheckFilePath checks whether a slash-separated file path is valid. 236 | func CheckFilePath(path string) error { 237 | if err := checkPath(path, true); err != nil { 238 | return fmt.Errorf("malformed file path %q: %v", path, err) 239 | } 240 | return nil 241 | } 242 | 243 | // badWindowsNames are the reserved file path elements on Windows. 244 | // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file 245 | var badWindowsNames = []string{ 246 | "CON", 247 | "PRN", 248 | "AUX", 249 | "NUL", 250 | "COM1", 251 | "COM2", 252 | "COM3", 253 | "COM4", 254 | "COM5", 255 | "COM6", 256 | "COM7", 257 | "COM8", 258 | "COM9", 259 | "LPT1", 260 | "LPT2", 261 | "LPT3", 262 | "LPT4", 263 | "LPT5", 264 | "LPT6", 265 | "LPT7", 266 | "LPT8", 267 | "LPT9", 268 | } 269 | 270 | // SplitPathVersion returns prefix and major version such that prefix+pathMajor == path 271 | // and version is either empty or "/vN" for N >= 2. 272 | // As a special case, gopkg.in paths are recognized directly; 273 | // they require ".vN" instead of "/vN", and for all N, not just N >= 2. 274 | func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { 275 | if strings.HasPrefix(path, "gopkg.in/") { 276 | return splitGopkgIn(path) 277 | } 278 | 279 | i := len(path) 280 | dot := false 281 | for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { 282 | if path[i-1] == '.' { 283 | dot = true 284 | } 285 | i-- 286 | } 287 | if i <= 1 || path[i-1] != 'v' || path[i-2] != '/' { 288 | return path, "", true 289 | } 290 | prefix, pathMajor = path[:i-2], path[i-2:] 291 | if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { 292 | return path, "", false 293 | } 294 | return prefix, pathMajor, true 295 | } 296 | 297 | // splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. 298 | func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { 299 | if !strings.HasPrefix(path, "gopkg.in/") { 300 | return path, "", false 301 | } 302 | i := len(path) 303 | if strings.HasSuffix(path, "-unstable") { 304 | i -= len("-unstable") 305 | } 306 | for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { 307 | i-- 308 | } 309 | if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { 310 | // All gopkg.in paths must end in vN for some N. 311 | return path, "", false 312 | } 313 | prefix, pathMajor = path[:i-2], path[i-2:] 314 | if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { 315 | return path, "", false 316 | } 317 | return prefix, pathMajor, true 318 | } 319 | 320 | // MatchPathMajor reports whether the semantic version v 321 | // matches the path major version pathMajor. 322 | func MatchPathMajor(v, pathMajor string) bool { 323 | if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { 324 | pathMajor = strings.TrimSuffix(pathMajor, "-unstable") 325 | } 326 | if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { 327 | // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. 328 | // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. 329 | return true 330 | } 331 | m := semver.Major(v) 332 | if pathMajor == "" { 333 | return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" 334 | } 335 | return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:] 336 | } 337 | 338 | // CanonicalVersion returns the canonical form of the version string v. 339 | // It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". 340 | func CanonicalVersion(v string) string { 341 | cv := semver.Canonical(v) 342 | if semver.Build(v) == "+incompatible" { 343 | cv += "+incompatible" 344 | } 345 | return cv 346 | } 347 | 348 | // Sort sorts the list by Path, breaking ties by comparing Versions. 349 | func Sort(list []Version) { 350 | sort.Slice(list, func(i, j int) bool { 351 | mi := list[i] 352 | mj := list[j] 353 | if mi.Path != mj.Path { 354 | return mi.Path < mj.Path 355 | } 356 | // To help go.sum formatting, allow version/file. 357 | // Compare semver prefix by semver rules, 358 | // file by string order. 359 | vi := mi.Version 360 | vj := mj.Version 361 | var fi, fj string 362 | if k := strings.Index(vi, "/"); k >= 0 { 363 | vi, fi = vi[:k], vi[k:] 364 | } 365 | if k := strings.Index(vj, "/"); k >= 0 { 366 | vj, fj = vj[:k], vj[k:] 367 | } 368 | if vi != vj { 369 | return semver.Compare(vi, vj) < 0 370 | } 371 | return fi < fj 372 | }) 373 | } 374 | 375 | // Safe encodings 376 | // 377 | // Module paths appear as substrings of file system paths 378 | // (in the download cache) and of web server URLs in the proxy protocol. 379 | // In general we cannot rely on file systems to be case-sensitive, 380 | // nor can we rely on web servers, since they read from file systems. 381 | // That is, we cannot rely on the file system to keep rsc.io/QUOTE 382 | // and rsc.io/quote separate. Windows and macOS don't. 383 | // Instead, we must never require two different casings of a file path. 384 | // Because we want the download cache to match the proxy protocol, 385 | // and because we want the proxy protocol to be possible to serve 386 | // from a tree of static files (which might be stored on a case-insensitive 387 | // file system), the proxy protocol must never require two different casings 388 | // of a URL path either. 389 | // 390 | // One possibility would be to make the safe encoding be the lowercase 391 | // hexadecimal encoding of the actual path bytes. This would avoid ever 392 | // needing different casings of a file path, but it would be fairly illegible 393 | // to most programmers when those paths appeared in the file system 394 | // (including in file paths in compiler errors and stack traces) 395 | // in web server logs, and so on. Instead, we want a safe encoding that 396 | // leaves most paths unaltered. 397 | // 398 | // The safe encoding is this: 399 | // replace every uppercase letter with an exclamation mark 400 | // followed by the letter's lowercase equivalent. 401 | // 402 | // For example, 403 | // github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. 404 | // github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy 405 | // github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. 406 | // 407 | // Import paths that avoid upper-case letters are left unchanged. 408 | // Note that because import paths are ASCII-only and avoid various 409 | // problematic punctuation (like : < and >), the safe encoding is also ASCII-only 410 | // and avoids the same problematic punctuation. 411 | // 412 | // Import paths have never allowed exclamation marks, so there is no 413 | // need to define how to encode a literal !. 414 | // 415 | // Although paths are disallowed from using Unicode (see pathOK above), 416 | // the eventual plan is to allow Unicode letters as well, to assume that 417 | // file systems and URLs are Unicode-safe (storing UTF-8), and apply 418 | // the !-for-uppercase convention. Note however that not all runes that 419 | // are different but case-fold equivalent are an upper/lower pair. 420 | // For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) 421 | // are considered to case-fold to each other. When we do add Unicode 422 | // letters, we must not assume that upper/lower are the only case-equivalent pairs. 423 | // Perhaps the Kelvin symbol would be disallowed entirely, for example. 424 | // Or perhaps it would encode as "!!k", or perhaps as "(212A)". 425 | // 426 | // Also, it would be nice to allow Unicode marks as well as letters, 427 | // but marks include combining marks, and then we must deal not 428 | // only with case folding but also normalization: both U+00E9 ('é') 429 | // and U+0065 U+0301 ('e' followed by combining acute accent) 430 | // look the same on the page and are treated by some file systems 431 | // as the same path. If we do allow Unicode marks in paths, there 432 | // must be some kind of normalization to allow only one canonical 433 | // encoding of any character used in an import path. 434 | 435 | // EncodePath returns the safe encoding of the given module path. 436 | // It fails if the module path is invalid. 437 | func EncodePath(path string) (encoding string, err error) { 438 | if err := CheckPath(path); err != nil { 439 | return "", err 440 | } 441 | 442 | return encodeString(path) 443 | } 444 | 445 | // EncodeVersion returns the safe encoding of the given module version. 446 | // Versions are allowed to be in non-semver form but must be valid file names 447 | // and not contain exclamation marks. 448 | func EncodeVersion(v string) (encoding string, err error) { 449 | if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { 450 | return "", fmt.Errorf("disallowed version string %q", v) 451 | } 452 | return encodeString(v) 453 | } 454 | 455 | func encodeString(s string) (encoding string, err error) { 456 | haveUpper := false 457 | for _, r := range s { 458 | if r == '!' || r >= utf8.RuneSelf { 459 | // This should be disallowed by CheckPath, but diagnose anyway. 460 | // The correctness of the encoding loop below depends on it. 461 | return "", fmt.Errorf("internal error: inconsistency in EncodePath") 462 | } 463 | if 'A' <= r && r <= 'Z' { 464 | haveUpper = true 465 | } 466 | } 467 | 468 | if !haveUpper { 469 | return s, nil 470 | } 471 | 472 | var buf []byte 473 | for _, r := range s { 474 | if 'A' <= r && r <= 'Z' { 475 | buf = append(buf, '!', byte(r+'a'-'A')) 476 | } else { 477 | buf = append(buf, byte(r)) 478 | } 479 | } 480 | return string(buf), nil 481 | } 482 | 483 | // DecodePath returns the module path of the given safe encoding. 484 | // It fails if the encoding is invalid or encodes an invalid path. 485 | func DecodePath(encoding string) (path string, err error) { 486 | path, ok := decodeString(encoding) 487 | if !ok { 488 | return "", fmt.Errorf("invalid module path encoding %q", encoding) 489 | } 490 | if err := CheckPath(path); err != nil { 491 | return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err) 492 | } 493 | return path, nil 494 | } 495 | 496 | // DecodeVersion returns the version string for the given safe encoding. 497 | // It fails if the encoding is invalid or encodes an invalid version. 498 | // Versions are allowed to be in non-semver form but must be valid file names 499 | // and not contain exclamation marks. 500 | func DecodeVersion(encoding string) (v string, err error) { 501 | v, ok := decodeString(encoding) 502 | if !ok { 503 | return "", fmt.Errorf("invalid version encoding %q", encoding) 504 | } 505 | if err := checkElem(v, true); err != nil { 506 | return "", fmt.Errorf("disallowed version string %q", v) 507 | } 508 | return v, nil 509 | } 510 | 511 | func decodeString(encoding string) (string, bool) { 512 | var buf []byte 513 | 514 | bang := false 515 | for _, r := range encoding { 516 | if r >= utf8.RuneSelf { 517 | return "", false 518 | } 519 | if bang { 520 | bang = false 521 | if r < 'a' || 'z' < r { 522 | return "", false 523 | } 524 | buf = append(buf, byte(r+'A'-'a')) 525 | continue 526 | } 527 | if r == '!' { 528 | bang = true 529 | continue 530 | } 531 | if 'A' <= r && r <= 'Z' { 532 | return "", false 533 | } 534 | buf = append(buf, byte(r)) 535 | } 536 | if bang { 537 | return "", false 538 | } 539 | return string(buf), true 540 | } 541 | --------------------------------------------------------------------------------