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