Merge "Add Jacoco support"
This commit is contained in:
commit
8b732c1cd5
|
@ -213,6 +213,7 @@ bootstrap_go_package {
|
|||
"java/app.go",
|
||||
"java/builder.go",
|
||||
"java/gen.go",
|
||||
"java/jacoco.go",
|
||||
"java/java.go",
|
||||
"java/proto.go",
|
||||
"java/resources.go",
|
||||
|
|
|
@ -42,6 +42,10 @@ func (library *Library) AndroidMk() android.AndroidMkData {
|
|||
}
|
||||
fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(library.deviceProperties.Sdk_version))
|
||||
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
|
||||
|
||||
if library.jacocoReportClassesFile != nil {
|
||||
fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", library.jacocoReportClassesFile.String())
|
||||
}
|
||||
},
|
||||
},
|
||||
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
|
||||
|
|
|
@ -54,6 +54,8 @@ type androidAppProperties struct {
|
|||
// list of directories relative to the Blueprints file containing
|
||||
// Android resources
|
||||
Resource_dirs []string
|
||||
|
||||
Instrumentation_for *string
|
||||
}
|
||||
|
||||
type AndroidApp struct {
|
||||
|
@ -119,6 +121,10 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|||
// a.properties.Proguard.Enabled = true
|
||||
//}
|
||||
|
||||
if String(a.appProperties.Instrumentation_for) == "" {
|
||||
a.properties.Instrument = true
|
||||
}
|
||||
|
||||
a.Module.compile(ctx)
|
||||
|
||||
aaptPackageFlags := append([]string(nil), aaptFlags...)
|
||||
|
|
|
@ -28,6 +28,16 @@ var (
|
|||
DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
|
||||
DefaultSystemModules = "core-system-modules"
|
||||
DefaultLibraries = []string{"ext", "framework", "okhttp"}
|
||||
|
||||
DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
|
||||
|
||||
InstrumentFrameworkModules = []string{
|
||||
"framework",
|
||||
"telephony-common",
|
||||
"services",
|
||||
"android.car",
|
||||
"android.car7",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -74,6 +84,7 @@ func init() {
|
|||
pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
|
||||
pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
|
||||
pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
|
||||
pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip")
|
||||
pctx.VariableFunc("DxCmd", func(config interface{}) (string, error) {
|
||||
if config.(android.Config).IsEnvFalse("USE_D8") {
|
||||
if config.(android.Config).UnbundledBuild() || config.(android.Config).IsPdkBuild() {
|
||||
|
@ -117,4 +128,6 @@ func init() {
|
|||
}
|
||||
return "", nil
|
||||
})
|
||||
|
||||
pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
|
||||
}
|
||||
|
|
|
@ -62,4 +62,7 @@ func makeVarsProvider(ctx android.MakeVarsContext) {
|
|||
|
||||
ctx.Strict("SOONG_JAVAC_WRAPPER", "${SoongJavacWrapper}")
|
||||
ctx.Strict("EXTRACT_SRCJARS", "${ExtractSrcJarsCmd}")
|
||||
|
||||
ctx.Strict("JACOCO_CLI_JAR", "${JacocoCLIJar}")
|
||||
ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// 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 java
|
||||
|
||||
// Rules for instrumenting classes using jacoco
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
|
||||
"android/soong/android"
|
||||
)
|
||||
|
||||
var (
|
||||
jacoco = pctx.AndroidStaticRule("jacoco", blueprint.RuleParams{
|
||||
Command: `${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` +
|
||||
`${config.JavaCmd} -jar ${config.JacocoCLIJar} instrument -quiet -dest $instrumentedJar $strippedJar && ` +
|
||||
`${config.Ziptime} $instrumentedJar && ` +
|
||||
`${config.MergeZipsCmd} --ignore-duplicates -j $out $instrumentedJar $in`,
|
||||
CommandDeps: []string{
|
||||
"${config.Zip2ZipCmd}",
|
||||
"${config.JavaCmd}",
|
||||
"${config.JacocoCLIJar}",
|
||||
"${config.Ziptime}",
|
||||
"${config.MergeZipsCmd}",
|
||||
},
|
||||
},
|
||||
"strippedJar", "stripSpec", "instrumentedJar")
|
||||
)
|
||||
|
||||
func jacocoInstrumentJar(ctx android.ModuleContext, outputJar, strippedJar android.WritablePath,
|
||||
inputJar android.Path, stripSpec string) {
|
||||
instrumentedJar := android.PathForModuleOut(ctx, "jacoco/instrumented.jar")
|
||||
|
||||
ctx.Build(pctx, android.BuildParams{
|
||||
Rule: jacoco,
|
||||
Description: "jacoco",
|
||||
Output: outputJar,
|
||||
ImplicitOutput: strippedJar,
|
||||
Input: inputJar,
|
||||
Args: map[string]string{
|
||||
"strippedJar": strippedJar.String(),
|
||||
"stripSpec": stripSpec,
|
||||
"instrumentedJar": instrumentedJar.String(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Module) jacocoStripSpecs(ctx android.ModuleContext) string {
|
||||
includes := jacocoFiltersToSpecs(ctx,
|
||||
j.properties.Jacoco.Include_filter, "jacoco.include_filter")
|
||||
excludes := jacocoFiltersToSpecs(ctx,
|
||||
j.properties.Jacoco.Exclude_filter, "jacoco.exclude_filter")
|
||||
|
||||
specs := ""
|
||||
if len(excludes) > 0 {
|
||||
specs += android.JoinWithPrefix(excludes, "-x") + " "
|
||||
}
|
||||
|
||||
if len(includes) > 0 {
|
||||
specs += strings.Join(includes, " ")
|
||||
} else {
|
||||
specs += "**/*.class"
|
||||
}
|
||||
|
||||
return specs
|
||||
}
|
||||
|
||||
func jacocoFiltersToSpecs(ctx android.ModuleContext, filters []string, property string) []string {
|
||||
specs := make([]string, len(filters))
|
||||
for i, f := range filters {
|
||||
specs[i] = jacocoFilterToSpec(ctx, f, property)
|
||||
}
|
||||
return specs
|
||||
}
|
||||
|
||||
func jacocoFilterToSpec(ctx android.ModuleContext, filter string, property string) string {
|
||||
wildcard := strings.HasSuffix(filter, "*")
|
||||
filter = strings.TrimSuffix(filter, "*")
|
||||
recursiveWildcard := wildcard && (strings.HasSuffix(filter, ".") || filter == "")
|
||||
|
||||
if strings.ContainsRune(filter, '*') {
|
||||
ctx.PropertyErrorf(property, "'*' is only supported as the last character in a filter")
|
||||
}
|
||||
|
||||
spec := strings.Replace(filter, ".", "/", -1)
|
||||
|
||||
if recursiveWildcard {
|
||||
spec += "**/*.class"
|
||||
} else if wildcard {
|
||||
spec += "*.class"
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
120
java/java.go
120
java/java.go
|
@ -51,9 +51,6 @@ func init() {
|
|||
// Renderscript
|
||||
// Post-jar passes:
|
||||
// Proguard
|
||||
// Jacoco
|
||||
// Jarjar
|
||||
// Dex
|
||||
// Rmtypedefs
|
||||
// DroidDoc
|
||||
// Findbugs
|
||||
|
@ -127,6 +124,26 @@ type CompilerProperties struct {
|
|||
// List of javac flags that should only be used when passing -source 1.9
|
||||
Javacflags []string
|
||||
}
|
||||
|
||||
Jacoco struct {
|
||||
// List of classes to include for instrumentation with jacoco to collect coverage
|
||||
// information at runtime when building with coverage enabled. If unset defaults to all
|
||||
// classes.
|
||||
// Supports '*' as the last character of an entry in the list as a wildcard match.
|
||||
// If preceded by '.' it matches all classes in the package and subpackages, otherwise
|
||||
// it matches classes in the package that have the class name as a prefix.
|
||||
Include_filter []string
|
||||
|
||||
// List of classes to exclude from instrumentation with jacoco to collect coverage
|
||||
// information at runtime when building with coverage enabled. Overrides classes selected
|
||||
// by the include_filter property.
|
||||
// Supports '*' as the last character of an entry in the list as a wildcard match.
|
||||
// If preceded by '.' it matches all classes in the package and subpackages, otherwise
|
||||
// it matches classes in the package that have the class name as a prefix.
|
||||
Exclude_filter []string
|
||||
}
|
||||
|
||||
Instrument bool `blueprint:"mutated"`
|
||||
}
|
||||
|
||||
type CompilerDeviceProperties struct {
|
||||
|
@ -177,6 +194,9 @@ type Module struct {
|
|||
// output file containing classes.dex
|
||||
dexJarFile android.Path
|
||||
|
||||
// output file containing uninstrumented classes that will be instrumented by jacoco
|
||||
jacocoReportClassesFile android.Path
|
||||
|
||||
// output file suitable for installing or running
|
||||
outputFile android.Path
|
||||
|
||||
|
@ -717,6 +737,20 @@ func (j *Module) compile(ctx android.ModuleContext) {
|
|||
j.headerJarFile = j.implementationJarFile
|
||||
}
|
||||
|
||||
if ctx.Device() && j.installable() {
|
||||
outputFile = j.desugar(ctx, flags, outputFile, jarName)
|
||||
}
|
||||
|
||||
if ctx.AConfig().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
|
||||
if inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
|
||||
j.properties.Instrument = true
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.AConfig().IsEnvTrue("EMMA_INSTRUMENT") && j.properties.Instrument {
|
||||
outputFile = j.instrument(ctx, flags, outputFile, jarName)
|
||||
}
|
||||
|
||||
if ctx.Device() && j.installable() {
|
||||
outputFile = j.compileDex(ctx, flags, outputFile, jarName)
|
||||
if ctx.Failed() {
|
||||
|
@ -766,6 +800,42 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars
|
|||
return headerJar
|
||||
}
|
||||
|
||||
func (j *Module) desugar(ctx android.ModuleContext, flags javaBuilderFlags,
|
||||
classesJar android.Path, jarName string) android.Path {
|
||||
|
||||
desugarFlags := []string{
|
||||
"--min_sdk_version " + j.minSdkVersionNumber(ctx),
|
||||
"--desugar_try_with_resources_if_needed=false",
|
||||
"--allow_empty_bootclasspath",
|
||||
}
|
||||
|
||||
if inList("--core-library", j.deviceProperties.Dxflags) {
|
||||
desugarFlags = append(desugarFlags, "--core_library")
|
||||
}
|
||||
|
||||
flags.desugarFlags = strings.Join(desugarFlags, " ")
|
||||
|
||||
desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
|
||||
TransformDesugar(ctx, desugarJar, classesJar, flags)
|
||||
|
||||
return desugarJar
|
||||
}
|
||||
|
||||
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
|
||||
classesJar android.Path, jarName string) android.Path {
|
||||
|
||||
specs := j.jacocoStripSpecs(ctx)
|
||||
|
||||
jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco", "jacoco-report-classes.jar")
|
||||
instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
|
||||
|
||||
jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
|
||||
|
||||
j.jacocoReportClassesFile = jacocoReportClassesFile
|
||||
|
||||
return instrumentedJar
|
||||
}
|
||||
|
||||
func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
|
||||
classesJar android.Path, jarName string) android.Path {
|
||||
|
||||
|
@ -792,47 +862,29 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
|
|||
"--dump-width=1000")
|
||||
}
|
||||
|
||||
var minSdkVersion string
|
||||
switch String(j.deviceProperties.Sdk_version) {
|
||||
case "", "current", "test_current", "system_current":
|
||||
minSdkVersion = strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
|
||||
default:
|
||||
minSdkVersion = String(j.deviceProperties.Sdk_version)
|
||||
}
|
||||
|
||||
dxFlags = append(dxFlags, "--min-sdk-version="+minSdkVersion)
|
||||
dxFlags = append(dxFlags, "--min-sdk-version="+j.minSdkVersionNumber(ctx))
|
||||
|
||||
flags.dxFlags = strings.Join(dxFlags, " ")
|
||||
|
||||
desugarFlags := []string{
|
||||
"--min_sdk_version " + minSdkVersion,
|
||||
"--desugar_try_with_resources_if_needed=false",
|
||||
"--allow_empty_bootclasspath",
|
||||
}
|
||||
|
||||
if inList("--core-library", dxFlags) {
|
||||
desugarFlags = append(desugarFlags, "--core_library")
|
||||
}
|
||||
|
||||
flags.desugarFlags = strings.Join(desugarFlags, " ")
|
||||
|
||||
desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
|
||||
TransformDesugar(ctx, desugarJar, classesJar, flags)
|
||||
if ctx.Failed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compile classes.jar into classes.dex and then javalib.jar
|
||||
javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
|
||||
TransformClassesJarToDexJar(ctx, javalibJar, desugarJar, flags)
|
||||
if ctx.Failed() {
|
||||
return nil
|
||||
}
|
||||
TransformClassesJarToDexJar(ctx, javalibJar, classesJar, flags)
|
||||
|
||||
j.dexJarFile = javalibJar
|
||||
return javalibJar
|
||||
}
|
||||
|
||||
// Returns a sdk version as a string that is guaranteed to be a parseable as a number. For
|
||||
// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns "10000".
|
||||
func (j *Module) minSdkVersionNumber(ctx android.ModuleContext) string {
|
||||
switch String(j.deviceProperties.Sdk_version) {
|
||||
case "", "current", "test_current", "system_current":
|
||||
return strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
|
||||
default:
|
||||
return String(j.deviceProperties.Sdk_version)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *Module) installable() bool {
|
||||
return j.properties.Installable == nil || *j.properties.Installable
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue