Add gcov coverage support to Rust modules.

This adds gcov coverage support for Rust device library and binary
modules (including test modules). Support is provided to pass Rust
static library gcno files to CC modules and visa versa.

Additional changes:
 * Begin mutator added for Rust modules.
 * SuffixInList added to android package.
 * CoverageEnabled added to Coverage interface.
 * CoverageFiles added to LinkableLibrary interface.
 * Fix in coverage mutator for non-CC modules which marked the wrong
   variant as the coverage variant.
 * Added coverage libraries to the cc.GatherRequiredDepsForTest.

Bug: 146448203
Test: NATIVE_COVERAGE=true COVERAGE_PATHS='*' m -j <rust_module>
Change-Id: If20728bdde42a1dd544a35a40f0d981b80a5835f
This commit is contained in:
Ivan Lozano 2020-04-09 09:56:02 -04:00
parent b8bb5d05ce
commit a0cd8f9acb
21 changed files with 628 additions and 91 deletions

View file

@ -391,6 +391,7 @@ bootstrap_go_package {
srcs: [ srcs: [
"rust/androidmk.go", "rust/androidmk.go",
"rust/compiler.go", "rust/compiler.go",
"rust/coverage.go",
"rust/binary.go", "rust/binary.go",
"rust/builder.go", "rust/builder.go",
"rust/library.go", "rust/library.go",
@ -403,6 +404,7 @@ bootstrap_go_package {
testSrcs: [ testSrcs: [
"rust/binary_test.go", "rust/binary_test.go",
"rust/compiler_test.go", "rust/compiler_test.go",
"rust/coverage_test.go",
"rust/library_test.go", "rust/library_test.go",
"rust/rust_test.go", "rust/rust_test.go",
"rust/test_test.go", "rust/test_test.go",

View file

@ -141,6 +141,16 @@ func PrefixInList(list []string, prefix string) bool {
return false return false
} }
// Returns true if any string in the given list has the given suffix.
func SuffixInList(list []string, suffix string) bool {
for _, s := range list {
if strings.HasSuffix(s, suffix) {
return true
}
}
return false
}
// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element. // IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
func IndexListPred(pred func(s string) bool, list []string) int { func IndexListPred(pred func(s string) bool, list []string) int {
for i, l := range list { for i, l := range list {

View file

@ -1611,6 +1611,8 @@ func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizer
return android.InList(sanitizerName, globalSanitizerNames) return android.InList(sanitizerName, globalSanitizerNames)
} }
var _ cc.Coverage = (*apexBundle)(nil)
func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled()) return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
} }
@ -1627,6 +1629,8 @@ func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
a.properties.IsCoverageVariant = coverage a.properties.IsCoverageVariant = coverage
} }
func (a *apexBundle) EnableCoverageIfNeeded() {}
// TODO(jiyong) move apexFileFor* close to the apexFile type definition // TODO(jiyong) move apexFileFor* close to the apexFile type definition
func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile { func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
// Decide the APEX-local directory by the multilib of the library // Decide the APEX-local directory by the multilib of the library

View file

@ -117,44 +117,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) {
}) })
t.Run("VNDK APEX supports coverage variants", func(t *testing.T) { t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
ctx, _ := testApex(t, bp+` ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
cc_library {
name: "libprofile-extras",
vendor_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
}
cc_library {
name: "libprofile-clang-extras",
vendor_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
}
cc_library {
name: "libprofile-extras_ndk",
vendor_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
sdk_version: "current",
}
cc_library {
name: "libprofile-clang-extras_ndk",
vendor_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
sdk_version: "current",
}
`, func(fs map[string][]byte, config android.Config) {
config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
}) })

View file

@ -437,7 +437,6 @@ var (
ndkLateStubDepTag = DependencyTag{Name: "ndk late stub", Library: true} ndkLateStubDepTag = DependencyTag{Name: "ndk late stub", Library: true}
vndkExtDepTag = DependencyTag{Name: "vndk extends", Library: true} vndkExtDepTag = DependencyTag{Name: "vndk extends", Library: true}
runtimeDepTag = DependencyTag{Name: "runtime lib"} runtimeDepTag = DependencyTag{Name: "runtime lib"}
coverageDepTag = DependencyTag{Name: "coverage"}
testPerSrcDepTag = DependencyTag{Name: "test_per_src"} testPerSrcDepTag = DependencyTag{Name: "test_per_src"}
) )
@ -741,6 +740,15 @@ func (c *Module) OutputFile() android.OptionalPath {
return c.outputFile return c.outputFile
} }
func (c *Module) CoverageFiles() android.Paths {
if c.linker != nil {
if library, ok := c.linker.(libraryInterface); ok {
return library.objs().coverageFiles
}
}
panic(fmt.Errorf("CoverageFiles called on non-library module: %q", c.BaseModuleName()))
}
var _ LinkableInterface = (*Module)(nil) var _ LinkableInterface = (*Module)(nil)
func (c *Module) UnstrippedOutputFile() android.Path { func (c *Module) UnstrippedOutputFile() android.Path {
@ -2489,13 +2497,16 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
// When combining coverage files for shared libraries and executables, coverage files // When combining coverage files for shared libraries and executables, coverage files
// in static libraries act as if they were whole static libraries. The same goes for // in static libraries act as if they were whole static libraries. The same goes for
// source based Abi dump files. // source based Abi dump files.
// This should only be done for cc.Modules
if c, ok := ccDep.(*Module); ok { if c, ok := ccDep.(*Module); ok {
staticLib := c.linker.(libraryInterface) staticLib := c.linker.(libraryInterface)
depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles, depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
staticLib.objs().coverageFiles...) staticLib.objs().coverageFiles...)
depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles, depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
staticLib.objs().sAbiDumpFiles...) staticLib.objs().sAbiDumpFiles...)
} else if c, ok := ccDep.(LinkableInterface); ok {
// Handle non-CC modules here
depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
c.CoverageFiles()...)
} }
} }

