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