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