View file

@ -65,10 +65,10 @@ func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
if cov.Properties.NeedCoverageVariant { if cov.Properties.NeedCoverageVariant {
ctx.AddVariationDependencies([]blueprint.Variation{ ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"}, {Mutator: "link", Variation: "static"},
}, coverageDepTag, getGcovProfileLibraryName(ctx)) }, CoverageDepTag, getGcovProfileLibraryName(ctx))
ctx.AddVariationDependencies([]blueprint.Variation{ ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"}, {Mutator: "link", Variation: "static"},
}, coverageDepTag, getClangProfileLibraryName(ctx)) }, CoverageDepTag, getClangProfileLibraryName(ctx))
} }
return deps return deps
} }
@ -134,14 +134,14 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags
if gcovCoverage { if gcovCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage") flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), coverageDepTag).(*Module) coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv") flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
} else if clangCoverage { } else if clangCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate") flags.Local.LdFlags = append(flags.Local.LdFlags, "-fprofile-instr-generate")
coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), coverageDepTag).(*Module) coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
} }
} }
@ -150,25 +150,30 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags
} }
func (cov *coverage) begin(ctx BaseModuleContext) { func (cov *coverage) begin(ctx BaseModuleContext) {
if ctx.Host() {
// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
// Just turn off for now.
} else {
cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion())
}
}
func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool,
useSdk bool, sdkVersion string) CoverageProperties {
// Coverage is disabled globally // Coverage is disabled globally
if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() { if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
return return properties
} }
var needCoverageVariant bool var needCoverageVariant bool
var needCoverageBuild bool var needCoverageBuild bool
if ctx.Host() { if moduleTypeHasCoverage {
// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
// Just turn off for now.
} else if !ctx.nativeCoverage() {
// Native coverage is not supported for this module type.
} else {
// Check if Native_coverage is set to false. This property defaults to true. // Check if Native_coverage is set to false. This property defaults to true.
needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true) needCoverageVariant = BoolDefault(properties.Native_coverage, true)
if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" { if useSdk && sdkVersion != "current" {
// Native coverage is not supported for SDK versions < 23 // Native coverage is not supported for SDK versions < 23
if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 { if fromApi, err := strconv.Atoi(sdkVersion); err == nil && fromApi < 23 {
needCoverageVariant = false needCoverageVariant = false
} }
} }
@ -179,8 +184,10 @@ func (cov *coverage) begin(ctx BaseModuleContext) {
} }
} }
cov.Properties.NeedCoverageBuild = needCoverageBuild properties.NeedCoverageBuild = needCoverageBuild
cov.Properties.NeedCoverageVariant = needCoverageVariant properties.NeedCoverageVariant = needCoverageVariant
return properties
} }
// Coverage is an interface for non-CC modules to implement to be mutated for coverage // Coverage is an interface for non-CC modules to implement to be mutated for coverage
@ -190,6 +197,7 @@ type Coverage interface {
PreventInstall() PreventInstall()
HideFromMake() HideFromMake()
MarkAsCoverageVariant(bool) MarkAsCoverageVariant(bool)
EnableCoverageIfNeeded()
} }
func coverageMutator(mctx android.BottomUpMutatorContext) { func coverageMutator(mctx android.BottomUpMutatorContext) {
@ -212,14 +220,17 @@ func coverageMutator(mctx android.BottomUpMutatorContext) {
m[1].(*Module).coverage.Properties.IsCoverageVariant = true m[1].(*Module).coverage.Properties.IsCoverageVariant = true
} }
} else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) { } else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
// APEX modules fall here // APEX and Rust modules fall here
// Note: variant "" is also created because an APEX can be depended on by another // Note: variant "" is also created because an APEX can be depended on by another
// module which are split into "" and "cov" variants. e.g. when cc_test refers // module which are split into "" and "cov" variants. e.g. when cc_test refers
// to an APEX via 'data' property. // to an APEX via 'data' property.
m := mctx.CreateVariations("", "cov") m := mctx.CreateVariations("", "cov")
m[0].(Coverage).MarkAsCoverageVariant(true) m[0].(Coverage).MarkAsCoverageVariant(false)
m[0].(Coverage).PreventInstall() m[0].(Coverage).PreventInstall()
m[0].(Coverage).HideFromMake() m[0].(Coverage).HideFromMake()
m[1].(Coverage).MarkAsCoverageVariant(true)
m[1].(Coverage).EnableCoverageIfNeeded()
} }
} }

