github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/please_go_test/gotest/find_cover_vars.go (about) 1 // Package gotest contains utilities used by plz_go_test. 2 package gotest 3 4 import ( 5 "go/build" 6 "io/ioutil" 7 "path" 8 "path/filepath" 9 "strings" 10 11 "gopkg.in/op/go-logging.v1" 12 13 "github.com/thought-machine/please/src/fs" 14 ) 15 16 var log = logging.MustGetLogger("buildgo") 17 18 // A CoverVar is just a combination of package path and variable name 19 // for one of the templated-in coverage variables. 20 type CoverVar struct { 21 Dir, ImportPath, ImportName, Var, File string 22 } 23 24 // replacer is used to replace characters in cover variables. 25 // The scheme here must match what we do in go_rules.build_defs 26 var replacer = strings.NewReplacer( 27 ".", "_", 28 "-", "_", 29 ) 30 31 // FindCoverVars searches the given directory recursively to find all Go files with coverage variables. 32 func FindCoverVars(dir, importPath string, exclude, srcs []string) ([]CoverVar, error) { 33 if dir == "" { 34 return nil, nil 35 } 36 excludeMap := map[string]struct{}{} 37 for _, e := range exclude { 38 excludeMap[e] = struct{}{} 39 } 40 ret := []CoverVar{} 41 42 err := fs.Walk(dir, func(name string, isDir bool) error { 43 if _, present := excludeMap[name]; present { 44 if isDir { 45 return filepath.SkipDir 46 } 47 } else if strings.HasSuffix(name, ".a") && !strings.ContainsRune(path.Base(name), '#') { 48 vars, err := findCoverVars(name, importPath, srcs) 49 if err != nil { 50 return err 51 } 52 for _, v := range vars { 53 ret = append(ret, v) 54 } 55 } 56 return nil 57 }) 58 return ret, err 59 } 60 61 // findCoverVars scans a directory containing a .a file for any go files. 62 func findCoverVars(filepath, importPath string, srcs []string) ([]CoverVar, error) { 63 dir, file := path.Split(filepath) 64 dir = strings.TrimRight(dir, "/") 65 fi, err := ioutil.ReadDir(dir) 66 if err != nil { 67 return nil, err 68 } 69 importPath = collapseFinalDir(strings.TrimSuffix(filepath, ".a"), importPath) 70 ret := make([]CoverVar, 0, len(fi)) 71 for _, info := range fi { 72 name := info.Name() 73 if name != file && strings.HasSuffix(name, ".a") { 74 log.Warning("multiple .a files in %s, can't determine coverage variables accurately", dir) 75 return nil, nil 76 } else if strings.HasSuffix(name, ".go") && !info.IsDir() && !contains(path.Join(dir, name), srcs) { 77 if ok, err := build.Default.MatchFile(dir, name); ok && err == nil { 78 v := "GoCover_" + replacer.Replace(name) 79 ret = append(ret, coverVar(dir, importPath, v)) 80 } 81 } 82 } 83 return ret, nil 84 } 85 86 func contains(needle string, haystack []string) bool { 87 for _, straw := range haystack { 88 if straw == needle { 89 return true 90 } 91 } 92 return false 93 } 94 95 func coverVar(dir, importPath, v string) CoverVar { 96 log.Info("Found cover variable: %s %s %s", dir, importPath, v) 97 f := path.Join(dir, strings.TrimPrefix(v, "GoCover_")) 98 if strings.HasSuffix(f, "_go") { 99 f = f[:len(f)-3] + ".go" 100 } 101 return CoverVar{ 102 Dir: dir, 103 ImportPath: importPath, 104 Var: v, 105 File: f, 106 } 107 } 108 109 // collapseFinalDir mimics what go does with import paths; if the final two components of 110 // the given path are the same (eg. "src/core/core") it collapses them into one ("src/core") 111 // Also if importPath is empty then it trims a leading src/ 112 func collapseFinalDir(s, importPath string) string { 113 if importPath == "" { 114 s = strings.TrimPrefix(s, "src/") 115 } 116 if path.Base(path.Dir(s)) == path.Base(s) { 117 return path.Dir(s) 118 } 119 return s 120 }