-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
About #8 Also some small bits of refactoring
- Loading branch information
1 parent
a4b1341
commit 5e8843d
Showing
52 changed files
with
651 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"bitbucket.org/JeremySchlatter/go-atexit" | ||
|
||
"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/loader" | ||
) | ||
|
||
var w = flag.Bool("w", false, "write result to (source) file instead of stdout") | ||
|
||
func usage() { | ||
log.Print( | ||
`godebug is a tool for debugging Go programs. | ||
Usage: | ||
godebug command [arguments] | ||
The commands are: | ||
run compile, run, and debug a Go program | ||
output generate debug source code, but do not build or run it | ||
Use "godebug help [command]" for more information about a command. | ||
`) | ||
exit(0) | ||
} | ||
|
||
func runUsage() { | ||
log.Print( | ||
`usage: godebug run gofiles... [arguments...] | ||
Run is a wrapper around 'go run'. It generates debugging code for | ||
the named Go source files and runs 'go run' on the result. | ||
`) | ||
} | ||
|
||
func outputUsage() { | ||
log.Print( | ||
`usage: godebug output [-w] <packages> | ||
Output outputs debugging code for <packages>. | ||
By default, output will print the resulting code to stdout. | ||
If the -w flag is given, output will overwrite the original | ||
source files. Use with caution. | ||
<packages> may take one of two forms: | ||
1. A list of *.go source files. | ||
All of the specified files are loaded, parsed and type-checked | ||
as a single package. All the files must belong to the same directory. | ||
2. A list of import paths, each denoting a package. | ||
The package's directory is found relative to the $GOROOT and | ||
$GOPATH using similar logic to 'go build', and the *.go files in | ||
that directory are loaded, parsed and type-checked as a single | ||
package. | ||
In addition, all *_test.go files in the directory are then loaded | ||
and parsed. Those files whose package declaration equals that of | ||
the non-*_test.go files are included in the primary package. Test | ||
files whose package declaration ends with "_test" are type-checked | ||
as another package, the 'external' test package, so that a single | ||
import path may denote two packages. | ||
`) | ||
} | ||
|
||
func main() { | ||
log.SetFlags(0) | ||
flag.Parse() | ||
flag.Usage = usage | ||
if flag.NArg() == 0 { | ||
usage() | ||
} | ||
switch flag.Arg(0) { | ||
case "help": | ||
doHelp() | ||
case "output": | ||
doOutput() | ||
case "run": | ||
doRun() | ||
default: | ||
usage() | ||
} | ||
} | ||
|
||
func doHelp() { | ||
if flag.NArg() < 2 { | ||
usage() | ||
} | ||
switch flag.Arg(1) { | ||
case "output": | ||
outputUsage() | ||
case "run": | ||
runUsage() | ||
default: | ||
log.Printf("Unknown help topic `%s`. Run 'godebug help'.\n", flag.Arg(1)) | ||
} | ||
} | ||
|
||
func doRun() { | ||
atexit.TrapSignals() | ||
defer atexit.CallExitFuncs() | ||
|
||
tmpDir := makeTmpDir() | ||
atexit.Run(func() { | ||
removeDir(tmpDir) | ||
}) | ||
|
||
var gofiles []string | ||
for _, arg := range flag.Args()[1:] { | ||
if !strings.HasSuffix(arg, ".go") { | ||
break | ||
} | ||
gofiles = append(gofiles, arg) | ||
} | ||
if len(gofiles) == 0 { | ||
logFatal("godebug run: no go files listed") | ||
} | ||
var conf loader.Config | ||
conf.SourceImports = true | ||
if err := conf.CreateFromFilenames("main", gofiles...); err != nil { | ||
logFatal(err) | ||
} | ||
prog, err := conf.Load() | ||
if err != nil { | ||
logFatal(err) | ||
} | ||
generate(prog, func(filename string) io.WriteCloser { | ||
f, err := os.Create(filepath.Join(tmpDir, filepath.Base(filename))) | ||
if err != nil { | ||
logFatal(err) | ||
} | ||
return f | ||
}) | ||
args := []string{"run"} | ||
args = append(args, mapToTmpDir(tmpDir, gofiles)...) | ||
shell("go", args...) | ||
} | ||
|
||
func shell(command string, args ...string) { | ||
cmd := exec.Command(command, args...) | ||
cmd.Stdout = os.Stdout | ||
cmd.Stdin = os.Stdin | ||
cmd.Stderr = os.Stderr | ||
err := cmd.Run() | ||
switch err.(type) { | ||
case nil: | ||
case *exec.ExitError: | ||
exit(1) | ||
default: | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
func mapToTmpDir(tmpDir string, gofiles []string) []string { | ||
result := make([]string, len(gofiles)) | ||
for i := range gofiles { | ||
result[i] = filepath.Join(tmpDir, filepath.Base(gofiles[i])) | ||
} | ||
return result | ||
} | ||
|
||
func makeTmpDir() (dirname string) { | ||
tmp, err := ioutil.TempDir("", "godebug") | ||
if err != nil { | ||
logFatal("Failed to create temporary directory:", err) | ||
} | ||
return tmp | ||
} | ||
|
||
func removeDir(dir string) { | ||
if err := os.RemoveAll(dir); err != nil { | ||
log.Print("Failed to clean up temporary directory:", err) | ||
} | ||
} | ||
|
||
func doOutput() { | ||
var conf loader.Config | ||
rest, err := conf.FromArgs(flag.Args()[1:], true) | ||
if len(rest) > 0 { | ||
fmt.Fprintf(os.Stderr, "Unrecognized arguments:\n%v\n\n", strings.Join(rest, "\n")) | ||
} | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error identifying packages: %v\n\n", err) | ||
} | ||
if len(rest) > 0 || err != nil { | ||
flag.Usage() | ||
} | ||
conf.SourceImports = true | ||
prog, err := conf.Load() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error loading packages: %v\n\n", err) | ||
flag.Usage() | ||
} | ||
generate(prog, func(filename string) io.WriteCloser { | ||
if *w { | ||
file, err := os.Create(filename) | ||
if err != nil { | ||
logFatal(err) | ||
} | ||
return file | ||
} | ||
return nopCloser{os.Stdout} | ||
}) | ||
} | ||
|
||
type nopCloser struct { | ||
io.Writer | ||
} | ||
|
||
func (nopCloser) Close() error { | ||
return nil | ||
} | ||
|
||
func logFatal(v ...interface{}) { | ||
atexit.CallExitFuncs() | ||
log.Fatal(v...) | ||
} | ||
|
||
func exit(n int) { | ||
atexit.CallExitFuncs() | ||
os.Exit(n) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
"testing" | ||
) | ||
|
||
// This file runs tests in the testdata directory, excluding those in testdata/single-file-tests | ||
|
||
func TestCLISessions(t *testing.T) { | ||
godebug := compileGodebug(t) | ||
defer os.Remove(godebug) | ||
|
||
// Read the testdata directory | ||
fd, err := os.Open("testdata") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer fd.Close() | ||
names, err := fd.Readdirnames(-1) | ||
if err != nil { | ||
t.Fatal("Readdirnames:", err) | ||
} | ||
tests := make([]string, 0, len(names)) | ||
for _, name := range names { | ||
if strings.HasSuffix(name, ".txt") { | ||
tests = append(tests, name) | ||
} | ||
} | ||
|
||
// Run tests in parallel | ||
var wg sync.WaitGroup | ||
wg.Add(len(tests)) | ||
for _, test := range tests { | ||
go func(filename string) { | ||
defer wg.Done() | ||
runTest(t, godebug, filename) | ||
}(filepath.Join("testdata", test)) | ||
} | ||
wg.Wait() | ||
} | ||
|
||
func runTest(t *testing.T, godebug, filename string) { | ||
var buf bytes.Buffer | ||
session := parseSession(t, filename) | ||
cmd := exec.Command(godebug, session.cmd[1:]...) | ||
cmd.Dir = filepath.FromSlash("testdata/test-filesystem/" + session.workingDir) | ||
cmd.Stdout = &buf | ||
cmd.Stderr = &buf | ||
cmd.Stdin = bytes.NewReader(session.input) | ||
setGopath(t, cmd) | ||
if err := cmd.Run(); err != nil { | ||
t.Fatalf("Command 'godebug %v' failed to run: %v\n%s", strings.Join(session.cmd[1:], " "), err, buf.Bytes()) | ||
} | ||
checkOutput(t, session, buf.Bytes()) | ||
} | ||
|
||
func setGopath(t *testing.T, cmd *exec.Cmd) { | ||
cmd.Env = os.Environ() | ||
cwd, err := os.Getwd() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
gopath := filepath.Join(cwd, "testdata", "test-filesystem", "gopath") | ||
sawGopath := false | ||
for i := range cmd.Env { | ||
keyVal := strings.SplitN(cmd.Env[i], "=", 2) | ||
if keyVal[0] == "GOPATH" { | ||
cmd.Env[i] = "GOPATH=" + gopath + string(filepath.ListSeparator) + keyVal[1] | ||
} | ||
} | ||
if !sawGopath { | ||
cmd.Env = append(cmd.Env, "GOPATH="+gopath) | ||
} | ||
} |
Oops, something went wrong.