func generateSourceFiles(conf *loader.Config, subcommand string) (tmpDirPath string) { // Make a temp directory. tmpDir := makeTmpDir() if *work { // Print the name of the directory and don't clean it up on exit. fmt.Println(tmpDir) } else { // Clean up the directory on exit. atexit.Run(func() { removeDir(tmpDir) }) } // Load the whole program from source files. This almost certainly causes more work than we need to do, // but it's an easy fix for a few problems I've encountered. Deleting this may be a good target for // future optimization work. conf.SourceImports = true // Mark the extra packages we want to instrument. var pkgs []string *instrument = strings.Trim(*instrument, ", ") if *instrument != "" { pkgs = strings.Split(*instrument, ",") } all := false for _, pkg := range pkgs { // check for the special reserved package names: "main", "all", and "std" // see 'go help packages' switch pkg { case "main": switch subcommand { case "run": // The main package is always instrumented anyway. Carry on. case "test": logFatal(`godebug test: can't pass reserved name "main" in the -instrument flag.`) } case "all": if !all { all = true fmt.Println(`godebug run: heads up: "all" means "all except std". godebug can't step into the standard library yet.` + "\n") } case "std": logFatalf("godebug %s: reserved name \"std\" cannot be passed in the -instrument flag."+ "\ngodebug cannot currently instrument packages in the standard library."+ "\nDo you wish it could? Chime in at https://github.com/mailgun/godebug/issues/12", subcommand) default: for _, path := range gotool.ImportPaths([]string{pkg}) { // wildcard "..." expansion conf.Import(path) } } } // Load the program. prog, err := conf.Load() exitIfErr(err) // If we're in "all" mode, mark all but the standard library packages and godebug itself for instrumenting. stdLib := getStdLibPkgs() if all { markAlmostAllPackages(prog, stdLib) } // Warn the user if they have breakpoints set in files that we are not instrumenting. checkForUnusedBreakpoints(subcommand, prog, stdLib) // Generate debugging-enabled source files. wd := getwd() gen.Generate(prog, ioutil.ReadFile, func(importPath, filename string) io.WriteCloser { if importPath == "main" { filename = filepath.Join(tmpDir, filepath.Base(filename)) } else { importPath, _ = findUnderGopath(wd, importPath) exitIfErr(os.MkdirAll(filepath.Join(tmpDir, "src", importPath), 0770)) filename = filepath.Join(tmpDir, "src", importPath, filepath.Base(filename)) } return createFileHook(filename, tmpDir) }) return tmpDir }
func doTest(args []string) { // Parse arguments. packages, testFlags := parseTestArguments(args) // Default to the package in the current directory. if len(packages) == 0 { packages = []string{"."} } // Expand ... packages = gotool.ImportPaths(packages) if len(packages) > 1 && (*c || *o != "") { logFatal("godebug test: cannot use -c or -o flag with multiple packages") } // Build a loader.Config from the provided packages. conf := newLoader() for _, pkg := range packages { exitIfErr(conf.ImportWithTests(pkg)) } tmpDir := generateSourceFiles(&conf, "test") wd := getwd() // Run 'go test -i' once without changing the GOPATH. // This will recompile and install any out-of-date packages. // When we modify the GOPATH in the next invocation of the go tool, // it will not check if any of the uninstrumented dependencies are out-of-date. shellGo("", []string{"test", "-tags", *tags, "-i"}, packages) // The target binary goes to -o if specified, otherwise to the default name // if -c is specified, otherwise to the temporary directory. bin := filepath.Join(tmpDir, "godebug-test-bin.test") if *c { bin = filepath.Base(mapPkgsToTmpDir(packages)[0]) + ".test.debug" } if *o != "" { bin = abs(*o) } // First compile the test with -c and then run the binary directly. // This resolves some issues that came up with running 'go test' directly: // (1) 'go test' changes the working directory to that of the source files of the test. // (2) 'go test' does not forward stdin to the test binary. // Do it once for each package since we can't use -c with multiple packages. for _, pkg := range mapPkgsToTmpDir(packages) { if len(packages) > 1 { fmt.Println("===", pkg) os.Remove(bin) } goArgs := []string{"test", "-tags", *tags, "-c", "-o", bin} shellGo(tmpDir, goArgs, []string{pkg}) // Skip execution if no binary was generated (no test files) or -c was passed if _, err := os.Stat(bin); err == nil && !*c { _, dir := findUnderGopath(wd, pkg) os.Chdir(dir) shell("", bin, testFlags...) os.Chdir(wd) } } }