@@ -24,29 +24,26 @@ import (
24
24
"bytes"
25
25
"encoding/json"
26
26
"fmt"
27
+ "github.com/axw/gocov"
28
+ "github.com/axw/gocov/gocovutil"
27
29
"go/ast"
28
- "go/build"
29
30
"go/parser"
30
31
"go/token"
32
+ "golang.org/x/tools/cover"
33
+ goPackages "golang.org/x/tools/go/packages"
31
34
"io"
35
+ "path"
32
36
"path/filepath"
33
37
"strings"
34
-
35
- "github.com/axw/gocov"
36
- "github.com/axw/gocov/gocovutil"
37
- "golang.org/x/tools/cover"
38
38
)
39
39
40
40
func marshalJson (w io.Writer , packages []* gocov.Package ) error {
41
41
return json .NewEncoder (w ).Encode (struct { Packages []* gocov.Package }{packages })
42
42
}
43
43
44
- type packagesCache map [string ]* build.Package
45
-
46
44
func ConvertProfiles (filenames ... string ) ([]byte , error ) {
47
45
var (
48
- ps gocovutil.Packages
49
- packages = make (packagesCache )
46
+ ps gocovutil.Packages
50
47
)
51
48
52
49
for i := range filenames {
@@ -57,9 +54,42 @@ func ConvertProfiles(filenames ...string) ([]byte, error) {
57
54
if err != nil {
58
55
return nil , err
59
56
}
60
- for _ , p := range profiles {
61
- if err := converter .convertProfile (packages , p ); err != nil {
62
- return nil , err
57
+
58
+ mapUniqPackageNames := make (map [string ]interface {})
59
+ uniqPackageNames := make ([]string , 0 , len (profiles ))
60
+ for _ , profile := range profiles {
61
+ packageName := path .Dir (profile .FileName )
62
+
63
+ if _ , ok := mapUniqPackageNames [packageName ]; ok {
64
+ continue
65
+ }
66
+
67
+ mapUniqPackageNames [packageName ] = nil
68
+ uniqPackageNames = append (uniqPackageNames , packageName )
69
+ }
70
+
71
+ packages , err := goPackages .Load (& goPackages.Config {
72
+ Mode : goPackages .NeedName | goPackages .NeedCompiledGoFiles ,
73
+ }, uniqPackageNames ... )
74
+ if err != nil {
75
+ return nil , fmt .Errorf ("load packages: %v" , err )
76
+ }
77
+
78
+ pkgmap := make (map [string ]* goPackages.Package , len (packages ))
79
+ for _ , pkg := range packages {
80
+ pkgmap [pkg .PkgPath ] = pkg
81
+ }
82
+
83
+ for _ , profile := range profiles {
84
+ pkgpath , filename := path .Split (profile .FileName )
85
+ pkgpath = strings .TrimSuffix (pkgpath , "/" )
86
+ pkg := pkgmap [pkgpath ]
87
+ for _ , abspath := range pkg .CompiledGoFiles {
88
+ if filepath .Base (abspath ) == filename {
89
+ if err := converter .convertProfile (profile , abspath , pkg .PkgPath ); err != nil {
90
+ return nil , fmt .Errorf ("convert profile %s: %w" , profile .FileName , err )
91
+ }
92
+ }
63
93
}
64
94
}
65
95
@@ -84,29 +114,26 @@ type statement struct {
84
114
* StmtExtent
85
115
}
86
116
87
- func (c * converter ) convertProfile (packages packagesCache , p * cover.Profile ) error {
88
- file , pkgpath , err := findFile (packages , p .FileName )
89
- if err != nil {
90
- return err
91
- }
92
- pkg := c .packages [pkgpath ]
117
+ func (c * converter ) convertProfile (p * cover.Profile , absFilePath , pkgPath string ) error {
118
+ pkg := c .packages [pkgPath ]
93
119
if pkg == nil {
94
- pkg = & gocov.Package {Name : pkgpath }
95
- c .packages [pkgpath ] = pkg
120
+ pkg = & gocov.Package {Name : pkgPath }
121
+ c .packages [pkgPath ] = pkg
96
122
}
97
123
// Find function and statement extents; create corresponding
98
124
// gocov.Functions and gocov.Statements, and keep a separate
99
125
// slice of gocov.Statements so we can match them with profile
100
126
// blocks.
101
- extents , err := findFuncs (file )
127
+ extents , err := findFuncs (absFilePath )
102
128
if err != nil {
103
129
return err
104
130
}
131
+
105
132
var stmts []statement
106
133
for _ , fe := range extents {
107
134
f := & gocov.Function {
108
135
Name : fe .name ,
109
- File : file ,
136
+ File : absFilePath ,
110
137
Start : fe .startOffset ,
111
138
End : fe .endOffset ,
112
139
}
@@ -138,25 +165,8 @@ func (c *converter) convertProfile(packages packagesCache, p *cover.Profile) err
138
165
break
139
166
}
140
167
}
141
- return nil
142
- }
143
-
144
- // findFile finds the location of the named file in GOROOT, GOPATH etc.
145
- func findFile (packages packagesCache , file string ) (filename , pkgpath string , err error ) {
146
- dir , file := filepath .Split (file )
147
- if dir != "" {
148
- dir = strings .TrimSuffix (dir , "/" )
149
- }
150
- pkg , ok := packages [dir ]
151
- if ! ok {
152
- pkg , err = build .Import (dir , "." , build .FindOnly )
153
- if err != nil {
154
- return "" , "" , fmt .Errorf ("can't find %q: %w" , file , err )
155
- }
156
- packages [dir ] = pkg
157
- }
158
168
159
- return filepath . Join ( pkg . Dir , file ), pkg . ImportPath , nil
169
+ return nil
160
170
}
161
171
162
172
// findFuncs parses the file and returns a slice of FuncExtent descriptors.
0 commit comments