1
0
mirror of https://github.com/golang/go synced 2024-10-01 01:08:33 -06:00

cmd/go: many bug fixes

* Reject import paths of the form cmd/x/y.
* Reject 'go install' of command outside GOPATH
* Clearer error rejecting 'go install' of package outside GOPATH.
* Name temporary binary for first file in 'go run' list or for test.
* Provide a way to pass -ldflags arguments with spaces.
* Pass all Go files (even +build ignored ones) to go fix, go fmt, go vet.
* Reject 'go run foo_test.go'.
* Silence 'exit 1' prints from 'go tool' invocations.
* Make go test -xxxprofile leave binary behind for analysis.
* Reject ~ in GOPATH except on Windows.
* Get a little less confused by symlinks.
* Document that go test x y z runs three test binaries.
* Fix go test -timeout=0.
* Add -tags flag to 'go list'.
* Use pkg/gccgo_$GOOS_$GOARCH for gccgo output.

Fixes #3389.
Fixes #3500.
Fixes #3503.
Fixes #3760.
Fixes #3941.
Fixes #4007.
Fixes #4032.
Fixes #4074.
Fixes #4127.
Fixes #4140.
Fixes #4311.
Fixes #4568.
Fixes #4576.
Fixes #4702.

R=adg
CC=golang-dev
https://golang.org/cl/7225074
This commit is contained in:
Russ Cox 2013-01-31 08:06:38 -08:00
parent 87c3c1b002
commit 8b6534b78a
15 changed files with 346 additions and 70 deletions

View File

@ -79,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands:
See the documentation for the go/build package for
more information about build tags.
The list flags accept a space-separated list of strings. To embed spaces
in an element in the list, surround it with either single or double quotes.
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
see 'go help gopath'.
@ -167,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) {
cmd.Flag.BoolVar(&buildX, "x", false, "")
}
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
type stringsFlag []string
func (v *stringsFlag) Set(s string) error {
*v = strings.Fields(s)
return nil
var err error
*v, err = splitQuotedFields(s)
return err
}
func splitQuotedFields(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func (v *stringsFlag) String() string {
@ -244,7 +288,7 @@ func runInstall(cmd *Command, args []string) {
for _, p := range pkgs {
if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") {
errorf("go install: no install location for %s", p.ImportPath)
errorf("go install: no install location for directory %s outside GOPATH", p.Dir)
}
}
exitIfErrors()
@ -514,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.f = (*builder).build
a.target = a.objpkg
if a.link {
// An executable file.
// (This is the name of a temporary file.)
a.target = a.objdir + "a.out" + exeSuffix
// An executable file. (This is the name of a temporary file.)
// Because we run the temporary file in 'go run' and 'go test',
// the name will show up in ps listings. If the caller has specified
// a name, use that instead of a.out. The binary is generated
// in an otherwise empty subdirectory named exe to avoid
// naming conflicts. The only possible conflict is if we were
// to create a top-level package named exe.
name := "a.out"
if p.exeName != "" {
name = p.exeName
}
a.target = a.objdir + filepath.Join("exe", name) + exeSuffix
}
}
@ -690,6 +743,14 @@ func (b *builder) build(a *action) (err error) {
return err
}
// make target directory
dir, _ := filepath.Split(a.target)
if dir != "" {
if err := b.mkdir(dir); err != nil {
return err
}
}
var gofiles, cfiles, sfiles, objects, cgoObjects []string
gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...)
@ -914,7 +975,7 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
incMap[dir] = true
if _, ok := buildToolchain.(gccgcToolchain); ok {
dir = filepath.Join(dir, "gccgo")
dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch)
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
if buildRace {

View File

@ -95,6 +95,9 @@ The build flags are shared by the build, install, run, and test commands:
See the documentation for the go/build package for
more information about build tags.
The list flags accept a space-separated list of strings. To embed spaces
in an element in the list, surround it with either single or double quotes.
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
see 'go help gopath'.
@ -272,7 +275,7 @@ List packages
Usage:
go list [-e] [-f format] [-json] [packages]
go list [-e] [-f format] [-json] [-tags 'tag list'] [packages]
List lists the packages named by the import paths, one per line.
@ -301,6 +304,7 @@ which calls strings.Join. The struct being passed to the template is:
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files
HFiles []string // .h source files
SFiles []string // .s source files
@ -341,6 +345,9 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing
(zeroed).
The -tags flag specifies a list of build tags, like in the 'go build'
command.
For more about specifying packages, see 'go help packages'.
@ -376,6 +383,7 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
By default, go test needs no arguments. It compiles and tests the package
with source in the current directory, including tests, and runs the tests.
@ -748,6 +756,9 @@ will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
The test flags that generate profiles also leave the test binary in pkg.test
for use when analyzing the profiles.
Description of testing functions
@ -763,8 +774,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature,
func BenchmarkXXX(b *testing.B) { ... }
An example function is similar to a test function but, instead of using *testing.T
to report success or failure, prints output to os.Stdout and os.Stderr.
An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
That output is compared against the function's "Output:" comment, which
must be the last comment in the function body (see example below). An
example with no such comment, or with no text after "Output:" is compiled

View File

@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
run(stringList(tool("fix"), relPaths(pkg.gofiles)))
run(stringList(tool("fix"), relPaths(pkg.allgofiles)))
}
}

