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/goroot"
17 "internal/goversion"
18 "io"
19 "io/fs"
20 "os"
21 "os/exec"
22 pathpkg "path"
23 "path/filepath"
24 "runtime"
25 "sort"
26 "strconv"
27 "strings"
28 "unicode"
29 "unicode/utf8"
30 )
31
32
33 type Context struct {
34 GOARCH string
35 GOOS string
36 GOROOT string
37 GOPATH string
38
39
40
41
42
43
44
45 Dir string
46
47 CgoEnabled bool
48 UseAllFiles bool
49 Compiler string
50
51
52
53
54
55
56
57
58
59
60
61 BuildTags []string
62 ToolTags []string
63 ReleaseTags []string
64
65
66
67
68
69
70
71 InstallSuffix string
72
73
74
75
76
77
78
79
80
81 JoinPath func(elem ...string) string
82
83
84
85 SplitPathList func(list string) []string
86
87
88
89 IsAbsPath func(path string) bool
90
91
92
93 IsDir func(path string) bool
94
95
96
97
98
99
100
101
102 HasSubdir func(root, dir string) (rel string, ok bool)
103
104
105
106
107 ReadDir func(dir string) ([]fs.FileInfo, error)
108
109
110
111 OpenFile func(path string) (io.ReadCloser, error)
112 }
113
114
115 func (ctxt *Context) joinPath(elem ...string) string {
116 if f := ctxt.JoinPath; f != nil {
117 return f(elem...)
118 }
119 return filepath.Join(elem...)
120 }
121
122
123 func (ctxt *Context) splitPathList(s string) []string {
124 if f := ctxt.SplitPathList; f != nil {
125 return f(s)
126 }
127 return filepath.SplitList(s)
128 }
129
130
131 func (ctxt *Context) isAbsPath(path string) bool {
132 if f := ctxt.IsAbsPath; f != nil {
133 return f(path)
134 }
135 return filepath.IsAbs(path)
136 }
137
138
139 func (ctxt *Context) isDir(path string) bool {
140 if f := ctxt.IsDir; f != nil {
141 return f(path)
142 }
143 fi, err := os.Stat(path)
144 return err == nil && fi.IsDir()
145 }
146
147
148
149 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
150 if f := ctxt.HasSubdir; f != nil {
151 return f(root, dir)
152 }
153
154
155 if rel, ok = hasSubdir(root, dir); ok {
156 return
157 }
158
159
160
161
162 rootSym, _ := filepath.EvalSymlinks(root)
163 dirSym, _ := filepath.EvalSymlinks(dir)
164
165 if rel, ok = hasSubdir(rootSym, dir); ok {
166 return
167 }
168 if rel, ok = hasSubdir(root, dirSym); ok {
169 return
170 }
171 return hasSubdir(rootSym, dirSym)
172 }
173
174
175 func hasSubdir(root, dir string) (rel string, ok bool) {
176 const sep = string(filepath.Separator)
177 root = filepath.Clean(root)
178 if !strings.HasSuffix(root, sep) {
179 root += sep
180 }
181 dir = filepath.Clean(dir)
182 if !strings.HasPrefix(dir, root) {
183 return "", false
184 }
185 return filepath.ToSlash(dir[len(root):]), true
186 }
187
188
189 func (ctxt *Context) readDir(path string) ([]fs.DirEntry, error) {
190
191 if f := ctxt.ReadDir; f != nil {
192 fis, err := f(path)
193 if err != nil {
194 return nil, err
195 }
196 des := make([]fs.DirEntry, len(fis))
197 for i, fi := range fis {
198 des[i] = fs.FileInfoToDirEntry(fi)
199 }
200 return des, nil
201 }
202 return os.ReadDir(path)
203 }
204
205
206 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
207 if fn := ctxt.OpenFile; fn != nil {
208 return fn(path)
209 }
210
211 f, err := os.Open(path)
212 if err != nil {
213 return nil, err
214 }
215 return f, nil
216 }
217
218
219
220
221 func (ctxt *Context) isFile(path string) bool {
222 f, err := ctxt.openFile(path)
223 if err != nil {
224 return false
225 }
226 f.Close()
227 return true
228 }
229
230
231 func (ctxt *Context) gopath() []string {
232 var all []string
233 for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
234 if p == "" || p == ctxt.GOROOT {
235
236
237
238
239 continue
240 }
241 if strings.HasPrefix(p, "~") {
242
243
244
245
246
247
248
249
250
251
252
253
254 continue
255 }
256 all = append(all, p)
257 }
258 return all
259 }
260
261
262
263
264 func (ctxt *Context) SrcDirs() []string {
265 var all []string
266 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
267 dir := ctxt.joinPath(ctxt.GOROOT, "src")
268 if ctxt.isDir(dir) {
269 all = append(all, dir)
270 }
271 }
272 for _, p := range ctxt.gopath() {
273 dir := ctxt.joinPath(p, "src")
274 if ctxt.isDir(dir) {
275 all = append(all, dir)
276 }
277 }
278 return all
279 }
280
281
282
283
284 var Default Context = defaultContext()
285
286 func defaultGOPATH() string {
287 env := "HOME"
288 if runtime.GOOS == "windows" {
289 env = "USERPROFILE"
290 } else if runtime.GOOS == "plan9" {
291 env = "home"
292 }
293 if home := os.Getenv(env); home != "" {
294 def := filepath.Join(home, "go")
295 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
296
297
298 return ""
299 }
300 return def
301 }
302 return ""
303 }
304
305 var defaultToolTags, defaultReleaseTags []string
306
307 func defaultContext() Context {
308 var c Context
309
310 c.GOARCH = buildcfg.GOARCH
311 c.GOOS = buildcfg.GOOS
312 if goroot := runtime.GOROOT(); goroot != "" {
313 c.GOROOT = filepath.Clean(goroot)
314 }
315 c.GOPATH = envOr("GOPATH", defaultGOPATH())
316 c.Compiler = runtime.Compiler
317
318
319
320
321
322
323 for _, exp := range buildcfg.Experiment.Enabled() {
324 c.ToolTags = append(c.ToolTags, "goexperiment."+exp)
325 }
326 defaultToolTags = append([]string{}, c.ToolTags...)
327
328
329
330
331
332
333
334
335 for i := 1; i <= goversion.Version; i++ {
336 c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i))
337 }
338
339 defaultReleaseTags = append([]string{}, c.ReleaseTags...)
340
341 env := os.Getenv("CGO_ENABLED")
342 if env == "" {
343 env = defaultCGO_ENABLED
344 }
345 switch env {
346 case "1":
347 c.CgoEnabled = true
348 case "0":
349 c.CgoEnabled = false
350 default:
351
352 if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
353 c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
354 break
355 }
356 c.CgoEnabled = false
357 }
358
359 return c
360 }
361
362 func envOr(name, def string) string {
363 s := os.Getenv(name)
364 if s == "" {
365 return def
366 }
367 return s
368 }
369
370
371 type ImportMode uint
372
373 const (
374
375
376
377 FindOnly ImportMode = 1 << iota
378
379
380
381
382
383
384
385
386
387
388 AllowBinary
389
390
391
392
393
394 ImportComment
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414 IgnoreVendor
415 )
416
417
418 type Package struct {
419 Dir string
420 Name string
421 ImportComment string
422 Doc string
423 ImportPath string
424 Root string
425 SrcRoot string
426 PkgRoot string
427 PkgTargetRoot string
428 BinDir string
429 Goroot bool
430 PkgObj string
431 AllTags []string
432 ConflictDir string
433 BinaryOnly bool
434
435
436 GoFiles []string
437 CgoFiles []string
438 IgnoredGoFiles []string
439 InvalidGoFiles []string
440 IgnoredOtherFiles []string
441 CFiles []string
442 CXXFiles []string
443 MFiles []string
444 HFiles []string
445 FFiles []string
446 SFiles []string
447 SwigFiles []string
448 SwigCXXFiles []string
449 SysoFiles []string
450
451
452 CgoCFLAGS []string
453 CgoCPPFLAGS []string
454 CgoCXXFLAGS []string
455 CgoFFLAGS []string
456 CgoLDFLAGS []string
457 CgoPkgConfig []string
458
459
460 TestGoFiles []string
461 XTestGoFiles []string
462
463
464 Imports []string
465 ImportPos map[string][]token.Position
466 TestImports []string
467 TestImportPos map[string][]token.Position
468 XTestImports []string
469 XTestImportPos map[string][]token.Position
470
471
472
473
474
475
476 EmbedPatterns []string
477 EmbedPatternPos map[string][]token.Position
478 TestEmbedPatterns []string
479 TestEmbedPatternPos map[string][]token.Position
480 XTestEmbedPatterns []string
481 XTestEmbedPatternPos map[string][]token.Position
482 }
483
484
485
486
487 func (p *Package) IsCommand() bool {
488 return p.Name == "main"
489 }
490
491
492
493 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
494 return ctxt.Import(".", dir, mode)
495 }
496
497
498
499
500 type NoGoError struct {
501 Dir string
502 }
503
504 func (e *NoGoError) Error() string {
505 return "no buildable Go source files in " + e.Dir
506 }
507
508
509
510 type MultiplePackageError struct {
511 Dir string
512 Packages []string
513 Files []string
514 }
515
516 func (e *MultiplePackageError) Error() string {
517
518 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)
519 }
520
521 func nameExt(name string) string {
522 i := strings.LastIndex(name, ".")
523 if i < 0 {
524 return ""
525 }
526 return name[i:]
527 }
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
545 p := &Package{
546 ImportPath: path,
547 }
548 if path == "" {
549 return p, fmt.Errorf("import %q: invalid import path", path)
550 }
551
552 var pkgtargetroot string
553 var pkga string
554 var pkgerr error
555 suffix := ""
556 if ctxt.InstallSuffix != "" {
557 suffix = "_" + ctxt.InstallSuffix
558 }
559 switch ctxt.Compiler {
560 case "gccgo":
561 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
562 case "gc":
563 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
564 default:
565
566 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
567 }
568 setPkga := func() {
569 switch ctxt.Compiler {
570 case "gccgo":
571 dir, elem := pathpkg.Split(p.ImportPath)
572 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
573 case "gc":
574 pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
575 }
576 }
577 setPkga()
578
579 binaryOnly := false
580 if IsLocalImport(path) {
581 pkga = ""
582 if srcDir == "" {
583 return p, fmt.Errorf("import %q: import relative to unknown directory", path)
584 }
585 if !ctxt.isAbsPath(path) {
586 p.Dir = ctxt.joinPath(srcDir, path)
587 }
588
589
590
591 inTestdata := func(sub string) bool {
592 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
593 }
594 if ctxt.GOROOT != "" {
595 root := ctxt.joinPath(ctxt.GOROOT, "src")
596 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
597 p.Goroot = true
598 p.ImportPath = sub
599 p.Root = ctxt.GOROOT
600 setPkga()
601 goto Found
602 }
603 }
604 all := ctxt.gopath()
605 for i, root := range all {
606 rootsrc := ctxt.joinPath(root, "src")
607 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
608
609
610
611 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
612 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
613 p.ConflictDir = dir
614 goto Found
615 }
616 }
617 for _, earlyRoot := range all[:i] {
618 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
619 p.ConflictDir = dir
620 goto Found
621 }
622 }
623
624
625
626 p.ImportPath = sub
627 p.Root = root
628 setPkga()
629 goto Found
630 }
631 }
632
633
634 } else {
635 if strings.HasPrefix(path, "/") {
636 return p, fmt.Errorf("import %q: cannot import absolute path", path)
637 }
638
639 if err := ctxt.importGo(p, path, srcDir, mode); err == nil {
640 goto Found
641 } else if err != errNoModules {
642 return p, err
643 }
644
645 gopath := ctxt.gopath()
646
647
648 var tried struct {
649 vendor []string
650 goroot string
651 gopath []string
652 }
653
654
655 if mode&IgnoreVendor == 0 && srcDir != "" {
656 searchVendor := func(root string, isGoroot bool) bool {
657 sub, ok := ctxt.hasSubdir(root, srcDir)
658 if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
659 return false
660 }
661 for {
662 vendor := ctxt.joinPath(root, sub, "vendor")
663 if ctxt.isDir(vendor) {
664 dir := ctxt.joinPath(vendor, path)
665 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
666 p.Dir = dir
667 p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
668 p.Goroot = isGoroot
669 p.Root = root
670 setPkga()
671 return true
672 }
673 tried.vendor = append(tried.vendor, dir)
674 }
675 i := strings.LastIndex(sub, "/")
676 if i < 0 {
677 break
678 }
679 sub = sub[:i]
680 }
681 return false
682 }
683 if ctxt.Compiler != "gccgo" && ctxt.GOROOT != "" && searchVendor(ctxt.GOROOT, true) {
684 goto Found
685 }
686 for _, root := range gopath {
687 if searchVendor(root, false) {
688 goto Found
689 }
690 }
691 }
692
693
694 if ctxt.GOROOT != "" {
695
696
697
698
699 gorootFirst := srcDir == "" || !strings.HasPrefix(path, "vendor/")
700 if !gorootFirst {
701 _, gorootFirst = ctxt.hasSubdir(ctxt.GOROOT, srcDir)
702 }
703 if gorootFirst {
704 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
705 if ctxt.Compiler != "gccgo" {
706 isDir := ctxt.isDir(dir)
707 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
708 if isDir || binaryOnly {
709 p.Dir = dir
710 p.Goroot = true
711 p.Root = ctxt.GOROOT
712 goto Found
713 }
714 }
715 tried.goroot = dir
716 }
717 if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) {
718
719
720
721 p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
722 p.Goroot = true
723 p.Root = ctxt.GOROOT
724 goto Found
725 }
726 }
727 for _, root := range gopath {
728 dir := ctxt.joinPath(root, "src", path)
729 isDir := ctxt.isDir(dir)
730 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
731 if isDir || binaryOnly {
732 p.Dir = dir
733 p.Root = root
734 goto Found
735 }
736 tried.gopath = append(tried.gopath, dir)
737 }
738
739
740
741
742 if ctxt.GOROOT != "" && tried.goroot == "" {
743 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
744 if ctxt.Compiler != "gccgo" {
745 isDir := ctxt.isDir(dir)
746 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
747 if isDir || binaryOnly {
748 p.Dir = dir
749 p.Goroot = true
750 p.Root = ctxt.GOROOT
751 goto Found
752 }
753 }
754 tried.goroot = dir
755 }
756
757
758 var paths []string
759 format := "\t%s (vendor tree)"
760 for _, dir := range tried.vendor {
761 paths = append(paths, fmt.Sprintf(format, dir))
762 format = "\t%s"
763 }
764 if tried.goroot != "" {
765 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
766 } else {
767 paths = append(paths, "\t($GOROOT not set)")
768 }
769 format = "\t%s (from $GOPATH)"
770 for _, dir := range tried.gopath {
771 paths = append(paths, fmt.Sprintf(format, dir))
772 format = "\t%s"
773 }
774 if len(tried.gopath) == 0 {
775 paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
776 }
777 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
778 }
779
780 Found:
781 if p.Root != "" {
782 p.SrcRoot = ctxt.joinPath(p.Root, "src")
783 p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
784 p.BinDir = ctxt.joinPath(p.Root, "bin")
785 if pkga != "" {
786 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
787 p.PkgObj = ctxt.joinPath(p.Root, pkga)
788 }
789 }
790
791
792
793
794
795
796 if IsLocalImport(path) && !ctxt.isDir(p.Dir) {
797 if ctxt.Compiler == "gccgo" && p.Goroot {
798
799 return p, nil
800 }
801
802
803 return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir)
804 }
805
806 if mode&FindOnly != 0 {
807 return p, pkgerr
808 }
809 if binaryOnly && (mode&AllowBinary) != 0 {
810 return p, pkgerr
811 }
812
813 if ctxt.Compiler == "gccgo" && p.Goroot {
814
815 return p, nil
816 }
817
818 dirs, err := ctxt.readDir(p.Dir)
819 if err != nil {
820 return p, err
821 }
822
823 var badGoError error
824 badFiles := make(map[string]bool)
825 badFile := func(name string, err error) {
826 if badGoError == nil {
827 badGoError = err
828 }
829 if !badFiles[name] {
830 p.InvalidGoFiles = append(p.InvalidGoFiles, name)
831 badFiles[name] = true
832 }
833 }
834
835 var Sfiles []string
836 var firstFile, firstCommentFile string
837 embedPos := make(map[string][]token.Position)
838 testEmbedPos := make(map[string][]token.Position)
839 xTestEmbedPos := make(map[string][]token.Position)
840 importPos := make(map[string][]token.Position)
841 testImportPos := make(map[string][]token.Position)
842 xTestImportPos := make(map[string][]token.Position)
843 allTags := make(map[string]bool)
844 fset := token.NewFileSet()
845 for _, d := range dirs {
846 if d.IsDir() {
847 continue
848 }
849 if d.Type() == fs.ModeSymlink {
850 if ctxt.isDir(ctxt.joinPath(p.Dir, d.Name())) {
851
852 continue
853 }
854 }
855
856 name := d.Name()
857 ext := nameExt(name)
858
859 info, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly, fset)
860 if err != nil {
861 badFile(name, err)
862 continue
863 }
864 if info == nil {
865 if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
866
867 } else if ext == ".go" {
868 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
869 } else if fileListForExt(p, ext) != nil {
870 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
871 }
872 continue
873 }
874 data, filename := info.header, info.name
875
876
877 switch ext {
878 case ".go":
879
880 case ".S", ".sx":
881
882 Sfiles = append(Sfiles, name)
883 continue
884 default:
885 if list := fileListForExt(p, ext); list != nil {
886 *list = append(*list, name)
887 }
888 continue
889 }
890
891 if info.parseErr != nil {
892 badFile(name, info.parseErr)
893
894
895 }
896
897 var pkg string
898 if info.parsed != nil {
899 pkg = info.parsed.Name.Name
900 if pkg == "documentation" {
901 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
902 continue
903 }
904 }
905
906 isTest := strings.HasSuffix(name, "_test.go")
907 isXTest := false
908 if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg {
909 isXTest = true
910 pkg = pkg[:len(pkg)-len("_test")]
911 }
912
913 if p.Name == "" {
914 p.Name = pkg
915 firstFile = name
916 } else if pkg != p.Name {
917
918
919
920 badFile(name, &MultiplePackageError{
921 Dir: p.Dir,
922 Packages: []string{p.Name, pkg},
923 Files: []string{firstFile, name},
924 })
925 }
926
927 if info.parsed != nil && info.parsed.Doc != nil && p.Doc == "" && !isTest && !isXTest {
928 p.Doc = doc.Synopsis(info.parsed.Doc.Text())
929 }
930
931 if mode&ImportComment != 0 {
932 qcom, line := findImportComment(data)
933 if line != 0 {
934 com, err := strconv.Unquote(qcom)
935 if err != nil {
936 badFile(name, fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
937 } else if p.ImportComment == "" {
938 p.ImportComment = com
939 firstCommentFile = name
940 } else if p.ImportComment != com {
941 badFile(name, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
942 }
943 }
944 }
945
946
947 isCgo := false
948 for _, imp := range info.imports {
949 if imp.path == "C" {
950 if isTest {
951 badFile(name, fmt.Errorf("use of cgo in test %s not supported", filename))
952 continue
953 }
954 isCgo = true
955 if imp.doc != nil {
956 if err := ctxt.saveCgo(filename, p, imp.doc); err != nil {
957 badFile(name, err)
958 }
959 }
960 }
961 }
962
963 var fileList *[]string
964 var importMap, embedMap map[string][]token.Position
965 switch {
966 case isCgo:
967 allTags["cgo"] = true
968 if ctxt.CgoEnabled {
969 fileList = &p.CgoFiles
970 importMap = importPos
971 embedMap = embedPos
972 } else {
973
974 fileList = &p.IgnoredGoFiles
975 }
976 case isXTest:
977 fileList = &p.XTestGoFiles
978 importMap = xTestImportPos
979 embedMap = xTestEmbedPos
980 case isTest:
981 fileList = &p.TestGoFiles
982 importMap = testImportPos
983 embedMap = testEmbedPos
984 default:
985 fileList = &p.GoFiles
986 importMap = importPos
987 embedMap = embedPos
988 }
989 *fileList = append(*fileList, name)
990 if importMap != nil {
991 for _, imp := range info.imports {
992 importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
993 }
994 }
995 if embedMap != nil {
996 for _, emb := range info.embeds {
997 embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
998 }
999 }
1000 }
1001
1002 for tag := range allTags {
1003 p.AllTags = append(p.AllTags, tag)
1004 }
1005 sort.Strings(p.AllTags)
1006
1007 p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
1008 p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos)
1009 p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos)
1010
1011 p.Imports, p.ImportPos = cleanDecls(importPos)
1012 p.TestImports, p.TestImportPos = cleanDecls(testImportPos)
1013 p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos)
1014
1015
1016
1017
1018 if len(p.CgoFiles) > 0 {
1019 p.SFiles = append(p.SFiles, Sfiles...)
1020 sort.Strings(p.SFiles)
1021 } else {
1022 p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
1023 sort.Strings(p.IgnoredOtherFiles)
1024 }
1025
1026 if badGoError != nil {
1027 return p, badGoError
1028 }
1029 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1030 return p, &NoGoError{p.Dir}
1031 }
1032 return p, pkgerr
1033 }
1034
1035 func fileListForExt(p *Package, ext string) *[]string {
1036 switch ext {
1037 case ".c":
1038 return &p.CFiles
1039 case ".cc", ".cpp", ".cxx":
1040 return &p.CXXFiles
1041 case ".m":
1042 return &p.MFiles
1043 case ".h", ".hh", ".hpp", ".hxx":
1044 return &p.HFiles
1045 case ".f", ".F", ".for", ".f90":
1046 return &p.FFiles
1047 case ".s", ".S", ".sx":
1048 return &p.SFiles
1049 case ".swig":
1050 return &p.SwigFiles
1051 case ".swigcxx":
1052 return &p.SwigCXXFiles
1053 case ".syso":
1054 return &p.SysoFiles
1055 }
1056 return nil
1057 }
1058
1059 func uniq(list []string) []string {
1060 if list == nil {
1061 return nil
1062 }
1063 out := make([]string, len(list))
1064 copy(out, list)
1065 sort.Strings(out)
1066 uniq := out[:0]
1067 for _, x := range out {
1068 if len(uniq) == 0 || uniq[len(uniq)-1] != x {
1069 uniq = append(uniq, x)
1070 }
1071 }
1072 return uniq
1073 }
1074
1075 var errNoModules = errors.New("not using modules")
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087 func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
1088
1089
1090
1091 if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
1092 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) {
1093 return errNoModules
1094 }
1095
1096
1097
1098
1099 if ctxt.GOROOT == "" {
1100 return errNoModules
1101 }
1102
1103
1104
1105
1106
1107 go111Module := os.Getenv("GO111MODULE")
1108 switch go111Module {
1109 case "off":
1110 return errNoModules
1111 default:
1112
1113 }
1114
1115 if srcDir != "" {
1116 var absSrcDir string
1117 if filepath.IsAbs(srcDir) {
1118 absSrcDir = srcDir
1119 } else if ctxt.Dir != "" {
1120 return fmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", srcDir)
1121 } else {
1122
1123
1124 var err error
1125 absSrcDir, err = filepath.Abs(srcDir)
1126 if err != nil {
1127 return errNoModules
1128 }
1129 }
1130
1131
1132
1133
1134 if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
1135 return errNoModules
1136 }
1137 }
1138
1139
1140 if dir := ctxt.joinPath(ctxt.GOROOT, "src", path); ctxt.isDir(dir) {
1141 return errNoModules
1142 }
1143
1144
1145
1146 if go111Module == "auto" {
1147 var (
1148 parent string
1149 err error
1150 )
1151 if ctxt.Dir == "" {
1152 parent, err = os.Getwd()
1153 if err != nil {
1154
1155 return errNoModules
1156 }
1157 } else {
1158 parent, err = filepath.Abs(ctxt.Dir)
1159 if err != nil {
1160
1161
1162 return err
1163 }
1164 }
1165 for {
1166 if f, err := ctxt.openFile(ctxt.joinPath(parent, "go.mod")); err == nil {
1167 buf := make([]byte, 100)
1168 _, err := f.Read(buf)
1169 f.Close()
1170 if err == nil || err == io.EOF {
1171
1172 break
1173 }
1174 }
1175 d := filepath.Dir(parent)
1176 if len(d) >= len(parent) {
1177 return errNoModules
1178 }
1179 parent = d
1180 }
1181 }
1182
1183 goCmd := filepath.Join(ctxt.GOROOT, "bin", "go")
1184 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)
1185
1186 if ctxt.Dir != "" {
1187 cmd.Dir = ctxt.Dir
1188 }
1189
1190 var stdout, stderr strings.Builder
1191 cmd.Stdout = &stdout
1192 cmd.Stderr = &stderr
1193
1194 cgo := "0"
1195 if ctxt.CgoEnabled {
1196 cgo = "1"
1197 }
1198 cmd.Env = append(cmd.Environ(),
1199 "GOOS="+ctxt.GOOS,
1200 "GOARCH="+ctxt.GOARCH,
1201 "GOROOT="+ctxt.GOROOT,
1202 "GOPATH="+ctxt.GOPATH,
1203 "CGO_ENABLED="+cgo,
1204 )
1205
1206 if err := cmd.Run(); err != nil {
1207 return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
1208 }
1209
1210 f := strings.SplitN(stdout.String(), "\n", 5)
1211 if len(f) != 5 {
1212 return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String())
1213 }
1214 dir := f[0]
1215 errStr := strings.TrimSpace(f[4])
1216 if errStr != "" && dir == "" {
1217
1218
1219 return errors.New(errStr)
1220 }
1221
1222
1223
1224
1225 p.Dir = dir
1226 p.ImportPath = f[1]
1227 p.Root = f[2]
1228 p.Goroot = f[3] == "true"
1229 return nil
1230 }
1231
1232 func equal(x, y []string) bool {
1233 if len(x) != len(y) {
1234 return false
1235 }
1236 for i, xi := range x {
1237 if xi != y[i] {
1238 return false
1239 }
1240 }
1241 return true
1242 }
1243
1244
1245
1246
1247
1248 func hasGoFiles(ctxt *Context, dir string) bool {
1249 ents, _ := ctxt.readDir(dir)
1250 for _, ent := range ents {
1251 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
1252 return true
1253 }
1254 }
1255 return false
1256 }
1257
1258 func findImportComment(data []byte) (s string, line int) {
1259
1260 word, data := parseWord(data)
1261 if string(word) != "package" {
1262 return "", 0
1263 }
1264
1265
1266 _, data = parseWord(data)
1267
1268
1269
1270 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
1271 data = data[1:]
1272 }
1273
1274 var comment []byte
1275 switch {
1276 case bytes.HasPrefix(data, slashSlash):
1277 comment, _, _ = bytes.Cut(data[2:], newline)
1278 case bytes.HasPrefix(data, slashStar):
1279 var ok bool
1280 comment, _, ok = bytes.Cut(data[2:], starSlash)
1281 if !ok {
1282
1283 return "", 0
1284 }
1285 if bytes.Contains(comment, newline) {
1286 return "", 0
1287 }
1288 }
1289 comment = bytes.TrimSpace(comment)
1290
1291
1292 word, arg := parseWord(comment)
1293 if string(word) != "import" {
1294 return "", 0
1295 }
1296
1297 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
1298 return strings.TrimSpace(string(arg)), line
1299 }
1300
1301 var (
1302 slashSlash = []byte("//")
1303 slashStar = []byte("/*")
1304 starSlash = []byte("*/")
1305 newline = []byte("\n")
1306 )
1307
1308
1309 func skipSpaceOrComment(data []byte) []byte {
1310 for len(data) > 0 {
1311 switch data[0] {
1312 case ' ', '\t', '\r', '\n':
1313 data = data[1:]
1314 continue
1315 case '/':
1316 if bytes.HasPrefix(data, slashSlash) {
1317 i := bytes.Index(data, newline)
1318 if i < 0 {
1319 return nil
1320 }
1321 data = data[i+1:]
1322 continue
1323 }
1324 if bytes.HasPrefix(data, slashStar) {
1325 data = data[2:]
1326 i := bytes.Index(data, starSlash)
1327 if i < 0 {
1328 return nil
1329 }
1330 data = data[i+2:]
1331 continue
1332 }
1333 }
1334 break
1335 }
1336 return data
1337 }
1338
1339
1340
1341
1342 func parseWord(data []byte) (word, rest []byte) {
1343 data = skipSpaceOrComment(data)
1344
1345
1346 rest = data
1347 for {
1348 r, size := utf8.DecodeRune(rest)
1349 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
1350 rest = rest[size:]
1351 continue
1352 }
1353 break
1354 }
1355
1356 word = data[:len(data)-len(rest)]
1357 if len(word) == 0 {
1358 return nil, nil
1359 }
1360
1361 return word, rest
1362 }
1363
1364
1365
1366
1367
1368
1369
1370 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
1371 info, err := ctxt.matchFile(dir, name, nil, nil, nil)
1372 return info != nil, err
1373 }
1374
1375 var dummyPkg Package
1376
1377
1378 type fileInfo struct {
1379 name string
1380 header []byte
1381 fset *token.FileSet
1382 parsed *ast.File
1383 parseErr error
1384 imports []fileImport
1385 embeds []fileEmbed
1386 }
1387
1388 type fileImport struct {
1389 path string
1390 pos token.Pos
1391 doc *ast.CommentGroup
1392 }
1393
1394 type fileEmbed struct {
1395 pattern string
1396 pos token.Position
1397 }
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411 func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
1412 if strings.HasPrefix(name, "_") ||
1413 strings.HasPrefix(name, ".") {
1414 return nil, nil
1415 }
1416
1417 i := strings.LastIndex(name, ".")
1418 if i < 0 {
1419 i = len(name)
1420 }
1421 ext := name[i:]
1422
1423 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
1424 return nil, nil
1425 }
1426
1427 if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
1428
1429 return nil, nil
1430 }
1431
1432 info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
1433 if ext == ".syso" {
1434
1435 return info, nil
1436 }
1437
1438 f, err := ctxt.openFile(info.name)
1439 if err != nil {
1440 return nil, err
1441 }
1442
1443 if strings.HasSuffix(name, ".go") {
1444 err = readGoInfo(f, info)
1445 if strings.HasSuffix(name, "_test.go") {
1446 binaryOnly = nil
1447 }
1448 } else {
1449 binaryOnly = nil
1450 info.header, err = readComments(f)
1451 }
1452 f.Close()
1453 if err != nil {
1454 return nil, fmt.Errorf("read %s: %v", info.name, err)
1455 }
1456
1457
1458 ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
1459 if err != nil {
1460 return nil, fmt.Errorf("%s: %v", name, err)
1461 }
1462 if !ok && !ctxt.UseAllFiles {
1463 return nil, nil
1464 }
1465
1466 if binaryOnly != nil && sawBinaryOnly {
1467 *binaryOnly = true
1468 }
1469
1470 return info, nil
1471 }
1472
1473 func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) {
1474 all := make([]string, 0, len(m))
1475 for path := range m {
1476 all = append(all, path)
1477 }
1478 sort.Strings(all)
1479 return all, m
1480 }
1481
1482
1483 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
1484 return Default.Import(path, srcDir, mode)
1485 }
1486
1487
1488 func ImportDir(dir string, mode ImportMode) (*Package, error) {
1489 return Default.ImportDir(dir, mode)
1490 }
1491
1492 var (
1493 bSlashSlash = []byte(slashSlash)
1494 bStarSlash = []byte(starSlash)
1495 bSlashStar = []byte(slashStar)
1496 bPlusBuild = []byte("+build")
1497
1498 goBuildComment = []byte("//go:build")
1499
1500 errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment")
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, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
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, bSlashSlash) {
1635 continue Lines
1636 }
1637 if bytes.HasPrefix(line, bSlashStar) {
1638 inSlashStar = true
1639 line = bytes.TrimSpace(line[len(bSlashStar):])
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