View file

@ -12,6 +12,7 @@ type LinkableInterface interface {
CcLibraryInterface() bool CcLibraryInterface() bool
OutputFile() android.OptionalPath OutputFile() android.OptionalPath
CoverageFiles() android.Paths
IncludeDirs() android.Paths IncludeDirs() android.Paths
SetDepsInLinkOrder([]android.Path) SetDepsInLinkOrder([]android.Path)
@ -83,4 +84,5 @@ var (
CrtBeginDepTag = DependencyTag{Name: "crtbegin"} CrtBeginDepTag = DependencyTag{Name: "crtbegin"}
CrtEndDepTag = DependencyTag{Name: "crtend"} CrtEndDepTag = DependencyTag{Name: "crtend"}
CoverageDepTag = DependencyTag{Name: "coverage"}
) )

View file

@ -192,6 +192,45 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string {
symbol_file: "", symbol_file: "",
sdk_version: "current", sdk_version: "current",
} }
// Coverage libraries
cc_library {
name: "libprofile-extras",
vendor_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
}
cc_library {
name: "libprofile-clang-extras",
vendor_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
}
cc_library {
name: "libprofile-extras_ndk",
vendor_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
sdk_version: "current",
}
cc_library {
name: "libprofile-clang-extras_ndk",
vendor_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
notice: "custom_notice",
sdk_version: "current",
}
cc_library { cc_library {
name: "libdl", name: "libdl",
no_libcrt: true, no_libcrt: true,

View file

@ -728,6 +728,8 @@ func (a *AndroidApp) MarkAsCoverageVariant(coverage bool) {
a.appProperties.IsCoverageVariant = coverage a.appProperties.IsCoverageVariant = coverage
} }
func (a *AndroidApp) EnableCoverageIfNeeded() {}
var _ cc.Coverage = (*AndroidApp)(nil) var _ cc.Coverage = (*AndroidApp)(nil)
// android_app compiles sources and Android resources into an Android application package `.apk` file. // android_app compiles sources and Android resources into an Android application package `.apk` file.

View file

@ -46,6 +46,12 @@ func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
} }
func (mod *Module) AndroidMk() android.AndroidMkData { func (mod *Module) AndroidMk() android.AndroidMkData {
if mod.Properties.HideFromMake {
return android.AndroidMkData{
Disabled: true,
}
}
ret := android.AndroidMkData{ ret := android.AndroidMkData{
OutputFile: mod.outputFile, OutputFile: mod.outputFile,
Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk", Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
@ -84,6 +90,9 @@ func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.Andr
ret.DistFile = binary.distFile ret.DistFile = binary.distFile
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String()) fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
if binary.coverageOutputZipFile.Valid() {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+binary.coverageOutputZipFile.String())
}
}) })
} }
@ -124,6 +133,10 @@ func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.An
if !library.rlib() { if !library.rlib() {
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String()) fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
} }
if library.coverageOutputZipFile.Valid() {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+library.coverageOutputZipFile.String())
}
}) })
} }

View file

@ -35,9 +35,10 @@ type BinaryCompilerProperties struct {
type binaryDecorator struct { type binaryDecorator struct {
*baseCompiler *baseCompiler
Properties BinaryCompilerProperties Properties BinaryCompilerProperties
distFile android.OptionalPath distFile android.OptionalPath
unstrippedOutputFile android.Path coverageOutputZipFile android.OptionalPath
unstrippedOutputFile android.Path
} }
var _ compiler = (*binaryDecorator)(nil) var _ compiler = (*binaryDecorator)(nil)
@ -104,6 +105,10 @@ func (binary *binaryDecorator) compilerProps() []interface{} {
&binary.Properties) &binary.Properties)
} }
func (binary *binaryDecorator) nativeCoverage() bool {
return true
}
func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path { func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix() fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
@ -114,7 +119,21 @@ func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps Path
flags.RustFlags = append(flags.RustFlags, deps.depFlags...) flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) outputs := TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
binary.coverageFile = outputs.coverageFile
var coverageFiles android.Paths
if outputs.coverageFile != nil {
coverageFiles = append(coverageFiles, binary.coverageFile)
}
if len(deps.coverageFiles) > 0 {
coverageFiles = append(coverageFiles, deps.coverageFiles...)
}
binary.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, binary.getStem(ctx))
return outputFile return outputFile
} }
func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath {
return binary.coverageOutputZipFile
}

View file

