github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/go/internal/gcimporter/gcimporter.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package gcimporter implements Import for gc-generated object files. 6 package gcimporter // import "go/internal/gcimporter" 7 8 import ( 9 "bufio" 10 "fmt" 11 "go/build" 12 "go/types" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "strings" 17 ) 18 19 // debugging/development support 20 const debug = false 21 22 var pkgExts = [...]string{".a", ".o"} 23 24 // FindPkg returns the filename and unique package id for an import 25 // path based on package information provided by build.Import (using 26 // the build.Default build.Context). A relative srcDir is interpreted 27 // relative to the current working directory. 28 // If no file was found, an empty filename is returned. 29 // 30 func FindPkg(path, srcDir string) (filename, id string) { 31 if path == "" { 32 return 33 } 34 35 var noext string 36 switch { 37 default: 38 // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" 39 // Don't require the source files to be present. 40 if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 41 srcDir = abs 42 } 43 bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) 44 if bp.PkgObj == "" { 45 return 46 } 47 noext = strings.TrimSuffix(bp.PkgObj, ".a") 48 id = bp.ImportPath 49 50 case build.IsLocalImport(path): 51 // "./x" -> "/this/directory/x.ext", "/this/directory/x" 52 noext = filepath.Join(srcDir, path) 53 id = noext 54 55 case filepath.IsAbs(path): 56 // for completeness only - go/build.Import 57 // does not support absolute imports 58 // "/x" -> "/x.ext", "/x" 59 noext = path 60 id = path 61 } 62 63 if false { // for debugging 64 if path != id { 65 fmt.Printf("%s -> %s\n", path, id) 66 } 67 } 68 69 // try extensions 70 for _, ext := range pkgExts { 71 filename = noext + ext 72 if f, err := os.Stat(filename); err == nil && !f.IsDir() { 73 return 74 } 75 } 76 77 filename = "" // not found 78 return 79 } 80 81 // Import imports a gc-generated package given its import path and srcDir, adds 82 // the corresponding package object to the packages map, and returns the object. 83 // The packages map must contain all packages already imported. 84 // 85 func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) { 86 filename, id := FindPkg(path, srcDir) 87 if filename == "" { 88 if path == "unsafe" { 89 return types.Unsafe, nil 90 } 91 err = fmt.Errorf("can't find import: %s", id) 92 return 93 } 94 95 // no need to re-import if the package was imported completely before 96 if pkg = packages[id]; pkg != nil && pkg.Complete() { 97 return 98 } 99 100 // open file 101 f, err := os.Open(filename) 102 if err != nil { 103 return 104 } 105 defer func() { 106 f.Close() 107 if err != nil { 108 // add file name to error 109 err = fmt.Errorf("%s: %v", filename, err) 110 } 111 }() 112 113 var hdr string 114 buf := bufio.NewReader(f) 115 if hdr, err = FindExportData(buf); err != nil { 116 return 117 } 118 119 switch hdr { 120 case "$$\n": 121 err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path) 122 case "$$B\n": 123 var data []byte 124 data, err = ioutil.ReadAll(buf) 125 if err == nil { 126 _, pkg, err = BImportData(packages, data, id) 127 return 128 } 129 default: 130 err = fmt.Errorf("unknown export data header: %q", hdr) 131 } 132 133 return 134 } 135 136 func deref(typ types.Type) types.Type { 137 if p, _ := typ.(*types.Pointer); p != nil { 138 return p.Elem() 139 } 140 return typ 141 } 142 143 type byPath []*types.Package 144 145 func (a byPath) Len() int { return len(a) } 146 func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 147 func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }