diff --git a/go.mod b/go.mod index edfb6d40..76dfc0f5 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,8 @@ module github.com/golang/mock require ( - golang.org/x/tools v0.0.0-20190425150028-36563e24a262 + golang.org/x/mod v0.2.0 + golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e rsc.io/quote/v3 v3.1.0 ) diff --git a/go.sum b/go.sum index 0549f947..5d7fe559 100644 --- a/go.sum +++ b/go.sum @@ -3,11 +3,15 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= diff --git a/mockgen/parse.go b/mockgen/parse.go index 0ba7f805..8ceed121 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -26,13 +26,14 @@ import ( "go/token" "io/ioutil" "log" + "os" "path" "path/filepath" "strconv" "strings" "github.com/golang/mock/mockgen/model" - "golang.org/x/tools/go/packages" + "golang.org/x/mod/modfile" ) var ( @@ -49,7 +50,7 @@ func sourceMode(source string) (*model.Package, error) { return nil, fmt.Errorf("failed getting source directory: %v", err) } - packageImport, err := parsePackageImport(source, srcDir) + packageImport, err := parsePackageImport(srcDir) if err != nil { return nil, err } @@ -604,31 +605,49 @@ func packageNameOfDir(srcDir string) (string, error) { return "", fmt.Errorf("go source file not found %s", srcDir) } - packageImport, err := parsePackageImport(goFilePath, srcDir) + packageImport, err := parsePackageImport(srcDir) if err != nil { return "", err } return packageImport, nil } +var errOutsideGoPath = errors.New("Source directory is outside GOPATH") + // parseImportPackage get package import path via source file -func parsePackageImport(source, srcDir string) (string, error) { - cfg := &packages.Config{ - Mode: packages.NeedName, - Tests: true, - Dir: srcDir, +// an alternative implementation is to use: +// cfg := &packages.Config{Mode: packages.NeedName, Tests: true, Dir: srcDir} +// pkgs, err := packages.Load(cfg, "file="+source) +// However, it will call "go list" and slow down the performance +func parsePackageImport(srcDir string) (string, error) { + moduleMode := os.Getenv("GO111MODULE") + // trying to find the module + if moduleMode != "off" { + currentDir := srcDir + for { + dat, err := ioutil.ReadFile(filepath.Join(currentDir, "go.mod")) + if os.IsNotExist(err) { + if currentDir == filepath.Dir(currentDir) { + // at the root + break + } + currentDir = filepath.Dir(currentDir) + continue + } else if err != nil { + return "", err + } + modulePath := modfile.ModulePath(dat) + return filepath.ToSlash(filepath.Join(modulePath, strings.TrimPrefix(srcDir, currentDir))), nil + } } - pkgs, err := packages.Load(cfg, "file="+source) - if err != nil { - return "", err + // fall back to GOPATH mode + goPath := os.Getenv("GOPATH") + if goPath == "" { + return "", fmt.Errorf("GOPATH is not set") } - if packages.PrintErrors(pkgs) > 0 || len(pkgs) == 0 { - return "", errors.New("loading package failed") + sourceRoot := filepath.Join(goPath, "src") + string(os.PathSeparator) + if !strings.HasPrefix(srcDir, sourceRoot) { + return "", errOutsideGoPath } - - packageImport := pkgs[0].PkgPath - - // It is illegal to import a _test package. - packageImport = strings.TrimSuffix(packageImport, "_test") - return packageImport, nil + return filepath.ToSlash(strings.TrimPrefix(srcDir, sourceRoot)), nil } diff --git a/mockgen/parse_test.go b/mockgen/parse_test.go index e555b45e..8a72c06a 100644 --- a/mockgen/parse_test.go +++ b/mockgen/parse_test.go @@ -4,6 +4,9 @@ import ( "go/ast" "go/parser" "go/token" + "io/ioutil" + "os" + "path/filepath" "testing" ) @@ -113,3 +116,69 @@ func Benchmark_parseFile(b *testing.B) { sourceMode(source) } } + +func TestParsePackageImport(t *testing.T) { + for _, testCase := range []struct { + name string + envs map[string]string + dir string + pkgPath string + err error + }{ + { + name: "go mod default", + envs: map[string]string{"GO111MODULE": ""}, + dir: "testdata/gomod/bar", + pkgPath: "github.com/golang/foo/bar", + }, + { + name: "go mod off", + envs: map[string]string{"GO111MODULE": "off", "GOPATH": "testdata/gopath"}, + dir: "testdata/gopath/src/example.com/foo", + pkgPath: "example.com/foo", + }, + { + name: "outside GOPATH", + envs: map[string]string{"GO111MODULE": "off", "GOPATH": "testdata/gopath"}, + dir: "testdata", + err: errOutsideGoPath, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + for key, value := range testCase.envs { + os.Setenv(key, value) + } + pkgPath, err := parsePackageImport(filepath.Clean(testCase.dir)) + if err != testCase.err { + t.Errorf("expect %v, got %v", testCase.err, err) + } + if pkgPath != testCase.pkgPath { + t.Errorf("expect %s, got %s", testCase.pkgPath, pkgPath) + } + }) + } +} + +func TestParsePackageImport_FallbackGoPath(t *testing.T) { + goPath, err := ioutil.TempDir("", "gopath") + if err != nil { + t.Error(err) + } + defer func() { + if err = os.RemoveAll(goPath); err != nil { + t.Error(err) + } + }() + srcDir := filepath.Join(goPath, "src/example.com/foo") + err = os.MkdirAll(srcDir, 0755) + if err != nil { + t.Error(err) + } + os.Setenv("GOPATH", goPath) + os.Setenv("GO111MODULE", "on") + pkgPath, err := parsePackageImport(srcDir) + expected := "example.com/foo" + if pkgPath != expected { + t.Errorf("expect %s, got %s", expected, pkgPath) + } +} diff --git a/mockgen/testdata/gomod/bar/bar.go b/mockgen/testdata/gomod/bar/bar.go new file mode 100644 index 00000000..ddac0faf --- /dev/null +++ b/mockgen/testdata/gomod/bar/bar.go @@ -0,0 +1 @@ +package bar diff --git a/mockgen/testdata/gomod/go.mod b/mockgen/testdata/gomod/go.mod new file mode 100644 index 00000000..d80591b4 --- /dev/null +++ b/mockgen/testdata/gomod/go.mod @@ -0,0 +1 @@ +module github.com/golang/foo