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 ()
@@ -60,6 +62,63 @@ type PkgInfo struct {
60
62
ModDir string // the module directory containing this package, empty if not a module
61
63
}
62
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
+
63
122
// GetPkgInfo fills the package info structure for the specified package path.
64
123
// It passes the `go list` the flags specified by `flags`.
65
124
func GetPkgInfo (pkgpath string , flags ... string ) PkgInfo {
@@ -74,13 +133,13 @@ func GetPkgInfo(pkgpath string, flags ...string) PkgInfo {
74
133
func GetModDir (pkgpath string , flags ... string ) string {
75
134
// enable module mode so that we can find a module root if it exists, even if go module support is
76
135
// disabled by a build
77
- mod , err := runGoListWithEnv ("{{.Module}}" , pkgpath , []string {"GO111MODULE=on" }, flags ... )
136
+ mod , err := runGoListWithEnv ("{{.Module}}" , [] string { pkgpath } , []string {"GO111MODULE=on" }, flags ... )
78
137
if err != nil || mod == "<nil>" {
79
138
// if the command errors or modules aren't being used, return the empty string
80
139
return ""
81
140
}
82
141
83
- modDir , err := runGoListWithEnv ("{{.Module.Dir}}" , pkgpath , []string {"GO111MODULE=on" }, flags ... )
142
+ modDir , err := runGoListWithEnv ("{{.Module.Dir}}" , [] string { pkgpath } , []string {"GO111MODULE=on" }, flags ... )
84
143
if err != nil {
85
144
return ""
86
145
}
@@ -96,7 +155,7 @@ func GetModDir(pkgpath string, flags ...string) string {
96
155
// GetPkgDir gets the absolute directory containing the package with path `pkgpath`. It passes the
97
156
// `go list` command the flags specified by `flags`.
98
157
func GetPkgDir (pkgpath string , flags ... string ) string {
99
- pkgDir , err := runGoList ("{{.Dir}}" , pkgpath , flags ... )
158
+ pkgDir , err := runGoList ("{{.Dir}}" , [] string { pkgpath } , flags ... )
100
159
if err != nil {
101
160
return ""
102
161
}
@@ -112,7 +171,7 @@ func GetPkgDir(pkgpath string, flags ...string) string {
112
171
// DepErrors checks there are any errors resolving dependencies for `pkgpath`. It passes the `go
113
172
// list` command the flags specified by `flags`.
114
173
func DepErrors (pkgpath string , flags ... string ) bool {
115
- out , err := runGoList ("{{if .DepsErrors}}{{else}}error{{end}}" , pkgpath , flags ... )
174
+ out , err := runGoList ("{{if .DepsErrors}}{{else}}error{{end}}" , [] string { pkgpath } , flags ... )
116
175
if err != nil {
117
176
// if go list failed, assume dependencies are broken
118
177
return false
0 commit comments