tg.grepStderrNot("no packages being tested depend on matches", "bad match message")
tg.grepStdout("coverage: 100", "no coverage")
}
+
+// Regression test for golang.org/issue/34499: version command should not crash
+// when executed in a deleted directory on Linux.
+func TestExecInDeletedDir(t *testing.T) {
+ // The crash has only been reproduced on Linux.
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+ t.Skip()
+ }
+ tg := testgo(t)
+ defer tg.cleanup()
+
+ wd, err := os.Getwd()
+ tg.check(err)
+ tg.makeTempdir()
+ tg.check(os.Chdir(tg.tempdir))
+ defer func() { tg.check(os.Chdir(wd)) }()
+
+ tg.check(os.Remove(tg.tempdir))
+
+ // `go version` should not fail
+ tg.run("version")
+}
"os"
"path/filepath"
"strings"
+ "sync"
)
-func getwd() string {
- wd, err := os.Getwd()
- if err != nil {
- Fatalf("cannot determine current directory: %v", err)
- }
- return wd
-}
+var cwd string
+var cwdOnce sync.Once
-var Cwd = getwd()
+// Cwd returns the current working directory at the time of the first call.
+func Cwd() string {
+ cwdOnce.Do(func() {
+ var err error
+ cwd, err = os.Getwd()
+ if err != nil {
+ Fatalf("cannot determine current directory: %v", err)
+ }
+ })
+ return cwd
+}
// ShortPath returns an absolute or relative name for path, whatever is shorter.
func ShortPath(path string) string {
- if rel, err := filepath.Rel(Cwd, path); err == nil && len(rel) < len(path) {
+ if rel, err := filepath.Rel(Cwd(), path); err == nil && len(rel) < len(path) {
return rel
}
return path
func RelPaths(paths []string) []string {
var out []string
for _, p := range paths {
- rel, err := filepath.Rel(Cwd, p)
+ rel, err := filepath.Rel(Cwd(), p)
if err == nil && len(rel) < len(p) {
p = rel
}
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
- if err := fsys.Init(base.Cwd); err != nil {
+ if err := fsys.Init(base.Cwd()); err != nil {
base.Fatalf("go: %v", err)
}
// TODO(matloob): encapsulate these in an io/fs-like interface
var overlay map[string]*node // path -> file or directory node
-var cwd string // copy of base.Cwd to avoid dependency
+var cwd string // copy of base.Cwd() to avoid dependency
// Canonicalize a path for looking it up in the overlay.
// Important: filepath.Join(cwd, path) doesn't always produce
load1 := func(path string, mode int) *load.Package {
if parent == nil {
mode := 0 // don't do module or vendor resolution
- return load.LoadImport(context.TODO(), load.PackageOpts{}, path, base.Cwd, nil, stk, nil, mode)
+ return load.LoadImport(context.TODO(), load.PackageOpts{}, path, base.Cwd(), nil, stk, nil, mode)
}
return load.LoadImport(context.TODO(), load.PackageOpts{}, path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
}
// Set is called each time the flag is encountered on the command line.
func (f *PerPackageFlag) Set(v string) error {
- return f.set(v, base.Cwd)
+ return f.set(v, base.Cwd())
}
// set is the implementation of Set, taking a cwd (current working directory) for easier testing.
})
packageDataCache.Delete(p.ImportPath)
}
- return LoadImport(context.TODO(), PackageOpts{}, arg, base.Cwd, nil, stk, nil, 0)
+ return LoadImport(context.TODO(), PackageOpts{}, arg, base.Cwd(), nil, stk, nil, 0)
}
// dirToImportPath returns the pseudo-import path we use for a package
case pre.sema <- struct{}{}:
go func(pkg string) {
mode := 0 // don't use vendoring or module import resolution
- bp, loaded, err := loadPackageData(ctx, pkg, "", base.Cwd, "", false, mode)
+ bp, loaded, err := loadPackageData(ctx, pkg, "", base.Cwd(), "", false, mode)
<-pre.sema
if bp != nil && loaded && err == nil && !opts.IgnoreImports {
pre.preloadImports(ctx, opts, bp.Imports, bp)
if pkg == "" {
panic(fmt.Sprintf("ImportPaths returned empty package for pattern %s", m.Pattern()))
}
- p := loadImport(ctx, opts, pre, pkg, base.Cwd, nil, &stk, nil, 0)
+ p := loadImport(ctx, opts, pre, pkg, base.Cwd(), nil, &stk, nil, 0)
p.Match = append(p.Match, m.Pattern())
p.Internal.CmdlinePkg = true
if m.IsLiteral() {
var err error
if dir == "" {
- dir = base.Cwd
+ dir = base.Cwd()
}
dir, err = filepath.Abs(dir)
if err != nil {
return
}
- if err := fsys.Init(base.Cwd); err != nil {
+ if err := fsys.Init(base.Cwd()); err != nil {
base.Fatalf("go: %v", err)
}
}
modRoot = ""
} else {
- modRoot = findModuleRoot(base.Cwd)
+ modRoot = findModuleRoot(base.Cwd())
if modRoot == "" {
if cfg.ModFile != "" {
base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
return false
}
- if modRoot := findModuleRoot(base.Cwd); modRoot == "" {
+ if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
// GO111MODULE is 'auto', and we can't find a module root.
// Stay in GOPATH mode.
return false
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
- if dir, name := findAltConfig(base.Cwd); dir != "" {
- rel, err := filepath.Rel(base.Cwd, dir)
+ if dir, name := findAltConfig(base.Cwd()); dir != "" {
+ rel, err := filepath.Rel(base.Cwd(), dir)
if err != nil {
rel = dir
}
// exactly the same as in the legacy configuration (for example, we can't get
// packages at multiple versions from the same module).
func CreateModFile(ctx context.Context, modPath string) {
- modRoot = base.Cwd
+ modRoot = base.Cwd()
Init()
modFilePath := ModFilePath()
if _, err := fsys.Stat(modFilePath); err == nil {
Target = m
targetPrefix = m.Path
- if rel := search.InDir(base.Cwd, cfg.GOROOTsrc); rel != "" {
+ if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" {
targetInGorootSrc = true
if m.Path == "std" {
// The "std" module in GOROOT/src is the Go standard library. Unlike other
dir := filepath.Dir(filepath.Clean(m.Pattern()[:i+3]))
absDir := dir
if !filepath.IsAbs(dir) {
- absDir = filepath.Join(base.Cwd, dir)
+ absDir = filepath.Join(base.Cwd(), dir)
}
if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(ctx, absDir, rs) == "" {
m.Dirs = []string{}
if filepath.IsAbs(dir) {
absDir = filepath.Clean(dir)
} else {
- absDir = filepath.Join(base.Cwd, dir)
+ absDir = filepath.Join(base.Cwd(), dir)
}
bp, err := cfg.BuildContext.ImportDir(absDir, 0)
LoadModFile(ctx) // Sets targetPrefix.
if !filepath.IsAbs(dir) {
- dir = filepath.Join(base.Cwd, dir)
+ dir = filepath.Join(base.Cwd(), dir)
} else {
dir = filepath.Clean(dir)
}
for i, dir := range m.Dirs {
absDir := dir
if !filepath.IsAbs(dir) {
- absDir = filepath.Join(base.Cwd, dir)
+ absDir = filepath.Join(base.Cwd(), dir)
}
if bp, _ := cfg.BuildContext.ImportDir(absDir, build.FindOnly); bp.ImportPath != "" && bp.ImportPath != "." {
m.Pkgs[i] = bp.ImportPath
if testCoverProfile == "" || testC {
return
}
- if !filepath.IsAbs(testCoverProfile) && testOutputDir != "" {
- testCoverProfile = filepath.Join(testOutputDir, testCoverProfile)
+ if !filepath.IsAbs(testCoverProfile) {
+ testCoverProfile = filepath.Join(testOutputDir.getAbs(), testCoverProfile)
}
// No mutex - caller's responsibility to call with no racing goroutines.
testJSON bool // -json flag
testList string // -list flag
testO string // -o flag
- testOutputDir = base.Cwd // -outputdir flag
+ testOutputDir outputdirFlag // -outputdir flag
testShuffle shuffleFlag // -shuffle flag
testTimeout time.Duration // -timeout flag
testV bool // -v flag
match := make([]func(*load.Package) bool, len(testCoverPaths))
matched := make([]bool, len(testCoverPaths))
for i := range testCoverPaths {
- match[i] = load.MatchPackage(testCoverPaths[i], base.Cwd)
+ match[i] = load.MatchPackage(testCoverPaths[i], base.Cwd())
}
// Select for coverage all dependencies matching the testCoverPaths patterns.
var installAction, cleanAction *work.Action
if testC || testNeedBinary() {
// -c or profiling flag: create action to copy binary to ./test.out.
- target := filepath.Join(base.Cwd, testBinary+cfg.ExeSuffix)
+ target := filepath.Join(base.Cwd(), testBinary+cfg.ExeSuffix)
if testO != "" {
target = testO
if !filepath.IsAbs(target) {
- target = filepath.Join(base.Cwd, target)
+ target = filepath.Join(base.Cwd(), target)
}
}
if target == os.DevNull {
cf.String("memprofilerate", "", "")
cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
cf.String("mutexprofilefraction", "", "")
- cf.Var(outputdirFlag{&testOutputDir}, "outputdir", "")
+ cf.Var(&testOutputDir, "outputdir", "")
cf.Int("parallel", 0, "")
cf.String("run", "", "")
cf.Bool("short", false, "")
cf.BoolVar(&testV, "v", false, "")
cf.Var(&testShuffle, "shuffle", "")
- for name, _ := range passFlagToTest {
+ for name := range passFlagToTest {
cf.Var(cf.Lookup(name).Value, "test."+name, "")
}
}
// outputdirFlag implements the -outputdir flag.
// It interprets an empty value as the working directory of the 'go' command.
type outputdirFlag struct {
- resolved *string
+ abs string
}
-func (f outputdirFlag) String() string { return *f.resolved }
-func (f outputdirFlag) Set(value string) (err error) {
+func (f *outputdirFlag) String() string {
+ return f.abs
+}
+func (f *outputdirFlag) Set(value string) (err error) {
if value == "" {
- // The empty string implies the working directory of the 'go' command.
- *f.resolved = base.Cwd
+ f.abs = ""
} else {
- *f.resolved, err = filepath.Abs(value)
+ f.abs, err = filepath.Abs(value)
}
return err
}
+func (f *outputdirFlag) getAbs() string {
+ if f.abs == "" {
+ return base.Cwd()
+ }
+ return f.abs
+}
// vetFlag implements the special parsing logic for the -vet flag:
// a comma-separated list, with a distinguished value "off" and
// command. Set it explicitly if it is needed due to some other flag that
// requests output.
if testProfile() != "" && !outputDirSet {
- injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir)
+ injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir.getAbs())
}
// If the user is explicitly passing -help or -h, show output
if strings.HasPrefix(t, "pkgpath ") {
t = strings.TrimPrefix(t, "pkgpath ")
t = strings.TrimSuffix(t, ";")
- pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd, nil, &stk, nil, 0))
+ pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
}
}
} else {
scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))
for scanner.Scan() {
t := scanner.Text()
- pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd, nil, &stk, nil, 0))
+ pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
}
}
return
}
}
var stk load.ImportStack
- p := load.LoadImportWithFlags(pkg, base.Cwd, nil, &stk, nil, 0)
+ p := load.LoadImportWithFlags(pkg, base.Cwd(), nil, &stk, nil, 0)
if p.Error != nil {
base.Fatalf("load %s: %v", pkg, p.Error)
}
if err != nil {
t.Fatal(err)
}
+ cwd := base.Cwd()
oldGopath := cfg.BuildContext.GOPATH
defer func() {
cfg.BuildContext.GOPATH = oldGopath
- os.Chdir(base.Cwd)
+ os.Chdir(cwd)
err := os.RemoveAll(tmpGopath)
if err != nil {
t.Error(err)
cmdargs := []interface{}{cmd, "-o", outfile, objs, flags}
dir := p.Dir
- out, err := b.runOut(a, base.Cwd, b.cCompilerEnv(), cmdargs...)
+ out, err := b.runOut(a, base.Cwd(), b.cCompilerEnv(), cmdargs...)
if len(out) > 0 {
// Filter out useless linker warnings caused by bugs outside Go.
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = []string{"-dynlinker"} // record path to dynamic linker
}
- return b.run(a, base.Cwd, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
+ return b.run(a, base.Cwd(), p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
}
// Run SWIG on all SWIG input files.
args = append(args, f)
}
- output, err = b.runOut(a, base.Cwd, nil, args...)
+ output, err = b.runOut(a, base.Cwd(), nil, args...)
return ofile, output, err
}
if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
if cfg.BuildTrimpath {
- args = append(args, "-ffile-prefix-map="+base.Cwd+"=.")
+ args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
}
if fsys.OverlayFile != "" {
}
toPath := absPath
// gccgo only applies the last matching rule, so also handle the case where
- // BuildTrimpath is true and the path is relative to base.Cwd.
- if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd) {
- toPath = "." + toPath[len(base.Cwd):]
+ // BuildTrimpath is true and the path is relative to base.Cwd().
+ if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
+ toPath = "." + toPath[len(base.Cwd()):]
}
args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
}
}
defs = tools.maybePIC(defs)
if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
- defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.")
+ defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
} else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
modload.Init()
instrumentInit()
buildModeInit()
- if err := fsys.Init(base.Cwd); err != nil {
+ if err := fsys.Init(base.Cwd()); err != nil {
base.Fatalf("go: %v", err)
}