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