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/parser"
14 "go/token"
15 exec "internal/execabs"
16 "internal/goroot"
17 "internal/goversion"
18 "io"
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) ([]os.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) ([]os.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 CFiles []string
417 CXXFiles []string
418 MFiles []string
419 HFiles []string
420 FFiles []string
421 SFiles []string
422 SwigFiles []string
423 SwigCXXFiles []string
424 SysoFiles []string
425
426
427 CgoCFLAGS []string
428 CgoCPPFLAGS []string
429 CgoCXXFLAGS []string
430 CgoFFLAGS []string
431 CgoLDFLAGS []string
432 CgoPkgConfig []string
433
434
435 Imports []string
436 ImportPos map[string][]token.Position
437
438
439 TestGoFiles []string
440 TestImports []string
441 TestImportPos map[string][]token.Position
442 XTestGoFiles []string
443 XTestImports []string
444 XTestImportPos map[string][]token.Position
445 }
446
447
448
449
450 func (p *Package) IsCommand() bool {
451 return p.Name == "main"
452 }
453
454
455
456 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
457 return ctxt.Import(".", dir, mode)
458 }
459
460
461
462
463 type NoGoError struct {
464 Dir string
465 }
466
467 func (e *NoGoError) Error() string {
468 return "no buildable Go source files in " + e.Dir
469 }
470
471
472
473 type MultiplePackageError struct {
474 Dir string
475 Packages []string
476 Files []string
477 }
478
479 func (e *MultiplePackageError) Error() string {
480
481 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)
482 }
483
484 func nameExt(name string) string {
485 i := strings.LastIndex(name, ".")
486 if i < 0 {
487 return ""
488 }
489 return name[i:]
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
509 p := &Package{
510 ImportPath: path,
511 }
512 if path == "" {
513 return p, fmt.Errorf("import %q: invalid import path", path)
514 }
515
516 var pkgtargetroot string
517 var pkga string
518 var pkgerr error
519 suffix := ""
520 if ctxt.InstallSuffix != "" {
521 suffix = "_" + ctxt.InstallSuffix
522 }
523 switch ctxt.Compiler {
524 case "gccgo":
525 pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
526 case "gc":
527 pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
528 default:
529
530 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
531 }
532 setPkga := func() {
533 switch ctxt.Compiler {
534 case "gccgo":
535 dir, elem := pathpkg.Split(p.ImportPath)
536 pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
537 case "gc":
538 pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
539 }
540 }
541 setPkga()
542
543 binaryOnly := false
544 if IsLocalImport(path) {
545 pkga = ""
546 if srcDir == "" {
547 return p, fmt.Errorf("import %q: import relative to unknown directory", path)
548 }
549 if !ctxt.isAbsPath(path) {
550 p.Dir = ctxt.joinPath(srcDir, path)
551 }
552
553
554
555 inTestdata := func(sub string) bool {
556 return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
557 }
558 if ctxt.GOROOT != "" {
559 root := ctxt.joinPath(ctxt.GOROOT, "src")
560 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
561 p.Goroot = true
562 p.ImportPath = sub
563 p.Root = ctxt.GOROOT
564 setPkga()
565 goto Found
566 }
567 }
568 all := ctxt.gopath()
569 for i, root := range all {
570 rootsrc := ctxt.joinPath(root, "src")
571 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
572
573
574
575 if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" {
576 if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
577 p.ConflictDir = dir
578 goto Found
579 }
580 }
581 for _, earlyRoot := range all[:i] {
582 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
583 p.ConflictDir = dir
584 goto Found
585 }
586 }
587
588
589
590 p.ImportPath = sub
591 p.Root = root
592 setPkga()
593 goto Found
594 }
595 }
596
597
598 } else {
599 if strings.HasPrefix(path, "/") {
600 return p, fmt.Errorf("import %q: cannot import absolute path", path)
601 }
602
603 if err := ctxt.importGo(p, path, srcDir, mode); err == nil {
604 goto Found
605 } else if err != errNoModules {
606 return p, err
607 }
608
609 gopath := ctxt.gopath()
610
611
612 var tried struct {
613 vendor []string
614 goroot string
615 gopath []string
616 }
617
618
619 if mode&IgnoreVendor == 0 && srcDir != "" {
620 searchVendor := func(root string, isGoroot bool) bool {
621 sub, ok := ctxt.hasSubdir(root, srcDir)
622 if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
623 return false
624 }
625 for {
626 vendor := ctxt.joinPath(root, sub, "vendor")
627 if ctxt.isDir(vendor) {
628 dir := ctxt.joinPath(vendor, path)
629 if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
630 p.Dir = dir
631 p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
632 p.Goroot = isGoroot
633 p.Root = root
634 setPkga()
635 return true
636 }
637 tried.vendor = append(tried.vendor, dir)
638 }
639 i := strings.LastIndex(sub, "/")
640 if i < 0 {
641 break
642 }
643 sub = sub[:i]
644 }
645 return false
646 }
647 if ctxt.Compiler != "gccgo" && searchVendor(ctxt.GOROOT, true) {
648 goto Found
649 }
650 for _, root := range gopath {
651 if searchVendor(root, false) {
652 goto Found
653 }
654 }
655 }
656
657
658 if ctxt.GOROOT != "" {
659
660
661
662
663 gorootFirst := srcDir == "" || !strings.HasPrefix(path, "vendor/")
664 if !gorootFirst {
665 _, gorootFirst = ctxt.hasSubdir(ctxt.GOROOT, srcDir)
666 }
667 if gorootFirst {
668 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
669 if ctxt.Compiler != "gccgo" {
670 isDir := ctxt.isDir(dir)
671 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
672 if isDir || binaryOnly {
673 p.Dir = dir
674 p.Goroot = true
675 p.Root = ctxt.GOROOT
676 goto Found
677 }
678 }
679 tried.goroot = dir
680 }
681 }
682 if ctxt.Compiler == "gccgo" && goroot.IsStandardPackage(ctxt.GOROOT, ctxt.Compiler, path) {
683 p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path)
684 p.Goroot = true
685 p.Root = ctxt.GOROOT
686 goto Found
687 }
688 for _, root := range gopath {
689 dir := ctxt.joinPath(root, "src", path)
690 isDir := ctxt.isDir(dir)
691 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
692 if isDir || binaryOnly {
693 p.Dir = dir
694 p.Root = root
695 goto Found
696 }
697 tried.gopath = append(tried.gopath, dir)
698 }
699
700
701
702
703 if ctxt.GOROOT != "" && tried.goroot == "" {
704 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
705 if ctxt.Compiler != "gccgo" {
706 isDir := ctxt.isDir(dir)
707 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
708 if isDir || binaryOnly {
709 p.Dir = dir
710 p.Goroot = true
711 p.Root = ctxt.GOROOT
712 goto Found
713 }
714 }
715 tried.goroot = dir
716 }
717
718
719 var paths []string
720 format := "\t%s (vendor tree)"
721 for _, dir := range tried.vendor {
722 paths = append(paths, fmt.Sprintf(format, dir))
723 format = "\t%s"
724 }
725 if tried.goroot != "" {
726 paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
727 } else {
728 paths = append(paths, "\t($GOROOT not set)")
729 }
730 format = "\t%s (from $GOPATH)"
731 for _, dir := range tried.gopath {
732 paths = append(paths, fmt.Sprintf(format, dir))
733 format = "\t%s"
734 }
735 if len(tried.gopath) == 0 {
736 paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
737 }
738 return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
739 }
740
741 Found:
742 if p.Root != "" {
743 p.SrcRoot = ctxt.joinPath(p.Root, "src")
744 p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
745 p.BinDir = ctxt.joinPath(p.Root, "bin")
746 if pkga != "" {
747 p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
748 p.PkgObj = ctxt.joinPath(p.Root, pkga)
749 }
750 }
751
752
753
754
755
756
757 if IsLocalImport(path) && !ctxt.isDir(p.Dir) {
758 if ctxt.Compiler == "gccgo" && p.Goroot {
759
760 return p, nil
761 }
762
763
764 return p, fmt.Errorf("cannot find package %q in:\n\t%s", path, p.Dir)
765 }
766
767 if mode&FindOnly != 0 {
768 return p, pkgerr
769 }
770 if binaryOnly && (mode&AllowBinary) != 0 {
771 return p, pkgerr
772 }
773
774 if ctxt.Compiler == "gccgo" && p.Goroot {
775
776 return p, nil
777 }
778
779 dirs, err := ctxt.readDir(p.Dir)
780 if err != nil {
781 return p, err
782 }
783
784 var badGoError error
785 var Sfiles []string
786 var firstFile, firstCommentFile string
787 imported := make(map[string][]token.Position)
788 testImported := make(map[string][]token.Position)
789 xTestImported := make(map[string][]token.Position)
790 allTags := make(map[string]bool)
791 fset := token.NewFileSet()
792 for _, d := range dirs {
793 if d.IsDir() {
794 continue
795 }
796
797 name := d.Name()
798 ext := nameExt(name)
799
800 badFile := func(err error) {
801 if badGoError == nil {
802 badGoError = err
803 }
804 p.InvalidGoFiles = append(p.InvalidGoFiles, name)
805 }
806
807 match, data, filename, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly)
808 if err != nil {
809 badFile(err)
810 continue
811 }
812 if !match {
813 if ext == ".go" {
814 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
815 }
816 continue
817 }
818
819
820 switch ext {
821 case ".c":
822 p.CFiles = append(p.CFiles, name)
823 continue
824 case ".cc", ".cpp", ".cxx":
825 p.CXXFiles = append(p.CXXFiles, name)
826 continue
827 case ".m":
828 p.MFiles = append(p.MFiles, name)
829 continue
830 case ".h", ".hh", ".hpp", ".hxx":
831 p.HFiles = append(p.HFiles, name)
832 continue
833 case ".f", ".F", ".for", ".f90":
834 p.FFiles = append(p.FFiles, name)
835 continue
836 case ".s":
837 p.SFiles = append(p.SFiles, name)
838 continue
839 case ".S", ".sx":
840 Sfiles = append(Sfiles, name)
841 continue
842 case ".swig":
843 p.SwigFiles = append(p.SwigFiles, name)
844 continue
845 case ".swigcxx":
846 p.SwigCXXFiles = append(p.SwigCXXFiles, name)
847 continue
848 case ".syso":
849
850
851
852 p.SysoFiles = append(p.SysoFiles, name)
853 continue
854 }
855
856 pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
857 if err != nil {
858 badFile(err)
859 continue
860 }
861
862 pkg := pf.Name.Name
863 if pkg == "documentation" {
864 p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
865 continue
866 }
867
868 isTest := strings.HasSuffix(name, "_test.go")
869 isXTest := false
870 if isTest && strings.HasSuffix(pkg, "_test") {
871 isXTest = true
872 pkg = pkg[:len(pkg)-len("_test")]
873 }
874
875 if p.Name == "" {
876 p.Name = pkg
877 firstFile = name
878 } else if pkg != p.Name {
879 badFile(&MultiplePackageError{
880 Dir: p.Dir,
881 Packages: []string{p.Name, pkg},
882 Files: []string{firstFile, name},
883 })
884 p.InvalidGoFiles = append(p.InvalidGoFiles, name)
885 }
886
887 if pf.Doc != nil && p.Doc == "" && !isTest && !isXTest {
888 p.Doc = doc.Synopsis(pf.Doc.Text())
889 }
890
891 if mode&ImportComment != 0 {
892 qcom, line := findImportComment(data)
893 if line != 0 {
894 com, err := strconv.Unquote(qcom)
895 if err != nil {
896 badFile(fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
897 } else if p.ImportComment == "" {
898 p.ImportComment = com
899 firstCommentFile = name
900 } else if p.ImportComment != com {
901 badFile(fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
902 }
903 }
904 }
905
906
907 type importPos struct {
908 path string
909 pos token.Pos
910 }
911 var fileImports []importPos
912 isCgo := false
913 for _, decl := range pf.Decls {
914 d, ok := decl.(*ast.GenDecl)
915 if !ok {
916 continue
917 }
918 for _, dspec := range d.Specs {
919 spec, ok := dspec.(*ast.ImportSpec)
920 if !ok {
921 continue
922 }
923 quoted := spec.Path.Value
924 path, err := strconv.Unquote(quoted)
925 if err != nil {
926 panic(fmt.Sprintf("%s: parser returned invalid quoted string: <%s>", filename, quoted))
927 }
928 fileImports = append(fileImports, importPos{path, spec.Pos()})
929 if path == "C" {
930 if isTest {
931 badFile(fmt.Errorf("use of cgo in test %s not supported", filename))
932 } else {
933 cg := spec.Doc
934 if cg == nil && len(d.Specs) == 1 {
935 cg = d.Doc
936 }
937 if cg != nil {
938 if err := ctxt.saveCgo(filename, p, cg); err != nil {
939 badFile(err)
940 }
941 }
942 isCgo = true
943 }
944 }
945 }
946 }
947
948 var fileList *[]string
949 var importMap map[string][]token.Position
950 switch {
951 case isCgo:
952 allTags["cgo"] = true
953 if ctxt.CgoEnabled {
954 fileList = &p.CgoFiles
955 importMap = imported
956 } else {
957
958 fileList = &p.IgnoredGoFiles
959 }
960 case isXTest:
961 fileList = &p.XTestGoFiles
962 importMap = xTestImported
963 case isTest:
964 fileList = &p.TestGoFiles
965 importMap = testImported
966 default:
967 fileList = &p.GoFiles
968 importMap = imported
969 }
970 *fileList = append(*fileList, name)
971 if importMap != nil {
972 for _, imp := range fileImports {
973 importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
974 }
975 }
976 }
977
978 for tag := range allTags {
979 p.AllTags = append(p.AllTags, tag)
980 }
981 sort.Strings(p.AllTags)
982
983 p.Imports, p.ImportPos = cleanImports(imported)
984 p.TestImports, p.TestImportPos = cleanImports(testImported)
985 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
986
987
988
989
990 if len(p.CgoFiles) > 0 {
991 p.SFiles = append(p.SFiles, Sfiles...)
992 sort.Strings(p.SFiles)
993 }
994
995 if badGoError != nil {
996 return p, badGoError
997 }
998 if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
999 return p, &NoGoError{p.Dir}
1000 }
1001 return p, pkgerr
1002 }
1003
1004 var errNoModules = errors.New("not using modules")
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016 func (ctxt *Context) importGo(p *Package, path, srcDir string, mode ImportMode) error {
1017
1018
1019
1020 if mode&AllowBinary != 0 || mode&IgnoreVendor != 0 ||
1021 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) {
1022 return errNoModules
1023 }
1024
1025
1026
1027
1028
1029 go111Module := os.Getenv("GO111MODULE")
1030 switch go111Module {
1031 case "off":
1032 return errNoModules
1033 default:
1034
1035 }
1036
1037 if srcDir != "" {
1038 var absSrcDir string
1039 if filepath.IsAbs(srcDir) {
1040 absSrcDir = srcDir
1041 } else if ctxt.Dir != "" {
1042 return fmt.Errorf("go/build: Dir is non-empty, so relative srcDir is not allowed: %v", srcDir)
1043 } else {
1044
1045
1046 var err error
1047 absSrcDir, err = filepath.Abs(srcDir)
1048 if err != nil {
1049 return errNoModules
1050 }
1051 }
1052
1053
1054
1055
1056 if _, ok := ctxt.hasSubdir(filepath.Join(ctxt.GOROOT, "src"), absSrcDir); ok {
1057 return errNoModules
1058 }
1059 }
1060
1061
1062 if ctxt.GOROOT != "" {
1063 dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
1064 if ctxt.isDir(dir) {
1065 return errNoModules
1066 }
1067 }
1068
1069
1070
1071 if go111Module != "on" {
1072 var (
1073 parent string
1074 err error
1075 )
1076 if ctxt.Dir == "" {
1077 parent, err = os.Getwd()
1078 if err != nil {
1079
1080 return errNoModules
1081 }
1082 } else {
1083 parent, err = filepath.Abs(ctxt.Dir)
1084 if err != nil {
1085
1086
1087 return err
1088 }
1089 }
1090 for {
1091 info, err := os.Stat(filepath.Join(parent, "go.mod"))
1092 if err == nil && !info.IsDir() {
1093 break
1094 }
1095 d := filepath.Dir(parent)
1096 if len(d) >= len(parent) {
1097 return errNoModules
1098 }
1099 parent = d
1100 }
1101 }
1102
1103 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)
1104
1105 if ctxt.Dir != "" {
1106 cmd.Dir = ctxt.Dir
1107 }
1108
1109 var stdout, stderr strings.Builder
1110 cmd.Stdout = &stdout
1111 cmd.Stderr = &stderr
1112
1113 cgo := "0"
1114 if ctxt.CgoEnabled {
1115 cgo = "1"
1116 }
1117 cmd.Env = append(os.Environ(),
1118 "GOOS="+ctxt.GOOS,
1119 "GOARCH="+ctxt.GOARCH,
1120 "GOROOT="+ctxt.GOROOT,
1121 "GOPATH="+ctxt.GOPATH,
1122 "CGO_ENABLED="+cgo,
1123 )
1124
1125 if err := cmd.Run(); err != nil {
1126 return fmt.Errorf("go/build: go list %s: %v\n%s\n", path, err, stderr.String())
1127 }
1128
1129 f := strings.SplitN(stdout.String(), "\n", 5)
1130 if len(f) != 5 {
1131 return fmt.Errorf("go/build: importGo %s: unexpected output:\n%s\n", path, stdout.String())
1132 }
1133 dir := f[0]
1134 errStr := strings.TrimSpace(f[4])
1135 if errStr != "" && dir == "" {
1136
1137
1138 return errors.New(errStr)
1139 }
1140
1141
1142
1143
1144 p.Dir = dir
1145 p.ImportPath = f[1]
1146 p.Root = f[2]
1147 p.Goroot = f[3] == "true"
1148 return nil
1149 }
1150
1151 func equal(x, y []string) bool {
1152 if len(x) != len(y) {
1153 return false
1154 }
1155 for i, xi := range x {
1156 if xi != y[i] {
1157 return false
1158 }
1159 }
1160 return true
1161 }
1162
1163
1164
1165
1166
1167 func hasGoFiles(ctxt *Context, dir string) bool {
1168 ents, _ := ctxt.readDir(dir)
1169 for _, ent := range ents {
1170 if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
1171 return true
1172 }
1173 }
1174 return false
1175 }
1176
1177 func findImportComment(data []byte) (s string, line int) {
1178
1179 word, data := parseWord(data)
1180 if string(word) != "package" {
1181 return "", 0
1182 }
1183
1184
1185 _, data = parseWord(data)
1186
1187
1188
1189 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
1190 data = data[1:]
1191 }
1192
1193 var comment []byte
1194 switch {
1195 case bytes.HasPrefix(data, slashSlash):
1196 i := bytes.Index(data, newline)
1197 if i < 0 {
1198 i = len(data)
1199 }
1200 comment = data[2:i]
1201 case bytes.HasPrefix(data, slashStar):
1202 data = data[2:]
1203 i := bytes.Index(data, starSlash)
1204 if i < 0 {
1205
1206 return "", 0
1207 }
1208 comment = data[:i]
1209 if bytes.Contains(comment, newline) {
1210 return "", 0
1211 }
1212 }
1213 comment = bytes.TrimSpace(comment)
1214
1215
1216 word, arg := parseWord(comment)
1217 if string(word) != "import" {
1218 return "", 0
1219 }
1220
1221 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
1222 return strings.TrimSpace(string(arg)), line
1223 }
1224
1225 var (
1226 slashSlash = []byte("//")
1227 slashStar = []byte("/*")
1228 starSlash = []byte("*/")
1229 newline = []byte("\n")
1230 )
1231
1232
1233 func skipSpaceOrComment(data []byte) []byte {
1234 for len(data) > 0 {
1235 switch data[0] {
1236 case ' ', '\t', '\r', '\n':
1237 data = data[1:]
1238 continue
1239 case '/':
1240 if bytes.HasPrefix(data, slashSlash) {
1241 i := bytes.Index(data, newline)
1242 if i < 0 {
1243 return nil
1244 }
1245 data = data[i+1:]
1246 continue
1247 }
1248 if bytes.HasPrefix(data, slashStar) {
1249 data = data[2:]
1250 i := bytes.Index(data, starSlash)
1251 if i < 0 {
1252 return nil
1253 }
1254 data = data[i+2:]
1255 continue
1256 }
1257 }
1258 break
1259 }
1260 return data
1261 }
1262
1263
1264
1265
1266 func parseWord(data []byte) (word, rest []byte) {
1267 data = skipSpaceOrComment(data)
1268
1269
1270 rest = data
1271 for {
1272 r, size := utf8.DecodeRune(rest)
1273 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
1274 rest = rest[size:]
1275 continue
1276 }
1277 break
1278 }
1279
1280 word = data[:len(data)-len(rest)]
1281 if len(word) == 0 {
1282 return nil, nil
1283 }
1284
1285 return word, rest
1286 }
1287
1288
1289
1290
1291
1292
1293
1294 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
1295 match, _, _, err = ctxt.matchFile(dir, name, nil, nil)
1296 return
1297 }
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool) (match bool, data []byte, filename string, err error) {
1308 if strings.HasPrefix(name, "_") ||
1309 strings.HasPrefix(name, ".") {
1310 return
1311 }
1312
1313 i := strings.LastIndex(name, ".")
1314 if i < 0 {
1315 i = len(name)
1316 }
1317 ext := name[i:]
1318
1319 if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
1320 return
1321 }
1322
1323 switch ext {
1324 case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".sx", ".swig", ".swigcxx":
1325
1326 case ".syso":
1327
1328 match = true
1329 return
1330 default:
1331
1332 return
1333 }
1334
1335 filename = ctxt.joinPath(dir, name)
1336 f, err := ctxt.openFile(filename)
1337 if err != nil {
1338 return
1339 }
1340
1341 if strings.HasSuffix(filename, ".go") {
1342 data, err = readImports(f, false, nil)
1343 if strings.HasSuffix(filename, "_test.go") {
1344 binaryOnly = nil
1345 }
1346 } else {
1347 binaryOnly = nil
1348 data, err = readComments(f)
1349 }
1350 f.Close()
1351 if err != nil {
1352 err = fmt.Errorf("read %s: %v", filename, err)
1353 return
1354 }
1355
1356
1357 var sawBinaryOnly bool
1358 if !ctxt.shouldBuild(data, allTags, &sawBinaryOnly) && !ctxt.UseAllFiles {
1359 return
1360 }
1361
1362 if binaryOnly != nil && sawBinaryOnly {
1363 *binaryOnly = true
1364 }
1365 match = true
1366 return
1367 }
1368
1369 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
1370 all := make([]string, 0, len(m))
1371 for path := range m {
1372 all = append(all, path)
1373 }
1374 sort.Strings(all)
1375 return all, m
1376 }
1377
1378
1379 func Import(path, srcDir string, mode ImportMode) (*Package, error) {
1380 return Default.Import(path, srcDir, mode)
1381 }
1382
1383
1384 func ImportDir(dir string, mode ImportMode) (*Package, error) {
1385 return Default.ImportDir(dir, mode)
1386 }
1387
1388 var slashslash = []byte("//")
1389
1390
1391
1392
1393 var binaryOnlyComment = []byte("//go:binary-only-package")
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool {
1412 sawBinaryOnly := false
1413
1414
1415
1416 end := 0
1417 p := content
1418 for len(p) > 0 {
1419 line := p
1420 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1421 line, p = line[:i], p[i+1:]
1422 } else {
1423 p = p[len(p):]
1424 }
1425 line = bytes.TrimSpace(line)
1426 if len(line) == 0 {
1427 end = len(content) - len(p)
1428 continue
1429 }
1430 if !bytes.HasPrefix(line, slashslash) {
1431 break
1432 }
1433 }
1434 content = content[:end]
1435
1436
1437 p = content
1438 allok := true
1439 for len(p) > 0 {
1440 line := p
1441 if i := bytes.IndexByte(line, '\n'); i >= 0 {
1442 line, p = line[:i], p[i+1:]
1443 } else {
1444 p = p[len(p):]
1445 }
1446 line = bytes.TrimSpace(line)
1447 if !bytes.HasPrefix(line, slashslash) {
1448 continue
1449 }
1450 if bytes.Equal(line, binaryOnlyComment) {
1451 sawBinaryOnly = true
1452 }
1453 line = bytes.TrimSpace(line[len(slashslash):])
1454 if len(line) > 0 && line[0] == '+' {
1455
1456 f := strings.Fields(string(line))
1457 if f[0] == "+build" {
1458 ok := false
1459 for _, tok := range f[1:] {
1460 if ctxt.match(tok, allTags) {
1461 ok = true
1462 }
1463 }
1464 if !ok {
1465 allok = false
1466 }
1467 }
1468 }
1469 }
1470
1471 if binaryOnly != nil && sawBinaryOnly {
1472 *binaryOnly = true
1473 }
1474
1475 return allok
1476 }
1477
1478
1479
1480
1481 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
1482 text := cg.Text()
1483 for _, line := range strings.Split(text, "\n") {
1484 orig := line
1485
1486
1487
1488
1489 line = strings.TrimSpace(line)
1490 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
1491 continue
1492 }
1493
1494
1495 line = strings.TrimSpace(line[4:])
1496 i := strings.Index(line, ":")
1497 if i < 0 {
1498 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1499 }
1500 line, argstr := line[:i], line[i+1:]
1501
1502
1503 f := strings.Fields(line)
1504 if len(f) < 1 {
1505 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1506 }
1507
1508 cond, verb := f[:len(f)-1], f[len(f)-1]
1509 if len(cond) > 0 {
1510 ok := false
1511 for _, c := range cond {
1512 if ctxt.match(c, nil) {
1513 ok = true
1514 break
1515 }
1516 }
1517 if !ok {
1518 continue
1519 }
1520 }
1521
1522 args, err := splitQuoted(argstr)
1523 if err != nil {
1524 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
1525 }
1526 var ok bool
1527 for i, arg := range args {
1528 if arg, ok = expandSrcDir(arg, di.Dir); !ok {
1529 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
1530 }
1531 args[i] = arg
1532 }
1533
1534 switch verb {
1535 case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS":
1536
1537 ctxt.makePathsAbsolute(args, di.Dir)
1538 }
1539
1540 switch verb {
1541 case "CFLAGS":
1542 di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
1543 case "CPPFLAGS":
1544 di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
1545 case "CXXFLAGS":
1546 di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
1547 case "FFLAGS":
1548 di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
1549 case "LDFLAGS":
1550 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
1551 case "pkg-config":
1552 di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
1553 default:
1554 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
1555 }
1556 }
1557 return nil
1558 }
1559
1560
1561
1562 func expandSrcDir(str string, srcdir string) (string, bool) {
1563
1564
1565
1566 srcdir = filepath.ToSlash(srcdir)
1567
1568 chunks := strings.Split(str, "${SRCDIR}")
1569 if len(chunks) < 2 {
1570 return str, safeCgoName(str)
1571 }
1572 ok := true
1573 for _, chunk := range chunks {
1574 ok = ok && (chunk == "" || safeCgoName(chunk))
1575 }
1576 ok = ok && (srcdir == "" || safeCgoName(srcdir))
1577 res := strings.Join(chunks, srcdir)
1578 return res, ok && res != ""
1579 }
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592 func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) {
1593 nextPath := false
1594 for i, arg := range args {
1595 if nextPath {
1596 if !filepath.IsAbs(arg) {
1597 args[i] = filepath.Join(srcDir, arg)
1598 }
1599 nextPath = false
1600 } else if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
1601 if len(arg) == 2 {
1602 nextPath = true
1603 } else {
1604 if !filepath.IsAbs(arg[2:]) {
1605 args[i] = arg[:2] + filepath.Join(srcDir, arg[2:])
1606 }
1607 }
1608 }
1609 }
1610 }
1611
1612
1613
1614
1615
1616
1617
1618
1619 const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^"
1620
1621 func safeCgoName(s string) bool {
1622 if s == "" {
1623 return false
1624 }
1625 for i := 0; i < len(s); i++ {
1626 if c := s[i]; c < utf8.RuneSelf && strings.IndexByte(safeString, c) < 0 {
1627 return false
1628 }
1629 }
1630 return true
1631 }
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649 func splitQuoted(s string) (r []string, err error) {
1650 var args []string
1651 arg := make([]rune, len(s))
1652 escaped := false
1653 quoted := false
1654 quote := '\x00'
1655 i := 0
1656 for _, rune := range s {
1657 switch {
1658 case escaped:
1659 escaped = false
1660 case rune == '\\':
1661 escaped = true
1662 continue
1663 case quote != '\x00':
1664 if rune == quote {
1665 quote = '\x00'
1666 continue
1667 }
1668 case rune == '"' || rune == '\'':
1669 quoted = true
1670 quote = rune
1671 continue
1672 case unicode.IsSpace(rune):
1673 if quoted || i > 0 {
1674 quoted = false
1675 args = append(args, string(arg[:i]))
1676 i = 0
1677 }
1678 continue
1679 }
1680 arg[i] = rune
1681 i++
1682 }
1683 if quoted || i > 0 {
1684 args = append(args, string(arg[:i]))
1685 }
1686 if quote != 0 {
1687 err = errors.New("unclosed quote")
1688 } else if escaped {
1689 err = errors.New("unfinished escaping")
1690 }
1691 return args, err
1692 }
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706 func (ctxt *Context) match(name string, allTags map[string]bool) bool {
1707 if name == "" {
1708 if allTags != nil {
1709 allTags[name] = true
1710 }
1711 return false
1712 }
1713 if i := strings.Index(name, ","); i >= 0 {
1714
1715 ok1 := ctxt.match(name[:i], allTags)
1716 ok2 := ctxt.match(name[i+1:], allTags)
1717 return ok1 && ok2
1718 }
1719 if strings.HasPrefix(name, "!!") {
1720 return false
1721 }
1722 if strings.HasPrefix(name, "!") {
1723 return len(name) > 1 && !ctxt.match(name[1:], allTags)
1724 }
1725
1726 if allTags != nil {
1727 allTags[name] = true
1728 }
1729
1730
1731
1732 for _, c := range name {
1733 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
1734 return false
1735 }
1736 }
1737
1738
1739 if ctxt.CgoEnabled && name == "cgo" {
1740 return true
1741 }
1742 if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
1743 return true
1744 }
1745 if ctxt.GOOS == "android" && name == "linux" {
1746 return true
1747 }
1748 if ctxt.GOOS == "illumos" && name == "solaris" {
1749 return true
1750 }
1751
1752
1753 for _, tag := range ctxt.BuildTags {
1754 if tag == name {
1755 return true
1756 }
1757 }
1758 for _, tag := range ctxt.ReleaseTags {
1759 if tag == name {
1760 return true
1761 }
1762 }
1763
1764 return false
1765 }
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779 func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
1780 if dot := strings.Index(name, "."); dot != -1 {
1781 name = name[:dot]
1782 }
1783
1784
1785
1786
1787
1788
1789
1790
1791 i := strings.Index(name, "_")
1792 if i < 0 {
1793 return true
1794 }
1795 name = name[i:]
1796
1797 l := strings.Split(name, "_")
1798 if n := len(l); n > 0 && l[n-1] == "test" {
1799 l = l[:n-1]
1800 }
1801 n := len(l)
1802 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
1803 return ctxt.match(l[n-1], allTags) && ctxt.match(l[n-2], allTags)
1804 }
1805 if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) {
1806 return ctxt.match(l[n-1], allTags)
1807 }
1808 return true
1809 }
1810
1811 var knownOS = make(map[string]bool)
1812 var knownArch = make(map[string]bool)
1813
1814 func init() {
1815 for _, v := range strings.Fields(goosList) {
1816 knownOS[v] = true
1817 }
1818 for _, v := range strings.Fields(goarchList) {
1819 knownArch[v] = true
1820 }
1821 }
1822
1823
1824 var ToolDir = getToolDir()
1825
1826
1827
1828 func IsLocalImport(path string) bool {
1829 return path == "." || path == ".." ||
1830 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
1831 }
1832
1833
1834
1835
1836
1837
1838 func ArchChar(goarch string) (string, error) {
1839 return "?", errors.New("architecture letter no longer used")
1840 }
1841
View as plain text