Merge "Make ANDROID_JAVA{8,9}_HOME available to config.mk."
diff --git a/android/androidmk.go b/android/androidmk.go
index fb3934f..704b560 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -230,9 +230,15 @@
if Bool(amod.commonProperties.Proprietary) {
fmt.Fprintln(&data.preamble, "LOCAL_PROPRIETARY_MODULE := true")
}
- if Bool(amod.commonProperties.Vendor) {
+ if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
fmt.Fprintln(&data.preamble, "LOCAL_VENDOR_MODULE := true")
}
+ if Bool(amod.commonProperties.Device_specific) {
+ fmt.Fprintln(&data.preamble, "LOCAL_ODM_MODULE := true")
+ }
+ if Bool(amod.commonProperties.Product_specific) {
+ fmt.Fprintln(&data.preamble, "LOCAL_OEM_MODULE := true")
+ }
if amod.commonProperties.Owner != nil {
fmt.Fprintln(&data.preamble, "LOCAL_MODULE_OWNER :=", *amod.commonProperties.Owner)
}
diff --git a/android/arch.go b/android/arch.go
index af3919c..3a22569 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -971,7 +971,7 @@
func getNdkAbisConfig() []archConfig {
return []archConfig{
- {"arm", "armv5te", "", []string{"armeabi"}},
+ {"arm", "armv7-a", "", []string{"armeabi"}},
{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
{"x86", "", "", []string{"x86"}},
{"x86_64", "", "", []string{"x86_64"}},
diff --git a/android/config.go b/android/config.go
index dd0301e..887291d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -661,6 +661,20 @@
return c.config.ProductVariables.ExtraVndkVersions
}
+func (c *deviceConfig) OdmPath() string {
+ if c.config.ProductVariables.OdmPath != nil {
+ return *c.config.ProductVariables.OdmPath
+ }
+ return "odm"
+}
+
+func (c *deviceConfig) OemPath() string {
+ if c.config.ProductVariables.OemPath != nil {
+ return *c.config.ProductVariables.OemPath
+ }
+ return "oem"
+}
+
func (c *deviceConfig) BtConfigIncludeDir() string {
return String(c.config.ProductVariables.BtConfigIncludeDir)
}
diff --git a/android/module.go b/android/module.go
index 3d8f683..cb068ab 100644
--- a/android/module.go
+++ b/android/module.go
@@ -65,7 +65,10 @@
Windows() bool
Debug() bool
PrimaryArch() bool
- InstallOnVendorPartition() bool
+ Platform() bool
+ DeviceSpecific() bool
+ SocSpecific() bool
+ ProductSpecific() bool
AConfig() Config
DeviceConfig() DeviceConfig
}
@@ -212,9 +215,26 @@
// vendor who owns this module
Owner *string
- // whether this module is device specific and should be installed into /vendor
+ // whether this module is specific to an SoC (System-On-a-Chip). When set to true,
+ // it is installed into /vendor (or /system/vendor if vendor partition does not exist).
+ // Use `soc_specific` instead for better meaning.
Vendor *bool
+ // whether this module is specific to an SoC (System-On-a-Chip). When set to true,
+ // it is installed into /vendor (or /system/vendor if vendor partition does not exist).
+ Soc_specific *bool
+
+ // whether this module is specific to a device, not only for SoC, but also for off-chip
+ // peripherals. When set to true, it is installed into /odm (or /vendor/odm if odm partition
+ // does not exist, or /system/vendor/odm if both odm and vendor partitions do not exist).
+ // This implies `soc_specific:true`.
+ Device_specific *bool
+
+ // whether this module is specific to a software configuration of a product (e.g. country,
+ // network operator, etc). When set to true, it is installed into /oem (or /system/oem if
+ // oem partition does not exist).
+ Product_specific *bool
+
// init.rc files to be installed if this module is installed
Init_rc []string
@@ -264,6 +284,30 @@
NeitherHostNorDeviceSupported
)
+type moduleKind int
+
+const (
+ platformModule moduleKind = iota
+ deviceSpecificModule
+ socSpecificModule
+ productSpecificModule
+)
+
+func (k moduleKind) String() string {
+ switch k {
+ case platformModule:
+ return "platform"
+ case deviceSpecificModule:
+ return "device-specific"
+ case socSpecificModule:
+ return "soc-specific"
+ case productSpecificModule:
+ return "product-specific"
+ default:
+ panic(fmt.Errorf("unknown module kind %d", k))
+ }
+}
+
func InitAndroidModule(m Module) {
base := m.base()
base.module = m
@@ -546,11 +590,48 @@
}
}
+func determineModuleKind(a *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
+ var socSpecific = Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
+ var deviceSpecific = Bool(a.commonProperties.Device_specific)
+ var productSpecific = Bool(a.commonProperties.Product_specific)
+
+ if ((socSpecific || deviceSpecific) && productSpecific) || (socSpecific && deviceSpecific) {
+ msg := "conflicting value set here"
+ if productSpecific {
+ ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
+ if deviceSpecific {
+ ctx.PropertyErrorf("device_specific", msg)
+ }
+ } else {
+ ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
+ }
+ if Bool(a.commonProperties.Vendor) {
+ ctx.PropertyErrorf("vendor", msg)
+ }
+ if Bool(a.commonProperties.Proprietary) {
+ ctx.PropertyErrorf("proprietary", msg)
+ }
+ if Bool(a.commonProperties.Soc_specific) {
+ ctx.PropertyErrorf("soc_specific", msg)
+ }
+ }
+
+ if productSpecific {
+ return productSpecificModule
+ } else if deviceSpecific {
+ return deviceSpecificModule
+ } else if socSpecific {
+ return socSpecificModule
+ } else {
+ return platformModule
+ }
+}
+
func (a *ModuleBase) androidBaseContextFactory(ctx blueprint.BaseModuleContext) androidBaseContextImpl {
return androidBaseContextImpl{
target: a.commonProperties.CompileTarget,
targetPrimary: a.commonProperties.CompilePrimary,
- vendor: Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Vendor),
+ kind: determineModuleKind(a, ctx),
config: ctx.Config().(Config),
}
}
@@ -606,7 +687,7 @@
target Target
targetPrimary bool
debug bool
- vendor bool
+ kind moduleKind
config Config
}
@@ -867,8 +948,20 @@
return DeviceConfig{a.config.deviceConfig}
}
-func (a *androidBaseContextImpl) InstallOnVendorPartition() bool {
- return a.vendor
+func (a *androidBaseContextImpl) Platform() bool {
+ return a.kind == platformModule
+}
+
+func (a *androidBaseContextImpl) DeviceSpecific() bool {
+ return a.kind == deviceSpecificModule
+}
+
+func (a *androidBaseContextImpl) SocSpecific() bool {
+ return a.kind == socSpecificModule
+}
+
+func (a *androidBaseContextImpl) ProductSpecific() bool {
+ return a.kind == productSpecificModule
}
func (a *androidModuleContext) InstallInData() bool {
diff --git a/android/paths.go b/android/paths.go
index e47b9e4..4d9c858 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -845,8 +845,12 @@
var partition string
if ctx.InstallInData() {
partition = "data"
- } else if ctx.InstallOnVendorPartition() {
+ } else if ctx.SocSpecific() {
partition = ctx.DeviceConfig().VendorPath()
+ } else if ctx.DeviceSpecific() {
+ partition = ctx.DeviceConfig().OdmPath()
+ } else if ctx.ProductSpecific() {
+ partition = ctx.DeviceConfig().OemPath()
} else {
partition = "system"
}
diff --git a/android/paths_test.go b/android/paths_test.go
index 1e4ba53..110974f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -246,12 +246,34 @@
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
target: deviceTarget,
- vendor: true,
+ kind: socSpecificModule,
},
},
in: []string{"bin", "my_test"},
out: "target/product/test_device/vendor/bin/my_test",
},
+ {
+ name: "odm binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: deviceSpecificModule,
+ },
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/odm/bin/my_test",
+ },
+ {
+ name: "oem binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productSpecificModule,
+ },
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/oem/bin/my_test",
+ },
{
name: "system native test binary",
@@ -269,7 +291,31 @@
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
target: deviceTarget,
- vendor: true,
+ kind: socSpecificModule,
+ },
+ inData: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/nativetest/my_test",
+ },
+ {
+ name: "odm native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: deviceSpecificModule,
+ },
+ inData: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/nativetest/my_test",
+ },
+ {
+ name: "oem native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productSpecificModule,
},
inData: true,
},
@@ -293,13 +339,37 @@
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
target: deviceTarget,
- vendor: true,
+ kind: socSpecificModule,
},
inSanitizerDir: true,
},
in: []string{"bin", "my_test"},
out: "target/product/test_device/data/asan/vendor/bin/my_test",
},
+ {
+ name: "sanitized odm binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: deviceSpecificModule,
+ },
+ inSanitizerDir: true,
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/data/asan/odm/bin/my_test",
+ },
+ {
+ name: "sanitized oem binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productSpecificModule,
+ },
+ inSanitizerDir: true,
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/data/asan/oem/bin/my_test",
+ },
{
name: "sanitized system native test binary",
@@ -318,7 +388,33 @@
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
target: deviceTarget,
- vendor: true,
+ kind: socSpecificModule,
+ },
+ inData: true,
+ inSanitizerDir: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/asan/data/nativetest/my_test",
+ },
+ {
+ name: "sanitized odm native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: deviceSpecificModule,
+ },
+ inData: true,
+ inSanitizerDir: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/asan/data/nativetest/my_test",
+ },
+ {
+ name: "sanitized oem native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productSpecificModule,
},
inData: true,
inSanitizerDir: true,
diff --git a/android/variable.go b/android/variable.go
index 155eee5..6962b0f 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -174,6 +174,8 @@
CFIIncludePaths *[]string `json:",omitempty"`
VendorPath *string `json:",omitempty"`
+ OdmPath *string `json:",omitempty"`
+ OemPath *string `json:",omitempty"`
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index c6e90f0..82b5eb9 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -152,6 +152,8 @@
"LOCAL_TIDY": "tidy",
"LOCAL_PROPRIETARY_MODULE": "proprietary",
"LOCAL_VENDOR_MODULE": "vendor",
+ "LOCAL_ODM_MODULE": "device_specific",
+ "LOCAL_OEM_MODULE": "product_specific",
"LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
"LOCAL_PRIVILEGED_MODULE": "privileged",
diff --git a/cc/cc.go b/cc/cc.go
index 04aa6a6..13d0e3b 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -425,8 +425,9 @@
moduleContextImpl
}
-func (ctx *moduleContext) InstallOnVendorPartition() bool {
- return ctx.ModuleContext.InstallOnVendorPartition() || (ctx.mod.useVndk() && !ctx.mod.isVndk())
+func (ctx *moduleContext) SocSpecific() bool {
+ return ctx.ModuleContext.SocSpecific() ||
+ (ctx.mod.hasVendorVariant() && ctx.mod.useVndk() && !ctx.mod.isVndk())
}
type moduleContextImpl struct {
@@ -1402,7 +1403,7 @@
mctx.CreateVariations(coreMode)
} else if Bool(props.Vendor_available) {
mctx.CreateVariations(coreMode, vendorMode)
- } else if mctx.InstallOnVendorPartition() {
+ } else if mctx.SocSpecific() || mctx.DeviceSpecific() {
mctx.CreateVariations(vendorMode)
} else {
mctx.CreateVariations(coreMode)
@@ -1416,9 +1417,9 @@
}
// Sanity check
- if m.VendorProperties.Vendor_available != nil && mctx.InstallOnVendorPartition() {
+ if m.VendorProperties.Vendor_available != nil && (mctx.SocSpecific() || mctx.DeviceSpecific()) {
mctx.PropertyErrorf("vendor_available",
- "doesn't make sense at the same time as `vendor: true` or `proprietary: true`")
+ "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
return
}
if vndk := m.vndkdep; vndk != nil {
@@ -1460,8 +1461,8 @@
vendor := mod[1].(*Module)
vendor.Properties.UseVndk = true
squashVendorSrcs(vendor)
- } else if mctx.InstallOnVendorPartition() && String(m.Properties.Sdk_version) == "" {
- // This will be available in /vendor only
+ } else if (mctx.SocSpecific() || mctx.DeviceSpecific()) && String(m.Properties.Sdk_version) == "" {
+ // This will be available in /vendor (or /odm) only
mod := mctx.CreateVariations(vendorMode)
vendor := mod[0].(*Module)
vendor.Properties.UseVndk = true
diff --git a/cc/compiler.go b/cc/compiler.go
index c9dcf95..7e8e8b8 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -148,6 +148,9 @@
// Stores the original list of source files before being cleared by library reuse
OriginalSrcs []string `blueprint:"mutated"`
+
+ // Build and link with OpenMP
+ Openmp *bool `android:"arch_variant"`
}
func NewBaseCompiler() *baseCompiler {
@@ -204,6 +207,10 @@
deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
}
+ if Bool(compiler.Properties.Openmp) {
+ deps.StaticLibs = append(deps.StaticLibs, "libomp")
+ }
+
return deps
}
@@ -494,6 +501,10 @@
}
}
+ if Bool(compiler.Properties.Openmp) {
+ flags.CFlags = append(flags.CFlags, "-fopenmp")
+ }
+
return flags
}
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 556dad0..df04358 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -19,6 +19,7 @@
"flag"
"fmt"
"hash/crc32"
+ "io/ioutil"
"log"
"os"
"path/filepath"
@@ -56,11 +57,13 @@
var (
sortEntries = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
emulateJar = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
+ emulatePar = flag.Bool("p", false, "merge zip entries based on par format")
stripDirs fileList
stripFiles fileList
zipsToNotStrip = make(zipsToNotStripSet)
stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file")
manifest = flag.String("m", "", "manifest file to insert in jar")
+ entrypoint = flag.String("e", "", "par entrypoint file to insert in par")
ignoreDuplicates = flag.Bool("ignore-duplicates", false, "take each entry from the first zip it exists in and don't warn")
)
@@ -72,7 +75,7 @@
func main() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, "usage: merge_zips [-jsD] [-m manifest] output [inputs...]")
+ fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [-e entrypoint] output [inputs...]")
flag.PrintDefaults()
}
@@ -118,8 +121,13 @@
log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
}
+ if *entrypoint != "" && !*emulatePar {
+ log.Fatal(errors.New("must specify -p when specifying a entrypoint via -e"))
+ }
+
// do merge
- err = mergeZips(readers, writer, *manifest, *sortEntries, *emulateJar, *stripDirEntries, *ignoreDuplicates)
+ err = mergeZips(readers, writer, *manifest, *entrypoint, *sortEntries, *emulateJar, *emulatePar,
+ *stripDirEntries, *ignoreDuplicates)
if err != nil {
log.Fatal(err)
}
@@ -210,8 +218,8 @@
source zipSource
}
-func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest string,
- sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) error {
+func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoint string,
+ sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool) error {
sourceByDest := make(map[string]zipSource, 0)
orderedMappings := []fileMapping{}
@@ -244,6 +252,68 @@
addMapping(jar.ManifestFile, fileSource)
}
+ if entrypoint != "" {
+ buf, err := ioutil.ReadFile(entrypoint)
+ if err != nil {
+ return err
+ }
+ fh := &zip.FileHeader{
+ Name: "entry_point.txt",
+ Method: zip.Store,
+ UncompressedSize64: uint64(len(buf)),
+ }
+ fh.SetMode(0700)
+ fh.SetModTime(jar.DefaultTime)
+ fileSource := bufferEntry{fh, buf}
+ addMapping("entry_point.txt", fileSource)
+ }
+
+ if emulatePar {
+ // the runfiles packages needs to be populated with "__init__.py".
+ newPyPkgs := []string{}
+ // the runfiles dirs have been treated as packages.
+ existingPyPkgSet := make(map[string]bool)
+ // put existing __init__.py files to a set first. This set is used for preventing
+ // generated __init__.py files from overwriting existing ones.
+ for _, namedReader := range readers {
+ for _, file := range namedReader.reader.File {
+ if filepath.Base(file.Name) != "__init__.py" {
+ continue
+ }
+ pyPkg := pathBeforeLastSlash(file.Name)
+ if _, found := existingPyPkgSet[pyPkg]; found {
+ panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q.", file.Name))
+ } else {
+ existingPyPkgSet[pyPkg] = true
+ }
+ }
+ }
+ for _, namedReader := range readers {
+ for _, file := range namedReader.reader.File {
+ var parentPath string /* the path after trimming last "/" */
+ if filepath.Base(file.Name) == "__init__.py" {
+ // for existing __init__.py files, we should trim last "/" for twice.
+ // eg. a/b/c/__init__.py ---> a/b
+ parentPath = pathBeforeLastSlash(pathBeforeLastSlash(file.Name))
+ } else {
+ parentPath = pathBeforeLastSlash(file.Name)
+ }
+ populateNewPyPkgs(parentPath, existingPyPkgSet, &newPyPkgs)
+ }
+ }
+ for _, pkg := range newPyPkgs {
+ var emptyBuf []byte
+ fh := &zip.FileHeader{
+ Name: filepath.Join(pkg, "__init__.py"),
+ Method: zip.Store,
+ UncompressedSize64: uint64(len(emptyBuf)),
+ }
+ fh.SetMode(0700)
+ fh.SetModTime(jar.DefaultTime)
+ fileSource := bufferEntry{fh, emptyBuf}
+ addMapping(filepath.Join(pkg, "__init__.py"), fileSource)
+ }
+ }
for _, namedReader := range readers {
_, skipStripThisZip := zipsToNotStrip[namedReader.path]
for _, file := range namedReader.reader.File {
@@ -305,6 +375,29 @@
return nil
}
+// Sets the given directory and all its ancestor directories as Python packages.
+func populateNewPyPkgs(pkgPath string, existingPyPkgSet map[string]bool, newPyPkgs *[]string) {
+ for pkgPath != "" {
+ if _, found := existingPyPkgSet[pkgPath]; !found {
+ existingPyPkgSet[pkgPath] = true
+ *newPyPkgs = append(*newPyPkgs, pkgPath)
+ // Gets its ancestor directory by trimming last slash.
+ pkgPath = pathBeforeLastSlash(pkgPath)
+ } else {
+ break
+ }
+ }
+}
+
+func pathBeforeLastSlash(path string) string {
+ ret := filepath.Dir(path)
+ // filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
+ if ret == "." || ret == "/" {
+ return ""
+ }
+ return ret
+}
+
func shouldStripFile(emulateJar bool, name string) bool {
for _, dir := range stripDirs {
if strings.HasPrefix(name, dir+"/") {
diff --git a/java/jacoco.go b/java/jacoco.go
index b26b046..59f2fd3 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -17,6 +17,7 @@
// Rules for instrumenting classes using jacoco
import (
+ "fmt"
"strings"
"github.com/google/blueprint"
@@ -59,41 +60,51 @@
})
}
-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") + " "
+func (j *Module) jacocoModuleToZipCommand(ctx android.ModuleContext) string {
+ includes, err := jacocoFiltersToSpecs(j.properties.Jacoco.Include_filter)
+ if err != nil {
+ ctx.PropertyErrorf("jacoco.include_filter", "%s", err.Error())
+ }
+ excludes, err := jacocoFiltersToSpecs(j.properties.Jacoco.Exclude_filter)
+ if err != nil {
+ ctx.PropertyErrorf("jacoco.exclude_filter", "%s", err.Error())
}
+ return jacocoFiltersToZipCommand(includes, excludes)
+}
+
+func jacocoFiltersToZipCommand(includes, excludes []string) string {
+ 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 {
+func jacocoFiltersToSpecs(filters []string) ([]string, error) {
specs := make([]string, len(filters))
+ var err error
for i, f := range filters {
- specs[i] = jacocoFilterToSpec(ctx, f, property)
+ specs[i], err = jacocoFilterToSpec(f)
+ if err != nil {
+ return nil, err
+ }
}
- return specs
+ return specs, nil
}
-func jacocoFilterToSpec(ctx android.ModuleContext, filter string, property string) string {
+func jacocoFilterToSpec(filter string) (string, error) {
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")
+ return "", fmt.Errorf("'*' is only supported as the last character in a filter")
}
spec := strings.Replace(filter, ".", "/", -1)
@@ -102,7 +113,9 @@
spec += "**/*.class"
} else if wildcard {
spec += "*.class"
+ } else {
+ spec += ".class"
}
- return spec
+ return spec, nil
}
diff --git a/java/jacoco_test.go b/java/jacoco_test.go
new file mode 100644
index 0000000..6e8b026
--- /dev/null
+++ b/java/jacoco_test.go
@@ -0,0 +1,95 @@
+// 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
+
+import "testing"
+
+func TestJacocoFilterToSpecs(t *testing.T) {
+ testCases := []struct {
+ name, in, out string
+ }{
+ {
+ name: "class",
+ in: "package.Class",
+ out: "package/Class.class",
+ },
+ {
+ name: "class wildcard",
+ in: "package.Class*",
+ out: "package/Class*.class",
+ },
+ {
+ name: "package wildcard",
+ in: "package.*",
+ out: "package/**/*.class",
+ },
+ {
+ name: "all wildcard",
+ in: "*",
+ out: "**/*.class",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ got, err := jacocoFilterToSpec(testCase.in)
+ if err != nil {
+ t.Error(err)
+ }
+ if got != testCase.out {
+ t.Errorf("expected %q got %q", testCase.out, got)
+ }
+ })
+ }
+}
+
+func TestJacocoFiltersToZipCommand(t *testing.T) {
+ testCases := []struct {
+ name string
+ includes, excludes []string
+ out string
+ }{
+ {
+ name: "implicit wildcard",
+ includes: []string{},
+ out: "**/*.class",
+ },
+ {
+ name: "only include",
+ includes: []string{"package/Class.class"},
+ out: "package/Class.class",
+ },
+ {
+ name: "multiple includes",
+ includes: []string{"package/Class.class", "package2/Class.class"},
+ out: "package/Class.class package2/Class.class",
+ },
+ {
+ name: "excludes",
+ includes: []string{"package/**/*.class"},
+ excludes: []string{"package/Class.class"},
+ out: "-x package/Class.class package/**/*.class",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ got := jacocoFiltersToZipCommand(testCase.includes, testCase.excludes)
+ if got != testCase.out {
+ t.Errorf("expected %q got %q", testCase.out, got)
+ }
+ })
+ }
+}
diff --git a/java/java.go b/java/java.go
index 05d38f2..e6ed931 100644
--- a/java/java.go
+++ b/java/java.go
@@ -894,7 +894,7 @@
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
classesJar android.Path, jarName string) android.Path {
- specs := j.jacocoStripSpecs(ctx)
+ specs := j.jacocoModuleToZipCommand(ctx)
jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco", "jacoco-report-classes.jar")
instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)