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