...

Source file src/github.com/bazelbuild/rules_go/go/tools/builders/cover.go

Documentation: github.com/bazelbuild/rules_go/go/tools/builders

     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  	"fmt"
    20  	"go/parser"
    21  	"go/token"
    22  	"io/ioutil"
    23  	"os"
    24  	"strconv"
    25  )
    26  
    27  // instrumentForCoverage runs "go tool cover" on a source file to produce
    28  // a coverage-instrumented version of the file. It also registers the file
    29  // with the coverdata package.
    30  func instrumentForCoverage(goenv *env, srcPath, srcName, coverVar, mode, outPath string) error {
    31  	goargs := goenv.goTool("cover", "-var", coverVar, "-mode", mode, "-o", outPath, srcPath)
    32  	if err := goenv.runCommand(goargs); err != nil {
    33  		return err
    34  	}
    35  
    36  	return registerCoverage(outPath, coverVar, srcName)
    37  }
    38  
    39  // registerCoverage modifies coverSrcFilename, the output file from go tool cover.
    40  // It adds a call to coverdata.RegisterCoverage, which ensures the coverage
    41  // data from each file is reported. The name by which the file is registered
    42  // need not match its original name (it may use the importpath).
    43  func registerCoverage(coverSrcFilename, varName, srcName string) error {
    44  	coverSrc, err := os.ReadFile(coverSrcFilename)
    45  	if err != nil {
    46  		return fmt.Errorf("instrumentForCoverage: reading instrumented source: %w", err)
    47  	}
    48  
    49  	// Parse the file.
    50  	fset := token.NewFileSet()
    51  	f, err := parser.ParseFile(fset, coverSrcFilename, coverSrc, parser.ParseComments)
    52  	if err != nil {
    53  		return nil // parse error: proceed and let the compiler fail
    54  	}
    55  
    56  	// Perform edits using a byte buffer instead of the AST, because
    57  	// we can not use go/format to write the AST back out without
    58  	// changing line numbers.
    59  	editor := NewBuffer(coverSrc)
    60  
    61  	// Ensure coverdata is imported. Use an existing import if present
    62  	// or add a new one.
    63  	const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata"
    64  	var coverdataName string
    65  	for _, imp := range f.Imports {
    66  		path, err := strconv.Unquote(imp.Path.Value)
    67  		if err != nil {
    68  			return nil // parse error: proceed and let the compiler fail
    69  		}
    70  		if path == coverdataPath {
    71  			if imp.Name != nil {
    72  				// renaming import
    73  				if imp.Name.Name == "_" {
    74  					// Change blank import to named import
    75  					editor.Replace(
    76  						fset.Position(imp.Name.Pos()).Offset,
    77  						fset.Position(imp.Name.End()).Offset,
    78  						"coverdata")
    79  					coverdataName = "coverdata"
    80  				} else {
    81  					coverdataName = imp.Name.Name
    82  				}
    83  			} else {
    84  				// default import
    85  				coverdataName = "coverdata"
    86  			}
    87  			break
    88  		}
    89  	}
    90  	if coverdataName == "" {
    91  		// No existing import. Add a new one.
    92  		coverdataName = "coverdata"
    93  		editor.Insert(fset.Position(f.Name.End()).Offset, fmt.Sprintf("; import %q", coverdataPath))
    94  	}
    95  
    96  	// Append an init function.
    97  	var buf = bytes.NewBuffer(editor.Bytes())
    98  	fmt.Fprintf(buf, `
    99  func init() {
   100  	%s.RegisterFile(%q,
   101  		%[3]s.Count[:],
   102  		%[3]s.Pos[:],
   103  		%[3]s.NumStmt[:])
   104  }
   105  `, coverdataName, srcName, varName)
   106  	if err := ioutil.WriteFile(coverSrcFilename, buf.Bytes(), 0666); err != nil {
   107  		return fmt.Errorf("registerCoverage: %v", err)
   108  	}
   109  	return nil
   110  }
   111  

View as plain text