github.phpd.cn/thought-machine/please@v12.2.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  	"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  // FindCoverVars searches the given directory recursively to find all Go files with coverage variables.
    25  func FindCoverVars(dir string, exclude, srcs []string) ([]CoverVar, error) {
    26  	if dir == "" {
    27  		return nil, nil
    28  	}
    29  	excludeMap := map[string]struct{}{}
    30  	for _, e := range exclude {
    31  		excludeMap[e] = struct{}{}
    32  	}
    33  	ret := []CoverVar{}
    34  
    35  	err := fs.Walk(dir, func(name string, isDir bool) error {
    36  		if _, present := excludeMap[name]; present {
    37  			if isDir {
    38  				return filepath.SkipDir
    39  			}
    40  		} else if strings.HasSuffix(name, ".a") && !strings.ContainsRune(path.Base(name), '#') {
    41  			vars, err := findCoverVars(name, srcs)
    42  			if err != nil {
    43  				return err
    44  			}
    45  			for _, v := range vars {
    46  				ret = append(ret, v)
    47  			}
    48  		}
    49  		return nil
    50  	})
    51  	return ret, err
    52  }
    53  
    54  // findCoverVars scans a directory containing a .a file for any go files.
    55  func findCoverVars(filepath string, srcs []string) ([]CoverVar, error) {
    56  	dir, file := path.Split(filepath)
    57  	dir = strings.TrimRight(dir, "/")
    58  	fi, err := ioutil.ReadDir(dir)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	importPath := collapseFinalDir(strings.TrimPrefix(strings.TrimSuffix(filepath, ".a"), "src/"))
    63  	ret := make([]CoverVar, 0, len(fi))
    64  	for _, info := range fi {
    65  		name := info.Name()
    66  		if name != file && strings.HasSuffix(name, ".a") {
    67  			log.Warning("multiple .a files in %s, can't determine coverage variables accurately", dir)
    68  			return nil, nil
    69  		} else if strings.HasSuffix(name, ".go") && !info.IsDir() && !contains(path.Join(dir, name), srcs) {
    70  			if ok, err := build.Default.MatchFile(dir, name); ok && err == nil {
    71  				// N.B. The scheme here must match what we do in go_rules.build_defs
    72  				v := "GoCover_" + strings.Replace(name, ".", "_", -1)
    73  				ret = append(ret, coverVar(dir, importPath, v))
    74  			}
    75  		}
    76  	}
    77  	return ret, nil
    78  }
    79  
    80  func contains(needle string, haystack []string) bool {
    81  	for _, straw := range haystack {
    82  		if straw == needle {
    83  			return true
    84  		}
    85  	}
    86  	return false
    87  }
    88  
    89  func coverVar(dir, importPath, v string) CoverVar {
    90  	log.Info("Found cover variable: %s %s %s", dir, importPath, v)
    91  	f := path.Join(dir, strings.TrimPrefix(v, "GoCover_"))
    92  	if strings.HasSuffix(f, "_go") {
    93  		f = f[:len(f)-3] + ".go"
    94  	}
    95  	return CoverVar{
    96  		Dir:        dir,
    97  		ImportPath: importPath,
    98  		Var:        v,
    99  		File:       f,
   100  	}
   101  }
   102  
   103  // collapseFinalDir mimics what go does with import paths; if the final two components of
   104  // the given path are the same (eg. "src/core/core") it collapses them into one ("src/core")
   105  func collapseFinalDir(s string) string {
   106  	if path.Base(path.Dir(s)) == path.Base(s) {
   107  		return path.Dir(s)
   108  	}
   109  	return s
   110  }