Exemplo n.º 1
0
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
}
Exemplo n.º 2
0
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)
		}
	}
}