Source file
src/go/build/build.go
1
2
3
4
5 package build
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "go/ast"
12 "go/build/constraint"
13 "go/doc"
14 "go/token"
15 "internal/buildcfg"
16 exec "internal/execabs"
17 "internal/goroot"
18 "internal/goversion"
19 "io"
20 "io/fs"
21 "io/ioutil"
22 "os"
23 pathpkg "path"
24 "path/filepath"
25 "runtime"
26 "sort"
27 "strconv"
28 "strings"
29 "unicode"
30 "unicode/utf8"
31 )
32
33
34 type Context struct {
35 GOARCH string
36 GOOS string
37 GOROOT string
38 GOPATH string
39
40
41
42
43
44
45
46 Dir string
47
48 CgoEnabled bool
49 UseAllFiles bool
50 Compiler string
51
52
53
54
55
56
57
58
59
60
61
62 BuildTags []string
63 ToolTags []string
64 ReleaseTags []string
65
66
67
68
69
70
71
72 InstallSuffix string
73
74
75
76
77
78
79
80
81
82 JoinPath func(elem ...string) string
83
84
85
86 SplitPathList func(list string) []string
87
88
89
90 IsAbsPath func(path string) bool
91
92
93
94 IsDir func(path string) bool
95
96
97
98
99
100
101
102
103 HasSubdir func(root, dir string) (rel string, ok bool)
104
105
106
107
108 ReadDir func(dir string) ([]fs.FileInfo, error)
109
110
111
112 OpenFile func(path string) (io.ReadCloser, error)
113 }
114
115
116 func (ctxt *Context) joinPath(elem ...string) string {
117 if f := ctxt.JoinPath; f != nil {
118 return f(elem...)
119 }
120 return filepath.Join(elem...)
121 }
122
123
124 func (ctxt *Context) splitPathList(s string) []string {
125 if f := ctxt.SplitPathList; f != nil {
126 return f(s)
127 }
128 return filepath.SplitList(s)
129 }
130
131
132 func (ctxt *Context) isAbsPath(path string) bool {
133 if f := ctxt.IsAbsPath; f != nil {
134 return f(path)
135 }
136 return filepath.IsAbs(path)
137 }
138
139
140 func (ctxt *Context) isDir(path string) bool {
141 if f := ctxt.IsDir; f != nil {
142 return f(path)
143 }
144 fi, err := os.Stat(path)
145 return err == nil && fi.IsDir()
146 }
147
148
149
150 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
151 if f := ctxt.HasSubdir; f != nil {
152 return f(root, dir)
153 }
154
155
156 if rel, ok = hasSubdir(root, dir); ok {
157 return
158 }
159
160
161
162
163 rootSym, _ := filepath.EvalSymlinks(root)
164 dirSym, _ := filepath.EvalSymlinks(dir)
165
166 if rel, ok = hasSubdir(rootSym, dir); ok {
167 return
168 }
169 if rel, ok = hasSubdir(root, dirSym); ok {
170 return
171 }
172 return hasSubdir(rootSym, dirSym)
173 }
174
175
176 func hasSubdir(root, dir string) (rel string, ok bool) {
177 const sep = string(filepath.Separator)
178 root = filepath.Clean(root)
179 if !strings.HasSuffix(root, sep) {
180 root += sep
181 }
182 dir = filepath.Clean(dir)
183 if !strings.HasPrefix(dir, root) {
184 return "", false
185 }
186 return filepath.ToSlash(dir[len(root):]), true
187 }
188
189
190 func (ctxt *Context) readDir(path string) ([]fs.FileInfo, error) {
191 if f := ctxt.ReadDir; f != nil {
192 return f(path)
193 }
194
195 return ioutil.ReadDir(path)
196 }
197
198
199 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
200 if fn := ctxt.OpenFile; fn != nil {
201 return fn(path)
202 }
203
204 f, err := os.Open(path)
205 if err != nil {
206 return nil, err
207 }
208 return f, nil
209 }
210
211
212
213
214 func (ctxt *Context) isFile(path string) bool {
215 f, err := ctxt.openFile(path)
216 if err != nil {
217 return false
218 }
219 f.Close()
220 return true
221 }
222
223
224 func (ctxt *Context) gopath() []string {
225 var all []string
226 for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
227 if p == "" || p == ctxt.GOROOT {
228
229
230
231
232 continue
233 }
234 if strings.HasPrefix(p, "~") {
235
236
237
238
239
240
241
242
243
244
245
246
247 continue
248 }
249 all = append(all, p)
250 }
251 return all
252 }
253
254
255
256
257 func (ctxt *Context) SrcDirs() []string {
258 var all []string
259 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
260 dir := ctxt.joinPath(ctxt.GOROOT, "src")
261 if ctxt.isDir(dir) {
262 all = append(all, dir)
263 }
264 }
265 for _, p := range ctxt.gopath() {
266 dir := ctxt.joinPath(p, "src")
267 if ctxt.isDir(dir) {
268 all = append(all, dir)
269 }
270 }
271 return all
272 }
273
274
275
276
277 var Default Context = defaultContext()
278
279 func defaultGOPATH() string {
280 env := "HOME"
281 if runtime.GOOS == "windows" {
282 env = "USERPROFILE"
283 } else if runtime.GOOS == "plan9" {
284 env = "home"
285 }
286 if home := os.Getenv(env); home != "" {
287 def := filepath.Join(home, "go")
288 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
289
290
291 return ""
292 }
293 return def
294 }
295 return ""
296 }
297
298 var defaultToolTags, defaultReleaseTags []string
299
300 func defaultContext() Context {
301 var c Context
302
303 c.GOARCH = buildcfg.GOARCH
304 c.GOOS = buildcfg.GOOS
305 c.GOROOT = pathpkg.Clean(runtime.GOROOT())
306 c.GOPATH = envOr("GOPATH", defaultGOPATH())
307 c.Compiler = runtime.Compiler
308
309
310
311
312
313
314 for _, exp := range buildcfg.EnabledExperiments() {
315 c.ToolTags = append(c.ToolTags, "goexperiment."+exp)
316 }
317 defaultToolTags = append([]string{}, c.ToolTags...)
318
319
320
321
322
323
324
325
326 for i := 1; i <= goversion.Version; i++ {
327 c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i))
328 }
329
330 defaultReleaseTags = append([]string{}, c.ReleaseTags...)
331
332 env := os.Getenv("CGO_ENABLED")
333 if env == "" {
334 env = defaultCGO_ENABLED
335 }
336 switch env {
337 case "1":
338 c.CgoEnabled = true
339 case "0":
340 c.CgoEnabled = false
341 default:
342
343 if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
344 c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
345 break
346 }
347 c.CgoEnabled = false
348 }
349
350 return c
351 }
352
353 func envOr(name, def string) string {
354 s := os.Getenv(name)
355 if s == "" {
356 return def
357 }
358 return s
359 }
360
361
362 type ImportMode uint
363
364 const (
365
366
367
368 FindOnly ImportMode = 1 << iota
369
370
371
372
373
374
375
376
377
378
379 AllowBinary
380
381
382
383
384
385 ImportComment
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405 IgnoreVendor
406 )
407
408
409 type Package struct {
410 Dir string
411 Name string
412 ImportComment string
413 Doc string
414 ImportPath string
415 Root string
416 SrcRoot string
417 PkgRoot string
418 PkgTargetRoot string
419 BinDir string
420 Goroot bool
421 PkgObj string
422 AllTags []string
423 ConflictDir string
424 BinaryOnly bool
425
426
427 GoFiles []string
428 CgoFiles []string
429 IgnoredGoFiles []string
430 InvalidGoFiles []string
431 IgnoredOtherFiles []string
432 CFiles []string
433 CXXFiles []string
434 MFiles []string
435 HFiles []string
436 FFiles []string
437 SFiles []string
438 SwigFiles []string
439 SwigCXXFiles []string
440 SysoFiles []string
441
442
443 CgoCFLAGS []string
444 CgoCPPFLAGS []string
445 CgoCXXFLAGS []string
446 CgoFFLAGS []string
447 CgoLDFLAGS []string
448 CgoPkgConfig []string
449
450
451 TestGoFiles []string
452 XTestGoFiles []string
453
454
455 Imports []string
456 ImportPos map[string][]token.Position
457 TestImports []string
458 TestImportPos map[string][]token.Position
459 XTestImports []string
460 XTestImportPos map[string][]token.Position
461
462
463
464
465
466
467 EmbedPatterns []string
468 EmbedPatternPos map[string][]token.Position
469 TestEmbedPatterns []string
470 TestEmbedPatternPos map[string][]token.Position
471 XTestEmbedPatterns []string
472 XTestEmbedPatternPos map[string][]token.Position
473 }
474
475
476
477
478 func (p *Package) IsCommand() bool {
479 return p.Name == "main"
480 }
481
482
483
484 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
485 return ctxt.Import(".", dir, mode)
486 }
487
488
489
490
491 type NoGoError struct {
492 Dir string
493 }
494
495 func (e *NoGoError) Error() string {
496 return "no buildable Go source files in " + e.Dir
497 }
498
499
500
501 type MultiplePackageError struct {
502 Dir string
503 Packages []string
504 Files []string
505 }
506
507 func (e *MultiplePackageError) Error() string {
508
509 return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
510 }
511
512 func nameExt(name string) string {
513 i := strings.LastIndex(name, ".")
514 if i < 0 {
515 return ""
516 }
517 return name[i:]
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
537 p := &Package{
538 ImportPath: path,
539 }
540 if path == "" {
541 return p, fmt.Errorf("import %q: invalid import path", path)
542 }
543
544 var pkgtargetroot string
545 var pkga string
546 var pkgerr error
547 suffix := ""
548 if ctxt.InstallSuffix != "" {
549 suffix = "_" + ctxt.InstallSuffix
550 }
551 switch ctxt.Compiler {
552 case "gccgo":
553 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
554 case "gc":
555 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
556 default:
557
558 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
559 }
560 setPkga := func() {
561 switch ctxt.Compiler {
562 case "gccgo":
563 dir, elem := pathpkg.Split(p.ImportPath)
564 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
565 case "gc":
566 pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
567 }
568 }
569 setPkga()
570
571 binaryOnly := false
572 if IsLocalImport(path) {
573 pkga = ""
574 if srcDir == "" {
575 return p, fmt.Errorf("import %q: import relative to unknown directory", path)
576 }
577 if !ctxt.isAbsPath(path) {
578 p.Dir = ctxt.joinPath(srcDir, path)
579 }
580
581
582
583 inTestdata := func(sub string) bool {
584 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
585 }
586 if ctxt.GOROOT != "" {
587 root := ctxt.joinPath(ctxt.GOROOT, "src")
588 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
589 p.Goroot = true
590 p.ImportPath = sub
591 p.Root = ctxt.GOROOT
592 setPkga()
593 goto Found
594 }
595 }
596 all := ctxt.gopath()
597 for i, root := range all {
598 rootsrc := ctxt.joinPath(root, "src")
599 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
600
601
602
603 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
604 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
605 p.ConflictDir = dir
606 goto Found
607 }
608 }
609 for _, earlyRoot := range all[:i] {
610 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
611 p.ConflictDir = dir
612 goto Found
613 }
614 }
615
616
617
618 p.ImportPath = sub
619 p.Root = root
620 setPkga()
621 goto Found
622 }
623 }
624
625
626 } else {
627 if strings.HasPrefix(path, "/") {
628 return p, fmt.Errorf("import %q: cannot import absolute path", path)
629 }
630
631 if err := ctxt.importGo(p, path, srcDir, mode); err == nil {
632 goto Found
633 } else if err != errNoModules {
634 return p, err
635 }
636
637 gopath := ctxt.gopath()
638
639
640 var tried struct {
641 vendor []string
642 goroot string
643 gopath []string
644 }
645
646
647 if mode&IgnoreVendor == 0 && srcDir != "" {
648 searchVendor := func(root string, isGoroot bool) bool {
649 sub, ok := ctxt.hasSubdir(root, srcDir)
650 if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
651 return false
652 }
653 for {
654 vendor := ctxt.joinPath(root, sub, "vendor")
655 if ctxt.isDir(vendor) {
656 dir := ctxt.joinPath(vendor, path)
657 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
658 p.Dir = dir
659 p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
660 p.Goroot = isGoroot
661 p.Root = root
662 setPkga()
663 return true
664 }
665 tried.vendor = append(tried.vendor, dir)
666 }
667 i := strings.LastIndex(sub, "/")
668 if i < 0 {
669 break
670 }
671 sub = sub[:i]
672 }
673 return false
674 }
675 if ctxt.Compiler != "gccgo" && searchVendor(ctxt.GOROOT, true) {
676 goto Found
677 }
678 for _, root := range gopath {
679 if searchVendor(root, false) {
680 goto Found
681 }
682 }
683 }
684
685
686 if ctxt.GOROOT != "" {
687
688
689
690
691 gorootFirst := srcDir == "" || !strings.HasPrefix(path, "vendor/")
692 if !gorootFirst {
693 _, gorootFirst = ctxt.hasSubdir(ctxt.GOROOT, srcDir)
694 }
695 if gorootFirst {
696 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
697 if ctxt.Compiler != "gccgo" {
698 isDir := ctxt.isDir(dir)
699 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
700 if isDir || binaryOnly {
701 p.Dir = dir
702 p.Goroot = true
703 p.Root = ctxt.GOROOT
704 goto Found
705 }
706 }
707 tried.goroot = dir
708 }
709 }
710 if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) {
711 p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
712 p.Goroot = true
713 p.Root = ctxt.GOROOT
714 goto Found
715 }
716 for _, root := range gopath {
717 dir := ctxt.joinPath(root, "src", path)
718 isDir := ctxt.isDir(dir)
719 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
720 if isDir || binaryOnly {
721 p.Dir = dir
722 p.Root = root
723 goto Found
724 }
725 tried.gopath = append(tried.gopath, dir)
726 }
727
728
729
730
731 if ctxt.GOROOT != "" && tried.goroot == "" {
732 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
733 if ctxt.Compiler != "gccgo" {
734 isDir := ctxt.isDir(dir)
735 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
736 if isDir || binaryOnly {
737 p.Dir = dir
738 p.Goroot = true
739 p.Root = ctxt.GOROOT
740 goto Found
741 }
742 }
743 tried.goroot = dir
744 }
745
746
747 var paths []string
748 format := "\t%s (vendor tree)"
749 for _, dir := range tried.vendor {
750 paths = append(paths, fmt.Sprintf(format, dir))
751 format = "\t%s"
752 }
753 if tried.goroot != "" {
754 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
755 } else {
756 paths = append(paths, "\t($GOROOT not set)")
757 }
758 format = "\t%s (from $GOPATH)"
759 for _, dir := range tried.gopath {
760 paths = append(paths, fmt.Sprintf(format, dir))
761 format = "\t%s"
762 }
763 if len(tried.gopath) == 0 {
764 paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
765 }
766 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
767 }
768
769 Found:
770 if p.Root != "" {
771 p.SrcRoot = ctxt.joinPath(p.Root, "src")
772 p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
773 p.BinDir = ctxt.joinPath(p.Root, "bin")
774 if pkga != "" {
775 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
776 p.PkgObj = ctxt.joinPath(p.Root, pkga)
777 }
778 }
779
780
781
782
783
784
785 if IsLocalImport(path) && !ctxt.isDir(p.Dir) {
786 if ctxt.Compiler == "gccgo" && p.Goroot {
787
788 return p, nil
789 }
790
791
792 return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir)
793 }
794
795 if mode&FindOnly != 0 {
796 return p, pkgerr
797 }
798 if binaryOnly && (mode&AllowBinary) != 0 {
799 return p, pkgerr
800 }
801
802 if ctxt.Compiler == "gccgo" && p.Goroot {
803
804 return p, nil
805 }
806
807 dirs, err := ctxt.readDir(p.Dir)
808 if err != nil {
809 return p, err
810 }
811
812 var badGoError error
813 badFiles := make(map[string]bool)
814 badFile := func(name string, err error) {
815 if badGoError == nil {
816 badGoError = err
817 }
818 if !badFiles[name] {
819 p.InvalidGoFiles = append(p.InvalidGoFiles, name)
820 badFiles[name] = true
821 }
822 }
823
824 var Sfiles []string
825 var firstFile, firstCommentFile string
826 embedPos := make(map[string][]token.Position)
827 testEmbedPos := make(map[string][]token.Position)
828 xTestEmbedPos := make(map[string][]token.Position)
829 importPos := make(map[string][]token.Position)
830 testImportPos := make(map[string][]token.Position)
831 xTestImportPos := make(map[string][]token.Position)
832 allTags := make(map[string]bool)
833 fset := token.NewFileSet()
834 for _, d := range dirs {
835 if d.IsDir() {
836 continue
837 }
838 if d.Mode()&fs.ModeSymlink != 0 {
839 if ctxt.isDir(ctxt.joinPath(p.Dir, d.Name())) {
840
841 continue
842 }
843 }
844
845 name := d.Name()
846 ext := nameExt(name)
847
848 info, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly, fset)
849 if err != nil {
850 badFile(name, err)
851 continue
852 }
853 if info == nil {
854 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
855
856 } else if ext == ".go" {
857 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
858 } else if fileListForExt(p, ext) != nil {
859 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
860 }
861 continue
862 }
863 data, filename := info.header, info.name
864
865
866 switch ext {
867 case ".go":
868
869 case ".S", ".sx":
870
871 Sfiles = append(Sfiles, name)
872 continue
873 default:
874 if list := fileListForExt(p, ext); list != nil {
875 *list = append(*list, name)
876 }
877 continue
878 }
879
880 if info.parseErr != nil {
881 badFile(name, info.parseErr)
882
883
884 }
885
886 var pkg string
887 if info.parsed != nil {
888 pkg = info.parsed.Name.Name
889 if pkg == "documentation" {
890 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
891 continue
892 }
893 }
894
895 isTest := strings.HasSuffix(name, "_test.go")
896 isXTest := false
897 if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg {
898 isXTest = true
899 pkg = pkg[:len(pkg)-len("_test")]
900 }
901
902 if p.Name == "" {
903 p.Name = pkg
904 firstFile = name
905 } else if pkg != p.Name {
906
907
908
909 badFile(name, &MultiplePackageError{
910 Dir: p.Dir,
911 Packages: []string{p.Name, pkg},
912 Files: []string{firstFile, name},
913 })
914 }
915
916 if info.parsed != nil && info.parsed.Doc != nil && p.Doc == "" && !isTest && !isXTest {
917 p.Doc = doc.Synopsis(info.parsed.Doc.Text())
918 }
919
920 if mode&ImportComment != 0 {
921 qcom, line := findImportComment(data)
922 if line != 0 {
923 com, err := strconv.Unquote(qcom)
924 if err != nil {
925 badFile(name, fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
926 } else if p.ImportComment == "" {
927 p.ImportComment = com
928 firstCommentFile = name
929 } else if p.ImportComment != com {
930 badFile(name, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
931 }
932 }
933 }
934
935
936 isCgo := false
937 for _, imp := range info.imports {
938 if imp.path == "C" {
939 if isTest {
940 badFile(name, fmt.Errorf("use of cgo in test %s not supported", filename))
941 continue
942 }
943 isCgo = true
944 if imp.doc != nil {
945 if err := ctxt.saveCgo(filename, p, imp.doc); err != nil {
946 badFile(name, err)
947 }
948 }
949 }
950 }
951
952 var fileList *[]string
953 var importMap, embedMap map[string][]token.Position
954 switch {
955 case isCgo:
956 allTags["cgo"] = true
957 if ctxt.CgoEnabled {
958 fileList = &p.CgoFiles
959 importMap = importPos
960 embedMap = embedPos
961 } else {
962
963 fileList = &p.IgnoredGoFiles
964 }
965 case isXTest:
966 fileList = &p.XTestGoFiles
967 importMap = xTestImportPos
968 embedMap = xTestEmbedPos
969 case isTest:
970 fileList = &p.TestGoFiles
971 importMap = testImportPos
972 embedMap = testEmbedPos
973 default:
974 fileList = &p.GoFiles
975 importMap = importPos
976 embedMap = embedPos
977 }
978 *fileList = append(*fileList, name)
979 if importMap != nil {
980 for _, imp := range info.imports {
981 importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
982 }
983 }
984 if embedMap != nil {
985 for _, emb := range info.embeds {
986 embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
987 }
988 }
989 }
990
991 for tag := range allTags {
992 p.AllTags = append(p.AllTags, tag)
993 }
994 sort.Strings(p.AllTags)
995
996 p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
997 p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos)
998 p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos)
999
1000 p.Imports, p.ImportPos = cleanDecls(importPos)
1001 p.TestImports, p.TestImportPos = cleanDecls(testImportPos)
1002 p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos)
1003
1004
1005
1006
1007 if len(p.CgoFiles) > 0 {
1008 p.SFiles = append(p.SFiles, Sfiles...)
1009 sort.Strings(p.SFiles)
1010 } else {
1011 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
1012 sort.Strings(p.IgnoredOtherFiles)
1013 }
1014
1015 if badGoError != nil {
1016 return p, badGoError
1017 }
1018 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1019 return p, &NoGoError{p.Dir}
1020 }
1021 return p, pkgerr
1022 }
1023
1024 func fileListForExt(p *Package, ext string) *[]string {
1025 switch ext {
1026 case ".c":
1027 return &p.CFiles
1028 case ".cc", ".cpp", ".cxx":
1029 return &p.CXXFiles
1030 case ".m":
1031 return &p.MFiles
1032 case ".h", ".hh", ".hpp", ".hxx":
1033 return &p.HFiles
1034 case ".f", ".F", ".for", ".f90":
1035 return &p.FFiles
1036 case ".s", ".S", ".sx":
1037 return &p.SFiles
1038 case ".swig":
1039 return &p.SwigFiles
1040 case ".swigcxx":
1041 return &p.SwigCXXFiles
1042 case ".syso":
1043 return &p.SysoFiles
1044 }
1045 return nil
1046 }
1047
1048 func uniq(list []string) []string {
1049 if list == nil {
1050 return nil
1051 }
1052 out := make([]string, len(list))
1053 copy(out, list)
1054 sort.Strings(out)
1055 uniq := out[:0]
1056 for _, x := range out {
1057 if len(uniq) == 0 || uniq[len(uniq)-1] != x {
1058 uniq = append(uniq, x)
1059 }
1060 }
1061 return uniq
1062 }
1063
1064 var errNoModules = errors.New("not using modules")
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076 func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
1077
1078
1079
1080 if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
1081 ctxt.JoinPath != nil || ctxt.SplitPathList != nil || ctxt.IsAbsPath != nil || ctxt.IsDir != nil || ctxt.HasSubdir != nil || ctxt.ReadDir != nil || ctxt.OpenFile != nil || !equal(ctxt.ToolTags, defaultToolTags) || !equal(ctxt.ReleaseTags, defaultReleaseTags) {
1082 return errNoModules
1083 }
1084
1085
1086
1087
1088
1089 go111Module := os.Getenv("GO111MODULE")
1090 switch go111Module {
1091 case "off":
1092 return errNoModules
1093 default:
1094
1095 }
1096
1097 if srcDir != "" {
1098 var absSrcDir string
1099 if filepath.IsAbs(srcDir) {
1100 absSrcDir = srcDir
1101 } else if ctxt.Dir != "" {
1102 return fmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", srcDir)
1103 } else {
1104
1105
1106 var err error
1107 absSrcDir, err = filepath.Abs(srcDir)
1108 if err != nil {
1109 return errNoModules
1110 }
1111 }
1112
1113
1114
1115
1116 if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
1117 return errNoModules
1118 }
1119 }
1120
1121
1122 if ctxt.GOROOT != "" {
1123 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
1124 if ctxt.isDir(dir) {
1125 return errNoModules
1126 }
1127 }
1128
1129
1130
1131 if go111Module == "auto" {
1132 var (
1133 parent string
1134 err error
1135 )
1136 if ctxt.Dir == "" {
1137 parent, err = os.Getwd()
1138 if err != nil {
1139
1140 return errNoModules
1141 }
1142 } else {
1143 parent, err = filepath.Abs(ctxt.Dir)
1144 if err != nil {
1145
1146
1147 return err
1148 }
1149 }
1150 for {
1151 if f, err := ctxt.openFile(ctxt.joinPath(parent, "go.mod")); err == nil {
1152 buf := make([]byte, 100)
1153 _, err := f.Read(buf)
1154 f.Close()
1155 if err == nil || err == io.EOF {
1156
1157 break
1158 }
1159 }
1160 d := filepath.Dir(parent)
1161 if len(d) >= len(parent) {
1162 return errNoModules
1163 }
1164 parent = d
1165 }
1166 }
1167
1168 cmd := exec.Command("go", "list", "-e", "-compiler="+ctxt.Compiler, "-tags="+strings.Join(ctxt.BuildTags, ","), "-installsuffix="+ctxt.InstallSuffix, "-f={{.Dir}}\n{{.ImportPath}}\n{{.Root}}\n{{.Goroot}}\n{{if .Error}}{{.Error}}{{end}}\n", "--", path)
1169
1170 if ctxt.Dir != "" {
1171 cmd.Dir = ctxt.Dir
1172 }
1173
1174 var stdout, stderr strings.Builder
1175 cmd.Stdout = &stdout
1176 cmd.Stderr = &stderr
1177
1178 cgo := "0"
1179 if ctxt.CgoEnabled {
1180 cgo = "1"
1181 }
1182 cmd.Env = append(os.Environ(),
1183 "GOOS="+ctxt.GOOS,
1184 "GOARCH="+ctxt.GOARCH,
1185 "GOROOT="+ctxt.GOROOT,
1186 "GOPATH="+ctxt.GOPATH,
1187 "CGO_ENABLED="+cgo,
1188 )
1189
1190 if err := cmd.Run(); err != nil {
1191 return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
1192 }
1193
1194 f := strings.SplitN(stdout.String(), "\n", 5)
1195 if len(f) != 5 {
1196 return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String())
1197 }
1198 dir := f[0]
1199 errStr := strings.TrimSpace(f[4])
1200 if errStr != "" && dir == "" {
1201
1202
1203 return errors.New(errStr)
1204 }
1205
1206
1207
1208
1209 p.Dir = dir
1210 p.ImportPath = f[1]
1211 p.Root = f[2]
1212 p.Goroot = f[3] == "true"
1213 return nil
1214 }
1215
1216 func equal(x, y []string) bool {
1217 if len(x) != len(y) {
1218 return false
1219 }
1220 for i, xi := range x {
1221 if xi != y[i] {
1222 return false
1223 }
1224 }
1225 return true
1226 }
1227
1228
1229
1230
1231
1232 func hasGoFiles(ctxt *Context, dir string) bool {
1233 ents, _ := ctxt.readDir(dir)
1234 for _, ent := range ents {
1235 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
1236 return true
1237 }
1238 }
1239 return false
1240 }
1241
1242 func findImportComment(data []byte) (s string, line int) {
1243
1244 word, data := parseWord(data)
1245 if string(word) != "package" {
1246 return "", 0
1247 }
1248
1249
1250 _, data = parseWord(data)
1251
1252
1253
1254 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
1255 data = data[1:]
1256 }
1257
1258 var comment []byte
1259 switch {
1260 case bytes.HasPrefix(data, slashSlash):
1261 comment, _, _ = bytes.Cut(data[2:], newline)
1262 case bytes.HasPrefix(data, slashStar):
1263 var ok bool
1264 comment, _, ok = bytes.Cut(data[2:], starSlash)
1265 if !ok {
1266
1267 return "", 0
1268 }
1269 if bytes.Contains(comment, newline) {
1270 return "", 0
1271 }
1272 }
1273 comment = bytes.TrimSpace(comment)
1274
1275
1276 word, arg := parseWord(comment)
1277 if string(word) != "import" {
1278 return "", 0
1279 }
1280
1281 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
1282 return strings.TrimSpace(string(arg)), line
1283 }
1284
1285 var (
1286 slashSlash = []byte("//")
1287 slashStar = []byte("/*")
1288 starSlash = []byte("*/")
1289 newline = []byte("\n")
1290 )
1291
1292
1293 func skipSpaceOrComment(data []byte) []byte {
1294 for len(data) > 0 {
1295 switch data[0] {
1296 case ' ', '\t', '\r', '\n':
1297 data = data[1:]
1298 continue
1299 case '/':
1300 if bytes.HasPrefix(data, slashSlash) {
1301 i := bytes.Index(data, newline)
1302 if i < 0 {
1303 return nil
1304 }
1305 data = data[i+1:]
1306 continue
1307 }
1308 if bytes.HasPrefix(data, slashStar) {
1309 data = data[2:]
1310 i := bytes.Index(data, starSlash)
1311 if i < 0 {
1312 return nil
1313 }
1314 data = data[i+2:]
1315 continue
1316 }
1317 }
1318 break
1319 }
1320 return data
1321 }
1322
1323
1324
1325
1326 func parseWord(data []byte) (word, rest []byte) {
1327 data = skipSpaceOrComment(data)
1328
1329
1330 rest = data
1331 for {
1332 r, size := utf8.DecodeRune(rest)
1333 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
1334 rest = rest[size:]
1335 continue
1336 }
1337 break
1338 }
1339
1340 word = data[:len(data)-len(rest)]
1341 if len(word) == 0 {
1342 return nil, nil
1343 }
1344
1345 return word, rest
1346 }
1347
1348
1349
1350
1351
1352
1353
1354 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
1355 info, err := ctxt.matchFile(dir, name, nil, nil, nil)
1356 return info != nil, err
1357 }
1358
1359 var dummyPkg Package
1360
1361
1362 type fileInfo struct {
1363 name string
1364 header []byte
1365 fset *token.FileSet
1366 parsed *ast.File
1367 parseErr error
1368 imports []fileImport
1369 embeds []fileEmbed
1370 embedErr error
1371 }
1372
1373 type fileImport struct {
1374 path string
1375 pos token.Pos
1376 doc *ast.CommentGroup
1377 }
1378
1379 type fileEmbed struct {
1380 pattern string
1381 pos token.Position
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396 func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
1397 if strings.HasPrefix(name, "_") ||
1398 strings.HasPrefix(name, ".") {
1399 return nil, nil
1400 }
1401
1402 i := strings.LastIndex(name, ".")
1403 if i < 0 {
1404 i = len(name)
1405 }
1406 ext := name[i:]
1407
1408 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
1409 return nil, nil
1410 }
1411
1412 if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
1413
1414 return nil, nil
1415 }
1416
1417 info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
1418 if ext == ".syso" {
1419
1420 return info, nil
1421 }
1422
1423 f, err := ctxt.openFile(info.name)
1424 if err != nil {
1425 return nil, err
1426 }
1427
1428 if strings.HasSuffix(name, ".go") {
1429 err = readGoInfo(f, info)
1430 if strings.HasSuffix(name, "_test.go") {
1431 binaryOnly = nil
1432 }
1433 } else {
1434 binaryOnly = nil
1435 info.header, err = readComments(f)
1436 }
1437 f.Close()
1438 if err != nil {
1439 return nil, fmt.Errorf("read %s: %v", info.name, err)
1440 }
1441
1442
1443 ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
1444 if err != nil {
1445 return nil, fmt.Errorf("%s: %v", name, err)
1446 }
1447 if !ok && !ctxt.UseAllFiles {
1448 return nil, nil
1449 }
1450
1451 if binaryOnly != nil && sawBinaryOnly {
1452 *binaryOnly = true
1453 }
1454
1455 return info, nil
1456 }
1457
1458 func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) {
1459 all := make([]string, 0, len(m))
1460 for path := range m {
1461 all = append(all, path)
1462 }
1463 sort.Strings(all)
1464 return all, m
1465 }
1466
1467
1468 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
1469 return Default.Import(path, srcDir, mode)
1470 }
1471
1472
1473 func ImportDir(dir string, mode ImportMode) (*Package, error) {
1474 return Default.ImportDir(dir, mode)
1475 }
1476
1477 var (
1478 bSlashSlash = []byte(slashSlash)
1479 bStarSlash = []byte(starSlash)
1480 bSlashStar = []byte(slashStar)
1481 bPlusBuild = []byte("+build")
1482
1483 goBuildComment = []byte("//go:build")
1484
1485 errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment")
1486 errMultipleGoBuild = errors.New("multiple //go:build comments")
1487 )
1488
1489 func isGoBuildComment(line []byte) bool {
1490 if !bytes.HasPrefix(line, goBuildComment) {
1491 return false
1492 }
1493 line = bytes.TrimSpace(line)
1494 rest := line[len(goBuildComment):]
1495 return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
1496 }
1497
1498
1499
1500
1501 var binaryOnlyComment = []byte("//go:binary-only-package")
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild, binaryOnly bool, err error) {
1521
1522
1523
1524 content, goBuild, sawBinaryOnly, err := parseFileHeader(content)
1525 if err != nil {
1526 return false, false, err
1527 }
1528
1529
1530
1531 switch {
1532 case goBuild != nil:
1533 x, err := constraint.Parse(string(goBuild))
1534 if err != nil {
1535 return false, false, fmt.Errorf("parsing //go:build line: %v", err)
1536 }
1537 shouldBuild = ctxt.eval(x, allTags)
1538
1539 default:
1540 shouldBuild = true
1541 p := content
1542 for len(p) > 0 {
1543 line := p
1544 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1545 line, p = line[:i], p[i+1:]
1546 } else {
1547 p = p[len(p):]
1548 }
1549 line = bytes.TrimSpace(line)
1550 if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
1551 continue
1552 }
1553 text := string(line)
1554 if !constraint.IsPlusBuild(text) {
1555 continue
1556 }
1557 if x, err := constraint.Parse(text); err == nil {
1558 if !ctxt.eval(x, allTags) {
1559 shouldBuild = false
1560 }
1561 }
1562 }
1563 }
1564
1565 return shouldBuild, sawBinaryOnly, nil
1566 }
1567
1568 func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
1569 end := 0
1570 p := content
1571 ended := false
1572 inSlashStar := false
1573
1574 Lines:
1575 for len(p) > 0 {
1576 line := p
1577 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1578 line, p = line[:i], p[i+1:]
1579 } else {
1580 p = p[len(p):]
1581 }
1582 line = bytes.TrimSpace(line)
1583 if len(line) == 0 && !ended {
1584
1585
1586
1587
1588
1589
1590
1591
1592 end = len(content) - len(p)
1593 continue Lines
1594 }
1595 if !bytes.HasPrefix(line, slashSlash) {
1596 ended = true
1597 }
1598
1599 if !inSlashStar && isGoBuildComment(line) {
1600 if goBuild != nil {
1601 return nil, nil, false, errMultipleGoBuild
1602 }
1603 goBuild = line
1604 }
1605 if !inSlashStar && bytes.Equal(line, binaryOnlyComment) {
1606 sawBinaryOnly = true
1607 }
1608
1609 Comments:
1610 for len(line) > 0 {
1611 if inSlashStar {
1612 if i := bytes.Index(line, starSlash); i >= 0 {
1613 inSlashStar = false
1614 line = bytes.TrimSpace(line[i+len(starSlash):])
1615 continue Comments
1616 }
1617 continue Lines
1618 }
1619 if bytes.HasPrefix(line, bSlashSlash) {
1620 continue Lines
1621 }
1622 if bytes.HasPrefix(line, bSlashStar) {
1623 inSlashStar = true
1624 line = bytes.TrimSpace(line[len(bSlashStar):])
1625 continue Comments
1626 }
1627
1628 break Lines
1629 }
1630 }
1631
1632 return content[:end], goBuild, sawBinaryOnly, nil
1633 }
1634
1635
1636
1637
1638 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
1639 text := cg.Text()
1640 for _, line := range strings.Split(text, "\n") {
1641 orig := line
1642
1643
1644
1645
1646 line = strings.TrimSpace(line)
1647 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
1648 continue
1649 }
1650
1651
1652 line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":")
1653 if !ok {
1654 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1655 }
1656
1657
1658 f := strings.Fields(line)
1659 if len(f) < 1 {
1660 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1661 }
1662
1663 cond, verb := f[:len(f)-1], f[len(f)-1]
1664 if len(cond) > 0 {
1665 ok := false
1666 for _, c := range cond {
1667 if ctxt.matchAuto(c, nil) {
1668 ok = true
1669 break
1670 }
1671 }
1672 if !ok {
1673 continue
1674 }
1675 }
1676
1677 args, err := splitQuoted(argstr)
1678 if err != nil {
1679 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1680 }
1681 for i, arg := range args {
1682 if arg, ok = expandSrcDir(arg, di.Dir); !ok {
1683 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
1684 }
1685 args[i] = arg
1686 }
1687
1688 switch verb {
1689 case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":
1690
1691 ctxt.makePathsAbsolute(args, di.Dir)
1692 }
1693
1694 switch verb {
1695 case "CFLAGS":
1696 di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
1697 case "CPPFLAGS":
1698 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
1699 case "CXXFLAGS":
1700 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
1701 case "FFLAGS":
1702 di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
1703 case "LDFLAGS":
1704 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
1705 case "pkg-config":
1706 di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
1707 default:
1708 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
1709 }
1710 }
1711 return nil
1712 }
1713
1714
1715
1716 func expandSrcDir(str string, srcdir string) (string, bool) {
1717
1718
1719
1720 srcdir = filepath.ToSlash(srcdir)
1721
1722 chunks := strings.Split(str, "${SRCDIR}")
1723 if len(chunks) < 2 {
1724 return str, safeCgoName(str)
1725 }
1726 ok := true
1727 for _, chunk := range chunks {
1728 ok = ok && (chunk == "" || safeCgoName(chunk))
1729 }
1730 ok = ok && (srcdir == "" || safeCgoName(srcdir))
1731 res := strings.Join(chunks, srcdir)
1732 return res, ok && res != ""
1733 }
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746 func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) {
1747 nextPath := false
1748 for i, arg := range args {
1749 if nextPath {
1750 if !filepath.IsAbs(arg) {
1751 args[i] = filepath.Join(srcDir, arg)
1752 }
1753 nextPath = false
1754 } else if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
1755 if len(arg) == 2 {
1756 nextPath = true
1757 } else {
1758 if !filepath.IsAbs(arg[2:]) {
1759 args[i] = arg[:2] + filepath.Join(srcDir, arg[2:])
1760 }
1761 }
1762 }
1763 }
1764 }
1765
1766
1767
1768
1769
1770
1771
1772
1773 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"
1774
1775 func safeCgoName(s string) bool {
1776 if s == "" {
1777 return false
1778 }
1779 for i := 0; i < len(s); i++ {
1780 if c := s[i]; c < utf8.RuneSelf && strings.IndexByte(safeString, c) < 0 {
1781 return false
1782 }
1783 }
1784 return true
1785 }
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803 func splitQuoted(s string) (r []string, err error) {
1804 var args []string
1805 arg := make([]rune, len(s))
1806 escaped := false
1807 quoted := false
1808 quote := '\x00'
1809 i := 0
1810 for _, rune := range s {
1811 switch {
1812 case escaped:
1813 escaped = false
1814 case rune == '\\':
1815 escaped = true
1816 continue
1817 case quote != '\x00':
1818 if rune == quote {
1819 quote = '\x00'
1820 continue
1821 }
1822 case rune == '"' || rune == '\'':
1823 quoted = true
1824 quote = rune
1825 continue
1826 case unicode.IsSpace(rune):
1827 if quoted || i > 0 {
1828 quoted = false
1829 args = append(args, string(arg[:i]))
1830 i = 0
1831 }
1832 continue
1833 }
1834 arg[i] = rune
1835 i++
1836 }
1837 if quoted || i > 0 {
1838 args = append(args, string(arg[:i]))
1839 }
1840 if quote != 0 {
1841 err = errors.New("unclosed quote")
1842 } else if escaped {
1843 err = errors.New("unfinished escaping")
1844 }
1845 return args, err
1846 }
1847
1848
1849
1850
1851
1852
1853 func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool {
1854 if strings.ContainsAny(text, "&|()") {
1855 text = "//go:build " + text
1856 } else {
1857 text = "// +build " + text
1858 }
1859 x, err := constraint.Parse(text)
1860 if err != nil {
1861 return false
1862 }
1863 return ctxt.eval(x, allTags)
1864 }
1865
1866 func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool {
1867 return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) })
1868 }
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881 func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
1882 if allTags != nil {
1883 allTags[name] = true
1884 }
1885
1886
1887 if ctxt.CgoEnabled && name == "cgo" {
1888 return true
1889 }
1890 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
1891 return true
1892 }
1893 if ctxt.GOOS == "android" && name == "linux" {
1894 return true
1895 }
1896 if ctxt.GOOS == "illumos" && name == "solaris" {
1897 return true
1898 }
1899 if ctxt.GOOS == "ios" && name == "darwin" {
1900 return true
1901 }
1902
1903
1904 for _, tag := range ctxt.BuildTags {
1905 if tag == name {
1906 return true
1907 }
1908 }
1909 for _, tag := range ctxt.ToolTags {
1910 if tag == name {
1911 return true
1912 }
1913 }
1914 for _, tag := range ctxt.ReleaseTags {
1915 if tag == name {
1916 return true
1917 }
1918 }
1919
1920 return false
1921 }
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
1939 name, _, _ = strings.Cut(name, ".")
1940
1941
1942
1943
1944
1945
1946
1947
1948 i := strings.Index(name, "_")
1949 if i < 0 {
1950 return true
1951 }
1952 name = name[i:]
1953
1954 l := strings.Split(name, "_")
1955 if n := len(l); n > 0 && l[n-1] == "test" {
1956 l = l[:n-1]
1957 }
1958 n := len(l)
1959 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
1960 return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags)
1961 }
1962 if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) {
1963 return ctxt.matchTag(l[n-1], allTags)
1964 }
1965 return true
1966 }
1967
1968 var knownOS = make(map[string]bool)
1969 var knownArch = make(map[string]bool)
1970
1971 func init() {
1972 for _, v := range strings.Fields(goosList) {
1973 knownOS[v] = true
1974 }
1975 for _, v := range strings.Fields(goarchList) {
1976 knownArch[v] = true
1977 }
1978 }
1979
1980
1981 var ToolDir = getToolDir()
1982
1983
1984
1985 func IsLocalImport(path string) bool {
1986 return path == "." || path == ".." ||
1987 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
1988 }
1989
1990
1991
1992
1993
1994
1995 func ArchChar(goarch string) (string, error) {
1996 return "?", errors.New("architecture letter no longer used")
1997 }
1998
View as plain text