github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/buildutil/fakecontext.go (about) 1 // Copyright 2015 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 buildutil 6 7 import ( 8 "fmt" 9 "go/build" 10 "io" 11 "io/ioutil" 12 "os" 13 "path" 14 "path/filepath" 15 "sort" 16 "strings" 17 "time" 18 ) 19 20 // FakeContext returns a build.Context for the fake file tree specified 21 // by pkgs, which maps package import paths to a mapping from file base 22 // names to contents. 23 // 24 // The fake Context has a GOROOT of "/go" and no GOPATH, and overrides 25 // the necessary file access methods to read from memory instead of the 26 // real file system. 27 // 28 // Unlike a real file tree, the fake one has only two levels---packages 29 // and files---so ReadDir("/go/src/") returns all packages under 30 // /go/src/ including, for instance, "math" and "math/big". 31 // ReadDir("/go/src/math/big") would return all the files in the 32 // "math/big" package. 33 func FakeContext(pkgs map[string]map[string]string) *build.Context { 34 clean := func(filename string) string { 35 f := path.Clean(filepath.ToSlash(filename)) 36 // Removing "/go/src" while respecting segment 37 // boundaries has this unfortunate corner case: 38 if f == "/go/src" { 39 return "" 40 } 41 return strings.TrimPrefix(f, "/go/src/") 42 } 43 44 ctxt := build.Default // copy 45 ctxt.GOROOT = "/go" 46 ctxt.GOPATH = "" 47 ctxt.Compiler = "gc" 48 ctxt.IsDir = func(dir string) bool { 49 dir = clean(dir) 50 if dir == "" { 51 return true // needed by (*build.Context).SrcDirs 52 } 53 return pkgs[dir] != nil 54 } 55 ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) { 56 dir = clean(dir) 57 var fis []os.FileInfo 58 if dir == "" { 59 // enumerate packages 60 for importPath := range pkgs { 61 fis = append(fis, fakeDirInfo(importPath)) 62 } 63 } else { 64 // enumerate files of package 65 for basename := range pkgs[dir] { 66 fis = append(fis, fakeFileInfo(basename)) 67 } 68 } 69 sort.Sort(byName(fis)) 70 return fis, nil 71 } 72 ctxt.OpenFile = func(filename string) (io.ReadCloser, error) { 73 filename = clean(filename) 74 dir, base := path.Split(filename) 75 content, ok := pkgs[path.Clean(dir)][base] 76 if !ok { 77 return nil, fmt.Errorf("file not found: %s", filename) 78 } 79 return ioutil.NopCloser(strings.NewReader(content)), nil 80 } 81 ctxt.IsAbsPath = func(path string) bool { 82 path = filepath.ToSlash(path) 83 // Don't rely on the default (filepath.Path) since on 84 // Windows, it reports virtual paths as non-absolute. 85 return strings.HasPrefix(path, "/") 86 } 87 return &ctxt 88 } 89 90 type byName []os.FileInfo 91 92 func (s byName) Len() int { return len(s) } 93 func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 94 func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } 95 96 type fakeFileInfo string 97 98 func (fi fakeFileInfo) Name() string { return string(fi) } 99 func (fakeFileInfo) Sys() interface{} { return nil } 100 func (fakeFileInfo) ModTime() time.Time { return time.Time{} } 101 func (fakeFileInfo) IsDir() bool { return false } 102 func (fakeFileInfo) Size() int64 { return 0 } 103 func (fakeFileInfo) Mode() os.FileMode { return 0644 } 104 105 type fakeDirInfo string 106 107 func (fd fakeDirInfo) Name() string { return string(fd) } 108 func (fakeDirInfo) Sys() interface{} { return nil } 109 func (fakeDirInfo) ModTime() time.Time { return time.Time{} } 110 func (fakeDirInfo) IsDir() bool { return true } 111 func (fakeDirInfo) Size() int64 { return 0 } 112 func (fakeDirInfo) Mode() os.FileMode { return 0755 }