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
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 // Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext. // queued in the BazelContext.
InvokeBazel() error InvokeBazel(config Config) error
// Returns true if bazel is enabled for the given configuration. // Returns true if bazel is enabled for the given configuration.
BazelEnabled() bool BazelEnabled() bool
@@ -191,7 +191,7 @@ func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (strin
return result, nil return result, nil
} }
func (m MockBazelContext) InvokeBazel() error { func (m MockBazelContext) InvokeBazel(config Config) error {
panic("unimplemented") panic("unimplemented")
} }
@@ -261,7 +261,7 @@ func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (strin
panic("unimplemented") panic("unimplemented")
} }
func (n noopBazelContext) InvokeBazel() error { func (n noopBazelContext) InvokeBazel(config Config) error {
panic("unimplemented") panic("unimplemented")
} }
@@ -361,6 +361,7 @@ type bazelCommand struct {
type mockBazelRunner struct { type mockBazelRunner struct {
bazelCommandResults map[bazelCommand]string bazelCommandResults map[bazelCommand]string
commands []bazelCommand commands []bazelCommand
extraFlags []string
} }
func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths, func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
@@ -368,6 +369,7 @@ func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
command bazelCommand, command bazelCommand,
extraFlags ...string) (string, string, error) { extraFlags ...string) (string, string, error) {
r.commands = append(r.commands, command) r.commands = append(r.commands, command)
r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
if ret, ok := r.bazelCommandResults[command]; ok { if ret, ok := r.bazelCommandResults[command]; ok {
return ret, "", nil return ret, "", nil
} }
@@ -676,7 +678,7 @@ func (p *bazelPaths) outDir() string {
// Issues commands to Bazel to receive results for all cquery requests // Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext. // queued in the BazelContext.
func (context *bazelContext) InvokeBazel() error { func (context *bazelContext) InvokeBazel(config Config) error {
context.results = make(map[cqueryKey]string) context.results = make(map[cqueryKey]string)
var cqueryOutput 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. // 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 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( aqueryOutput, _, err = context.issueBazelCommand(
context.paths, context.paths,
bazel.AqueryBuildRootRunName, bazel.AqueryBuildRootRunName,
bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}, bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's // 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. // proto sources, which would add a number of unnecessary dependencies.
"--output=jsonproto") extraFlags...)
if err != nil { if err != nil {
return err return err

View File

@@ -4,11 +4,14 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"testing" "testing"
"android/soong/bazel/cquery" "android/soong/bazel/cquery"
) )
var testConfig = TestConfig("out", nil, "", nil)
func TestRequestResultsAfterInvokeBazel(t *testing.T) { func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "//foo:bar" label := "//foo:bar"
cfg := configKey{"arm64_armv8-a", Android} 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`, 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) bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
err := bazelContext.InvokeBazel() err := bazelContext.InvokeBazel(testConfig)
if err != nil { if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err) 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) { func TestInvokeBazelWritesBazelFiles(t *testing.T) {
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{}) bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
err := bazelContext.InvokeBazel() err := bazelContext.InvokeBazel(testConfig)
if err != nil { if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err) 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 { if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err) 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) { func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
t.Helper() t.Helper()
p := bazelPaths{ p := bazelPaths{

View File

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

View File

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

View File

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

View File

@@ -55,6 +55,8 @@ type staticOrSharedAttributes struct {
Enabled bazel.BoolAttribute Enabled bazel.BoolAttribute
Native_coverage bazel.BoolAttribute
sdkAttributes sdkAttributes
} }
@@ -568,10 +570,15 @@ func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module)
} }
} }
} }
compilerAttrs.convertStlProps(ctx, module) compilerAttrs.convertStlProps(ctx, module)
(&linkerAttrs).convertStripProps(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) productVariableProps := android.ProductVariableProperties(ctx)
(&compilerAttrs).convertProductVariables(ctx, productVariableProps) (&compilerAttrs).convertProductVariables(ctx, productVariableProps)

View File

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