diff --git a/cmd/callgraph/main.go b/cmd/callgraph/main.go index 8979b5b4..2aa9739a 100644 --- a/cmd/callgraph/main.go +++ b/cmd/callgraph/main.go @@ -30,6 +30,7 @@ import ( "runtime" "text/template" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/callgraph/rta" @@ -50,6 +51,10 @@ var formatFlag = flag.String("format", "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}", "A template expression specifying how to format an edge") +func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) +} + const Usage = `callgraph: display the the call graph of a Go program. Usage: diff --git a/cmd/eg/eg.go b/cmd/eg/eg.go index 5c2c28d8..5970c1ac 100644 --- a/cmd/eg/eg.go +++ b/cmd/eg/eg.go @@ -6,6 +6,7 @@ package main // import "golang.org/x/tools/cmd/eg" import ( "flag" "fmt" + "go/build" "go/parser" "go/printer" "go/token" @@ -13,6 +14,7 @@ import ( "os/exec" "strings" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/refactor/eg" ) @@ -26,6 +28,10 @@ var ( verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics") ) +func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) +} + const usage = `eg: an example-based refactoring tool. Usage: eg -t template.go [-w] [-transitive] ... diff --git a/cmd/gomvpkg/main.go b/cmd/gomvpkg/main.go index 86b8067f..959b84ef 100644 --- a/cmd/gomvpkg/main.go +++ b/cmd/gomvpkg/main.go @@ -12,6 +12,7 @@ import ( "go/build" "os" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/refactor/rename" ) @@ -22,6 +23,10 @@ var ( helpFlag = flag.Bool("help", false, "show usage message") ) +func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) +} + const Usage = `gomvpkg: moves a package, updating import declarations Usage: diff --git a/cmd/gorename/main.go b/cmd/gorename/main.go index ea08f881..922dc0ca 100644 --- a/cmd/gorename/main.go +++ b/cmd/gorename/main.go @@ -14,6 +14,7 @@ import ( "os" "runtime" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/refactor/rename" ) @@ -25,6 +26,7 @@ var ( ) func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) flag.BoolVar(&rename.Force, "force", false, "proceed, even if conflicts were reported") flag.BoolVar(&rename.DryRun, "dryrun", false, "show the change, but do not apply it") flag.BoolVar(&rename.Verbose, "v", false, "print verbose information") diff --git a/cmd/oracle/main.go b/cmd/oracle/main.go index da411f63..079ba26b 100644 --- a/cmd/oracle/main.go +++ b/cmd/oracle/main.go @@ -23,6 +23,7 @@ import ( "runtime" "runtime/pprof" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/oracle" ) @@ -38,6 +39,10 @@ var formatFlag = flag.String("format", "plain", "Output format. One of {plain,j var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).") +func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) +} + const useHelp = "Run 'oracle -help' for more information.\n" const helpMessage = `Go source code oracle. diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go index 588b8240..75f16018 100644 --- a/cmd/ssadump/main.go +++ b/cmd/ssadump/main.go @@ -13,6 +13,7 @@ import ( "runtime" "runtime/pprof" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/interp" @@ -39,7 +40,7 @@ Usage: ssadump [ ...] ... Use -help flag to display options. Examples: -% ssadump -build=FP -importbin hello.go # quickly dump SSA form of a single package +% ssadump -build=F hello.go # dump SSA form of a single package % ssadump -run -interp=T hello.go # interpret a program, with tracing % ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely ` + loader.FromArgsUsage + @@ -53,6 +54,8 @@ if set, it runs the tests of each package. var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") func init() { + flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) + // If $GOMAXPROCS isn't set, use the full capacity of the machine. // For small machines, use at least 4 threads. if os.Getenv("GOMAXPROCS") == "" { @@ -76,8 +79,8 @@ func doMain() error { args := flag.Args() conf := loader.Config{Build: &build.Default} - // TODO(adonovan): make go/types choose its default Sizes from - // build.Default or a specified *build.Context. + + // Choose types.Sizes from conf.Build. var wordSize int64 = 8 switch conf.Build.GOARCH { case "386", "arm": diff --git a/go/buildutil/tags.go b/go/buildutil/tags.go new file mode 100644 index 00000000..30b38d37 --- /dev/null +++ b/go/buildutil/tags.go @@ -0,0 +1,73 @@ +package buildutil + +// This logic was copied from stringsFlag from $GOROOT/src/cmd/go/build.go. + +import "fmt" + +const TagsFlagDoc = `'tag list'\ta list of build tags to consider satisfied during the build. +For more information about build tags, see the description of +build constraints in the documentation for the go/build package` + +// TagsFlag is an implementation of the flag.Value interface that parses +// a flag value in the same manner as go build's -tags flag and +// populates a []string slice. +// +// See $GOROOT/src/go/build/doc.go for description of build tags. +// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag. +// +// Example: +// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsDoc) +type TagsFlag []string + +func (v *TagsFlag) Set(s string) error { + var err error + *v, err = splitQuotedFields(s) + if *v == nil { + *v = []string{} + } + return err +} + +func splitQuotedFields(s string) ([]string, error) { + // Split fields allowing '' or "" around elements. + // Quotes further inside the string do not count. + var f []string + for len(s) > 0 { + for len(s) > 0 && isSpaceByte(s[0]) { + s = s[1:] + } + if len(s) == 0 { + break + } + // Accepted quoted string. No unescaping inside. + if s[0] == '"' || s[0] == '\'' { + quote := s[0] + s = s[1:] + i := 0 + for i < len(s) && s[i] != quote { + i++ + } + if i >= len(s) { + return nil, fmt.Errorf("unterminated %c string", quote) + } + f = append(f, s[:i]) + s = s[i+1:] + continue + } + i := 0 + for i < len(s) && !isSpaceByte(s[i]) { + i++ + } + f = append(f, s[:i]) + s = s[i:] + } + return f, nil +} + +func (v *TagsFlag) String() string { + return "" +} + +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} diff --git a/go/buildutil/tags_test.go b/go/buildutil/tags_test.go new file mode 100644 index 00000000..0fc26180 --- /dev/null +++ b/go/buildutil/tags_test.go @@ -0,0 +1,28 @@ +package buildutil_test + +import ( + "flag" + "go/build" + "reflect" + "testing" + + "golang.org/x/tools/go/buildutil" +) + +func TestTags(t *testing.T) { + f := flag.NewFlagSet("TestTags", flag.PanicOnError) + var ctxt build.Context + f.Var((*buildutil.TagsFlag)(&ctxt.BuildTags), "tags", buildutil.TagsFlagDoc) + f.Parse([]string{"-tags", ` 'one'"two" 'three "four"'`, "rest"}) + + // BuildTags + want := []string{"one", "two", "three \"four\""} + if !reflect.DeepEqual(ctxt.BuildTags, want) { + t.Errorf("BuildTags = %q, want %q", ctxt.BuildTags, want) + } + + // Args() + if want := []string{"rest"}; !reflect.DeepEqual(f.Args(), want) { + t.Errorf("f.Args() = %q, want %q", f.Args(), want) + } +}