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:
parent
b8bb5d05ce
commit
a0cd8f9acb
|
@ -391,6 +391,7 @@ bootstrap_go_package {
|
|||
srcs: [
|
||||
"rust/androidmk.go",
|
||||
"rust/compiler.go",
|
||||
"rust/coverage.go",
|
||||
"rust/binary.go",
|
||||
"rust/builder.go",
|
||||
"rust/library.go",
|
||||
|
@ -403,6 +404,7 @@ bootstrap_go_package {
|
|||
testSrcs: [
|
||||
"rust/binary_test.go",
|
||||
"rust/compiler_test.go",
|
||||
"rust/coverage_test.go",
|
||||
"rust/library_test.go",
|
||||
"rust/rust_test.go",
|
||||
"rust/test_test.go",
|
||||
|
|
|
@ -141,6 +141,16 @@ func PrefixInList(list []string, prefix string) bool {
|
|||
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.
|
||||
func IndexListPred(pred func(s string) bool, list []string) int {
|
||||
for i, l := range list {
|
||||
|
|
|
@ -1611,6 +1611,8 @@ func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizer
|
|||
return android.InList(sanitizerName, globalSanitizerNames)
|
||||
}
|
||||
|
||||
var _ cc.Coverage = (*apexBundle)(nil)
|
||||
|
||||
func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
|
||||
return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
|
||||
}
|
||||
|
@ -1627,6 +1629,8 @@ func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
|
|||
a.properties.IsCoverageVariant = coverage
|
||||
}
|
||||
|
||||
func (a *apexBundle) EnableCoverageIfNeeded() {}
|
||||
|
||||
// TODO(jiyong) move apexFileFor* close to the apexFile type definition
|
||||
func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
|
||||
// Decide the APEX-local directory by the multilib of the library
|
||||
|
|
|
@ -117,44 +117,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
|
||||
ctx, _ := testApex(t, bp+`
|
||||
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) {
|
||||
ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
|
||||
config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
|
||||
})
|
||||
|
||||
|
|
15
cc/cc.go
15
cc/cc.go
|
@ -437,7 +437,6 @@ var (
|
|||
ndkLateStubDepTag = DependencyTag{Name: "ndk late stub", Library: true}
|
||||
vndkExtDepTag = DependencyTag{Name: "vndk extends", Library: true}
|
||||
runtimeDepTag = DependencyTag{Name: "runtime lib"}
|
||||
coverageDepTag = DependencyTag{Name: "coverage"}
|
||||
testPerSrcDepTag = DependencyTag{Name: "test_per_src"}
|
||||
)
|
||||
|
||||
|
@ -741,6 +740,15 @@ func (c *Module) OutputFile() android.OptionalPath {
|
|||
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)
|
||||
|
||||
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
|
||||
// in static libraries act as if they were whole static libraries. The same goes for
|
||||
// source based Abi dump files.
|
||||
// This should only be done for cc.Modules
|
||||
if c, ok := ccDep.(*Module); ok {
|
||||
staticLib := c.linker.(libraryInterface)
|
||||
depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
|
||||
staticLib.objs().coverageFiles...)
|
||||
depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.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()...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,10 +65,10 @@ func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
|
|||
if cov.Properties.NeedCoverageVariant {
|
||||
ctx.AddVariationDependencies([]blueprint.Variation{
|
||||
{Mutator: "link", Variation: "static"},
|
||||
}, coverageDepTag, getGcovProfileLibraryName(ctx))
|
||||
}, CoverageDepTag, getGcovProfileLibraryName(ctx))
|
||||
ctx.AddVariationDependencies([]blueprint.Variation{
|
||||
{Mutator: "link", Variation: "static"},
|
||||
}, coverageDepTag, getClangProfileLibraryName(ctx))
|
||||
}, CoverageDepTag, getClangProfileLibraryName(ctx))
|
||||
}
|
||||
return deps
|
||||
}
|
||||
|
@ -134,14 +134,14 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags
|
|||
if gcovCoverage {
|
||||
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())
|
||||
|
||||
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
|
||||
} else if clangCoverage {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -150,25 +150,30 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags
|
|||
}
|
||||
|
||||
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
|
||||
if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
|
||||
return
|
||||
return properties
|
||||
}
|
||||
|
||||
var needCoverageVariant bool
|
||||
var needCoverageBuild bool
|
||||
|
||||
if ctx.Host() {
|
||||
// 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 {
|
||||
if moduleTypeHasCoverage {
|
||||
// Check if Native_coverage is set to false. This property defaults to true.
|
||||
needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
|
||||
if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
|
||||
needCoverageVariant = BoolDefault(properties.Native_coverage, true)
|
||||
if useSdk && sdkVersion != "current" {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
@ -179,8 +184,10 @@ func (cov *coverage) begin(ctx BaseModuleContext) {
|
|||
}
|
||||
}
|
||||
|
||||
cov.Properties.NeedCoverageBuild = needCoverageBuild
|
||||
cov.Properties.NeedCoverageVariant = needCoverageVariant
|
||||
properties.NeedCoverageBuild = needCoverageBuild
|
||||
properties.NeedCoverageVariant = needCoverageVariant
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
// Coverage is an interface for non-CC modules to implement to be mutated for coverage
|
||||
|
@ -190,6 +197,7 @@ type Coverage interface {
|
|||
PreventInstall()
|
||||
HideFromMake()
|
||||
MarkAsCoverageVariant(bool)
|
||||
EnableCoverageIfNeeded()
|
||||
}
|
||||
|
||||
func coverageMutator(mctx android.BottomUpMutatorContext) {
|
||||
|
@ -212,14 +220,17 @@ func coverageMutator(mctx android.BottomUpMutatorContext) {
|
|||
m[1].(*Module).coverage.Properties.IsCoverageVariant = true
|
||||
}
|
||||
} 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
|
||||
// module which are split into "" and "cov" variants. e.g. when cc_test refers
|
||||
// to an APEX via 'data' property.
|
||||
m := mctx.CreateVariations("", "cov")
|
||||
m[0].(Coverage).MarkAsCoverageVariant(true)
|
||||
m[0].(Coverage).MarkAsCoverageVariant(false)
|
||||
m[0].(Coverage).PreventInstall()
|
||||
m[0].(Coverage).HideFromMake()
|
||||
|
||||
m[1].(Coverage).MarkAsCoverageVariant(true)
|
||||
m[1].(Coverage).EnableCoverageIfNeeded()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ type LinkableInterface interface {
|
|||
CcLibraryInterface() bool
|
||||
|
||||
OutputFile() android.OptionalPath
|
||||
CoverageFiles() android.Paths
|
||||
|
||||
IncludeDirs() android.Paths
|
||||
SetDepsInLinkOrder([]android.Path)
|
||||
|
@ -83,4 +84,5 @@ var (
|
|||
|
||||
CrtBeginDepTag = DependencyTag{Name: "crtbegin"}
|
||||
CrtEndDepTag = DependencyTag{Name: "crtend"}
|
||||
CoverageDepTag = DependencyTag{Name: "coverage"}
|
||||
)
|
||||
|
|
|
@ -192,6 +192,45 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string {
|
|||
symbol_file: "",
|
||||
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 {
|
||||
name: "libdl",
|
||||
no_libcrt: true,
|
||||
|
|
|
@ -728,6 +728,8 @@ func (a *AndroidApp) MarkAsCoverageVariant(coverage bool) {
|
|||
a.appProperties.IsCoverageVariant = coverage
|
||||
}
|
||||
|
||||
func (a *AndroidApp) EnableCoverageIfNeeded() {}
|
||||
|
||||
var _ cc.Coverage = (*AndroidApp)(nil)
|
||||
|
||||
// android_app compiles sources and Android resources into an Android application package `.apk` file.
|
||||
|
|
|
@ -46,6 +46,12 @@ func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
|
|||
}
|
||||
|
||||
func (mod *Module) AndroidMk() android.AndroidMkData {
|
||||
if mod.Properties.HideFromMake {
|
||||
return android.AndroidMkData{
|
||||
Disabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
ret := android.AndroidMkData{
|
||||
OutputFile: mod.outputFile,
|
||||
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.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
|
||||
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() {
|
||||
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
|
||||
}
|
||||
if library.coverageOutputZipFile.Valid() {
|
||||
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+library.coverageOutputZipFile.String())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -35,9 +35,10 @@ type BinaryCompilerProperties struct {
|
|||
type binaryDecorator struct {
|
||||
*baseCompiler
|
||||
|
||||
Properties BinaryCompilerProperties
|
||||
distFile android.OptionalPath
|
||||
unstrippedOutputFile android.Path
|
||||
Properties BinaryCompilerProperties
|
||||
distFile android.OptionalPath
|
||||
coverageOutputZipFile android.OptionalPath
|
||||
unstrippedOutputFile android.Path
|
||||
}
|
||||
|
||||
var _ compiler = (*binaryDecorator)(nil)
|
||||
|
@ -104,6 +105,10 @@ func (binary *binaryDecorator) compilerProps() []interface{} {
|
|||
&binary.Properties)
|
||||
}
|
||||
|
||||
func (binary *binaryDecorator) nativeCoverage() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
|
||||
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...)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (binary *binaryDecorator) coverageOutputZipPath() android.OptionalPath {
|
||||
return binary.coverageOutputZipFile
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/pathtools"
|
||||
|
||||
"android/soong/android"
|
||||
)
|
||||
|
@ -36,44 +37,57 @@ var (
|
|||
Depfile: "$out.d",
|
||||
},
|
||||
"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,
|
||||
outputFile android.WritablePath, includeDirs []string) {
|
||||
outputFile android.WritablePath, includeDirs []string) buildOutput {
|
||||
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,
|
||||
outputFile android.WritablePath, includeDirs []string) {
|
||||
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
|
||||
outputFile android.WritablePath, includeDirs []string) buildOutput {
|
||||
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", includeDirs)
|
||||
}
|
||||
|
||||
func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
|
||||
outputFile android.WritablePath, includeDirs []string) {
|
||||
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
|
||||
outputFile android.WritablePath, includeDirs []string) buildOutput {
|
||||
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", includeDirs)
|
||||
}
|
||||
|
||||
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")
|
||||
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,
|
||||
outputFile android.WritablePath, includeDirs []string) {
|
||||
outputFile android.WritablePath, includeDirs []string) buildOutput {
|
||||
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,
|
||||
flags Flags, outputFile android.WritablePath, includeDirs []string) {
|
||||
transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
|
||||
flags Flags, outputFile android.WritablePath, includeDirs []string) buildOutput {
|
||||
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", includeDirs)
|
||||
}
|
||||
|
||||
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,
|
||||
outputFile android.WritablePath, crate_type string, includeDirs []string) {
|
||||
outputFile android.WritablePath, crate_type string, includeDirs []string) buildOutput {
|
||||
|
||||
var inputs android.Paths
|
||||
var implicits android.Paths
|
||||
var output buildOutput
|
||||
var libFlags, rustcFlags, linkFlags []string
|
||||
var implicitOutputs android.WritablePaths
|
||||
|
||||
output.outputFile = outputFile
|
||||
crate_name := ctx.(ModuleContext).CrateName()
|
||||
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())
|
||||
}
|
||||
|
||||
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{
|
||||
Rule: rustc,
|
||||
Description: "rustc " + main.Rel(),
|
||||
Output: outputFile,
|
||||
Inputs: inputs,
|
||||
Implicits: implicits,
|
||||
Rule: rustc,
|
||||
Description: "rustc " + main.Rel(),
|
||||
Output: outputFile,
|
||||
ImplicitOutputs: implicitOutputs,
|
||||
Inputs: inputs,
|
||||
Implicits: implicits,
|
||||
Args: map[string]string{
|
||||
"rustcFlags": strings.Join(rustcFlags, " "),
|
||||
"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{}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ type baseCompiler struct {
|
|||
linkDirs []string
|
||||
edition string
|
||||
src android.Path //rustc takes a single src file
|
||||
coverageFile android.Path //rustc generates a single gcno file
|
||||
|
||||
// Install related
|
||||
dir string
|
||||
|
@ -120,6 +121,10 @@ type baseCompiler struct {
|
|||
location installLocation
|
||||
}
|
||||
|
||||
func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
|
||||
panic("baseCompiler does not implement coverageOutputZipPath()")
|
||||
}
|
||||
|
||||
var _ compiler = (*baseCompiler)(nil)
|
||||
|
||||
func (compiler *baseCompiler) inData() bool {
|
||||
|
@ -235,6 +240,10 @@ func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath
|
|||
compiler.relativeInstallPath(), compiler.relative)
|
||||
}
|
||||
|
||||
func (compiler *baseCompiler) nativeCoverage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
|
||||
compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), 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, "")
|
||||
}
|
||||
}
|
|
@ -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"])
|
||||
}
|
||||
}
|
|
@ -75,11 +75,12 @@ type LibraryMutatedProperties struct {
|
|||
type libraryDecorator struct {
|
||||
*baseCompiler
|
||||
|
||||
Properties LibraryCompilerProperties
|
||||
MutatedProperties LibraryMutatedProperties
|
||||
distFile android.OptionalPath
|
||||
unstrippedOutputFile android.Path
|
||||
includeDirs android.Paths
|
||||
Properties LibraryCompilerProperties
|
||||
MutatedProperties LibraryMutatedProperties
|
||||
distFile android.OptionalPath
|
||||
coverageOutputZipFile android.OptionalPath
|
||||
unstrippedOutputFile android.Path
|
||||
includeDirs android.Paths
|
||||
}
|
||||
|
||||
type libraryInterface interface {
|
||||
|
@ -107,6 +108,10 @@ type libraryInterface interface {
|
|||
BuildOnlyShared()
|
||||
}
|
||||
|
||||
func (library *libraryDecorator) nativeCoverage() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (library *libraryDecorator) exportedDirs() []string {
|
||||
return library.linkDirs
|
||||
}
|
||||
|
@ -351,24 +356,37 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
|
|||
fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
|
||||
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() {
|
||||
fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
|
||||
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() {
|
||||
fileName := library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
|
||||
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() {
|
||||
fileName := library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
|
||||
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() {
|
||||
library.reexportDirs(deps.linkDirs...)
|
||||
library.reexportDepFlags(deps.depFlags...)
|
||||
|
|
|
@ -69,3 +69,7 @@ func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Dep
|
|||
deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
|
||||
return deps
|
||||
}
|
||||
|
||||
func (prebuilt *prebuiltLibraryDecorator) nativeCoverage() bool {
|
||||
return false
|
||||
}
|
||||
|
|
107
rust/rust.go
107
rust/rust.go
|
@ -40,6 +40,7 @@ func init() {
|
|||
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
|
||||
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
|
||||
ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
|
||||
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
|
||||
})
|
||||
pctx.Import("android/soong/rust/config")
|
||||
}
|
||||
|
@ -51,6 +52,7 @@ type Flags struct {
|
|||
LinkFlags []string // Flags that apply to linker
|
||||
RustFlagsDeps android.Paths // Files depended on by compiler flags
|
||||
Toolchain config.Toolchain
|
||||
Coverage bool
|
||||
}
|
||||
|
||||
type BaseProperties struct {
|
||||
|
@ -60,6 +62,8 @@ type BaseProperties struct {
|
|||
AndroidMkSharedLibs []string
|
||||
AndroidMkStaticLibs []string
|
||||
SubName string `blueprint:"mutated"`
|
||||
PreventInstall bool
|
||||
HideFromMake bool
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
|
@ -72,6 +76,7 @@ type Module struct {
|
|||
multilib android.Multilib
|
||||
|
||||
compiler compiler
|
||||
coverage *coverage
|
||||
cachedToolchain config.Toolchain
|
||||
subAndroidMkOnce map[subAndroidMkProvider]bool
|
||||
outputFile android.OptionalPath
|
||||
|
@ -224,6 +229,8 @@ type PathDeps struct {
|
|||
depFlags []string
|
||||
//ReexportedDeps android.Paths
|
||||
|
||||
coverageFiles android.Paths
|
||||
|
||||
CrtBegin android.OptionalPath
|
||||
CrtEnd android.OptionalPath
|
||||
}
|
||||
|
@ -245,6 +252,34 @@ type compiler interface {
|
|||
inData() bool
|
||||
install(ctx ModuleContext, path android.Path)
|
||||
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 {
|
||||
|
@ -268,6 +303,7 @@ func DefaultsFactory(props ...interface{}) android.Module {
|
|||
&ProcMacroCompilerProperties{},
|
||||
&PrebuiltProperties{},
|
||||
&TestProperties{},
|
||||
&cc.CoverageProperties{},
|
||||
)
|
||||
|
||||
android.InitDefaultsModule(module)
|
||||
|
@ -395,6 +431,18 @@ func (mod *Module) HasStaticVariant() bool {
|
|||
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)
|
||||
|
||||
func (mod *Module) Init() android.Module {
|
||||
|
@ -403,6 +451,10 @@ func (mod *Module) Init() android.Module {
|
|||
if mod.compiler != nil {
|
||||
mod.AddProperties(mod.compiler.compilerProps()...)
|
||||
}
|
||||
if mod.coverage != nil {
|
||||
mod.AddProperties(mod.coverage.props()...)
|
||||
}
|
||||
|
||||
android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
|
||||
|
||||
android.InitDefaultableModule(mod)
|
||||
|
@ -432,6 +484,7 @@ func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib)
|
|||
}
|
||||
func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
|
||||
module := newBaseModule(hod, multilib)
|
||||
module.coverage = &coverage{}
|
||||
return module
|
||||
}
|
||||
|
||||
|
@ -454,6 +507,7 @@ type ModuleContextIntf interface {
|
|||
toolchain() config.Toolchain
|
||||
baseModuleName() string
|
||||
CrateName() string
|
||||
nativeCoverage() bool
|
||||
}
|
||||
|
||||
type depsContext struct {
|
||||
|
@ -466,6 +520,14 @@ type moduleContext struct {
|
|||
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 {
|
||||
mod *Module
|
||||
ctx BaseModuleContext
|
||||
|
@ -508,9 +570,17 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
|
|||
|
||||
if mod.compiler != nil {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
if mod.coverage != nil {
|
||||
deps = mod.coverage.deps(ctx, deps)
|
||||
}
|
||||
|
||||
deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
|
||||
deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
|
||||
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
|
||||
|
@ -553,6 +627,12 @@ var (
|
|||
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 {
|
||||
var depPaths PathDeps
|
||||
|
||||
|
@ -588,6 +668,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
|
|||
ctx.ModuleErrorf("mod %q not an rlib library", depName)
|
||||
return
|
||||
}
|
||||
depPaths.coverageFiles = append(depPaths.coverageFiles, rustDep.CoverageFiles()...)
|
||||
directRlibDeps = append(directRlibDeps, rustDep)
|
||||
mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
|
||||
case procMacroDepTag:
|
||||
|
@ -642,6 +723,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
|
|||
depFlag = "-lstatic=" + libName
|
||||
depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
|
||||
depPaths.depFlags = append(depPaths.depFlags, depFlag)
|
||||
depPaths.coverageFiles = append(depPaths.coverageFiles, ccDep.CoverageFiles()...)
|
||||
directStaticLibDeps = append(directStaticLibDeps, ccDep)
|
||||
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
|
||||
case cc.SharedDepTag:
|
||||
|
@ -772,6 +854,29 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
|
|||
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 {
|
||||
name := mod.ModuleBase.Name()
|
||||
if p, ok := mod.compiler.(interface {
|
||||
|
|
|
@ -21,6 +21,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/blueprint/proptools"
|
||||
|
||||
"android/soong/android"
|
||||
"android/soong/cc"
|
||||
)
|
||||
|
@ -57,6 +59,7 @@ func testConfig(bp string) android.Config {
|
|||
|
||||
fs := map[string][]byte{
|
||||
"foo.rs": nil,
|
||||
"foo.c": nil,
|
||||
"src/bar.rs": nil,
|
||||
"liby.so": nil,
|
||||
"libz.so": nil,
|
||||
|
@ -68,6 +71,14 @@ func testConfig(bp string) android.Config {
|
|||
}
|
||||
|
||||
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)
|
||||
if runtime.GOOS != "linux" {
|
||||
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()
|
||||
config := testConfig(bp)
|
||||
|
||||
if coverage {
|
||||
config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
|
||||
config.TestProductVariables.CoveragePaths = []string{"*"}
|
||||
}
|
||||
|
||||
t.Helper()
|
||||
ctx := CreateTestContext()
|
||||
ctx.Register(config)
|
||||
|
|
|
@ -50,6 +50,10 @@ type testDecorator struct {
|
|||
testConfig android.Path
|
||||
}
|
||||
|
||||
func (test *testDecorator) nativeCoverage() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
|
||||
module := newModule(hod, android.MultilibFirst)
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ func CreateTestContext() *android.TestContext {
|
|||
// rust mutators
|
||||
ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
|
||||
ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
|
||||
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
|
||||
})
|
||||
|
||||
return ctx
|
||||
|
|
Loading…
Reference in New Issue