@ -18,6 +18,7 @@ import (
"strings" "strings"
"github.com/google/blueprint" "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"android/soong/android" "android/soong/android"
) )
@ -36,44 +37,57 @@ var (
Depfile: "$out.d", Depfile: "$out.d",
}, },
"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd") "rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
zip = pctx.AndroidStaticRule("zip",
blueprint.RuleParams{
Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
CommandDeps: []string{"${SoongZipCmd}"},
Rspfile: "$out.rsp",
RspfileContent: "$in",
})
) )
func init() { type buildOutput struct {
outputFile android.Path
coverageFile android.Path
}
func init() {
pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
} }
func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, includeDirs []string) { outputFile android.WritablePath, includeDirs []string) buildOutput {
flags.RustFlags = append(flags.RustFlags, "-C lto") flags.RustFlags = append(flags.RustFlags, "-C lto")
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", includeDirs)
} }
func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, includeDirs []string) { outputFile android.WritablePath, includeDirs []string) buildOutput {
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
} }
func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, includeDirs []string) { outputFile android.WritablePath, includeDirs []string) buildOutput {
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
} }
func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, func TransformSrctoStatic(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, includeDirs []string) { outputFile android.WritablePath, includeDirs []string) buildOutput {
flags.RustFlags = append(flags.RustFlags, "-C lto") flags.RustFlags = append(flags.RustFlags, "-C lto")
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", includeDirs)
} }
func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, func TransformSrctoShared(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, includeDirs []string) { outputFile android.WritablePath, includeDirs []string) buildOutput {
flags.RustFlags = append(flags.RustFlags, "-C lto") flags.RustFlags = append(flags.RustFlags, "-C lto")
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", includeDirs)
} }
func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps,
flags Flags, outputFile android.WritablePath, includeDirs []string) { flags Flags, outputFile android.WritablePath, includeDirs []string) buildOutput {
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs) return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
} }
func rustLibsToPaths(libs RustLibraries) android.Paths { func rustLibsToPaths(libs RustLibraries) android.Paths {
@ -85,11 +99,15 @@ func rustLibsToPaths(libs RustLibraries) android.Paths {
} }
func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, crate_type string, includeDirs []string) { outputFile android.WritablePath, crate_type string, includeDirs []string) buildOutput {
var inputs android.Paths var inputs android.Paths
var implicits android.Paths var implicits android.Paths
var output buildOutput
var libFlags, rustcFlags, linkFlags []string var libFlags, rustcFlags, linkFlags []string
var implicitOutputs android.WritablePaths
output.outputFile = outputFile
crate_name := ctx.(ModuleContext).CrateName() crate_name := ctx.(ModuleContext).CrateName()
targetTriple := ctx.(ModuleContext).toolchain().RustTriple() targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
@ -141,12 +159,26 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path
implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path()) implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
} }
if flags.Coverage {
var gcnoFile android.WritablePath
if outputFile.Ext() != "" {
gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno"))
} else {
gcnoFile = android.PathForModuleOut(ctx, outputFile.Base()+".gcno")
}
implicitOutputs = append(implicitOutputs, gcnoFile)
output.coverageFile = gcnoFile
}
ctx.Build(pctx, android.BuildParams{ ctx.Build(pctx, android.BuildParams{
Rule: rustc, Rule: rustc,
Description: "rustc " + main.Rel(), Description: "rustc " + main.Rel(),
Output: outputFile, Output: outputFile,
Inputs: inputs, ImplicitOutputs: implicitOutputs,
Implicits: implicits, Inputs: inputs,
Implicits: implicits,
Args: map[string]string{ Args: map[string]string{
"rustcFlags": strings.Join(rustcFlags, " "), "rustcFlags": strings.Join(rustcFlags, " "),
"linkFlags": strings.Join(linkFlags, " "), "linkFlags": strings.Join(linkFlags, " "),
@ -156,4 +188,23 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path
}, },
}) })
return output
}
func TransformCoverageFilesToZip(ctx android.ModuleContext,
covFiles android.Paths, baseName string) android.OptionalPath {
if len(covFiles) > 0 {
outputFile := android.PathForModuleOut(ctx, baseName+".zip")
ctx.Build(pctx, android.BuildParams{
Rule: zip,
Description: "zip " + outputFile.Base(),
Inputs: covFiles,
Output: outputFile,
})
return android.OptionalPathForPath(outputFile)
}
return android.OptionalPath{}
} }

View file

@ -110,6 +110,7 @@ type baseCompiler struct {
linkDirs []string linkDirs []string
edition string edition string
src android.Path //rustc takes a single src file src android.Path //rustc takes a single src file
coverageFile android.Path //rustc generates a single gcno file
// Install related // Install related
dir string dir string
@ -120,6 +121,10 @@ type baseCompiler struct {
location installLocation location installLocation
} }
func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
panic("baseCompiler does not implement coverageOutputZipPath()")
}
var _ compiler = (*baseCompiler)(nil) var _ compiler = (*baseCompiler)(nil)
func (compiler *baseCompiler) inData() bool { func (compiler *baseCompiler) inData() bool {
@ -235,6 +240,10 @@ func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath
compiler.relativeInstallPath(), compiler.relative) compiler.relativeInstallPath(), compiler.relative)
} }
func (compiler *baseCompiler) nativeCoverage() bool {
return false
}
func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) { func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file) compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file)
} }

