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 = 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 || !equal(ctxt.ToolTags, defaultToolTags) || !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 func equal(x, y []string) bool {
1283 if len(x) != len(y) {
1284 return false
1285 }
1286 for i, xi := range x {
1287 if xi != y[i] {
1288 return false
1289 }
1290 }
1291 return true
1292 }
1293
1294
1295
1296
1297
1298 func hasGoFiles(ctxt *Context, dir string) bool {
1299 ents, _ := ctxt.readDir(dir)
1300 for _, ent := range ents {
1301 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
1302 return true
1303 }
1304 }
1305 return false
1306 }
1307
1308 func findImportComment(data []byte) (s string, line int) {
1309
1310 word, data := parseWord(data)
1311 if string(word) != "package" {
1312 return "", 0
1313 }
1314
1315
1316 _, data = parseWord(data)
1317
1318
1319
1320 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
1321 data = data[1:]
1322 }
1323
1324 var comment []byte
1325 switch {
1326 case bytes.HasPrefix(data, slashSlash):
1327 comment, _, _ = bytes.Cut(data[2:], newline)
1328 case bytes.HasPrefix(data, slashStar):
1329 var ok bool
1330 comment, _, ok = bytes.Cut(data[2:], starSlash)
1331 if !ok {
1332
1333 return "", 0
1334 }
1335 if bytes.Contains(comment, newline) {
1336 return "", 0
1337 }
1338 }
1339 comment = bytes.TrimSpace(comment)
1340
1341
1342 word, arg := parseWord(comment)
1343 if string(word) != "import" {
1344 return "", 0
1345 }
1346
1347 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
1348 return strings.TrimSpace(string(arg)), line
1349 }
1350
1351 var (
1352 slashSlash = []byte("//")
1353 slashStar = []byte("/*")
1354 starSlash = []byte("*/")
1355 newline = []byte("\n")
1356 )
1357
1358
1359 func skipSpaceOrComment(data []byte) []byte {
1360 for len(data) > 0 {
1361 switch data[0] {
1362 case ' ', '\t', '\r', '\n':
1363 data = data[1:]
1364 continue
1365 case '/':
1366 if bytes.HasPrefix(data, slashSlash) {
1367 i := bytes.Index(data, newline)
1368 if i < 0 {
1369 return nil
1370 }
1371 data = data[i+1:]
1372 continue
1373 }
1374 if bytes.HasPrefix(data, slashStar) {
1375 data = data[2:]
1376 i := bytes.Index(data, starSlash)
1377 if i < 0 {
1378 return nil
1379 }
1380 data = data[i+2:]
1381 continue
1382 }
1383 }
1384 break
1385 }
1386 return data
1387 }
1388
1389
1390
1391
1392 func parseWord(data []byte) (word, rest []byte) {
1393 data = skipSpaceOrComment(data)
1394
1395
1396 rest = data
1397 for {
1398 r, size := utf8.DecodeRune(rest)
1399 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
1400 rest = rest[size:]
1401 continue
1402 }
1403 break
1404 }
1405
1406 word = data[:len(data)-len(rest)]
1407 if len(word) == 0 {
1408 return nil, nil
1409 }
1410
1411 return word, rest
1412 }
1413
1414
1415
1416
1417
1418
1419
1420 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
1421 info, err := ctxt.matchFile(dir, name, nil, nil, nil)
1422 return info != nil, err
1423 }
1424
1425 var dummyPkg Package
1426
1427
1428 type fileInfo struct {
1429 name string
1430 header []byte
1431 fset *token.FileSet
1432 parsed *ast.File
1433 parseErr error
1434 imports []fileImport
1435 embeds []fileEmbed
1436 directives []Directive
1437 }
1438
1439 type fileImport struct {
1440 path string
1441 pos token.Pos
1442 doc *ast.CommentGroup
1443 }
1444
1445 type fileEmbed struct {
1446 pattern string
1447 pos token.Position
1448 }
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462 func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
1463 if strings.HasPrefix(name, "_") ||
1464 strings.HasPrefix(name, ".") {
1465 return nil, nil
1466 }
1467
1468 i := strings.LastIndex(name, ".")
1469 if i < 0 {
1470 i = len(name)
1471 }
1472 ext := name[i:]
1473
1474 if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
1475
1476 return nil, nil
1477 }
1478
1479 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
1480 return nil, nil
1481 }
1482
1483 info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
1484 if ext == ".syso" {
1485
1486 return info, nil
1487 }
1488
1489 f, err := ctxt.openFile(info.name)
1490 if err != nil {
1491 return nil, err
1492 }
1493
1494 if strings.HasSuffix(name, ".go") {
1495 err = readGoInfo(f, info)
1496 if strings.HasSuffix(name, "_test.go") {
1497 binaryOnly = nil
1498 }
1499 } else {
1500 binaryOnly = nil
1501 info.header, err = readComments(f)
1502 }
1503 f.Close()
1504 if err != nil {
1505 return info, fmt.Errorf("read %s: %v", info.name, err)
1506 }
1507
1508
1509 ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
1510 if err != nil {
1511 return nil, fmt.Errorf("%s: %v", name, err)
1512 }
1513 if !ok && !ctxt.UseAllFiles {
1514 return nil, nil
1515 }
1516
1517 if binaryOnly != nil && sawBinaryOnly {
1518 *binaryOnly = true
1519 }
1520
1521 return info, nil
1522 }
1523
1524 func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) {
1525 all := make([]string, 0, len(m))
1526 for path := range m {
1527 all = append(all, path)
1528 }
1529 slices.Sort(all)
1530 return all, m
1531 }
1532
1533
1534 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
1535 return Default.Import(path, srcDir, mode)
1536 }
1537
1538
1539 func ImportDir(dir string, mode ImportMode) (*Package, error) {
1540 return Default.ImportDir(dir, mode)
1541 }
1542
1543 var (
1544 plusBuild = []byte("+build")
1545
1546 goBuildComment = []byte("//go:build")
1547
1548 errMultipleGoBuild = errors.New("multiple //go:build comments")
1549 )
1550
1551 func isGoBuildComment(line []byte) bool {
1552 if !bytes.HasPrefix(line, goBuildComment) {
1553 return false
1554 }
1555 line = bytes.TrimSpace(line)
1556 rest := line[len(goBuildComment):]
1557 return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
1558 }
1559
1560
1561
1562
1563 var binaryOnlyComment = []byte("//go:binary-only-package")
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild, binaryOnly bool, err error) {
1583
1584
1585
1586 content, goBuild, sawBinaryOnly, err := parseFileHeader(content)
1587 if err != nil {
1588 return false, false, err
1589 }
1590
1591
1592
1593 switch {
1594 case goBuild != nil:
1595 x, err := constraint.Parse(string(goBuild))
1596 if err != nil {
1597 return false, false, fmt.Errorf("parsing //go:build line: %v", err)
1598 }
1599 shouldBuild = ctxt.eval(x, allTags)
1600
1601 default:
1602 shouldBuild = true
1603 p := content
1604 for len(p) > 0 {
1605 line := p
1606 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1607 line, p = line[:i], p[i+1:]
1608 } else {
1609 p = p[len(p):]
1610 }
1611 line = bytes.TrimSpace(line)
1612 if !bytes.HasPrefix(line, slashSlash) || !bytes.Contains(line, plusBuild) {
1613 continue
1614 }
1615 text := string(line)
1616 if !constraint.IsPlusBuild(text) {
1617 continue
1618 }
1619 if x, err := constraint.Parse(text); err == nil {
1620 if !ctxt.eval(x, allTags) {
1621 shouldBuild = false
1622 }
1623 }
1624 }
1625 }
1626
1627 return shouldBuild, sawBinaryOnly, nil
1628 }
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639 func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
1640 end := 0
1641 p := content
1642 ended := false
1643 inSlashStar := false
1644
1645 Lines:
1646 for len(p) > 0 {
1647 line := p
1648 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1649 line, p = line[:i], p[i+1:]
1650 } else {
1651 p = p[len(p):]
1652 }
1653 line = bytes.TrimSpace(line)
1654 if len(line) == 0 && !ended {
1655
1656
1657
1658
1659
1660
1661
1662
1663 end = len(content) - len(p)
1664 continue Lines
1665 }
1666 if !bytes.HasPrefix(line, slashSlash) {
1667 ended = true
1668 }
1669
1670 if !inSlashStar && isGoBuildComment(line) {
1671 if goBuild != nil {
1672 return nil, nil, false, errMultipleGoBuild
1673 }
1674 goBuild = line
1675 }
1676 if !inSlashStar && bytes.Equal(line, binaryOnlyComment) {
1677 sawBinaryOnly = true
1678 }
1679
1680 Comments:
1681 for len(line) > 0 {
1682 if inSlashStar {
1683 if i := bytes.Index(line, starSlash); i >= 0 {
1684 inSlashStar = false
1685 line = bytes.TrimSpace(line[i+len(starSlash):])
1686 continue Comments
1687 }
1688 continue Lines
1689 }
1690 if bytes.HasPrefix(line, slashSlash) {
1691 continue Lines
1692 }
1693 if bytes.HasPrefix(line, slashStar) {
1694 inSlashStar = true
1695 line = bytes.TrimSpace(line[len(slashStar):])
1696 continue Comments
1697 }
1698
1699 break Lines
1700 }
1701 }
1702
1703 return content[:end], goBuild, sawBinaryOnly, nil
1704 }
1705
1706
1707
1708
1709 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
1710 text := cg.Text()
1711 for _, line := range strings.Split(text, "\n") {
1712 orig := line
1713
1714
1715
1716
1717 line = strings.TrimSpace(line)
1718 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
1719 continue
1720 }
1721
1722
1723 if fields := strings.Fields(line); len(fields) == 3 && (fields[1] == "nocallback" || fields[1] == "noescape") {
1724 continue
1725 }
1726
1727
1728 line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":")
1729 if !ok {
1730 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1731 }
1732
1733
1734 f := strings.Fields(line)
1735 if len(f) < 1 {
1736 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1737 }
1738
1739 cond, verb := f[:len(f)-1], f[len(f)-1]
1740 if len(cond) > 0 {
1741 ok := false
1742 for _, c := range cond {
1743 if ctxt.matchAuto(c, nil) {
1744 ok = true
1745 break
1746 }
1747 }
1748 if !ok {
1749 continue
1750 }
1751 }
1752
1753 args, err := splitQuoted(argstr)
1754 if err != nil {
1755 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1756 }
1757 for i, arg := range args {
1758 if arg, ok = expandSrcDir(arg, di.Dir); !ok {
1759 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
1760 }
1761 args[i] = arg
1762 }
1763
1764 switch verb {
1765 case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":
1766
1767 ctxt.makePathsAbsolute(args, di.Dir)
1768 }
1769
1770 switch verb {
1771 case "CFLAGS":
1772 di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
1773 case "CPPFLAGS":
1774 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
1775 case "CXXFLAGS":
1776 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
1777 case "FFLAGS":
1778 di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
1779 case "LDFLAGS":
1780 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
1781 case "pkg-config":
1782 di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
1783 default:
1784 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
1785 }
1786 }
1787 return nil
1788 }
1789
1790
1791
1792 func expandSrcDir(str string, srcdir string) (string, bool) {
1793
1794
1795
1796 srcdir = filepath.ToSlash(srcdir)
1797
1798 chunks := strings.Split(str, "${SRCDIR}")
1799 if len(chunks) < 2 {
1800 return str, safeCgoName(str)
1801 }
1802 ok := true
1803 for _, chunk := range chunks {
1804 ok = ok && (chunk == "" || safeCgoName(chunk))
1805 }
1806 ok = ok && (srcdir == "" || safeCgoName(srcdir))
1807 res := strings.Join(chunks, srcdir)
1808 return res, ok && res != ""
1809 }
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822 func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) {
1823 nextPath := false
1824 for i, arg := range args {
1825 if nextPath {
1826 if !filepath.IsAbs(arg) {
1827 args[i] = filepath.Join(srcDir, arg)
1828 }
1829 nextPath = false
1830 } else if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
1831 if len(arg) == 2 {
1832 nextPath = true
1833 } else {
1834 if !filepath.IsAbs(arg[2:]) {
1835 args[i] = arg[:2] + filepath.Join(srcDir, arg[2:])
1836 }
1837 }
1838 }
1839 }
1840 }
1841
1842
1843
1844
1845
1846
1847
1848
1849 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"
1850
1851 func safeCgoName(s string) bool {
1852 if s == "" {
1853 return false
1854 }
1855 for i := 0; i < len(s); i++ {
1856 if c := s[i]; c < utf8.RuneSelf && strings.IndexByte(safeString, c) < 0 {
1857 return false
1858 }
1859 }
1860 return true
1861 }
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878 func splitQuoted(s string) (r []string, err error) {
1879 var args []string
1880 arg := make([]rune, len(s))
1881 escaped := false
1882 quoted := false
1883 quote := '\x00'
1884 i := 0
1885 for _, rune := range s {
1886 switch {
1887 case escaped:
1888 escaped = false
1889 case rune == '\\':
1890 escaped = true
1891 continue
1892 case quote != '\x00':
1893 if rune == quote {
1894 quote = '\x00'
1895 continue
1896 }
1897 case rune == '"' || rune == '\'':
1898 quoted = true
1899 quote = rune
1900 continue
1901 case unicode.IsSpace(rune):
1902 if quoted || i > 0 {
1903 quoted = false
1904 args = append(args, string(arg[:i]))
1905 i = 0
1906 }
1907 continue
1908 }
1909 arg[i] = rune
1910 i++
1911 }
1912 if quoted || i > 0 {
1913 args = append(args, string(arg[:i]))
1914 }
1915 if quote != 0 {
1916 err = errors.New("unclosed quote")
1917 } else if escaped {
1918 err = errors.New("unfinished escaping")
1919 }
1920 return args, err
1921 }
1922
1923
1924
1925
1926
1927
1928 func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool {
1929 if strings.ContainsAny(text, "&|()") {
1930 text = "//go:build " + text
1931 } else {
1932 text = "// +build " + text
1933 }
1934 x, err := constraint.Parse(text)
1935 if err != nil {
1936 return false
1937 }
1938 return ctxt.eval(x, allTags)
1939 }
1940
1941 func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool {
1942 return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) })
1943 }
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959 func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
1960 if allTags != nil {
1961 allTags[name] = true
1962 }
1963
1964
1965 if ctxt.CgoEnabled && name == "cgo" {
1966 return true
1967 }
1968 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
1969 return true
1970 }
1971 if ctxt.GOOS == "android" && name == "linux" {
1972 return true
1973 }
1974 if ctxt.GOOS == "illumos" && name == "solaris" {
1975 return true
1976 }
1977 if ctxt.GOOS == "ios" && name == "darwin" {
1978 return true
1979 }
1980 if name == "unix" && syslist.UnixOS[ctxt.GOOS] {
1981 return true
1982 }
1983 if name == "boringcrypto" {
1984 name = "goexperiment.boringcrypto"
1985 }
1986
1987
1988 for _, tag := range ctxt.BuildTags {
1989 if tag == name {
1990 return true
1991 }
1992 }
1993 for _, tag := range ctxt.ToolTags {
1994 if tag == name {
1995 return true
1996 }
1997 }
1998 for _, tag := range ctxt.ReleaseTags {
1999 if tag == name {
2000 return true
2001 }
2002 }
2003
2004 return false
2005 }
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
2023 name, _, _ = strings.Cut(name, ".")
2024
2025
2026
2027
2028
2029
2030
2031
2032 i := strings.Index(name, "_")
2033 if i < 0 {
2034 return true
2035 }
2036 name = name[i:]
2037
2038 l := strings.Split(name, "_")
2039 if n := len(l); n > 0 && l[n-1] == "test" {
2040 l = l[:n-1]
2041 }
2042 n := len(l)
2043 if n >= 2 && syslist.KnownOS[l[n-2]] && syslist.KnownArch[l[n-1]] {
2044 if allTags != nil {
2045
2046 allTags[l[n-2]] = true
2047 }
2048 return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags)
2049 }
2050 if n >= 1 && (syslist.KnownOS[l[n-1]] || syslist.KnownArch[l[n-1]]) {
2051 return ctxt.matchTag(l[n-1], allTags)
2052 }
2053 return true
2054 }
2055
2056
2057 var ToolDir = getToolDir()
2058
2059
2060
2061 func IsLocalImport(path string) bool {
2062 return path == "." || path == ".." ||
2063 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
2064 }
2065
2066
2067
2068
2069
2070
2071 func ArchChar(goarch string) (string, error) {
2072 return "?", errors.New("architecture letter no longer used")
2073 }
2074
View as plain text