Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit 92f53b0

Browse files
authored
Avoid using packages.Load (#420)
Partially fixing #419 It turn out there are two places calling `go list`: * [createPackageMap](https://github.com/golang/mock/blob/0b87a54da2167cf3446363bb4b00c6de7677ceaa/mockgen/mockgen.go#L629) * [parsePackageImport](https://github.com/golang/mock/blob/0b87a54da2167cf3446363bb4b00c6de7677ceaa/mockgen/parse.go#L552), which calls `packages.Load`, which calls `go list` by default. This PR avoids calling `packages.Load` and saves one of the `go list` calls.
1 parent 6d816de commit 92f53b0

File tree

6 files changed

+159
-42
lines changed

6 files changed

+159
-42
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module github.com/golang/mock
22

33
require (
4-
golang.org/x/tools v0.0.0-20190425150028-36563e24a262
4+
golang.org/x/mod v0.3.0
5+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
56
rsc.io/quote/v3 v3.1.0
67
)
78

go.sum

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2-
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
3-
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
2+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
3+
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
4+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
5+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
6+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
7+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
48
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
59
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
10+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
611
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
712
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
813
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
9-
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
10-
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
14+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
15+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
16+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
17+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
18+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1119
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
1220
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
1321
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=

mockgen/internal/tests/import_embedded_interface/bugreport_mock.go

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mockgen/internal/tests/import_embedded_interface/net_mock.go

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mockgen/parse.go

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ import (
2626
"go/token"
2727
"io/ioutil"
2828
"log"
29+
"os"
2930
"path"
3031
"path/filepath"
3132
"strconv"
3233
"strings"
3334

3435
"github.com/golang/mock/mockgen/model"
35-
"golang.org/x/tools/go/packages"
36+
"golang.org/x/mod/modfile"
3637
)
3738

3839
var (
@@ -49,7 +50,7 @@ func sourceMode(source string) (*model.Package, error) {
4950
return nil, fmt.Errorf("failed getting source directory: %v", err)
5051
}
5152

52-
packageImport, err := parsePackageImport(source, srcDir)
53+
packageImport, err := parsePackageImport(srcDir)
5354
if err != nil {
5455
return nil, err
5556
}
@@ -604,31 +605,49 @@ func packageNameOfDir(srcDir string) (string, error) {
604605
return "", fmt.Errorf("go source file not found %s", srcDir)
605606
}
606607

607-
packageImport, err := parsePackageImport(goFilePath, srcDir)
608+
packageImport, err := parsePackageImport(srcDir)
608609
if err != nil {
609610
return "", err
610611
}
611612
return packageImport, nil
612613
}
613614

615+
var errOutsideGoPath = errors.New("Source directory is outside GOPATH")
616+
614617
// parseImportPackage get package import path via source file
615-
func parsePackageImport(source, srcDir string) (string, error) {
616-
cfg := &packages.Config{
617-
Mode: packages.NeedName,
618-
Tests: true,
619-
Dir: srcDir,
618+
// an alternative implementation is to use:
619+
// cfg := &packages.Config{Mode: packages.NeedName, Tests: true, Dir: srcDir}
620+
// pkgs, err := packages.Load(cfg, "file="+source)
621+
// However, it will call "go list" and slow down the performance
622+
func parsePackageImport(srcDir string) (string, error) {
623+
moduleMode := os.Getenv("GO111MODULE")
624+
// trying to find the module
625+
if moduleMode != "off" {
626+
currentDir := srcDir
627+
for {
628+
dat, err := ioutil.ReadFile(filepath.Join(currentDir, "go.mod"))
629+
if os.IsNotExist(err) {
630+
if currentDir == filepath.Dir(currentDir) {
631+
// at the root
632+
break
633+
}
634+
currentDir = filepath.Dir(currentDir)
635+
continue
636+
} else if err != nil {
637+
return "", err
638+
}
639+
modulePath := modfile.ModulePath(dat)
640+
return filepath.ToSlash(filepath.Join(modulePath, strings.TrimPrefix(srcDir, currentDir))), nil
641+
}
620642
}
621-
pkgs, err := packages.Load(cfg, "file="+source)
622-
if err != nil {
623-
return "", err
643+
// fall back to GOPATH mode
644+
goPath := os.Getenv("GOPATH")
645+
if goPath == "" {
646+
return "", fmt.Errorf("GOPATH is not set")
624647
}
625-
if packages.PrintErrors(pkgs) > 0 || len(pkgs) == 0 {
626-
return "", errors.New("loading package failed")
648+
sourceRoot := filepath.Join(goPath, "src") + string(os.PathSeparator)
649+
if !strings.HasPrefix(srcDir, sourceRoot) {
650+
return "", errOutsideGoPath
627651
}
628-
629-
packageImport := pkgs[0].PkgPath
630-
631-
// It is illegal to import a _test package.
632-
packageImport = strings.TrimSuffix(packageImport, "_test")
633-
return packageImport, nil
652+
return filepath.ToSlash(strings.TrimPrefix(srcDir, sourceRoot)), nil
634653
}

mockgen/parse_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"go/ast"
55
"go/parser"
66
"go/token"
7+
"io/ioutil"
8+
"os"
9+
"path/filepath"
710
"testing"
811
)
912

@@ -113,3 +116,89 @@ func Benchmark_parseFile(b *testing.B) {
113116
sourceMode(source)
114117
}
115118
}
119+
120+
func TestParsePackageImport(t *testing.T) {
121+
testRoot, err := ioutil.TempDir("", "test_root")
122+
if err != nil {
123+
t.Fatal("cannot create tempdir")
124+
}
125+
defer func() {
126+
if err = os.RemoveAll(testRoot); err != nil {
127+
t.Errorf("cannot clean up tempdir at %s: %v", testRoot, err)
128+
}
129+
}()
130+
barDir := filepath.Join(testRoot, "gomod/bar")
131+
if err = os.MkdirAll(barDir, 0755); err != nil {
132+
t.Fatalf("error creating %s: %v", barDir, err)
133+
}
134+
if err = ioutil.WriteFile(filepath.Join(barDir, "bar.go"), []byte("package bar"), 0644); err != nil {
135+
t.Fatalf("error creating gomod/bar/bar.go: %v", err)
136+
}
137+
if err = ioutil.WriteFile(filepath.Join(testRoot, "gomod/go.mod"), []byte("module github.com/golang/foo"), 0644); err != nil {
138+
t.Fatalf("error creating gomod/go.mod: %v", err)
139+
}
140+
goPath := filepath.Join(testRoot, "gopath")
141+
for _, testCase := range []struct {
142+
name string
143+
envs map[string]string
144+
dir string
145+
pkgPath string
146+
err error
147+
}{
148+
{
149+
name: "go mod default",
150+
envs: map[string]string{"GO111MODULE": ""},
151+
dir: barDir,
152+
pkgPath: "github.com/golang/foo/bar",
153+
},
154+
{
155+
name: "go mod off",
156+
envs: map[string]string{"GO111MODULE": "off", "GOPATH": goPath},
157+
dir: filepath.Join(testRoot, "gopath/src/example.com/foo"),
158+
pkgPath: "example.com/foo",
159+
},
160+
{
161+
name: "outside GOPATH",
162+
envs: map[string]string{"GO111MODULE": "off", "GOPATH": goPath},
163+
dir: "testdata",
164+
err: errOutsideGoPath,
165+
},
166+
} {
167+
t.Run(testCase.name, func(t *testing.T) {
168+
for key, value := range testCase.envs {
169+
os.Setenv(key, value)
170+
}
171+
pkgPath, err := parsePackageImport(filepath.Clean(testCase.dir))
172+
if err != testCase.err {
173+
t.Errorf("expect %v, got %v", testCase.err, err)
174+
}
175+
if pkgPath != testCase.pkgPath {
176+
t.Errorf("expect %s, got %s", testCase.pkgPath, pkgPath)
177+
}
178+
})
179+
}
180+
}
181+
182+
func TestParsePackageImport_FallbackGoPath(t *testing.T) {
183+
goPath, err := ioutil.TempDir("", "gopath")
184+
if err != nil {
185+
t.Error(err)
186+
}
187+
defer func() {
188+
if err = os.RemoveAll(goPath); err != nil {
189+
t.Error(err)
190+
}
191+
}()
192+
srcDir := filepath.Join(goPath, "src/example.com/foo")
193+
err = os.MkdirAll(srcDir, 0755)
194+
if err != nil {
195+
t.Error(err)
196+
}
197+
os.Setenv("GOPATH", goPath)
198+
os.Setenv("GO111MODULE", "on")
199+
pkgPath, err := parsePackageImport(srcDir)
200+
expected := "example.com/foo"
201+
if pkgPath != expected {
202+
t.Errorf("expect %s, got %s", expected, pkgPath)
203+
}
204+
}

0 commit comments

Comments
 (0)