OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package build | 5 package build |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "errors" | 9 "errors" |
10 "fmt" | 10 "fmt" |
11 "go/ast" | 11 "go/ast" |
12 "go/doc" | 12 "go/doc" |
13 "go/parser" | 13 "go/parser" |
14 "go/token" | 14 "go/token" |
15 "io" | 15 "io" |
16 "io/ioutil" | 16 "io/ioutil" |
17 "log" | 17 "log" |
18 "os" | 18 "os" |
19 pathpkg "path" | 19 pathpkg "path" |
20 "path/filepath" | 20 "path/filepath" |
21 "runtime" | 21 "runtime" |
22 "sort" | 22 "sort" |
23 "strconv" | 23 "strconv" |
24 "strings" | 24 "strings" |
25 "unicode" | 25 "unicode" |
| 26 "unicode/utf8" |
26 ) | 27 ) |
27 | 28 |
28 // A Context specifies the supporting context for a build. | 29 // A Context specifies the supporting context for a build. |
29 type Context struct { | 30 type Context struct { |
30 GOARCH string // target architecture | 31 GOARCH string // target architecture |
31 GOOS string // target operating system | 32 GOOS string // target operating system |
32 GOROOT string // Go root | 33 GOROOT string // Go root |
33 GOPATH string // Go path | 34 GOPATH string // Go path |
34 CgoEnabled bool // whether cgo can be used | 35 CgoEnabled bool // whether cgo can be used |
35 UseAllFiles bool // use files regardless of +build lines, file names | 36 UseAllFiles bool // use files regardless of +build lines, file names |
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 | 331 |
331 const ( | 332 const ( |
332 // If FindOnly is set, Import stops after locating the directory | 333 // If FindOnly is set, Import stops after locating the directory |
333 // that should contain the sources for a package. It does not | 334 // that should contain the sources for a package. It does not |
334 // read any files in the directory. | 335 // read any files in the directory. |
335 FindOnly ImportMode = 1 << iota | 336 FindOnly ImportMode = 1 << iota |
336 | 337 |
337 // If AllowBinary is set, Import can be satisfied by a compiled | 338 // If AllowBinary is set, Import can be satisfied by a compiled |
338 // package object without corresponding sources. | 339 // package object without corresponding sources. |
339 AllowBinary | 340 AllowBinary |
| 341 |
| 342 // If ImportComment is set, parse import comments on package statements. |
| 343 // Import returns an error if it finds a comment it cannot understand |
| 344 // or finds conflicting comments in multiple source files. |
| 345 // See golang.org/s/go14customimport for more information. |
| 346 ImportComment |
340 ) | 347 ) |
341 | 348 |
342 // A Package describes the Go package found in a directory. | 349 // A Package describes the Go package found in a directory. |
343 type Package struct { | 350 type Package struct { |
344 » Dir string // directory containing package sources | 351 » Dir string // directory containing package sources |
345 » Name string // package name | 352 » Name string // package name |
346 » Doc string // documentation synopsis | 353 » ImportComment string // path in import comment on package statement |
347 » ImportPath string // import path of package ("" if unknown) | 354 » Doc string // documentation synopsis |
348 » Root string // root of Go tree where this package lives | 355 » ImportPath string // import path of package ("" if unknown) |
349 » SrcRoot string // package source root directory ("" if unknown) | 356 » Root string // root of Go tree where this package lives |
350 » PkgRoot string // package install root directory ("" if unknown) | 357 » SrcRoot string // package source root directory ("" if unknown) |
351 » BinDir string // command install directory ("" if unknown) | 358 » PkgRoot string // package install root directory ("" if unknown) |
352 » Goroot bool // package found in Go root | 359 » BinDir string // command install directory ("" if unknown) |
353 » PkgObj string // installed .a file | 360 » Goroot bool // package found in Go root |
354 » AllTags []string // tags that can influence file selection in this d
irectory | 361 » PkgObj string // installed .a file |
355 » ConflictDir string // this directory shadows Dir in $GOPATH | 362 » AllTags []string // tags that can influence file selection in this
directory |
| 363 » ConflictDir string // this directory shadows Dir in $GOPATH |
356 | 364 |
357 // Source files | 365 // Source files |
358 GoFiles []string // .go source files (excluding CgoFiles, TestGoF
iles, XTestGoFiles) | 366 GoFiles []string // .go source files (excluding CgoFiles, TestGoF
iles, XTestGoFiles) |
359 CgoFiles []string // .go source files that import "C" | 367 CgoFiles []string // .go source files that import "C" |
360 IgnoredGoFiles []string // .go source files ignored for this build | 368 IgnoredGoFiles []string // .go source files ignored for this build |
361 CFiles []string // .c source files | 369 CFiles []string // .c source files |
362 CXXFiles []string // .cc, .cpp and .cxx source files | 370 CXXFiles []string // .cc, .cpp and .cxx source files |
363 MFiles []string // .m (Objective-C) source files | 371 MFiles []string // .m (Objective-C) source files |
364 HFiles []string // .h, .hh, .hpp and .hxx source files | 372 HFiles []string // .h, .hh, .hpp and .hxx source files |
365 SFiles []string // .s source files | 373 SFiles []string // .s source files |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
514 } | 522 } |
515 | 523 |
516 // tried records the location of unsuccessful package lookups | 524 // tried records the location of unsuccessful package lookups |
517 var tried struct { | 525 var tried struct { |
518 goroot string | 526 goroot string |
519 gopath []string | 527 gopath []string |
520 } | 528 } |
521 | 529 |
522 // Determine directory from import path. | 530 // Determine directory from import path. |
523 if ctxt.GOROOT != "" { | 531 if ctxt.GOROOT != "" { |
524 » » » dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path) | 532 » » » var dir string |
| 533 » » » if strings.HasPrefix(path, "cmd/") { |
| 534 » » » » dir = ctxt.joinPath(ctxt.GOROOT, "src", path) |
| 535 » » » } else { |
| 536 » » » » dir = ctxt.joinPath(ctxt.GOROOT, "src", "pkg", p
ath) |
| 537 » » » } |
525 isDir := ctxt.isDir(dir) | 538 isDir := ctxt.isDir(dir) |
526 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga !=
"" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) | 539 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga !=
"" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) |
527 if isDir || binaryOnly { | 540 if isDir || binaryOnly { |
528 p.Dir = dir | 541 p.Dir = dir |
529 p.Goroot = true | 542 p.Goroot = true |
530 p.Root = ctxt.GOROOT | 543 p.Root = ctxt.GOROOT |
531 goto Found | 544 goto Found |
532 } | 545 } |
533 tried.goroot = dir | 546 tried.goroot = dir |
534 } | 547 } |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
585 if binaryOnly && (mode&AllowBinary) != 0 { | 598 if binaryOnly && (mode&AllowBinary) != 0 { |
586 return p, pkgerr | 599 return p, pkgerr |
587 } | 600 } |
588 | 601 |
589 dirs, err := ctxt.readDir(p.Dir) | 602 dirs, err := ctxt.readDir(p.Dir) |
590 if err != nil { | 603 if err != nil { |
591 return p, err | 604 return p, err |
592 } | 605 } |
593 | 606 |
594 var Sfiles []string // files with ".S" (capital S) | 607 var Sfiles []string // files with ".S" (capital S) |
595 » var firstFile string | 608 » var firstFile, firstCommentFile string |
596 imported := make(map[string][]token.Position) | 609 imported := make(map[string][]token.Position) |
597 testImported := make(map[string][]token.Position) | 610 testImported := make(map[string][]token.Position) |
598 xTestImported := make(map[string][]token.Position) | 611 xTestImported := make(map[string][]token.Position) |
599 allTags := make(map[string]bool) | 612 allTags := make(map[string]bool) |
600 fset := token.NewFileSet() | 613 fset := token.NewFileSet() |
601 for _, d := range dirs { | 614 for _, d := range dirs { |
602 if d.IsDir() { | 615 if d.IsDir() { |
603 continue | 616 continue |
604 } | 617 } |
605 | 618 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
672 if p.Name == "" { | 685 if p.Name == "" { |
673 p.Name = pkg | 686 p.Name = pkg |
674 firstFile = name | 687 firstFile = name |
675 } else if pkg != p.Name { | 688 } else if pkg != p.Name { |
676 return p, fmt.Errorf("found packages %s (%s) and %s (%s)
in %s", p.Name, firstFile, pkg, name, p.Dir) | 689 return p, fmt.Errorf("found packages %s (%s) and %s (%s)
in %s", p.Name, firstFile, pkg, name, p.Dir) |
677 } | 690 } |
678 if pf.Doc != nil && p.Doc == "" { | 691 if pf.Doc != nil && p.Doc == "" { |
679 p.Doc = doc.Synopsis(pf.Doc.Text()) | 692 p.Doc = doc.Synopsis(pf.Doc.Text()) |
680 } | 693 } |
681 | 694 |
| 695 if mode&ImportComment != 0 { |
| 696 qcom, line := findImportComment(data) |
| 697 if line != 0 { |
| 698 com, err := strconv.Unquote(qcom) |
| 699 if err != nil { |
| 700 return p, fmt.Errorf("%s:%d: cannot pars
e import comment", filename, line) |
| 701 } |
| 702 if p.ImportComment == "" { |
| 703 p.ImportComment = com |
| 704 firstCommentFile = name |
| 705 } else if p.ImportComment != com { |
| 706 return p, fmt.Errorf("found import comme
nts %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.
Dir) |
| 707 } |
| 708 } |
| 709 } |
| 710 |
682 // Record imports and information about cgo. | 711 // Record imports and information about cgo. |
683 isCgo := false | 712 isCgo := false |
684 for _, decl := range pf.Decls { | 713 for _, decl := range pf.Decls { |
685 d, ok := decl.(*ast.GenDecl) | 714 d, ok := decl.(*ast.GenDecl) |
686 if !ok { | 715 if !ok { |
687 continue | 716 continue |
688 } | 717 } |
689 for _, dspec := range d.Specs { | 718 for _, dspec := range d.Specs { |
690 spec, ok := dspec.(*ast.ImportSpec) | 719 spec, ok := dspec.(*ast.ImportSpec) |
691 if !ok { | 720 if !ok { |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
752 // (which means gcc will compile them). | 781 // (which means gcc will compile them). |
753 // The standard assemblers expect .s files. | 782 // The standard assemblers expect .s files. |
754 if len(p.CgoFiles) > 0 { | 783 if len(p.CgoFiles) > 0 { |
755 p.SFiles = append(p.SFiles, Sfiles...) | 784 p.SFiles = append(p.SFiles, Sfiles...) |
756 sort.Strings(p.SFiles) | 785 sort.Strings(p.SFiles) |
757 } | 786 } |
758 | 787 |
759 return p, pkgerr | 788 return p, pkgerr |
760 } | 789 } |
761 | 790 |
| 791 func findImportComment(data []byte) (s string, line int) { |
| 792 // expect keyword package |
| 793 word, data := parseWord(data) |
| 794 if string(word) != "package" { |
| 795 return "", 0 |
| 796 } |
| 797 |
| 798 // expect package name |
| 799 _, data = parseWord(data) |
| 800 |
| 801 // now ready for import comment, a // or /* */ comment |
| 802 // beginning and ending on the current line. |
| 803 for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\
r') { |
| 804 data = data[1:] |
| 805 } |
| 806 |
| 807 var comment []byte |
| 808 switch { |
| 809 case bytes.HasPrefix(data, slashSlash): |
| 810 i := bytes.Index(data, newline) |
| 811 if i < 0 { |
| 812 i = len(data) |
| 813 } |
| 814 comment = data[2:i] |
| 815 case bytes.HasPrefix(data, slashStar): |
| 816 data = data[2:] |
| 817 i := bytes.Index(data, starSlash) |
| 818 if i < 0 { |
| 819 // malformed comment |
| 820 return "", 0 |
| 821 } |
| 822 comment = data[:i] |
| 823 if bytes.Contains(comment, newline) { |
| 824 return "", 0 |
| 825 } |
| 826 } |
| 827 comment = bytes.TrimSpace(comment) |
| 828 |
| 829 // split comment into `import`, `"pkg"` |
| 830 word, arg := parseWord(comment) |
| 831 if string(word) != "import" { |
| 832 return "", 0 |
| 833 } |
| 834 |
| 835 line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline) |
| 836 return strings.TrimSpace(string(arg)), line |
| 837 } |
| 838 |
| 839 var ( |
| 840 slashSlash = []byte("//") |
| 841 slashStar = []byte("/*") |
| 842 starSlash = []byte("*/") |
| 843 newline = []byte("\n") |
| 844 ) |
| 845 |
| 846 // skipSpaceOrComment returns data with any leading spaces or comments removed. |
| 847 func skipSpaceOrComment(data []byte) []byte { |
| 848 for len(data) > 0 { |
| 849 switch data[0] { |
| 850 case ' ', '\t', '\r', '\n': |
| 851 data = data[1:] |
| 852 continue |
| 853 case '/': |
| 854 if bytes.HasPrefix(data, slashSlash) { |
| 855 i := bytes.Index(data, newline) |
| 856 if i < 0 { |
| 857 return nil |
| 858 } |
| 859 data = data[i+1:] |
| 860 continue |
| 861 } |
| 862 if bytes.HasPrefix(data, slashStar) { |
| 863 data = data[2:] |
| 864 i := bytes.Index(data, starSlash) |
| 865 if i < 0 { |
| 866 return nil |
| 867 } |
| 868 data = data[i+2:] |
| 869 continue |
| 870 } |
| 871 } |
| 872 break |
| 873 } |
| 874 return data |
| 875 } |
| 876 |
| 877 // parseWord skips any leading spaces or comments in data |
| 878 // and then parses the beginning of data as an identifier or keyword, |
| 879 // returning that word and what remains after the word. |
| 880 func parseWord(data []byte) (word, rest []byte) { |
| 881 data = skipSpaceOrComment(data) |
| 882 |
| 883 // Parse past leading word characters. |
| 884 rest = data |
| 885 for { |
| 886 r, size := utf8.DecodeRune(rest) |
| 887 if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' { |
| 888 rest = rest[size:] |
| 889 continue |
| 890 } |
| 891 break |
| 892 } |
| 893 |
| 894 word = data[:len(data)-len(rest)] |
| 895 if len(word) == 0 { |
| 896 return nil, nil |
| 897 } |
| 898 |
| 899 return word, rest |
| 900 } |
| 901 |
762 // MatchFile reports whether the file with the given name in the given directory | 902 // MatchFile reports whether the file with the given name in the given directory |
763 // matches the context and would be included in a Package created by ImportDir | 903 // matches the context and would be included in a Package created by ImportDir |
764 // of that directory. | 904 // of that directory. |
765 // | 905 // |
766 // MatchFile considers the name of the file and may use ctxt.OpenFile to | 906 // MatchFile considers the name of the file and may use ctxt.OpenFile to |
767 // read some or all of the file's content. | 907 // read some or all of the file's content. |
768 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { | 908 func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { |
769 match, _, _, err = ctxt.matchFile(dir, name, false, nil) | 909 match, _, _, err = ctxt.matchFile(dir, name, false, nil) |
770 return | 910 return |
771 } | 911 } |
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1228 return "8", nil | 1368 return "8", nil |
1229 case "amd64", "amd64p32": | 1369 case "amd64", "amd64p32": |
1230 return "6", nil | 1370 return "6", nil |
1231 case "arm": | 1371 case "arm": |
1232 return "5", nil | 1372 return "5", nil |
1233 case "power64", "power64le": | 1373 case "power64", "power64le": |
1234 return "9", nil | 1374 return "9", nil |
1235 } | 1375 } |
1236 return "", errors.New("unsupported GOARCH " + goarch) | 1376 return "", errors.New("unsupported GOARCH " + goarch) |
1237 } | 1377 } |
OLD | NEW |