View File

@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles)))
run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
}
}

View File

@ -204,7 +204,7 @@ func download(arg string, stk *importStack) {
// due to wildcard expansion.
for _, p := range pkgs {
if *getFix {
run(stringList(tool("fix"), relPaths(p.gofiles)))
run(stringList(tool("fix"), relPaths(p.allgofiles)))
// The imports might have changed, so reload again.
p = reloadPackage(arg, stk)

View File

@ -14,7 +14,7 @@ import (
)
var cmdList = &Command{
UsageLine: "list [-e] [-f format] [-json] [packages]",
UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]",
Short: "list packages",
Long: `
List lists the packages named by the import paths, one per line.
@ -44,6 +44,7 @@ which calls strings.Join. The struct being passed to the template is:
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files
HFiles []string // .h source files
SFiles []string // .s source files
@ -84,6 +85,9 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing
(zeroed).
The -tags flag specifies a list of build tags, like in the 'go build'
command.
For more about specifying packages, see 'go help packages'.
`,
}
@ -91,6 +95,7 @@ For more about specifying packages, see 'go help packages'.
func init() {
cmdList.Run = runList // break init cycle
cmdList.Flag.Var(buildCompiler{}, "compiler", "")
cmdList.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
}
var listE = cmdList.Flag.Bool("e", false, "")

View File

@ -129,6 +129,10 @@ func main() {
fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
} else {
for _, p := range filepath.SplitList(gopath) {
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p)
os.Exit(2)
}
if build.IsLocalImport(p) {
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p)
os.Exit(2)

View File

@ -38,6 +38,7 @@ type Package struct {
// Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints
CFiles []string `json:",omitempty"` // .c source files
HFiles []string `json:",omitempty"` // .h source files
SFiles []string `json:",omitempty"` // .s source files
@ -71,12 +72,14 @@ type Package struct {
imports []*Package
deps []*Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
allgofiles []string // gofiles + IgnoredGoFiles, absolute paths
target string // installed file for this package (may be executable)
fake bool // synthesized package
forceBuild bool // this package must be rebuilt
forceLibrary bool // this package is a library (even if named "main")
local bool // imported via local path (./ or ../)
localPrefix string // interpret ./ and ../ imports relative to this prefix
exeName string // desired name for temporary executable
}
func (p *Package) copyBuild(pp *build.Package) {
@ -92,6 +95,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
p.CFiles = pp.CFiles
p.HFiles = pp.HFiles
p.SFiles = pp.SFiles
@ -318,11 +322,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
if p.build.BinDir != "" {
p.target = filepath.Join(p.build.BinDir, elem)
}
if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) {
p.target = filepath.Join(gorootPkg, "tool", full)
}
if buildContext.GOOS == "windows" {
if p.target != "" && buildContext.GOOS == "windows" {
p.target += ".exe"
}
} else if p.local {
@ -357,6 +363,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
sort.Strings(p.gofiles)
p.allgofiles = stringList(p.IgnoredGoFiles)
for i := range p.allgofiles {
p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i])
}
p.allgofiles = append(p.allgofiles, p.gofiles...)
sort.Strings(p.allgofiles)
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
deps := make(map[string]bool)
@ -431,7 +444,7 @@ func (p *Package) swigSoname(file string) string {
func (p *Package) swigDir(ctxt *build.Context) string {
dir := p.build.PkgRoot
if ctxt.Compiler == "gccgo" {
dir = filepath.Join(dir, "gccgo")
dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH)
} else {
dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH)
}
@ -596,12 +609,23 @@ func loadPackage(arg string, stk *importStack) *Package {
arg = sub
}
}
if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") {
if strings.HasPrefix(arg, "cmd/") {
if p := cmdCache[arg]; p != nil {
return p
}
stk.push(arg)
defer stk.pop()
if strings.Contains(arg[4:], "/") {
p := &Package{
Error: &PackageError{
ImportStack: stk.copy(),
Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
},
}
return p
}
bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0)
bp.ImportPath = arg
bp.Goroot = true

View File

@ -46,6 +46,13 @@ func runRun(cmd *Command, args []string) {
if len(files) == 0 {
fatalf("go run: no go files listed")
}
for _, file := range files {
if strings.HasSuffix(file, "_test.go") {
// goFilesPackage is going to assign this to TestGoFiles.
// Reject since it won't be part of the build.
fatalf("go run: cannot run *_test.go files (%s)", file)
}
}
p := goFilesPackage(files)
if p.Error != nil {
fatalf("%s", p.Error)
@ -58,6 +65,13 @@ func runRun(cmd *Command, args []string) {
fatalf("go run: cannot run non-main package")
}
p.target = "" // must build - not up to date
var src string
if len(p.GoFiles) > 0 {
src = p.GoFiles[0]
} else {
src = p.CgoFiles[0]
}
p.exeName = src[:len(src)-len(".go")] // name temporary executable for first go file
a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
b.do(a)

View File

@ -183,7 +183,7 @@ fi
# issue 4186. go get cannot be used to download packages to $GOROOT
# Test that without GOPATH set, go get should fail
d=$(mktemp -d)
d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg
if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
@ -191,7 +191,7 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch
fi
rm -rf $d
# Test that with GOPATH=$GOROOT, go get should fail
d=$(mktemp -d)
d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg
if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
@ -199,6 +199,86 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat
fi
rm -rf $d
# issue 3941: args with spaces
d=$(mktemp -d -t testgo)
cat >$d/main.go<<EOF
package main
var extern string
func main() {
println(extern)
}
EOF
./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out
if ! grep -q '^hello world' hello.out; then
echo "ldflags -X main.extern 'hello world' failed. Output:"
cat hello.out
ok=false
fi
rm -rf $d
# test that go test -cpuprofile leaves binary behind
./testgo test -cpuprofile strings.prof strings || ok=false
if [ ! -x strings.test ]; then
echo "go test -cpuprofile did not create strings.test"
ok=false
fi
rm -f strings.prof strings.test
# issue 4568. test that symlinks don't screw things up too badly.
old=$(pwd)
d=$(mktemp -d -t testgo)
mkdir -p $d/src
(
ln -s $d $d/src/dir1
cd $d/src/dir1
echo package p >p.go
export GOPATH=$d
if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then
echo got lost in symlink tree:
pwd
env|grep WD
$old/testgo list -json . dir1
touch $d/failed
fi
)
if [ -f $d/failed ]; then
ok=false
fi
rm -rf $d
# issue 4515.
d=$(mktemp -d -t testgo)
mkdir -p $d/src/example/a $d/src/example/b $d/bin
cat >$d/src/example/a/main.go <<EOF
package main
func main() {}
EOF
cat >$d/src/example/b/main.go <<EOF
// +build mytag
package main
func main() {}
EOF
GOPATH=$d ./testgo install -tags mytag example/a example/b || ok=false
if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then
echo go install example/a example/b did not install binaries
ok=false
fi
rm -f $d/bin/*
GOPATH=$d ./testgo install -tags mytag example/... || ok=false
if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then
echo go install example/... did not install binaries
ok=false
fi
rm -f $d/bin/*go
export GOPATH=$d
if [ "$(./testgo list -tags mytag example/b...)" != "example/b" ]; then
echo go list example/b did not find example/b
ok=false
fi
unset GOPATH
rm -rf $d
# clean up
rm -rf testdata/bin testdata/bin1
rm -f testgo

View File

@ -48,6 +48,7 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
By default, go test needs no arguments. It compiles and tests the package
with source in the current directory, including tests, and runs the tests.
@ -156,6 +157,9 @@ here are passed through unaltered. For instance, the command
will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
The test flags that generate profiles also leave the test binary in pkg.test
for use when analyzing the profiles.
`,
}
@ -206,6 +210,7 @@ See the documentation of the testing package for more information.
var (
testC bool // -c flag
testProfile bool // some profiling flag
testI bool // -i flag
testV bool // -v flag
testFiles []string // -file flag(s) TODO: not respected
@ -231,12 +236,15 @@ func runTest(cmd *Command, args []string) {
if testC && len(pkgs) != 1 {
fatalf("cannot use -c flag with multiple packages")
}
if testProfile && len(pkgs) != 1 {
fatalf("cannot use test profile flag with multiple packages")
}
// If a test timeout was given and is parseable, set our kill timeout
// to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire.
if dt, err := time.ParseDuration(testTimeout); err == nil {
if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 {
testKillTimeout = dt + 1*time.Minute
}
@ -427,7 +435,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// Use last element of import path, not package name.
// They differ when package name is "main".
_, elem := path.Split(p.ImportPath)
// But if the import path is "command-line-arguments",
// like it is during 'go run', use the package name.
var elem string
if p.ImportPath == "command-line-arguments" {
elem = p.Name
} else {
_, elem = path.Split(p.ImportPath)
}
testBinary := elem + ".test"
// The ptest package needs to be importable under the
@ -554,14 +569,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a.target = filepath.Join(testDir, testBinary) + exeSuffix
pmainAction := a
if testC {
// -c flag: create action to copy binary to ./test.out.
if testC || testProfile {
// -c or profiling flag: create action to copy binary to ./test.out.
runAction = &action{
f: (*builder).install,
deps: []*action{pmainAction},
p: pmain,
target: testBinary + exeSuffix,
target: filepath.Join(cwd, testBinary+exeSuffix),
}
pmainAction = runAction // in case we are running the test
}
if testC {
printAction = &action{p: p, deps: []*action{runAction}} // nop
} else {
// run test
@ -655,7 +673,7 @@ func (b *builder) runTest(a *action) error {
case <-tick.C:
cmd.Process.Kill()
err = <-done
fmt.Fprintf(&buf, "*** Test killed: ran too long.\n")
fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout)
}
tick.Stop()
}

View File

@ -134,6 +134,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, args[i])
continue
}
var err error
switch f.name {
// bool flags.
case "a", "c", "i", "n", "x", "v", "work", "race":
@ -141,11 +142,20 @@ func testFlags(args []string) (packageNames, passToTest []string) {
case "p":
setIntFlag(&buildP, value)
case "gcflags":
buildGcflags = strings.Fields(value)
buildGcflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "ldflags":
buildLdflags = strings.Fields(value)
buildLdflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "gccgoflags":
buildGccgoflags = strings.Fields(value)
buildGccgoflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "tags":
buildContext.BuildTags = strings.Fields(value)
case "compiler":
@ -157,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
testBench = true
case "timeout":
testTimeout = value
case "blockprofile", "cpuprofile", "memprofile":
testProfile = true
}
if extraWord {
i++

View File

@ -100,7 +100,14 @@ func runTool(cmd *Command, args []string) {
}
err := toolCmd.Run()
if err != nil {
// Only print about the exit status if the command
// didn't even run (not an ExitError) or it didn't exit cleanly
// or we're printing command lines too (-x mode).
// Assume if command exited cleanly (even with non-zero status)
// it printed any messages it wanted to print.
if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || buildX {
fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
}
setExitStatus(1)
return
}

View File

@ -32,6 +32,6 @@ func runVet(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
run(tool("vet"), relPaths(pkg.gofiles))
run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
}
}

View File

@ -117,12 +117,27 @@ func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
return f(root, dir)
}
if p, err := filepath.EvalSymlinks(root); err == nil {
root = p
// Try using paths we received.
if rel, ok = hasSubdir(root, dir); ok {
return
}
if p, err := filepath.EvalSymlinks(dir); err == nil {
dir = p
// Try expanding symlinks and comparing
// expanded against unexpanded and
// expanded against expanded.
rootSym, _ := filepath.EvalSymlinks(root)
dirSym, _ := filepath.EvalSymlinks(dir)
if rel, ok = hasSubdir(rootSym, dir); ok {
return
}
if rel, ok = hasSubdir(root, dirSym); ok {
return
}
return hasSubdir(rootSym, dirSym)
}
func hasSubdir(root, dir string) (rel string, ok bool) {
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
@ -181,6 +196,21 @@ func (ctxt *Context) gopath() []string {
// Do not get confused by this common mistake.
continue
}
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
// Path segments containing ~ on Unix are almost always
// users who have incorrectly quoted ~ while setting GOPATH,
// preventing it from expanding to $HOME.
// The situation is made more confusing by the fact that
// bash allows quoted ~ in $PATH (most shells do not).
// Do not get confused by this, and do not try to use the path.
// It does not exist, and printing errors about it confuses
// those users even more, because they think "sure ~ exists!".
// The go command diagnoses this situation and prints a
// useful error.
// On Windows, ~ is used in short names, such as c:\progra~1
// for c:\program files.
continue
}
all = append(all, p)
}
return all
@ -286,6 +316,7 @@ type Package struct {
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
IgnoredGoFiles []string // .go source files ignored for this build
CFiles []string // .c source files
HFiles []string // .h source files
SFiles []string // .s source files
@ -519,15 +550,20 @@ Found:
strings.HasPrefix(name, ".") {
continue
}
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
continue
}
i := strings.LastIndex(name, ".")
if i < 0 {
i = len(name)
}
ext := name[i:]
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
continue
}
switch ext {
case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
// tentatively okay - read to make sure
@ -561,6 +597,9 @@ Found:
// Look for +build comments to accept or reject the file.
if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
continue
}
@ -593,6 +632,7 @@ Found:
pkg := pf.Name.Name
if pkg == "documentation" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
continue
}