| package buildutil |
| |
| import ( |
| "fmt" |
| "go/build" |
| "io" |
| "io/ioutil" |
| "os" |
| "path" |
| "path/filepath" |
| "sort" |
| "strings" |
| "time" |
| ) |
| |
| // FakeContext returns a build.Context for the fake file tree specified |
| // by pkgs, which maps package import paths to a mapping from file base |
| // names to contents. |
| // |
| // The fake Context has a GOROOT of "/go" and no GOPATH, and overrides |
| // the necessary file access methods to read from memory instead of the |
| // real file system. |
| // |
| // Unlike a real file tree, the fake one has only two levels---packages |
| // and files---so ReadDir("/go/src/") returns all packages under |
| // /go/src/ including, for instance, "math" and "math/big". |
| // ReadDir("/go/src/math/big") would return all the files in the |
| // "math/big" package. |
| // |
| func FakeContext(pkgs map[string]map[string]string) *build.Context { |
| clean := func(filename string) string { |
| f := path.Clean(filepath.ToSlash(filename)) |
| // Removing "/go/src" while respecting segment |
| // boundaries has this unfortunate corner case: |
| if f == "/go/src" { |
| return "" |
| } |
| return strings.TrimPrefix(f, "/go/src/") |
| } |
| |
| ctxt := build.Default // copy |
| ctxt.GOROOT = "/go" |
| ctxt.GOPATH = "" |
| ctxt.IsDir = func(dir string) bool { |
| dir = clean(dir) |
| if dir == "" { |
| return true // needed by (*build.Context).SrcDirs |
| } |
| return pkgs[dir] != nil |
| } |
| ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) { |
| dir = clean(dir) |
| var fis []os.FileInfo |
| if dir == "" { |
| // enumerate packages |
| for importPath := range pkgs { |
| fis = append(fis, fakeDirInfo(importPath)) |
| } |
| } else { |
| // enumerate files of package |
| for basename := range pkgs[dir] { |
| fis = append(fis, fakeFileInfo(basename)) |
| } |
| } |
| sort.Sort(byName(fis)) |
| return fis, nil |
| } |
| ctxt.OpenFile = func(filename string) (io.ReadCloser, error) { |
| filename = clean(filename) |
| dir, base := path.Split(filename) |
| content, ok := pkgs[path.Clean(dir)][base] |
| if !ok { |
| return nil, fmt.Errorf("file not found: %s", filename) |
| } |
| return ioutil.NopCloser(strings.NewReader(content)), nil |
| } |
| ctxt.IsAbsPath = func(path string) bool { |
| path = filepath.ToSlash(path) |
| // Don't rely on the default (filepath.Path) since on |
| // Windows, it reports virtual paths as non-absolute. |
| return strings.HasPrefix(path, "/") |
| } |
| return &ctxt |
| } |
| |
| type byName []os.FileInfo |
| |
| func (s byName) Len() int { return len(s) } |
| func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } |
| |
| type fakeFileInfo string |
| |
| func (fi fakeFileInfo) Name() string { return string(fi) } |
| func (fakeFileInfo) Sys() interface{} { return nil } |
| func (fakeFileInfo) ModTime() time.Time { return time.Time{} } |
| func (fakeFileInfo) IsDir() bool { return false } |
| func (fakeFileInfo) Size() int64 { return 0 } |
| func (fakeFileInfo) Mode() os.FileMode { return 0644 } |
| |
| type fakeDirInfo string |
| |
| func (fd fakeDirInfo) Name() string { return string(fd) } |
| func (fakeDirInfo) Sys() interface{} { return nil } |
| func (fakeDirInfo) ModTime() time.Time { return time.Time{} } |
| func (fakeDirInfo) IsDir() bool { return true } |
| func (fakeDirInfo) Size() int64 { return 0 } |
| func (fakeDirInfo) Mode() os.FileMode { return 0755 } |