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