72
rust/coverage.go Normal file
View file

@ -0,0 +1,72 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rust
import (
"github.com/google/blueprint"
"android/soong/cc"
)
var CovLibraryName = "libprofile-extras"
type coverage struct {
Properties cc.CoverageProperties
// Whether binaries containing this module need --coverage added to their ldflags
linkCoverage bool
}
func (cov *coverage) props() []interface{} {
return []interface{}{&cov.Properties}
}
func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
if cov.Properties.NeedCoverageVariant {
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, cc.CoverageDepTag, CovLibraryName)
}
return deps
}
func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
return flags, deps
}
if cov.Properties.CoverageEnabled {
flags.Coverage = true
coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
flags.RustFlags = append(flags.RustFlags,
"-Z profile", "-g", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads")
flags.LinkFlags = append(flags.LinkFlags,
"--coverage", "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,getenv")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
}
return flags, deps
}
func (cov *coverage) begin(ctx BaseModuleContext) {
if ctx.Host() {
// Host coverage not yet supported.
} else {
// Update useSdk and sdkVersion args if Rust modules become SDK aware.
cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), false, "")
}
}

181
rust/coverage_test.go Normal file
View file

@ -0,0 +1,181 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rust
import (
"strings"
"testing"
"android/soong/android"
)
// Test that coverage flags are being correctly generated.
func TestCoverageFlags(t *testing.T) {
ctx := testRustCov(t, `
rust_library {
name: "libfoo_cov",
srcs: ["foo.rs"],
crate_name: "foo",
}
rust_binary {
name: "fizz_cov",
srcs: ["foo.rs"],
}
rust_binary {
name: "buzzNoCov",
srcs: ["foo.rs"],
native_coverage: false,
}
rust_library {
name: "libbar_nocov",
srcs: ["foo.rs"],
crate_name: "bar",
native_coverage: false,
}`)
// Make sure native_coverage: false isn't creating a coverage variant.
if android.InList("android_arm64_armv8-a_dylib_cov", ctx.ModuleVariantsForTests("libbar_nocov")) {
t.Fatalf("coverage variant created for module 'libbar_nocov' with native coverage disabled")
}
// Just test the dylib variants unless the library coverage logic changes to distinguish between the types.
libfooCov := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc")
libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
rustcCoverageFlags := []string{"-Z profile", " -g ", "-C opt-level=0", "-C link-dead-code", "-Z no-landing-pads"}
for _, flag := range rustcCoverageFlags {
missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
if !strings.Contains(fizzCov.Args["rustcFlags"], flag) {
t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["rustcFlags"])
}
if !strings.Contains(libfooCov.Args["rustcFlags"], flag) {
t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["rustcFlags"])
}
if strings.Contains(buzzNoCov.Args["rustcFlags"], flag) {
t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["rustcFlags"])
}
if strings.Contains(libbarNoCov.Args["rustcFlags"], flag) {
t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["rustcFlags"])
}
}
linkCoverageFlags := []string{"--coverage", " -g "}
for _, flag := range linkCoverageFlags {
missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
if !strings.Contains(fizzCov.Args["linkFlags"], flag) {
t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"])
}
if !strings.Contains(libfooCov.Args["linkFlags"], flag) {
t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"])
}
if strings.Contains(buzzNoCov.Args["linkFlags"], flag) {
t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"])
}
if strings.Contains(libbarNoCov.Args["linkFlags"], flag) {
t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"])
}
}
}
// Test coverage files are included correctly
func TestCoverageZip(t *testing.T) {
ctx := testRustCov(t, `
rust_library {
name: "libfoo",
srcs: ["foo.rs"],
rlibs: ["librlib"],
crate_name: "foo",
}
rust_library_rlib {
name: "librlib",
srcs: ["foo.rs"],
crate_name: "rlib",
}
rust_binary {
name: "fizz",
rlibs: ["librlib"],
static_libs: ["libfoo"],
srcs: ["foo.rs"],
}
cc_binary {
name: "buzz",
static_libs: ["libfoo"],
srcs: ["foo.c"],
}
cc_library {
name: "libbar",
static_libs: ["libfoo"],
compile_multilib: "64",
srcs: ["foo.c"],
}`)
fizzZipInputs := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
libfooZipInputs := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib_cov").Rule("zip").Inputs.Strings()
buzzZipInputs := ctx.ModuleForTests("buzz", "android_arm64_armv8-a_cov").Rule("zip").Inputs.Strings()
libbarZipInputs := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared_cov").Rule("zip").Inputs.Strings()
// Make sure the expected number of input files are included.
if len(fizzZipInputs) != 3 {
t.Fatalf("expected only 3 coverage inputs for rust 'fizz' binary, got %#v: %#v", len(fizzZipInputs), fizzZipInputs)
}
if len(libfooZipInputs) != 2 {
t.Fatalf("expected only 2 coverage inputs for rust 'libfoo' library, got %#v: %#v", len(libfooZipInputs), libfooZipInputs)
}
if len(buzzZipInputs) != 2 {
t.Fatalf("expected only 2 coverage inputs for cc 'buzz' binary, got %#v: %#v", len(buzzZipInputs), buzzZipInputs)
}
if len(libbarZipInputs) != 2 {
t.Fatalf("expected only 2 coverage inputs for cc 'libbar' library, got %#v: %#v", len(libbarZipInputs), libbarZipInputs)
}
// Make sure the expected inputs are provided to the zip rule.
if !android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") ||
!android.SuffixInList(fizzZipInputs, "android_arm64_armv8-a_cov/fizz.gcno") {
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
}
if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_cov/librlib.gcno") ||
!android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
}
if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
!android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
t.Fatalf("missing expected coverage files for cc 'buzz' binary: %#v", buzzZipInputs)
}
if !android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/obj/foo.gcno") ||
!android.SuffixInList(libbarZipInputs, "android_arm64_armv8-a_static_cov/libfoo.gcno") {
t.Fatalf("missing expected coverage files for cc 'libbar' library: %#v", libbarZipInputs)
}
}
func TestCoverageDeps(t *testing.T) {
ctx := testRustCov(t, `
rust_binary {
name: "fizz",
srcs: ["foo.rs"],
}`)
fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
if !strings.Contains(fizz.Args["linkFlags"], "libprofile-extras.a") {
t.Fatalf("missing expected coverage 'libprofile-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
}
}

View file

@ -75,11 +75,12 @@ type LibraryMutatedProperties struct {
type libraryDecorator struct { type libraryDecorator struct {
*baseCompiler *baseCompiler
Properties LibraryCompilerProperties Properties LibraryCompilerProperties
MutatedProperties LibraryMutatedProperties MutatedProperties LibraryMutatedProperties
distFile android.OptionalPath distFile android.OptionalPath
unstrippedOutputFile android.Path coverageOutputZipFile android.OptionalPath
includeDirs android.Paths unstrippedOutputFile android.Path
includeDirs android.Paths
} }
type libraryInterface interface { type libraryInterface interface {
@ -107,6 +108,10 @@ type libraryInterface interface {
BuildOnlyShared() BuildOnlyShared()
} }
func (library *libraryDecorator) nativeCoverage() bool {
return true
}
func (library *libraryDecorator) exportedDirs() []string { func (library *libraryDecorator) exportedDirs() []string {
return library.linkDirs return library.linkDirs
} }
@ -351,24 +356,37 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix() fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName) outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) outputs := TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
library.coverageFile = outputs.coverageFile
} else if library.dylib() { } else if library.dylib() {
fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix() fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName) outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) outputs := TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
library.coverageFile = outputs.coverageFile
} else if library.static() { } else if library.static() {
fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix() fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName) outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) outputs := TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
library.coverageFile = outputs.coverageFile
} else if library.shared() { } else if library.shared() {
fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix() fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName) outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs) outputs := TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
library.coverageFile = outputs.coverageFile
} }
var coverageFiles android.Paths
if library.coverageFile != nil {
coverageFiles = append(coverageFiles, library.coverageFile)
}
if len(deps.coverageFiles) > 0 {
coverageFiles = append(coverageFiles, deps.coverageFiles...)
}
library.coverageOutputZipFile = TransformCoverageFilesToZip(ctx, coverageFiles, library.getStem(ctx))
if library.rlib() || library.dylib() { if library.rlib() || library.dylib() {
library.reexportDirs(deps.linkDirs...) library.reexportDirs(deps.linkDirs...)
library.reexportDepFlags(deps.depFlags...) library.reexportDepFlags(deps.depFlags...)

View file

@ -69,3 +69,7 @@ func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Dep
deps = prebuilt.baseCompiler.compilerDeps(ctx, deps) deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
return deps return deps
} }
func (prebuilt *prebuiltLibraryDecorator) nativeCoverage() bool {
return false
}

