@@ -10,6 +10,7 @@ import (
10
10
"encoding/json"
11
11
"fmt"
12
12
"go/types"
13
+ "io/ioutil"
13
14
"log"
14
15
"os"
15
16
"os/exec"
@@ -208,56 +209,58 @@ extractQueries:
208
209
}
209
210
}
210
211
211
- modifiedPkgs , needPkgs , err := state .processGolistOverlay (response )
212
- if err != nil {
213
- return nil , err
214
- }
212
+ // Only use go/packages' overlay processing if we're using a Go version
213
+ // below 1.16. Otherwise, go list handles it.
214
+ if goVersion , err := state .getGoVersion (); err == nil && goVersion < 16 {
215
+ modifiedPkgs , needPkgs , err := state .processGolistOverlay (response )
216
+ if err != nil {
217
+ return nil , err
218
+ }
215
219
216
- var containsCandidates []string
217
- if len (containFiles ) > 0 {
218
- containsCandidates = append (containsCandidates , modifiedPkgs ... )
219
- containsCandidates = append (containsCandidates , needPkgs ... )
220
- }
221
- if err := state .addNeededOverlayPackages (response , needPkgs ); err != nil {
222
- return nil , err
223
- }
224
- // Check candidate packages for containFiles.
225
- if len (containFiles ) > 0 {
226
- for _ , id := range containsCandidates {
227
- pkg , ok := response .seenPackages [id ]
228
- if ! ok {
229
- response .addPackage (& Package {
230
- ID : id ,
231
- Errors : []Error {
232
- {
220
+ var containsCandidates []string
221
+ if len (containFiles ) > 0 {
222
+ containsCandidates = append (containsCandidates , modifiedPkgs ... )
223
+ containsCandidates = append (containsCandidates , needPkgs ... )
224
+ }
225
+ if err := state .addNeededOverlayPackages (response , needPkgs ); err != nil {
226
+ return nil , err
227
+ }
228
+ // Check candidate packages for containFiles.
229
+ if len (containFiles ) > 0 {
230
+ for _ , id := range containsCandidates {
231
+ pkg , ok := response .seenPackages [id ]
232
+ if ! ok {
233
+ response .addPackage (& Package {
234
+ ID : id ,
235
+ Errors : []Error {{
233
236
Kind : ListError ,
234
237
Msg : fmt .Sprintf ("package %s expected but not seen" , id ),
235
- },
236
- },
237
- })
238
- continue
239
- }
240
- for _ , f := range containFiles {
241
- for _ , g := range pkg . GoFiles {
242
- if sameFile ( f , g ) {
243
- response . addRoot ( id )
238
+ }} ,
239
+ })
240
+ continue
241
+ }
242
+ for _ , f := range containFiles {
243
+ for _ , g := range pkg . GoFiles {
244
+ if sameFile ( f , g ) {
245
+ response . addRoot ( id )
246
+ }
244
247
}
245
248
}
246
249
}
247
250
}
248
- }
249
- // Add root for any package that matches a pattern. This applies only to
250
- // packages that are modified by overlays, since they are not added as
251
- // roots automatically.
252
- for _ , pattern := range restPatterns {
253
- match := matchPattern ( pattern )
254
- for _ , pkgID := range modifiedPkgs {
255
- pkg , ok := response . seenPackages [ pkgID ]
256
- if ! ok {
257
- continue
258
- }
259
- if match (pkg .PkgPath ) {
260
- response . addRoot ( pkg . ID )
251
+ // Add root for any package that matches a pattern. This applies only to
252
+ // packages that are modified by overlays, since they are not added as
253
+ // roots automatically.
254
+ for _ , pattern := range restPatterns {
255
+ match := matchPattern ( pattern )
256
+ for _ , pkgID := range modifiedPkgs {
257
+ pkg , ok := response . seenPackages [ pkgID ]
258
+ if ! ok {
259
+ continue
260
+ }
261
+ if match ( pkg . PkgPath ) {
262
+ response . addRoot (pkg .ID )
263
+ }
261
264
}
262
265
}
263
266
}
@@ -835,6 +838,26 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
835
838
cfg := state .cfg
836
839
837
840
inv := state .cfgInvocation ()
841
+
842
+ // For Go versions 1.16 and above, `go list` accepts overlays directly via
843
+ // the -overlay flag. Set it, if it's available.
844
+ //
845
+ // The check for "list" is not necessarily required, but we should avoid
846
+ // getting the go version if possible.
847
+ if verb == "list" {
848
+ goVersion , err := state .getGoVersion ()
849
+ if err != nil {
850
+ return nil , err
851
+ }
852
+ if goVersion >= 16 {
853
+ filename , cleanup , err := state .writeOverlays ()
854
+ if err != nil {
855
+ return nil , err
856
+ }
857
+ defer cleanup ()
858
+ inv .Overlay = filename
859
+ }
860
+ }
838
861
inv .Verb = verb
839
862
inv .Args = args
840
863
gocmdRunner := cfg .gocmdRunner
@@ -976,6 +999,67 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
976
999
return stdout , nil
977
1000
}
978
1001
1002
+ // OverlayJSON is the format overlay files are expected to be in.
1003
+ // The Replace map maps from overlaid paths to replacement paths:
1004
+ // the Go command will forward all reads trying to open
1005
+ // each overlaid path to its replacement path, or consider the overlaid
1006
+ // path not to exist if the replacement path is empty.
1007
+ //
1008
+ // From golang/go#39958.
1009
+ type OverlayJSON struct {
1010
+ Replace map [string ]string `json:"replace,omitempty"`
1011
+ }
1012
+
1013
+ // writeOverlays writes out files for go list's -overlay flag, as described
1014
+ // above.
1015
+ func (state * golistState ) writeOverlays () (filename string , cleanup func (), err error ) {
1016
+ // Do nothing if there are no overlays in the config.
1017
+ if len (state .cfg .Overlay ) == 0 {
1018
+ return "" , func () {}, nil
1019
+ }
1020
+ dir , err := ioutil .TempDir ("" , "gopackages-*" )
1021
+ if err != nil {
1022
+ return "" , nil , err
1023
+ }
1024
+ // The caller must clean up this directory, unless this function returns an
1025
+ // error.
1026
+ cleanup = func () {
1027
+ os .RemoveAll (dir )
1028
+ }
1029
+ defer func () {
1030
+ if err != nil {
1031
+ cleanup ()
1032
+ }
1033
+ }()
1034
+ overlays := map [string ]string {}
1035
+ for k , v := range state .cfg .Overlay {
1036
+ // Create a unique filename for the overlaid files, to avoid
1037
+ // creating nested directories.
1038
+ noSeparator := strings .Join (strings .Split (filepath .ToSlash (k ), "/" ), "" )
1039
+ f , err := ioutil .TempFile (dir , fmt .Sprintf ("*-%s" , noSeparator ))
1040
+ if err != nil {
1041
+ return "" , func () {}, err
1042
+ }
1043
+ if _ , err := f .Write (v ); err != nil {
1044
+ return "" , func () {}, err
1045
+ }
1046
+ if err := f .Close (); err != nil {
1047
+ return "" , func () {}, err
1048
+ }
1049
+ overlays [k ] = f .Name ()
1050
+ }
1051
+ b , err := json .Marshal (OverlayJSON {Replace : overlays })
1052
+ if err != nil {
1053
+ return "" , func () {}, err
1054
+ }
1055
+ // Write out the overlay file that contains the filepath mappings.
1056
+ filename = filepath .Join (dir , "overlay.json" )
1057
+ if err := ioutil .WriteFile (filename , b , 0665 ); err != nil {
1058
+ return "" , func () {}, err
1059
+ }
1060
+ return filename , cleanup , nil
1061
+ }
1062
+
979
1063
func containsGoFile (s []string ) bool {
980
1064
for _ , f := range s {
981
1065
if strings .HasSuffix (f , ".go" ) {
0 commit comments