Merge "Support cc code coverage for mixed build" am: 57c1edc4d9

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/2103671

Change-Id: I1e51589b6ad47517e0f277fb440a89bd8b11e3ca
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Yu Liu 2022-06-13 19:47:45 +00:00 committed by Automerger Merge Worker
commit 56868c621b
7 changed files with 101 additions and 13 deletions

View file

@ -111,7 +111,7 @@ type BazelContext interface {
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
InvokeBazel() error
InvokeBazel(config Config) error
// Returns true if bazel is enabled for the given configuration.
BazelEnabled() bool
@ -191,7 +191,7 @@ func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (strin
return result, nil
}
func (m MockBazelContext) InvokeBazel() error {
func (m MockBazelContext) InvokeBazel(config Config) error {
panic("unimplemented")
}
@ -261,7 +261,7 @@ func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (strin
panic("unimplemented")
}
func (n noopBazelContext) InvokeBazel() error {
func (n noopBazelContext) InvokeBazel(config Config) error {
panic("unimplemented")
}
@ -361,6 +361,7 @@ type bazelCommand struct {
type mockBazelRunner struct {
bazelCommandResults map[bazelCommand]string
commands []bazelCommand
extraFlags []string
}
func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
@ -368,6 +369,7 @@ func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
command bazelCommand,
extraFlags ...string) (string, string, error) {
r.commands = append(r.commands, command)
r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
if ret, ok := r.bazelCommandResults[command]; ok {
return ret, "", nil
}
@ -676,7 +678,7 @@ func (p *bazelPaths) outDir() string {
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
func (context *bazelContext) InvokeBazel() error {
func (context *bazelContext) InvokeBazel(config Config) error {
context.results = make(map[cqueryKey]string)
var cqueryOutput string
@ -759,15 +761,31 @@ func (context *bazelContext) InvokeBazel() error {
// Issue an aquery command to retrieve action information about the bazel build tree.
//
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
var aqueryOutput string
var coverageFlags []string
if Bool(config.productVariables.ClangCoverage) {
coverageFlags = append(coverageFlags, "--collect_code_coverage")
if len(config.productVariables.NativeCoveragePaths) > 0 ||
len(config.productVariables.NativeCoverageExcludePaths) > 0 {
includePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoveragePaths, "+", ",")
excludePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoverageExcludePaths, "-", ",")
if len(includePaths) > 0 && len(excludePaths) > 0 {
includePaths += ","
}
coverageFlags = append(coverageFlags, fmt.Sprintf(`--instrumentation_filter=%s`,
includePaths+excludePaths))
}
}
extraFlags := append([]string{"--output=jsonproto"}, coverageFlags...)
aqueryOutput, _, err = context.issueBazelCommand(
context.paths,
bazel.AqueryBuildRootRunName,
bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
// proto sources, which would add a number of unnecessary dependencies.
"--output=jsonproto")
extraFlags...)
if err != nil {
return err

View file

@ -4,11 +4,14 @@ import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"android/soong/bazel/cquery"
)
var testConfig = TestConfig("out", nil, "", nil)
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "//foo:bar"
cfg := configKey{"arm64_armv8-a", Android}
@ -16,7 +19,7 @@ func TestRequestResultsAfterInvokeBazel(t *testing.T) {
bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
})
bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
err := bazelContext.InvokeBazel()
err := bazelContext.InvokeBazel(testConfig)
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@ -30,7 +33,7 @@ func TestRequestResultsAfterInvokeBazel(t *testing.T) {
func TestInvokeBazelWritesBazelFiles(t *testing.T) {
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
err := bazelContext.InvokeBazel()
err := bazelContext.InvokeBazel(testConfig)
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@ -86,7 +89,7 @@ func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
}]
}`,
})
err := bazelContext.InvokeBazel()
err := bazelContext.InvokeBazel(testConfig)
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@ -97,6 +100,54 @@ func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
}
}
func TestCoverageFlagsAfterInvokeBazel(t *testing.T) {
testConfig.productVariables.ClangCoverage = boolPtr(true)
testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`)
testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`)
testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
testConfig.productVariables.NativeCoverageExcludePaths = nil
verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`)
testConfig.productVariables.NativeCoveragePaths = nil
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
testConfig.productVariables.ClangCoverage = boolPtr(false)
actual := verifyExtraFlags(t, testConfig, ``)
if strings.Contains(actual, "--collect_code_coverage") ||
strings.Contains(actual, "--instrumentation_filter=") {
t.Errorf("Expected code coverage disabled, but got %#v", actual)
}
}
func verifyExtraFlags(t *testing.T, config Config, expected string) string {
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
err := bazelContext.InvokeBazel(config)
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags
if expected := 3; len(flags) != expected {
t.Errorf("Expected %d extra flags got %#v", expected, flags)
}
actual := flags[1]
if !strings.Contains(actual, expected) {
t.Errorf("Expected %#v got %#v", expected, actual)
}
return actual
}
func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
t.Helper()
p := bazelPaths{

View file

@ -32,6 +32,12 @@ func CopyOf(s []string) []string {
// JoinWithPrefix prepends the prefix to each string in the list and
// returns them joined together with " " as separator.
func JoinWithPrefix(strs []string, prefix string) string {
return JoinWithPrefixAndSeparator(strs, prefix, " ")
}
// JoinWithPrefixAndSeparator prepends the prefix to each string in the list and
// returns them joined together with the given separator.
func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string {
if len(strs) == 0 {
return ""
}
@ -40,7 +46,7 @@ func JoinWithPrefix(strs []string, prefix string) string {
buf.WriteString(prefix)
buf.WriteString(strs[0])
for i := 1; i < len(strs); i++ {
buf.WriteString(" ")
buf.WriteString(sep)
buf.WriteString(prefix)
buf.WriteString(strs[i])
}

View file

@ -664,6 +664,9 @@ func shouldSkipAction(a action) bool {
if a.Mnemonic == "FileWrite" {
return true
}
if a.Mnemonic == "BaselineCoverage" {
return true
}
return false
}

View file

@ -959,11 +959,12 @@ func TestCcLibraryFeatures(t *testing.T) {
"features": `[
"disable_pack_relocations",
"-no_undefined_symbols",
"-coverage",
]`,
"srcs": `["a.cpp"]`,
})...)
expected_targets = append(expected_targets, makeCcLibraryTargets("b", attrNameToString{
"features": `select({
"features": `["-coverage"] + select({
"//build/bazel/platforms/arch:x86_64": [
"disable_pack_relocations",
"-no_undefined_symbols",
@ -994,6 +995,7 @@ cc_library {
pack_relocations: false,
allow_undefined_symbols: true,
include_build_directory: false,
native_coverage: false,
}
cc_library {
@ -1006,6 +1008,7 @@ cc_library {
},
},
include_build_directory: false,
native_coverage: false,
}
cc_library {

View file

@ -55,6 +55,8 @@ type staticOrSharedAttributes struct {
Enabled bazel.BoolAttribute
Native_coverage bazel.BoolAttribute
sdkAttributes
}
@ -568,10 +570,15 @@ func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module)
}
}
}
compilerAttrs.convertStlProps(ctx, module)
(&linkerAttrs).convertStripProps(ctx, module)
if module.coverage != nil && module.coverage.Properties.Native_coverage != nil &&
!Bool(module.coverage.Properties.Native_coverage) {
// Native_coverage is arch neutral
(&linkerAttrs).features.Append(bazel.MakeStringListAttribute([]string{"-coverage"}))
}
productVariableProps := android.ProductVariableProperties(ctx)
(&compilerAttrs).convertProductVariables(ctx, productVariableProps)

View file

@ -139,7 +139,7 @@ func runMixedModeBuild(configuration android.Config, ctx *android.Context, extra
bazelHook := func() error {
ctx.EventHandler.Begin("bazel")
defer ctx.EventHandler.End("bazel")
return configuration.BazelContext.InvokeBazel()
return configuration.BazelContext.InvokeBazel(configuration)
}
ctx.SetBeforePrepareBuildActionsHook(bazelHook)