1
1
package util
2
2
3
3
import (
4
+ "encoding/json"
4
5
"errors"
6
+ "io"
5
7
"log"
6
8
"os"
7
9
"os/exec"
@@ -31,13 +33,13 @@ func Getenv(key string, aliases ...string) string {
31
33
32
34
// runGoList is a helper function for running go list with format `format` and flags `flags` on
33
35
// package `pkgpath`.
34
- func runGoList (format string , pkgpath string , flags ... string ) (string , error ) {
35
- return runGoListWithEnv (format , pkgpath , nil , flags ... )
36
+ func runGoList (format string , patterns [] string , flags ... string ) (string , error ) {
37
+ return runGoListWithEnv (format , patterns , nil , flags ... )
36
38
}
37
39
38
- func runGoListWithEnv (format string , pkgpath string , additionalEnv []string , flags ... string ) (string , error ) {
40
+ func runGoListWithEnv (format string , patterns [] string , additionalEnv []string , flags ... string ) (string , error ) {
39
41
args := append ([]string {"list" , "-e" , "-f" , format }, flags ... )
40
- args = append (args , pkgpath )
42
+ args = append (args , patterns ... )
41
43
cmd := exec .Command ("go" , args ... )
42
44
cmd .Env = append (os .Environ (), additionalEnv ... )
43
45
out , err := cmd .Output ()
@@ -54,18 +56,89 @@ func runGoListWithEnv(format string, pkgpath string, additionalEnv []string, fla
54
56
return strings .TrimSpace (string (out )), nil
55
57
}
56
58
59
+ // PkgInfo holds package directory and module directory (if any) for a package
60
+ type PkgInfo struct {
61
+ PkgDir string // the directory directly containing source code of this package
62
+ ModDir string // the module directory containing this package, empty if not a module
63
+ }
64
+
65
+ // GetPkgsInfo gets the absolute module and package root directories for the packages matched by the
66
+ // patterns `patterns`. It passes to `go list` the flags specified by `flags`. If `includingDeps`
67
+ // is true, all dependencies will also be included.
68
+ func GetPkgsInfo (patterns []string , includingDeps bool , flags ... string ) (map [string ]PkgInfo , error ) {
69
+ // enable module mode so that we can find a module root if it exists, even if go module support is
70
+ // disabled by a build
71
+ if includingDeps {
72
+ // the flag `-deps` causes all dependencies to be retrieved
73
+ flags = append (flags , "-deps" )
74
+ }
75
+
76
+ // using -json overrides -f format
77
+ output , err := runGoList ("" , patterns , append (flags , "-json" )... )
78
+ if err != nil {
79
+ return nil , err
80
+ }
81
+
82
+ // the output of `go list -json` is a stream of json object
83
+ type goListPkgInfo struct {
84
+ ImportPath string
85
+ Dir string
86
+ Module * struct {
87
+ Dir string
88
+ }
89
+ }
90
+ pkgInfoMapping := make (map [string ]PkgInfo )
91
+ streamDecoder := json .NewDecoder (strings .NewReader (output ))
92
+ for {
93
+ var pkgInfo goListPkgInfo
94
+ decErr := streamDecoder .Decode (& pkgInfo )
95
+ if decErr == io .EOF {
96
+ break
97
+ }
98
+ if decErr != nil {
99
+ log .Printf ("Error decoding output of go list -json: %s" , err .Error ())
100
+ return nil , decErr
101
+ }
102
+ pkgAbsDir , err := filepath .Abs (pkgInfo .Dir )
103
+ if err != nil {
104
+ log .Printf ("Unable to make package dir %s absolute: %s" , pkgInfo .Dir , err .Error ())
105
+ }
106
+ var modAbsDir string
107
+ if pkgInfo .Module != nil {
108
+ modAbsDir , err = filepath .Abs (pkgInfo .Module .Dir )
109
+ if err != nil {
110
+ log .Printf ("Unable to make module dir %s absolute: %s" , pkgInfo .Module .Dir , err .Error ())
111
+ }
112
+ }
113
+ pkgInfoMapping [pkgInfo .ImportPath ] = PkgInfo {
114
+ PkgDir : pkgAbsDir ,
115
+ ModDir : modAbsDir ,
116
+ }
117
+ }
118
+ return pkgInfoMapping , nil
119
+ }
120
+
121
+ // GetPkgInfo fills the package info structure for the specified package path.
122
+ // It passes the `go list` the flags specified by `flags`.
123
+ func GetPkgInfo (pkgpath string , flags ... string ) PkgInfo {
124
+ return PkgInfo {
125
+ PkgDir : GetPkgDir (pkgpath , flags ... ),
126
+ ModDir : GetModDir (pkgpath , flags ... ),
127
+ }
128
+ }
129
+
57
130
// GetModDir gets the absolute directory of the module containing the package with path
58
131
// `pkgpath`. It passes the `go list` the flags specified by `flags`.
59
132
func GetModDir (pkgpath string , flags ... string ) string {
60
133
// enable module mode so that we can find a module root if it exists, even if go module support is
61
134
// disabled by a build
62
- mod , err := runGoListWithEnv ("{{.Module}}" , pkgpath , []string {"GO111MODULE=on" }, flags ... )
135
+ mod , err := runGoListWithEnv ("{{.Module}}" , [] string { pkgpath } , []string {"GO111MODULE=on" }, flags ... )
63
136
if err != nil || mod == "<nil>" {
64
137
// if the command errors or modules aren't being used, return the empty string
65
138
return ""
66
139
}
67
140
68
- modDir , err := runGoListWithEnv ("{{.Module.Dir}}" , pkgpath , []string {"GO111MODULE=on" }, flags ... )
141
+ modDir , err := runGoListWithEnv ("{{.Module.Dir}}" , [] string { pkgpath } , []string {"GO111MODULE=on" }, flags ... )
69
142
if err != nil {
70
143
return ""
71
144
}
@@ -81,7 +154,7 @@ func GetModDir(pkgpath string, flags ...string) string {
81
154
// GetPkgDir gets the absolute directory containing the package with path `pkgpath`. It passes the
82
155
// `go list` command the flags specified by `flags`.
83
156
func GetPkgDir (pkgpath string , flags ... string ) string {
84
- pkgDir , err := runGoList ("{{.Dir}}" , pkgpath , flags ... )
157
+ pkgDir , err := runGoList ("{{.Dir}}" , [] string { pkgpath } , flags ... )
85
158
if err != nil {
86
159
return ""
87
160
}
@@ -97,7 +170,7 @@ func GetPkgDir(pkgpath string, flags ...string) string {
97
170
// DepErrors checks there are any errors resolving dependencies for `pkgpath`. It passes the `go
98
171
// list` command the flags specified by `flags`.
99
172
func DepErrors (pkgpath string , flags ... string ) bool {
100
- out , err := runGoList ("{{if .DepsErrors}}{{else}}error{{end}}" , pkgpath , flags ... )
173
+ out , err := runGoList ("{{if .DepsErrors}}{{else}}error{{end}}" , [] string { pkgpath } , flags ... )
101
174
if err != nil {
102
175
// if go list failed, assume dependencies are broken
103
176
return false
0 commit comments