1
2
3
4
5 package packages
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "fmt"
12 "log"
13 "os"
14 "os/exec"
15 "path"
16 "path/filepath"
17 "reflect"
18 "sort"
19 "strconv"
20 "strings"
21 "sync"
22 "unicode"
23
24 "golang.org/x/tools/go/internal/packagesdriver"
25 "golang.org/x/tools/internal/gocommand"
26 "golang.org/x/tools/internal/packagesinternal"
27 )
28
29
30 var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
31
32
33
34 type goTooOldError struct {
35 error
36 }
37
38
39 type responseDeduper struct {
40 seenRoots map[string]bool
41 seenPackages map[string]*Package
42 dr *DriverResponse
43 }
44
45 func newDeduper() *responseDeduper {
46 return &responseDeduper{
47 dr: &DriverResponse{},
48 seenRoots: map[string]bool{},
49 seenPackages: map[string]*Package{},
50 }
51 }
52
53
54 func (r *responseDeduper) addAll(dr *DriverResponse) {
55 for _, pkg := range dr.Packages {
56 r.addPackage(pkg)
57 }
58 for _, root := range dr.Roots {
59 r.addRoot(root)
60 }
61 r.dr.GoVersion = dr.GoVersion
62 }
63
64 func (r *responseDeduper) addPackage(p *Package) {
65 if r.seenPackages[p.ID] != nil {
66 return
67 }
68 r.seenPackages[p.ID] = p
69 r.dr.Packages = append(r.dr.Packages, p)
70 }
71
72 func (r *responseDeduper) addRoot(id string) {
73 if r.seenRoots[id] {
74 return
75 }
76 r.seenRoots[id] = true
77 r.dr.Roots = append(r.dr.Roots, id)
78 }
79
80 type golistState struct {
81 cfg *Config
82 ctx context.Context
83
84 envOnce sync.Once
85 goEnvError error
86 goEnv map[string]string
87
88 rootsOnce sync.Once
89 rootDirsError error
90 rootDirs map[string]string
91
92 goVersionOnce sync.Once
93 goVersionError error
94 goVersion int
95
96
97 vendorDirs map[string]bool
98 }
99
100
101
102 func (state *golistState) getEnv() (map[string]string, error) {
103 state.envOnce.Do(func() {
104 var b *bytes.Buffer
105 b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
106 if state.goEnvError != nil {
107 return
108 }
109
110 state.goEnv = make(map[string]string)
111 decoder := json.NewDecoder(b)
112 if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
113 return
114 }
115 })
116 return state.goEnv, state.goEnvError
117 }
118
119
120 func (state *golistState) mustGetEnv() map[string]string {
121 env, err := state.getEnv()
122 if err != nil {
123 panic(fmt.Sprintf("mustGetEnv: %v", err))
124 }
125 return env
126 }
127
128
129
130
131 func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) {
132
133 parentCtx := cfg.Context
134 if parentCtx == nil {
135 parentCtx = context.Background()
136 }
137 ctx, cancel := context.WithCancel(parentCtx)
138 defer cancel()
139
140 response := newDeduper()
141
142 state := &golistState{
143 cfg: cfg,
144 ctx: ctx,
145 vendorDirs: map[string]bool{},
146 }
147
148
149 if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
150 errCh := make(chan error)
151 go func() {
152 compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
153 response.dr.Compiler = compiler
154 response.dr.Arch = arch
155 errCh <- err
156 }()
157 defer func() {
158 if sizesErr := <-errCh; sizesErr != nil {
159 err = sizesErr
160 }
161 }()
162 }
163
164
165 var containFiles []string
166 restPatterns := make([]string, 0, len(patterns))
167
168
169 extractQueries:
170 for _, pattern := range patterns {
171 eqidx := strings.Index(pattern, "=")
172 if eqidx < 0 {
173 restPatterns = append(restPatterns, pattern)
174 } else {
175 query, value := pattern[:eqidx], pattern[eqidx+len("="):]
176 switch query {
177 case "file":
178 containFiles = append(containFiles, value)
179 case "pattern":
180 restPatterns = append(restPatterns, value)
181 case "":
182 restPatterns = append(restPatterns, pattern)
183 default:
184 for _, rune := range query {
185 if rune < 'a' || rune > 'z' {
186 restPatterns = append(restPatterns, pattern)
187 continue extractQueries
188 }
189 }
190
191 return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
192 }
193 }
194 }
195
196
197
198
199 if len(restPatterns) > 0 || len(patterns) == 0 {
200 dr, err := state.createDriverResponse(restPatterns...)
201 if err != nil {
202 return nil, err
203 }
204 response.addAll(dr)
205 }
206
207 if len(containFiles) != 0 {
208 if err := state.runContainsQueries(response, containFiles); err != nil {
209 return nil, err
210 }
211 }
212
213
214 return response.dr, nil
215 }
216
217 func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
218 for _, query := range queries {
219
220 fdir := filepath.Dir(query)
221
222
223 pattern, err := filepath.Abs(fdir)
224 if err != nil {
225 return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
226 }
227 dirResponse, err := state.createDriverResponse(pattern)
228
229
230
231
232
233
234 if err != nil || len(dirResponse.Packages) == 0 || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
235 len(dirResponse.Packages[0].Errors) == 1 {
236 var queryErr error
237 if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
238 return err
239 }
240 }
241 isRoot := make(map[string]bool, len(dirResponse.Roots))
242 for _, root := range dirResponse.Roots {
243 isRoot[root] = true
244 }
245 for _, pkg := range dirResponse.Packages {
246
247
248
249
250 response.addPackage(pkg)
251
252 if !isRoot[pkg.ID] {
253 continue
254 }
255 for _, pkgFile := range pkg.GoFiles {
256 if filepath.Base(query) == filepath.Base(pkgFile) {
257 response.addRoot(pkg.ID)
258 break
259 }
260 }
261 }
262 }
263 return nil
264 }
265
266
267
268 func (state *golistState) adhocPackage(pattern, query string) (*DriverResponse, error) {
269 response, err := state.createDriverResponse(query)
270 if err != nil {
271 return nil, err
272 }
273
274
275
276 if len(response.Packages) == 0 {
277 response.Packages = append(response.Packages, &Package{
278 ID: "command-line-arguments",
279 PkgPath: query,
280 GoFiles: []string{query},
281 CompiledGoFiles: []string{query},
282 Imports: make(map[string]*Package),
283 })
284 response.Roots = append(response.Roots, "command-line-arguments")
285 }
286
287 if len(response.Packages) == 1 {
288
289
290
291 if response.Packages[0].ID == "command-line-arguments" ||
292 filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
293 if len(response.Packages[0].GoFiles) == 0 {
294 filename := filepath.Join(pattern, filepath.Base(query))
295
296 for path := range state.cfg.Overlay {
297 if path == filename {
298 response.Packages[0].Errors = nil
299 response.Packages[0].GoFiles = []string{path}
300 response.Packages[0].CompiledGoFiles = []string{path}
301 }
302 }
303 }
304 }
305 }
306 return response, nil
307 }
308
309
310
311 type jsonPackage struct {
312 ImportPath string
313 Dir string
314 Name string
315 Export string
316 GoFiles []string
317 CompiledGoFiles []string
318 IgnoredGoFiles []string
319 IgnoredOtherFiles []string
320 EmbedPatterns []string
321 EmbedFiles []string
322 CFiles []string
323 CgoFiles []string
324 CXXFiles []string
325 MFiles []string
326 HFiles []string
327 FFiles []string
328 SFiles []string
329 SwigFiles []string
330 SwigCXXFiles []string
331 SysoFiles []string
332 Imports []string
333 ImportMap map[string]string
334 Deps []string
335 Module *Module
336 TestGoFiles []string
337 TestImports []string
338 XTestGoFiles []string
339 XTestImports []string
340 ForTest string
341 DepOnly bool
342
343 Error *packagesinternal.PackageError
344 DepsErrors []*packagesinternal.PackageError
345 }
346
347 type jsonPackageError struct {
348 ImportStack []string
349 Pos string
350 Err string
351 }
352
353 func otherFiles(p *jsonPackage) [][]string {
354 return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
355 }
356
357
358
359 func (state *golistState) createDriverResponse(words ...string) (*DriverResponse, error) {
360
361
362
363
364
365
366
367
368
369
370
371
372
373 goVersion, err := state.getGoVersion()
374 if err != nil {
375 return nil, err
376 }
377 buf, err := state.invokeGo("list", golistargs(state.cfg, words, goVersion)...)
378 if err != nil {
379 return nil, err
380 }
381
382 seen := make(map[string]*jsonPackage)
383 pkgs := make(map[string]*Package)
384 additionalErrors := make(map[string][]Error)
385
386 response := &DriverResponse{
387 GoVersion: goVersion,
388 }
389 for dec := json.NewDecoder(buf); dec.More(); {
390 p := new(jsonPackage)
391 if err := dec.Decode(p); err != nil {
392 return nil, fmt.Errorf("JSON decoding failed: %v", err)
393 }
394
395 if p.ImportPath == "" {
396
397
398
399
400 if p.Error != nil {
401 return nil, Error{
402 Pos: p.Error.Pos,
403 Msg: p.Error.Err,
404 }
405 }
406 return nil, fmt.Errorf("package missing import path: %+v", p)
407 }
408
409
410
411
412
413
414
415
416 if filepath.IsAbs(p.ImportPath) && p.Error != nil {
417 pkgPath, ok, err := state.getPkgPath(p.ImportPath)
418 if err != nil {
419 return nil, err
420 }
421 if ok {
422 p.ImportPath = pkgPath
423 }
424 }
425
426 if old, found := seen[p.ImportPath]; found {
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 if old.Error == nil && p.Error == nil {
447 if !reflect.DeepEqual(p, old) {
448 return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
449 }
450 continue
451 }
452
453
454
455
456 if old.Error != nil {
457 var errkind string
458 if strings.Contains(old.Error.Err, "not an importable package") {
459 errkind = "not an importable package"
460 } else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
461 errkind = "use of internal package not allowed"
462 }
463 if errkind != "" {
464 if len(old.Error.ImportStack) < 1 {
465 return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
466 }
467 importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
468 if importingPkg == old.ImportPath {
469
470
471
472 if len(old.Error.ImportStack) < 2 {
473 return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
474 }
475 importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
476 }
477 additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
478 Pos: old.Error.Pos,
479 Msg: old.Error.Err,
480 Kind: ListError,
481 })
482 }
483 }
484
485
486
487 if old.Error == nil {
488 continue
489 }
490
491
492 }
493 seen[p.ImportPath] = p
494
495 pkg := &Package{
496 Name: p.Name,
497 ID: p.ImportPath,
498 GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
499 CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
500 OtherFiles: absJoin(p.Dir, otherFiles(p)...),
501 EmbedFiles: absJoin(p.Dir, p.EmbedFiles),
502 EmbedPatterns: absJoin(p.Dir, p.EmbedPatterns),
503 IgnoredFiles: absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles),
504 forTest: p.ForTest,
505 depsErrors: p.DepsErrors,
506 Module: p.Module,
507 }
508
509 if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
510 if len(p.CompiledGoFiles) > len(p.GoFiles) {
511
512
513
514
515
516 cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
517 pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
518 } else {
519
520 pkg.CompiledGoFiles = nil
521 pkg.Errors = append(pkg.Errors, Error{
522 Msg: "go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.",
523 Kind: ListError,
524 })
525 }
526 }
527
528
529
530
531
532 if len(pkg.CompiledGoFiles) > 0 {
533 out := pkg.CompiledGoFiles[:0]
534 for _, f := range pkg.CompiledGoFiles {
535 if ext := filepath.Ext(f); ext != ".go" && ext != "" {
536 continue
537 }
538 out = append(out, f)
539 }
540 pkg.CompiledGoFiles = out
541 }
542
543
544 if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
545 pkg.PkgPath = pkg.ID[:i]
546 } else {
547 pkg.PkgPath = pkg.ID
548 }
549
550 if pkg.PkgPath == "unsafe" {
551 pkg.CompiledGoFiles = nil
552 } else if len(pkg.CompiledGoFiles) == 0 {
553
554
555
556 pkg.CompiledGoFiles = pkg.GoFiles
557 }
558
559
560 if p.Dir != "" && !filepath.IsAbs(p.Dir) {
561 log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
562 }
563
564 if p.Export != "" && !filepath.IsAbs(p.Export) {
565 pkg.ExportFile = filepath.Join(p.Dir, p.Export)
566 } else {
567 pkg.ExportFile = p.Export
568 }
569
570
571
572
573
574 ids := make(map[string]bool)
575 for _, id := range p.Imports {
576 ids[id] = true
577 }
578 pkg.Imports = make(map[string]*Package)
579 for path, id := range p.ImportMap {
580 pkg.Imports[path] = &Package{ID: id}
581 delete(ids, id)
582 }
583 for id := range ids {
584 if id == "C" {
585 continue
586 }
587
588 pkg.Imports[id] = &Package{ID: id}
589 }
590 if !p.DepOnly {
591 response.Roots = append(response.Roots, pkg.ID)
592 }
593
594
595
596
597
598
599
600 if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
601 addFilenameFromPos := func(pos string) bool {
602 split := strings.Split(pos, ":")
603 if len(split) < 1 {
604 return false
605 }
606 filename := strings.TrimSpace(split[0])
607 if filename == "" {
608 return false
609 }
610 if !filepath.IsAbs(filename) {
611 filename = filepath.Join(state.cfg.Dir, filename)
612 }
613 info, _ := os.Stat(filename)
614 if info == nil {
615 return false
616 }
617 pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
618 pkg.GoFiles = append(pkg.GoFiles, filename)
619 return true
620 }
621 found := addFilenameFromPos(err.Pos)
622
623
624
625 if !found {
626 addFilenameFromPos(err.Err)
627 }
628 }
629
630 if p.Error != nil {
631 msg := strings.TrimSpace(p.Error.Err)
632
633 if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
634 msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
635 }
636 pkg.Errors = append(pkg.Errors, Error{
637 Pos: p.Error.Pos,
638 Msg: msg,
639 Kind: ListError,
640 })
641 }
642
643 pkgs[pkg.ID] = pkg
644 }
645
646 for id, errs := range additionalErrors {
647 if p, ok := pkgs[id]; ok {
648 p.Errors = append(p.Errors, errs...)
649 }
650 }
651 for _, pkg := range pkgs {
652 response.Packages = append(response.Packages, pkg)
653 }
654 sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
655
656 return response, nil
657 }
658
659 func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
660 if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
661 return false
662 }
663
664 goV, err := state.getGoVersion()
665 if err != nil {
666 return false
667 }
668
669
670
671 if goV < 15 {
672 return len(p.Error.ImportStack) == 0
673 }
674
675
676
677
678
679 return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
680 }
681
682
683 func (state *golistState) getGoVersion() (int, error) {
684 state.goVersionOnce.Do(func() {
685 state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
686 })
687 return state.goVersion, state.goVersionError
688 }
689
690
691
692 func (state *golistState) getPkgPath(dir string) (string, bool, error) {
693 absDir, err := filepath.Abs(dir)
694 if err != nil {
695 return "", false, err
696 }
697 roots, err := state.determineRootDirs()
698 if err != nil {
699 return "", false, err
700 }
701
702 for rdir, rpath := range roots {
703
704
705 if !strings.HasPrefix(absDir, rdir) {
706 continue
707 }
708
709 r, err := filepath.Rel(rdir, dir)
710 if err != nil {
711 continue
712 }
713 if rpath != "" {
714
715
716
717
718
719
720 return path.Join(rpath, filepath.ToSlash(r)), true, nil
721 }
722 return filepath.ToSlash(r), true, nil
723 }
724 return "", false, nil
725 }
726
727
728 func absJoin(dir string, fileses ...[]string) (res []string) {
729 for _, files := range fileses {
730 for _, file := range files {
731 if !filepath.IsAbs(file) {
732 file = filepath.Join(dir, file)
733 }
734 res = append(res, file)
735 }
736 }
737 return res
738 }
739
740 func jsonFlag(cfg *Config, goVersion int) string {
741 if goVersion < 19 {
742 return "-json"
743 }
744 var fields []string
745 added := make(map[string]bool)
746 addFields := func(fs ...string) {
747 for _, f := range fs {
748 if !added[f] {
749 added[f] = true
750 fields = append(fields, f)
751 }
752 }
753 }
754 addFields("Name", "ImportPath", "Error")
755 if cfg.Mode&NeedFiles != 0 || cfg.Mode&NeedTypes != 0 {
756 addFields("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles",
757 "CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles",
758 "SwigFiles", "SwigCXXFiles", "SysoFiles")
759 if cfg.Tests {
760 addFields("TestGoFiles", "XTestGoFiles")
761 }
762 }
763 if cfg.Mode&NeedTypes != 0 {
764
765
766
767
768 addFields("Dir", "CompiledGoFiles")
769 }
770 if cfg.Mode&NeedCompiledGoFiles != 0 {
771 addFields("Dir", "CompiledGoFiles", "Export")
772 }
773 if cfg.Mode&NeedImports != 0 {
774
775
776 addFields("DepOnly", "Imports", "ImportMap")
777 if cfg.Tests {
778 addFields("TestImports", "XTestImports")
779 }
780 }
781 if cfg.Mode&NeedDeps != 0 {
782 addFields("DepOnly")
783 }
784 if usesExportData(cfg) {
785
786 addFields("Dir", "Export")
787 }
788 if cfg.Mode&needInternalForTest != 0 {
789 addFields("ForTest")
790 }
791 if cfg.Mode&needInternalDepsErrors != 0 {
792 addFields("DepsErrors")
793 }
794 if cfg.Mode&NeedModule != 0 {
795 addFields("Module")
796 }
797 if cfg.Mode&NeedEmbedFiles != 0 {
798 addFields("EmbedFiles")
799 }
800 if cfg.Mode&NeedEmbedPatterns != 0 {
801 addFields("EmbedPatterns")
802 }
803 return "-json=" + strings.Join(fields, ",")
804 }
805
806 func golistargs(cfg *Config, words []string, goVersion int) []string {
807 const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
808 fullargs := []string{
809 "-e", jsonFlag(cfg, goVersion),
810 fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
811 fmt.Sprintf("-test=%t", cfg.Tests),
812 fmt.Sprintf("-export=%t", usesExportData(cfg)),
813 fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
814
815
816 fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
817 }
818
819
820
821
822
823 if goVersion >= 21 {
824 fullargs = append(fullargs, "-pgo=off")
825 }
826
827 fullargs = append(fullargs, cfg.BuildFlags...)
828 fullargs = append(fullargs, "--")
829 fullargs = append(fullargs, words...)
830 return fullargs
831 }
832
833
834 func (state *golistState) cfgInvocation() gocommand.Invocation {
835 cfg := state.cfg
836 return gocommand.Invocation{
837 BuildFlags: cfg.BuildFlags,
838 ModFile: cfg.modFile,
839 ModFlag: cfg.modFlag,
840 CleanEnv: cfg.Env != nil,
841 Env: cfg.Env,
842 Logf: cfg.Logf,
843 WorkingDir: cfg.Dir,
844 }
845 }
846
847
848 func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
849 cfg := state.cfg
850
851 inv := state.cfgInvocation()
852
853
854
855
856
857
858 if verb == "list" {
859 goVersion, err := state.getGoVersion()
860 if err != nil {
861 return nil, err
862 }
863 if goVersion >= 16 {
864 filename, cleanup, err := state.writeOverlays()
865 if err != nil {
866 return nil, err
867 }
868 defer cleanup()
869 inv.Overlay = filename
870 }
871 }
872 inv.Verb = verb
873 inv.Args = args
874 gocmdRunner := cfg.gocmdRunner
875 if gocmdRunner == nil {
876 gocmdRunner = &gocommand.Runner{}
877 }
878 stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
879 if err != nil {
880
881 if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
882 return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
883 }
884
885 exitErr, ok := err.(*exec.ExitError)
886 if !ok {
887
888
889 return nil, fmt.Errorf("couldn't run 'go': %w", err)
890 }
891
892
893 if strings.Contains(stderr.String(), "flag provided but not defined") {
894 return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
895 }
896
897
898 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
899 return nil, friendlyErr
900 }
901
902
903
904
905
906 isPkgPathRune := func(r rune) bool {
907
908
909
910
911
912 return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
913 !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
914 }
915
916 msg := stderr.String()
917 for strings.HasPrefix(msg, "go: downloading") {
918 msg = msg[strings.IndexRune(msg, '\n')+1:]
919 }
920 if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
921 msg := msg[len("# "):]
922 if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
923 return stdout, nil
924 }
925
926 if strings.HasPrefix(msg, "pkg-config") {
927 return stdout, nil
928 }
929 }
930
931
932
933
934 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
935 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
936 strings.Trim(stderr.String(), "\n"))
937 return bytes.NewBufferString(output), nil
938 }
939
940
941 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
942 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
943 strings.Trim(stderr.String(), "\n"))
944 return bytes.NewBufferString(output), nil
945 }
946
947
948
949
950 const noSuchDirectory = "no such directory"
951 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
952 errstr := stderr.String()
953 abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
954 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
955 abspath, strings.Trim(stderr.String(), "\n"))
956 return bytes.NewBufferString(output), nil
957 }
958
959
960
961 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
962 output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
963 strings.Trim(stderr.String(), "\n"))
964 return bytes.NewBufferString(output), nil
965 }
966
967
968
969 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
970 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
971
972 "command-line-arguments", strings.Trim(stderr.String(), "\n"))
973 return bytes.NewBufferString(output), nil
974 }
975
976
977 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
978 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
979
980 "command-line-arguments", strings.Trim(stderr.String(), "\n"))
981 return bytes.NewBufferString(output), nil
982 }
983
984
985
986
987 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
988
989 if len(stdout.String()) > 0 {
990 return stdout, nil
991 }
992
993 stderrStr := stderr.String()
994 var importPath string
995 colon := strings.Index(stderrStr, ":")
996 if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
997 importPath = stderrStr[len("go build "):colon]
998 }
999 output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
1000 importPath, strings.Trim(stderrStr, "\n"))
1001 return bytes.NewBufferString(output), nil
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011 if !usesExportData(cfg) && !containsGoFile(args) {
1012 return nil, friendlyErr
1013 }
1014 }
1015 return stdout, nil
1016 }
1017
1018
1019
1020
1021
1022
1023
1024
1025 type OverlayJSON struct {
1026 Replace map[string]string `json:"replace,omitempty"`
1027 }
1028
1029
1030
1031 func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
1032
1033 if len(state.cfg.Overlay) == 0 {
1034 return "", func() {}, nil
1035 }
1036 dir, err := os.MkdirTemp("", "gopackages-*")
1037 if err != nil {
1038 return "", nil, err
1039 }
1040
1041
1042 cleanup = func() {
1043 os.RemoveAll(dir)
1044 }
1045 defer func() {
1046 if err != nil {
1047 cleanup()
1048 }
1049 }()
1050 overlays := map[string]string{}
1051 for k, v := range state.cfg.Overlay {
1052
1053
1054 noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
1055 f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator))
1056 if err != nil {
1057 return "", func() {}, err
1058 }
1059 if _, err := f.Write(v); err != nil {
1060 return "", func() {}, err
1061 }
1062 if err := f.Close(); err != nil {
1063 return "", func() {}, err
1064 }
1065 overlays[k] = f.Name()
1066 }
1067 b, err := json.Marshal(OverlayJSON{Replace: overlays})
1068 if err != nil {
1069 return "", func() {}, err
1070 }
1071
1072 filename = filepath.Join(dir, "overlay.json")
1073 if err := os.WriteFile(filename, b, 0665); err != nil {
1074 return "", func() {}, err
1075 }
1076 return filename, cleanup, nil
1077 }
1078
1079 func containsGoFile(s []string) bool {
1080 for _, f := range s {
1081 if strings.HasSuffix(f, ".go") {
1082 return true
1083 }
1084 }
1085 return false
1086 }
1087
1088 func cmdDebugStr(cmd *exec.Cmd) string {
1089 env := make(map[string]string)
1090 for _, kv := range cmd.Env {
1091 split := strings.SplitN(kv, "=", 2)
1092 k, v := split[0], split[1]
1093 env[k] = v
1094 }
1095
1096 var args []string
1097 for _, arg := range cmd.Args {
1098 quoted := strconv.Quote(arg)
1099 if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
1100 args = append(args, quoted)
1101 } else {
1102 args = append(args, arg)
1103 }
1104 }
1105 return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
1106 }
1107
View as plain text