github.com/0xKiwi/rules_go@v0.24.3/go/tools/builders/cover.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "bytes" 19 "flag" 20 "fmt" 21 "go/format" 22 "go/parser" 23 "go/token" 24 "io/ioutil" 25 "strconv" 26 ) 27 28 // cover transforms a source file with "go tool cover". It is invoked by the 29 // Go rules as an action. 30 func cover(args []string) error { 31 args, err := expandParamsFiles(args) 32 if err != nil { 33 return err 34 } 35 flags := flag.NewFlagSet("cover", flag.ExitOnError) 36 var coverSrc, coverVar, origSrc, srcName, mode string 37 flags.StringVar(&coverSrc, "o", "", "coverage output file") 38 flags.StringVar(&coverVar, "var", "", "name of cover variable") 39 flags.StringVar(&origSrc, "src", "", "original source file") 40 flags.StringVar(&srcName, "srcname", "", "source name printed in coverage data") 41 flags.StringVar(&mode, "mode", "set", "coverage mode to use") 42 goenv := envFlags(flags) 43 if err := flags.Parse(args); err != nil { 44 return err 45 } 46 if err := goenv.checkFlags(); err != nil { 47 return err 48 } 49 if coverSrc == "" { 50 return fmt.Errorf("-o was not set") 51 } 52 if coverVar == "" { 53 return fmt.Errorf("-var was not set") 54 } 55 if origSrc == "" { 56 return fmt.Errorf("-src was not set") 57 } 58 if srcName == "" { 59 srcName = origSrc 60 } 61 62 return instrumentForCoverage(goenv, origSrc, srcName, coverVar, mode, coverSrc) 63 } 64 65 // instrumentForCoverage runs "go tool cover" on a source file to produce 66 // a coverage-instrumented version of the file. It also registers the file 67 // with the coverdata package. 68 func instrumentForCoverage(goenv *env, srcPath, srcName, coverVar, mode, outPath string) error { 69 goargs := goenv.goTool("cover", "-var", coverVar, "-mode", mode, "-o", outPath, srcPath) 70 if err := goenv.runCommand(goargs); err != nil { 71 return err 72 } 73 74 return registerCoverage(outPath, coverVar, srcName) 75 } 76 77 // registerCoverage modifies coverSrc, the output file from go tool cover. It 78 // adds a call to coverdata.RegisterCoverage, which ensures the coverage 79 // data from each file is reported. The name by which the file is registered 80 // need not match its original name (it may use the importpath). 81 func registerCoverage(coverSrc, varName, srcName string) error { 82 // Parse the file. 83 fset := token.NewFileSet() 84 f, err := parser.ParseFile(fset, coverSrc, nil, parser.ParseComments) 85 if err != nil { 86 return nil // parse error: proceed and let the compiler fail 87 } 88 89 // Ensure coverdata is imported in the AST. Use an existing import if present 90 // or add a new one. 91 const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata" 92 var coverdataName string 93 for _, imp := range f.Imports { 94 path, err := strconv.Unquote(imp.Path.Value) 95 if err != nil { 96 return nil // parse error: proceed and let the compiler fail 97 } 98 if path == coverdataPath { 99 if imp.Name != nil { 100 // renaming import 101 if imp.Name.Name == "_" { 102 // Change blank import to named import 103 imp.Name.Name = "coverdata" 104 } 105 coverdataName = imp.Name.Name 106 } else { 107 // default import 108 coverdataName = "coverdata" 109 } 110 break 111 } 112 } 113 if coverdataName == "" { 114 // No existing import. Add a new one. 115 coverdataName = "coverdata" 116 addNamedImport(fset, f, coverdataName, coverdataPath) 117 } 118 var buf bytes.Buffer 119 if err := format.Node(&buf, fset, f); err != nil { 120 return fmt.Errorf("registerCoverage: could not reformat coverage source %s: %v", coverSrc, err) 121 } 122 123 // Append an init function. 124 fmt.Fprintf(&buf, ` 125 func init() { 126 %s.RegisterFile(%q, 127 %[3]s.Count[:], 128 %[3]s.Pos[:], 129 %[3]s.NumStmt[:]) 130 } 131 `, coverdataName, srcName, varName) 132 if err := ioutil.WriteFile(coverSrc, buf.Bytes(), 0666); err != nil { 133 return fmt.Errorf("registerCoverage: %v", err) 134 } 135 136 return nil 137 }