os.Setenv("GOARCH", runtime.GOARCH)
}
- useTmp := true
- runInDir := false
+ var (
+ runInDir = t.tempDir
+ tempDirIsGOPATH = false
+ )
runcmd := func(args ...string) ([]byte, error) {
cmd := exec.Command(args[0], args[1:]...)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
- cmd.Env = os.Environ()
- if useTmp {
- cmd.Dir = t.tempDir
- cmd.Env = envForDir(cmd.Dir)
+ cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=")
+ if runInDir != "" {
+ cmd.Dir = runInDir
+ // Set PWD to match Dir to speed up os.Getwd in the child process.
+ cmd.Env = append(cmd.Env, "PWD="+cmd.Dir)
}
- if runInDir {
- cmd.Dir = t.goDirName()
+ if tempDirIsGOPATH {
+ cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
}
var err error
}
case "runindir":
- // run "go run ." in t.goDirName()
- // It's used when test requires go build and run the binary success.
- // Example when long import path require (see issue29612.dir) or test
- // contains assembly file (see issue15609.dir).
- // Verify the expected output.
- useTmp = false
- runInDir = true
+ // Make a shallow copy of t.goDirName() in its own module and GOPATH, and
+ // run "go run ." in it. The module path (and hence import path prefix) of
+ // the copy is equal to the basename of the source directory.
+ //
+ // It's used when test a requires a full 'go build' in order to compile
+ // the sources, such as when importing multiple packages (issue29612.dir)
+ // or compiling a package containing assembly files (see issue15609.dir),
+ // but still needs to be run to verify the expected output.
+ tempDirIsGOPATH = true
+ srcDir := t.goDirName()
+ modName := filepath.Base(srcDir)
+ gopathSrcDir := filepath.Join(t.tempDir, "src", modName)
+ runInDir = gopathSrcDir
+
+ if err := overlayDir(gopathSrcDir, srcDir); err != nil {
+ t.err = err
+ return
+ }
+
+ modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName)
+ if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil {
+ t.err = err
+ return
+ }
+
cmd := []string{goTool(), "run", goGcflags()}
if *linkshared {
cmd = append(cmd, "-linkshared")
// Run Go file if no special go command flags are provided;
// otherwise build an executable and run it.
// Verify the output.
- useTmp = false
+ runInDir = ""
var out []byte
var err error
if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
defer func() {
<-rungatec
}()
- useTmp = false
+ runInDir = ""
cmd := []string{goTool(), "run", goGcflags()}
if *linkshared {
cmd = append(cmd, "-linkshared")
case "errorcheckoutput":
// Run Go file and write its output into temporary Go file.
// Compile and errorCheck generated Go file.
- useTmp = false
+ runInDir = ""
cmd := []string{goTool(), "run", goGcflags()}
if *linkshared {
cmd = append(cmd, "-linkshared")
assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
}
-// envForDir returns a copy of the environment
-// suitable for running in the given directory.
-// The environment is the current process's environment
-// but with an updated $PWD, so that an os.Getwd in the
-// child will be faster.
-func envForDir(dir string) []string {
- env := os.Environ()
- for i, kv := range env {
- if strings.HasPrefix(kv, "PWD=") {
- env[i] = "PWD=" + dir
- return env
- }
- }
- env = append(env, "PWD="+dir)
- return env
-}
-
func getenv(key, def string) string {
value := os.Getenv(key)
if value != "" {
}
return def
}
+
+// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
+func overlayDir(dstRoot, srcRoot string) error {
+ dstRoot = filepath.Clean(dstRoot)
+ if err := os.MkdirAll(dstRoot, 0777); err != nil {
+ return err
+ }
+
+ srcRoot, err := filepath.Abs(srcRoot)
+ if err != nil {
+ return err
+ }
+
+ return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
+ if err != nil || srcPath == srcRoot {
+ return err
+ }
+
+ suffix := strings.TrimPrefix(srcPath, srcRoot)
+ for len(suffix) > 0 && suffix[0] == filepath.Separator {
+ suffix = suffix[1:]
+ }
+ dstPath := filepath.Join(dstRoot, suffix)
+
+ perm := info.Mode() & os.ModePerm
+ if info.Mode()&os.ModeSymlink != 0 {
+ info, err = os.Stat(srcPath)
+ if err != nil {
+ return err
+ }
+ perm = info.Mode() & os.ModePerm
+ }
+
+ // Always copy directories (don't symlink them).
+ // If we add a file in the overlay, we don't want to add it in the original.
+ if info.IsDir() {
+ return os.MkdirAll(dstPath, perm|0200)
+ }
+
+ // If the OS supports symlinks, use them instead of copying bytes.
+ if err := os.Symlink(srcPath, dstPath); err == nil {
+ return nil
+ }
+
+ // Otherwise, copy the bytes.
+ src, err := os.Open(srcPath)
+ if err != nil {
+ return err
+ }
+ defer src.Close()
+
+ dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(dst, src)
+ if closeErr := dst.Close(); err == nil {
+ err = closeErr
+ }
+ return err
+ })
+}