cmd/stringer: add a -tags flag that supports build tags
This is reapplying CL121995 after rolling back the change to the importing methods. There is still a need for a flag to control tags. The original CL decription: The feature has been requested but, like build tags in general, only works in a directory, not when files are specified explicitly. Unlike the build tools, report when the feature is misused like this to avoid confusion. Fixes golang/go#9449 Change-Id: I732627d5f2e6323367e3bdd5de746923868890a9 Reviewed-on: https://go-review.googlesource.com/122537 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
8cb83b71b4
commit
18f0b668f1
|
@ -9,6 +9,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
|
@ -26,17 +27,8 @@ import (
|
|||
// binary panics if the String method for X is not correct, including for error cases.
|
||||
|
||||
func TestEndToEnd(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "stringer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dir, stringer := buildStringer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
// Create stringer in temporary directory.
|
||||
stringer := filepath.Join(dir, "stringer.exe")
|
||||
err = run("go", "build", "-o", stringer)
|
||||
if err != nil {
|
||||
t.Fatalf("building stringer: %s", err)
|
||||
}
|
||||
// Read the testdata directory.
|
||||
fd, err := os.Open("testdata")
|
||||
if err != nil {
|
||||
|
@ -53,6 +45,10 @@ func TestEndToEnd(t *testing.T) {
|
|||
t.Errorf("%s is not a Go file", name)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(name, "tag_") {
|
||||
// This file is used for tag processing in TestTags, below.
|
||||
continue
|
||||
}
|
||||
if name == "cgo.go" && !build.Default.CgoEnabled {
|
||||
t.Logf("cgo is not enabled for %s", name)
|
||||
continue
|
||||
|
@ -63,9 +59,69 @@ func TestEndToEnd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestTags verifies that the -tags flag works as advertised.
|
||||
func TestTags(t *testing.T) {
|
||||
dir, stringer := buildStringer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
var (
|
||||
protectedConst = []byte("TagProtected")
|
||||
output = filepath.Join(dir, "const_string.go")
|
||||
)
|
||||
for _, file := range []string{"tag_main.go", "tag_tag.go"} {
|
||||
|
||||
err := copy(filepath.Join(dir, file), filepath.Join("testdata", file))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
err := run(stringer, "-type", "Const", dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result, err := ioutil.ReadFile(output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Contains(result, protectedConst) {
|
||||
t.Fatal("tagged variable appears in untagged run")
|
||||
}
|
||||
err = os.Remove(output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = run(stringer, "-type", "Const", "-tags", "tag", dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result, err = ioutil.ReadFile(output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Contains(result, protectedConst) {
|
||||
t.Fatal("tagged variable does not appear in tagged run")
|
||||
}
|
||||
}
|
||||
|
||||
// buildStringer creates a temporary directory and installs stringer there.
|
||||
func buildStringer(t *testing.T) (dir string, stringer string) {
|
||||
t.Helper()
|
||||
dir, err := ioutil.TempDir("", "stringer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stringer = filepath.Join(dir, "stringer.exe")
|
||||
err = run("go", "build", "-o", stringer)
|
||||
if err != nil {
|
||||
t.Fatalf("building stringer: %s", err)
|
||||
}
|
||||
return dir, stringer
|
||||
}
|
||||
|
||||
// stringerCompileAndRun runs stringer for the named file and compiles and
|
||||
// runs the target binary in directory dir. That binary will panic if the String method is incorrect.
|
||||
func stringerCompileAndRun(t *testing.T, dir, stringer, typeName, fileName string) {
|
||||
t.Helper()
|
||||
t.Logf("run: %s %s\n", fileName, typeName)
|
||||
source := filepath.Join(dir, fileName)
|
||||
err := copy(source, filepath.Join("testdata", fileName))
|
||||
|
|
|
@ -82,6 +82,7 @@ var (
|
|||
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
|
||||
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
|
||||
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
|
||||
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
|
||||
)
|
||||
|
||||
// Usage is a replacement usage function for the flags package.
|
||||
|
@ -105,6 +106,10 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
types := strings.Split(*typeNames, ",")
|
||||
var tags []string
|
||||
if len(*buildTags) > 0 {
|
||||
tags = strings.Split(*buildTags, ",")
|
||||
}
|
||||
|
||||
// We accept either one directory or a list of files. Which do we have?
|
||||
args := flag.Args()
|
||||
|
@ -121,8 +126,11 @@ func main() {
|
|||
}
|
||||
if len(args) == 1 && isDirectory(args[0]) {
|
||||
dir = args[0]
|
||||
g.parsePackageDir(args[0])
|
||||
g.parsePackageDir(args[0], tags)
|
||||
} else {
|
||||
if len(tags) != 0 {
|
||||
log.Fatal("-tags option applies only to directories, not when files are specified")
|
||||
}
|
||||
dir = filepath.Dir(args[0])
|
||||
g.parsePackageFiles(args)
|
||||
}
|
||||
|
@ -197,9 +205,15 @@ type Package struct {
|
|||
typesPkg *types.Package
|
||||
}
|
||||
|
||||
func buildContext(tags []string) *build.Context {
|
||||
ctx := build.Default
|
||||
ctx.BuildTags = tags
|
||||
return &ctx
|
||||
}
|
||||
|
||||
// parsePackageDir parses the package residing in the directory.
|
||||
func (g *Generator) parsePackageDir(directory string) {
|
||||
pkg, err := build.Default.ImportDir(directory, 0)
|
||||
func (g *Generator) parsePackageDir(directory string, tags []string) {
|
||||
pkg, err := buildContext(tags).ImportDir(directory, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot process directory %s: %s", directory, err)
|
||||
}
|
||||
|
@ -261,14 +275,17 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac
|
|||
g.pkg.name = astFiles[0].Name.Name
|
||||
g.pkg.files = files
|
||||
g.pkg.dir = directory
|
||||
// Type check the package.
|
||||
g.pkg.check(fs, astFiles)
|
||||
g.pkg.typeCheck(fs, astFiles)
|
||||
}
|
||||
|
||||
// check type-checks the package. The package must be OK to proceed.
|
||||
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
|
||||
// check type-checks the package so we can evaluate contants whose values we are printing.
|
||||
func (pkg *Package) typeCheck(fs *token.FileSet, astFiles []*ast.File) {
|
||||
pkg.defs = make(map[*ast.Ident]types.Object)
|
||||
config := types.Config{Importer: defaultImporter(), FakeImportC: true}
|
||||
config := types.Config{
|
||||
IgnoreFuncBodies: true, // We only need to evaluate constants.
|
||||
Importer: defaultImporter(),
|
||||
FakeImportC: true,
|
||||
}
|
||||
info := &types.Info{
|
||||
Defs: pkg.defs,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// No build tag in this file.
|
||||
|
||||
package main
|
||||
|
||||
type Const int
|
||||
|
||||
const (
|
||||
A Const = iota
|
||||
B
|
||||
C
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
// This file has a build tag "tag"
|
||||
|
||||
// +build tag
|
||||
|
||||
package main
|
||||
|
||||
const TagProtected Const = C + 1
|
Loading…
Reference in New Issue