View file

@ -40,6 +40,7 @@ func init() {
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel() ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
}) })
pctx.Import("android/soong/rust/config") pctx.Import("android/soong/rust/config")
} }
@ -51,6 +52,7 @@ type Flags struct {
LinkFlags []string // Flags that apply to linker LinkFlags []string // Flags that apply to linker
RustFlagsDeps android.Paths // Files depended on by compiler flags RustFlagsDeps android.Paths // Files depended on by compiler flags
Toolchain config.Toolchain Toolchain config.Toolchain
Coverage bool
} }
type BaseProperties struct { type BaseProperties struct {
@ -60,6 +62,8 @@ type BaseProperties struct {
AndroidMkSharedLibs []string AndroidMkSharedLibs []string
AndroidMkStaticLibs []string AndroidMkStaticLibs []string
SubName string `blueprint:"mutated"` SubName string `blueprint:"mutated"`
PreventInstall bool
HideFromMake bool
} }
type Module struct { type Module struct {
@ -72,6 +76,7 @@ type Module struct {
multilib android.Multilib multilib android.Multilib
compiler compiler compiler compiler
coverage *coverage
cachedToolchain config.Toolchain cachedToolchain config.Toolchain
subAndroidMkOnce map[subAndroidMkProvider]bool subAndroidMkOnce map[subAndroidMkProvider]bool
outputFile android.OptionalPath outputFile android.OptionalPath
@ -224,6 +229,8 @@ type PathDeps struct {
depFlags []string depFlags []string
//ReexportedDeps android.Paths //ReexportedDeps android.Paths
coverageFiles android.Paths
CrtBegin android.OptionalPath CrtBegin android.OptionalPath
CrtEnd android.OptionalPath CrtEnd android.OptionalPath
} }
@ -245,6 +252,34 @@ type compiler interface {
inData() bool inData() bool
install(ctx ModuleContext, path android.Path) install(ctx ModuleContext, path android.Path)
relativeInstallPath() string relativeInstallPath() string
nativeCoverage() bool
}
func (mod *Module) isCoverageVariant() bool {
return mod.coverage.Properties.IsCoverageVariant
}
var _ cc.Coverage = (*Module)(nil)
func (mod *Module) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
}
func (mod *Module) PreventInstall() {
mod.Properties.PreventInstall = true
}
func (mod *Module) HideFromMake() {
mod.Properties.HideFromMake = true
}
func (mod *Module) MarkAsCoverageVariant(coverage bool) {
mod.coverage.Properties.IsCoverageVariant = coverage
}
func (mod *Module) EnableCoverageIfNeeded() {
mod.coverage.Properties.CoverageEnabled = mod.coverage.Properties.NeedCoverageBuild
} }
func defaultsFactory() android.Module { func defaultsFactory() android.Module {
@ -268,6 +303,7 @@ func DefaultsFactory(props ...interface{}) android.Module {
&ProcMacroCompilerProperties{}, &ProcMacroCompilerProperties{},
&PrebuiltProperties{}, &PrebuiltProperties{},
&TestProperties{}, &TestProperties{},
&cc.CoverageProperties{},
) )
android.InitDefaultsModule(module) android.InitDefaultsModule(module)
@ -395,6 +431,18 @@ func (mod *Module) HasStaticVariant() bool {
return false return false
} }
func (mod *Module) CoverageFiles() android.Paths {
if mod.compiler != nil {
if library, ok := mod.compiler.(*libraryDecorator); ok {
if library.coverageFile != nil {
return android.Paths{library.coverageFile}
}
return android.Paths{}
}
}
panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName()))
}
var _ cc.LinkableInterface = (*Module)(nil) var _ cc.LinkableInterface = (*Module)(nil)
func (mod *Module) Init() android.Module { func (mod *Module) Init() android.Module {
@ -403,6 +451,10 @@ func (mod *Module) Init() android.Module {
if mod.compiler != nil { if mod.compiler != nil {
mod.AddProperties(mod.compiler.compilerProps()...) mod.AddProperties(mod.compiler.compilerProps()...)
} }
if mod.coverage != nil {
mod.AddProperties(mod.coverage.props()...)
}
android.InitAndroidArchModule(mod, mod.hod, mod.multilib) android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
android.InitDefaultableModule(mod) android.InitDefaultableModule(mod)
@ -432,6 +484,7 @@ func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib)
} }
func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module { func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
module := newBaseModule(hod, multilib) module := newBaseModule(hod, multilib)
module.coverage = &coverage{}
return module return module
} }
@ -454,6 +507,7 @@ type ModuleContextIntf interface {
toolchain() config.Toolchain toolchain() config.Toolchain
baseModuleName() string baseModuleName() string
CrateName() string CrateName() string
nativeCoverage() bool
} }
type depsContext struct { type depsContext struct {
@ -466,6 +520,14 @@ type moduleContext struct {
moduleContextImpl moduleContextImpl
} }
func (ctx *moduleContextImpl) nativeCoverage() bool {
return ctx.mod.nativeCoverage()
}
func (mod *Module) nativeCoverage() bool {
return mod.compiler != nil && mod.compiler.nativeCoverage()
}
type moduleContextImpl struct { type moduleContextImpl struct {
mod *Module mod *Module
ctx BaseModuleContext ctx BaseModuleContext
@ -508,9 +570,17 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
if mod.compiler != nil { if mod.compiler != nil {
flags = mod.compiler.compilerFlags(ctx, flags) flags = mod.compiler.compilerFlags(ctx, flags)
}
if mod.coverage != nil {
flags, deps = mod.coverage.flags(ctx, flags, deps)
}
if mod.compiler != nil {
outputFile := mod.compiler.compile(ctx, flags, deps) outputFile := mod.compiler.compile(ctx, flags, deps)
mod.outputFile = android.OptionalPathForPath(outputFile) mod.outputFile = android.OptionalPathForPath(outputFile)
mod.compiler.install(ctx, mod.outputFile.Path()) if !mod.Properties.PreventInstall {
mod.compiler.install(ctx, mod.outputFile.Path())
}
} }
} }
@ -521,6 +591,10 @@ func (mod *Module) deps(ctx DepsContext) Deps {
deps = mod.compiler.compilerDeps(ctx, deps) deps = mod.compiler.compilerDeps(ctx, deps)
} }
if mod.coverage != nil {
deps = mod.coverage.deps(ctx, deps)
}
deps.Rlibs = android.LastUniqueStrings(deps.Rlibs) deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
deps.Dylibs = android.LastUniqueStrings(deps.Dylibs) deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros) deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
@ -553,6 +627,12 @@ var (
testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"} testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
) )
func (mod *Module) begin(ctx BaseModuleContext) {
if mod.coverage != nil {
mod.coverage.begin(ctx)
}
}
func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps var depPaths PathDeps
@ -588,6 +668,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
ctx.ModuleErrorf("mod %q not an rlib library", depName) ctx.ModuleErrorf("mod %q not an rlib library", depName)
return return
} }
depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
directRlibDeps = append(directRlibDeps, rustDep) directRlibDeps = append(directRlibDeps, rustDep)
mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName) mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
case procMacroDepTag: case procMacroDepTag:
@ -642,6 +723,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
depFlag = "-lstatic=" + libName depFlag = "-lstatic=" + libName
depPaths.linkDirs = append(depPaths.linkDirs, linkPath) depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
depPaths.depFlags = append(depPaths.depFlags, depFlag) depPaths.depFlags = append(depPaths.depFlags, depFlag)
depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...)
directStaticLibDeps = append(directStaticLibDeps, ccDep) directStaticLibDeps = append(directStaticLibDeps, ccDep)
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName) mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
case cc.SharedDepTag: case cc.SharedDepTag:
@ -772,6 +854,29 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...) actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
} }
func BeginMutator(ctx android.BottomUpMutatorContext) {
if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
mod.beginMutator(ctx)
}
}
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
}
func (mod *Module) beginMutator(actx android.BottomUpMutatorContext) {
ctx := &baseModuleContext{
BaseModuleContext: actx,
moduleContextImpl: moduleContextImpl{
mod: mod,
},
}
ctx.ctx = ctx
mod.begin(ctx)
}
func (mod *Module) Name() string { func (mod *Module) Name() string {
name := mod.ModuleBase.Name() name := mod.ModuleBase.Name()
if p, ok := mod.compiler.(interface { if p, ok := mod.compiler.(interface {

View file

@ -21,6 +21,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/google/blueprint/proptools"
"android/soong/android" "android/soong/android"
"android/soong/cc" "android/soong/cc"
) )
@ -57,6 +59,7 @@ func testConfig(bp string) android.Config {
fs := map[string][]byte{ fs := map[string][]byte{
"foo.rs": nil, "foo.rs": nil,
"foo.c": nil,
"src/bar.rs": nil, "src/bar.rs": nil,
"liby.so": nil, "liby.so": nil,
"libz.so": nil, "libz.so": nil,
@ -68,6 +71,14 @@ func testConfig(bp string) android.Config {
} }
func testRust(t *testing.T, bp string) *android.TestContext { func testRust(t *testing.T, bp string) *android.TestContext {
return testRustContext(t, bp, false)
}
func testRustCov(t *testing.T, bp string) *android.TestContext {
return testRustContext(t, bp, true)
}
func testRustContext(t *testing.T, bp string, coverage bool) *android.TestContext {
// TODO (b/140435149) // TODO (b/140435149)
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
t.Skip("Only the Linux toolchain is supported for Rust") t.Skip("Only the Linux toolchain is supported for Rust")
@ -76,6 +87,11 @@ func testRust(t *testing.T, bp string) *android.TestContext {
t.Helper() t.Helper()
config := testConfig(bp) config := testConfig(bp)
if coverage {
config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
config.TestProductVariables.CoveragePaths = []string{"*"}
}
t.Helper() t.Helper()
ctx := CreateTestContext() ctx := CreateTestContext()
ctx.Register(config) ctx.Register(config)

View file

@ -50,6 +50,10 @@ type testDecorator struct {
testConfig android.Path testConfig android.Path
} }
func (test *testDecorator) nativeCoverage() bool {
return true
}
func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
module := newModule(hod, android.MultilibFirst) module := newModule(hod, android.MultilibFirst)

View file

@ -98,6 +98,7 @@ func CreateTestContext() *android.TestContext {
// rust mutators // rust mutators
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel() ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
}) })
return ctx return ctx