Merge master@5406228 into qt-dev.
am: 996d82708d
Change-Id: I6b1eb68ffb4fedcc70d4ef1a73fc992b4f88da31
diff --git a/android/namespace.go b/android/namespace.go
index dca2b8c..50bdcba 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -355,15 +355,19 @@
var _ blueprint.Namespace = (*Namespace)(nil)
+type namespaceProperties struct {
+ // a list of namespaces that contain modules that will be referenced
+ // by modules in this namespace.
+ Imports []string `android:"path"`
+}
+
type NamespaceModule struct {
ModuleBase
namespace *Namespace
resolver *NameResolver
- properties struct {
- Imports []string
- }
+ properties namespaceProperties
}
func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -376,6 +380,16 @@
return *n.nameProperties.Name
}
+// soong_namespace provides a scope to modules in an Android.bp file to prevent
+// module name conflicts with other defined modules in different Android.bp
+// files. Once soong_namespace has been defined in an Android.bp file, the
+// namespacing is applied to all modules that follow the soong_namespace in
+// the current Android.bp file, as well as modules defined in Android.bp files
+// in subdirectories. An Android.bp file in a subdirectory can define its own
+// soong_namespace which is applied to all its modules and as well as modules
+// defined in subdirectories Android.bp files. Modules in a soong_namespace are
+// visible to Make by listing the namespace path in PRODUCT_SOONG_NAMESPACES
+// make variable in a makefile.
func NamespaceFactory() Module {
module := &NamespaceModule{}
diff --git a/android/override_module.go b/android/override_module.go
index 02db359..119bca1 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -134,10 +134,15 @@
// Overrides a base module with the given OverrideModule.
func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
+ // Adds the base module to the overrides property, if exists, of the overriding module. See the
+ // comment on OverridableModuleBase.overridesProperty for details.
+ if b.overridesProperty != nil {
+ *b.overridesProperty = append(*b.overridesProperty, b.Name())
+ }
for _, p := range b.overridableProperties {
for _, op := range o.getOverridingProperties() {
if proptools.TypeEqual(p, op) {
- err := proptools.PrependProperties(p, op, nil)
+ err := proptools.AppendProperties(p, op, nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -148,13 +153,6 @@
}
}
}
- // Adds the base module to the overrides property, if exists, of the overriding module. See the
- // comment on OverridableModuleBase.overridesProperty for details.
- if b.overridesProperty != nil {
- *b.overridesProperty = append(*b.overridesProperty, b.Name())
- }
- // The base module name property has to be updated separately for Name() to work as intended.
- b.module.base().nameProperties.Name = proptools.StringPtr(o.Name())
}
// Mutators for override/overridable modules. All the fun happens in these functions. It is critical
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5bd0e2d..df25a89 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -18,6 +18,7 @@
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
// This file implements common functionality for handling modules that may exist as prebuilts,
@@ -42,6 +43,7 @@
properties PrebuiltProperties
module Module
srcs *[]string
+ src *string
}
func (p *Prebuilt) Name(name string) string {
@@ -49,19 +51,27 @@
}
func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
- if len(*p.srcs) == 0 {
- ctx.PropertyErrorf("srcs", "missing prebuilt source file")
- return nil
- }
+ if p.srcs != nil {
+ if len(*p.srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "missing prebuilt source file")
+ return nil
+ }
- if len(*p.srcs) > 1 {
- ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
- return nil
- }
+ if len(*p.srcs) > 1 {
+ ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
+ return nil
+ }
- // Return the singleton source after expanding any filegroup in the
- // sources.
- return PathForModuleSrc(ctx, (*p.srcs)[0])
+ // Return the singleton source after expanding any filegroup in the
+ // sources.
+ return PathForModuleSrc(ctx, (*p.srcs)[0])
+ } else {
+ if proptools.String(p.src) == "" {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ return nil
+ }
+ return PathForModuleSrc(ctx, *p.src)
+ }
}
func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
@@ -70,13 +80,19 @@
p.srcs = srcs
}
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+ p := module.Prebuilt()
+ module.AddProperties(&p.properties)
+ p.src = src
+}
+
type PrebuiltInterface interface {
Module
Prebuilt() *Prebuilt
}
func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
- ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
+ ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
}
func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
@@ -84,9 +100,9 @@
ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
}
-// prebuiltMutator ensures that there is always a module with an undecorated name, and marks
+// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
// prebuilt modules that have both a prebuilt and a source module.
-func prebuiltMutator(ctx BottomUpMutatorContext) {
+func PrebuiltMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
name := m.base().BaseModuleName()
@@ -104,7 +120,7 @@
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
- if p.srcs == nil {
+ if p.srcs == nil && p.src == nil {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
@@ -143,7 +159,11 @@
// usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt
// will be used if it is marked "prefer" or if the source module is disabled.
func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
- if len(*p.srcs) == 0 {
+ if p.srcs != nil && len(*p.srcs) == 0 {
+ return false
+ }
+
+ if p.src != nil && *p.src == "" {
return false
}
diff --git a/apex/apex.go b/apex/apex.go
index c1f52a6..e07fae0 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -149,6 +149,7 @@
android.RegisterModuleType("apex", apexBundleFactory)
android.RegisterModuleType("apex_test", testApexBundleFactory)
android.RegisterModuleType("apex_defaults", defaultsFactory)
+ android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
@@ -1283,3 +1284,113 @@
android.InitDefaultsModule(module)
return module
}
+
+//
+// Prebuilt APEX
+//
+type Prebuilt struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties PrebuiltProperties
+
+ inputApex android.Path
+ installDir android.OutputPath
+}
+
+type PrebuiltProperties struct {
+ // the path to the prebuilt .apex file to import.
+ Source string `blueprint:"mutated"`
+
+ Src *string
+ Arch struct {
+ Arm struct {
+ Src *string
+ }
+ Arm64 struct {
+ Src *string
+ }
+ X86 struct {
+ Src *string
+ }
+ X86_64 struct {
+ Src *string
+ }
+ }
+
+ // the name of the apex_key module that contains the matching public key to be installed.
+ Key *string
+}
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if String(p.properties.Key) == "" {
+ ctx.ModuleErrorf("key is missing")
+ return
+ }
+ ctx.AddDependency(ctx.Module(), keyTag, *p.properties.Key)
+
+ // This is called before prebuilt_select and prebuilt_postdeps mutators
+ // The mutators requires that src to be set correctly for each arch so that
+ // arch variants are disabled when src is not provided for the arch.
+ if len(ctx.MultiTargets()) != 1 {
+ ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ return
+ }
+ var src string
+ switch ctx.MultiTargets()[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.properties.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.properties.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.properties.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.properties.Arch.X86_64.Src)
+ default:
+ ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+ return
+ }
+ if src == "" {
+ src = String(p.properties.Src)
+ }
+ p.properties.Source = src
+}
+
+func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // TODO(jungjw): Check the key validity.
+ p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.installDir = android.PathForModuleInstall(ctx, "apex")
+ ctx.InstallFile(p.installDir, ctx.ModuleName()+imageApexSuffix, p.inputApex)
+}
+
+func (p *Prebuilt) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *Prebuilt) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *Prebuilt) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.inputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", p.BaseModuleName()+imageApexSuffix)
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(p.properties.Key))
+ },
+ },
+ }
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func PrebuiltFactory() android.Module {
+ module := &Prebuilt{}
+ module.AddProperties(&module.properties)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Source)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8a2e55a..1e8d5b4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -15,6 +15,8 @@
package apex
import (
+ "bufio"
+ "bytes"
"io/ioutil"
"os"
"strings"
@@ -36,11 +38,14 @@
ctx.RegisterModuleType("apex_test", android.ModuleFactoryAdaptor(testApexBundleFactory))
ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))
ctx.RegisterModuleType("apex_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+ ctx.RegisterModuleType("prebuilt_apex", android.ModuleFactoryAdaptor(PrebuiltFactory))
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator)
+ ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
+ ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
})
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
@@ -54,6 +59,9 @@
ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
+ })
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("image", cc.ImageMutator).Parallel()
ctx.BottomUp("link", cc.LinkageMutator).Parallel()
@@ -163,6 +171,8 @@
"custom_notice": nil,
"testkey2.avbpubkey": nil,
"testkey2.pem": nil,
+ "myapex-arm64.apex": nil,
+ "myapex-arm.apex": nil,
})
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -1229,3 +1239,54 @@
t.Errorf("wrong private key path. expected %q. actual %q", expected_privkey, actual_privkey)
}
}
+
+func TestPrebuilt(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ key: "myapex.key"
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ product_specific: true,
+ }
+ `)
+
+ prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+
+ expectedInput := "myapex-arm64.apex"
+ if prebuilt.inputApex.String() != expectedInput {
+ t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String())
+ }
+
+ // Check if the key module is added as a required module.
+ buf := &bytes.Buffer{}
+ prebuilt.AndroidMk().Extra[0](buf, nil)
+ found := false
+ scanner := bufio.NewScanner(bytes.NewReader(buf.Bytes()))
+ expected := "myapex.key"
+ for scanner.Scan() {
+ line := scanner.Text()
+ tok := strings.Split(line, " := ")
+ if tok[0] == "LOCAL_REQUIRED_MODULES" {
+ found = true
+ if tok[1] != "myapex.key" {
+ t.Errorf("Unexpected LOCAL_REQUIRED_MODULES '%s', expected '%s'", tok[1], expected)
+ }
+ }
+ }
+ if !found {
+ t.Errorf("Couldn't find a LOCAL_REQUIRED_MODULES entry")
+ }
+}
diff --git a/apex/key.go b/apex/key.go
index 848e8ce..fbd29bc 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -93,7 +93,7 @@
pubKeyName := m.public_key_file.Base()[0 : len(m.public_key_file.Base())-len(m.public_key_file.Ext())]
privKeyName := m.private_key_file.Base()[0 : len(m.private_key_file.Base())-len(m.private_key_file.Ext())]
- if pubKeyName != privKeyName {
+ if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
m.public_key_file.String(), pubKeyName, m.private_key_file, privKeyName)
return
diff --git a/cc/binary.go b/cc/binary.go
index 7f7c600..51e68fc 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -57,13 +57,13 @@
android.RegisterModuleType("cc_binary_host", binaryHostFactory)
}
-// Module factory for binaries
+// cc_binary produces a binary that is runnable on a device.
func BinaryFactory() android.Module {
module, _ := NewBinary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host binaries
+// cc_binary_host produces a binary that is runnable on a host.
func binaryHostFactory() android.Module {
module, _ := NewBinary(android.HostSupported)
return module.Init()
@@ -417,6 +417,10 @@
return binary.symlinks
}
+func (binary *binaryDecorator) nativeCoverage() bool {
+ return true
+}
+
// /system/bin/linker -> /apex/com.android.runtime/bin/linker
func (binary *binaryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
dir := binary.baseInstaller.installDir(ctx)
diff --git a/cc/builder.go b/cc/builder.go
index dab887c..6dd7c05 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -70,6 +70,8 @@
CommandDeps: []string{"$ldCmd"},
Rspfile: "${out}.rsp",
RspfileContent: "${in}",
+ // clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
+ Restat: true,
},
"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags")
diff --git a/cc/cc.go b/cc/cc.go
index c80d00c..f5a1567 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -267,6 +267,7 @@
isStubs() bool
bootstrap() bool
mustUseVendorVariant() bool
+ nativeCoverage() bool
}
type ModuleContext interface {
@@ -312,6 +313,8 @@
link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
appendLdflags([]string)
unstrippedOutputFilePath() android.Path
+
+ nativeCoverage() bool
}
type installer interface {
@@ -604,6 +607,10 @@
return Bool(c.Properties.Bootstrap)
}
+func (c *Module) nativeCoverage() bool {
+ return c.linker != nil && c.linker.nativeCoverage()
+}
+
func isBionic(name string) bool {
switch name {
case "libc", "libm", "libdl", "linker":
@@ -794,6 +801,10 @@
return ctx.mod.bootstrap()
}
+func (ctx *moduleContextImpl) nativeCoverage() bool {
+ return ctx.mod.nativeCoverage()
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -1966,6 +1977,11 @@
func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
+// cc_defaults provides a set of properties that can be inherited by other cc
+// modules. A module can use the properties from a cc_defaults using
+// `defaults: ["<:default_module_name>"]`. Properties of both modules are
+// merged (when possible) by prepending the default module's values to the
+// depending module's values.
func defaultsFactory() android.Module {
return DefaultsFactory()
}
diff --git a/cc/compiler.go b/cc/compiler.go
index 0ab1f01..fe46a3c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -226,7 +226,8 @@
}
if compiler.hasSrcExt(".sysprop") {
- deps.SharedLibs = append(deps.SharedLibs, "libbase")
+ deps.HeaderLibs = append(deps.HeaderLibs, "libbase_headers")
+ deps.SharedLibs = append(deps.SharedLibs, "liblog")
}
if Bool(compiler.Properties.Openmp) {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 16415ca..a87d569 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -132,13 +132,13 @@
// Warnings from clang-8.0
"-Wno-defaulted-function-deleted",
- }, " "))
- pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
// Disable -Winconsistent-missing-override until we can clean up the existing
// codebase for it.
"-Wno-inconsistent-missing-override",
+ }, " "))
+ pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
// Enable clang's thread-safety annotations in libcxx.
// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
diff --git a/cc/config/global.go b/cc/config/global.go
index e3fab0c..372ffc4 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -120,8 +120,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r353983"
- ClangDefaultShortVersion = "9.0.1"
+ ClangDefaultVersion = "clang-r353983b"
+ ClangDefaultShortVersion = "9.0.2"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/coverage.go b/cc/coverage.go
index ad2f1e4..9dc7f06 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -23,6 +23,9 @@
type CoverageProperties struct {
Native_coverage *bool
+ NeedCoverageVariant bool `blueprint:"mutated"`
+ NeedCoverageBuild bool `blueprint:"mutated"`
+
CoverageEnabled bool `blueprint:"mutated"`
IsCoverageVariant bool `blueprint:"mutated"`
}
@@ -38,9 +41,24 @@
return []interface{}{&cov.Properties}
}
-func (cov *coverage) begin(ctx BaseModuleContext) {}
-
func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
+ if cov.Properties.NeedCoverageBuild {
+ // Link libprofile-extras/libprofile-extras_ndk when coverage
+ // variant is required. This is a no-op unless coverage is
+ // actually enabled during linking, when
+ // '-uinit_profile_extras' is added (in flags()) to force the
+ // setup code in libprofile-extras be linked into the
+ // binary/library.
+ //
+ // We cannot narrow it further to only the 'cov' variant since
+ // the mutator hasn't run (and we don't have the 'cov' variant
+ // yet).
+ if !ctx.useSdk() {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras")
+ } else {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras_ndk")
+ }
+ }
return deps
}
@@ -95,46 +113,54 @@
if cov.linkCoverage {
flags.LdFlags = append(flags.LdFlags, "--coverage")
+
+ // Force linking of constructor/setup code in libprofile-extras
+ flags.LdFlags = append(flags.LdFlags, "-uinit_profile_extras")
}
return flags
}
-func coverageMutator(mctx android.BottomUpMutatorContext) {
+func (cov *coverage) begin(ctx BaseModuleContext) {
// Coverage is disabled globally
- if !mctx.DeviceConfig().NativeCoverageEnabled() {
+ if !ctx.DeviceConfig().NativeCoverageEnabled() {
return
}
- if c, ok := mctx.Module().(*Module); ok {
- var needCoverageVariant bool
- var needCoverageBuild bool
+ var needCoverageVariant bool
+ var needCoverageBuild bool
- if mctx.Host() {
- // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
- // Just turn off for now.
- } else if c.IsStubs() {
- // Do not enable coverage for platform stub libraries
- } else if c.isNDKStubLibrary() {
- // Do not enable coverage for NDK stub libraries
- } else if c.coverage != nil {
- // Check if Native_coverage is set to false. This property defaults to true.
- needCoverageVariant = BoolDefault(c.coverage.Properties.Native_coverage, true)
+ 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 {
+ // Check if Native_coverage is set to false. This property defaults to true.
+ needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
- if sdk_version := String(c.Properties.Sdk_version); sdk_version != "current" {
- // Native coverage is not supported for SDK versions < 23
- if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
- needCoverageVariant = false
- }
- }
-
- if needCoverageVariant {
- // Coverage variant is actually built with coverage if enabled for its module path
- needCoverageBuild = mctx.DeviceConfig().CoverageEnabledForPath(mctx.ModuleDir())
+ if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
+ // Native coverage is not supported for SDK versions < 23
+ if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
+ needCoverageVariant = false
}
}
if needCoverageVariant {
+ // Coverage variant is actually built with coverage if enabled for its module path
+ needCoverageBuild = ctx.DeviceConfig().CoverageEnabledForPath(ctx.ModuleDir())
+ }
+ }
+
+ cov.Properties.NeedCoverageBuild = needCoverageBuild
+ cov.Properties.NeedCoverageVariant = needCoverageVariant
+}
+
+func coverageMutator(mctx android.BottomUpMutatorContext) {
+ if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
+ needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
+ needCoverageBuild := c.coverage.Properties.NeedCoverageBuild
+ if needCoverageVariant {
m := mctx.CreateVariations("", "cov")
// Setup the non-coverage version and set HideFromMake and
diff --git a/cc/library.go b/cc/library.go
index e5bb347..cab75ac 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -153,42 +153,48 @@
android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
}
-// Module factory for combined static + shared libraries, device by default but with possible host
-// support
+// cc_library creates both static and/or shared libraries for a device and/or
+// host. By default, a cc_library has a single variant that targets the device.
+// Specifying `host_supported: true` also creates a library that targets the
+// host.
func LibraryFactory() android.Module {
module, _ := NewLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for static libraries
+// cc_library_static creates a static library for a device and/or host binary.
func LibraryStaticFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for shared libraries
+// cc_library_shared creates a shared library for a device and/or host.
func LibrarySharedFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for host static libraries
+// cc_library_host_static creates a static library that is linkable to a host
+// binary.
func LibraryHostStaticFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for host shared libraries
+// cc_library_host_shared creates a shared library that is usable on a host.
func LibraryHostSharedFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for header-only libraries
+// cc_library_headers contains a set of c/c++ headers which are imported by
+// other soong cc modules using the header_libs property. For best practices,
+// use export_include_dirs property or LOCAL_EXPORT_C_INCLUDE_DIRS for
+// Make.
func LibraryHeaderFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.HeaderOnly()
@@ -755,6 +761,13 @@
return library.unstrippedOutputFile
}
+func (library *libraryDecorator) nativeCoverage() bool {
+ if library.header() || library.buildStubs() {
+ return false
+ }
+ return true
+}
+
func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
isLlndk := inList(ctx.baseModuleName(), llndkLibraries) || inList(ctx.baseModuleName(), ndkMigratedLibs)
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index cdd2c48..5a36b7f 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -161,6 +161,10 @@
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *llndkStubDecorator) nativeCoverage() bool {
+ return false
+}
+
func NewLLndkStubLibrary() *Module {
module, library := NewLibrary(android.DeviceSupported)
library.BuildOnlyShared()
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 3ae4452..7199467 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -338,6 +338,10 @@
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *stubDecorator) nativeCoverage() bool {
+ return false
+}
+
func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
arch := ctx.Target().Arch.ArchType.Name
apiLevel := stub.properties.ApiLevel
diff --git a/cc/object.go b/cc/object.go
index b9c5742..50ecc38 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -33,6 +33,9 @@
Properties ObjectLinkerProperties
}
+// cc_object runs the compiler without running the linker. It is rarely
+// necessary, but sometimes used to generate .s files from .c files to use as
+// input to a cc_genrule module.
func ObjectFactory() android.Module {
module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
module.linker = &objectLinker{
@@ -111,3 +114,7 @@
func (object *objectLinker) unstrippedOutputFilePath() android.Path {
return nil
}
+
+func (object *objectLinker) nativeCoverage() bool {
+ return true
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index 9363916..7334ea2 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -27,8 +27,11 @@
var (
// Add flags to ignore warnings that profiles are old or missing for
- // some functions
- profileUseOtherFlags = []string{"-Wno-backend-plugin"}
+ // some functions, and turn on the experimental new pass manager.
+ profileUseOtherFlags = []string{
+ "-Wno-backend-plugin",
+ "-fexperimental-new-pass-manager",
+ }
globalPgoProfileProjects = []string{
"toolchain/pgo-profiles",
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 4c893d4..966ec36 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -108,6 +108,10 @@
return p.libraryDecorator.shared()
}
+func (p *prebuiltLibraryLinker) nativeCoverage() bool {
+ return false
+}
+
func prebuiltSharedLibraryFactory() android.Module {
module, _ := NewPrebuiltSharedLibrary(android.HostAndDeviceSupported)
return module.Init()
diff --git a/cc/test.go b/cc/test.go
index 045cc4f..dae2a37 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -74,31 +74,41 @@
android.RegisterModuleType("cc_benchmark_host", BenchmarkHostFactory)
}
-// Module factory for tests
+// cc_test generates a test config file and an executable binary file to test
+// specific functionality on a device. The executable binary gets an implicit
+// static_libs dependency on libgtests unless the gtest flag is set to false.
func TestFactory() android.Module {
module := NewTest(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for test libraries
+// cc_test_library creates an archive of files (i.e. .o files) which is later
+// referenced by another module (such as cc_test, cc_defaults or cc_test_library)
+// for archiving or linking.
func TestLibraryFactory() android.Module {
module := NewTestLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for benchmarks
+// cc_benchmark compiles an executable binary that performs benchmark testing
+// of a specific component in a device. Additional files such as test suites
+// and test configuration are installed on the side of the compiled executed
+// binary.
func BenchmarkFactory() android.Module {
module := NewBenchmark(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host tests
+// cc_test_host compiles a test host binary.
func TestHostFactory() android.Module {
module := NewTest(android.HostSupported)
return module.Init()
}
-// Module factory for host benchmarks
+// cc_benchmark_host compiles an executable binary that performs benchmark
+// testing of a specific component in the host. Additional files such as
+// test suites and test configuration are installed on the side of the
+// compiled executed binary.
func BenchmarkHostFactory() android.Module {
module := NewBenchmark(android.HostSupported)
return module.Init()
diff --git a/cc/testing.go b/cc/testing.go
index b3b2756..2f41de1 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -70,16 +70,6 @@
}
cc_library {
- name: "libbase",
- no_libgcc: true,
- nocrt: true,
- vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- }
- }
- cc_library {
name: "libc",
no_libgcc: true,
nocrt: true,
diff --git a/cc/tidy.go b/cc/tidy.go
index 0b78d6f..5455392 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -31,6 +31,9 @@
// Extra checks to enable or disable in clang-tidy
Tidy_checks []string
+
+ // Checks that should be treated as errors.
+ Tidy_checks_as_errors []string
}
type tidyFeature struct {
@@ -116,5 +119,9 @@
}
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
+ if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+ flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
+ }
return flags
}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 5811b01..ae08b1c 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -77,3 +77,7 @@
return android.PathForSource(ctx, *library.Properties.Src)
}
+
+func (library *toolchainLibraryDecorator) nativeCoverage() bool {
+ return false
+}
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
new file mode 100644
index 0000000..5397f4b
--- /dev/null
+++ b/cmd/diff_target_files/Android.bp
@@ -0,0 +1,16 @@
+blueprint_go_binary {
+ name: "diff_target_files",
+ srcs: [
+ "compare.go",
+ "diff_target_files.go",
+ "glob.go",
+ "target_files.go",
+ "whitelist.go",
+ "zip_artifact.go",
+ ],
+ testSrcs: [
+ "compare_test.go",
+ "glob_test.go",
+ "whitelist_test.go",
+ ],
+}
diff --git a/cmd/diff_target_files/compare.go b/cmd/diff_target_files/compare.go
new file mode 100644
index 0000000..00cd9ca
--- /dev/null
+++ b/cmd/diff_target_files/compare.go
@@ -0,0 +1,133 @@
+// Copyright 2019 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 main
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// compareTargetFiles takes two ZipArtifacts and compares the files they contain by examining
+// the path, size, and CRC of each file.
+func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, whitelists []whitelist, filters []string) (zipDiff, error) {
+ priZipFiles, err := priZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from primary zip %v", err)
+ }
+
+ refZipFiles, err := refZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from reference zip %v", err)
+ }
+
+ priZipFiles, err = filterTargetZipFiles(priZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ refZipFiles, err = filterTargetZipFiles(refZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ // Compare the file lists from both builds
+ diff := diffTargetFilesLists(refZipFiles, priZipFiles)
+
+ return applyWhitelists(diff, whitelists)
+}
+
+// zipDiff contains the list of files that differ between two zip files.
+type zipDiff struct {
+ modified [][2]*ZipArtifactFile
+ onlyInA, onlyInB []*ZipArtifactFile
+}
+
+// String pretty-prints the list of files that differ between two zip files.
+func (d *zipDiff) String() string {
+ buf := &bytes.Buffer{}
+
+ must := func(n int, err error) {
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ var sizeChange int64
+
+ if len(d.modified) > 0 {
+ must(fmt.Fprintln(buf, "files modified:"))
+ for _, f := range d.modified {
+ must(fmt.Fprintf(buf, " %v (%v bytes -> %v bytes)\n", f[0].Name, f[0].UncompressedSize64, f[1].UncompressedSize64))
+ sizeChange += int64(f[1].UncompressedSize64) - int64(f[0].UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInA) > 0 {
+ must(fmt.Fprintln(buf, "files removed:"))
+ for _, f := range d.onlyInA {
+ must(fmt.Fprintf(buf, " - %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange -= int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInB) > 0 {
+ must(fmt.Fprintln(buf, "files added:"))
+ for _, f := range d.onlyInB {
+ must(fmt.Fprintf(buf, " + %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange += int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.modified) > 0 || len(d.onlyInA) > 0 || len(d.onlyInB) > 0 {
+ must(fmt.Fprintf(buf, "total size change: %v bytes\n", sizeChange))
+ }
+
+ return buf.String()
+}
+
+func diffTargetFilesLists(a, b []*ZipArtifactFile) zipDiff {
+ i := 0
+ j := 0
+
+ diff := zipDiff{}
+
+ for i < len(a) && j < len(b) {
+ if a[i].Name == b[j].Name {
+ if a[i].UncompressedSize64 != b[j].UncompressedSize64 || a[i].CRC32 != b[j].CRC32 {
+ diff.modified = append(diff.modified, [2]*ZipArtifactFile{a[i], b[j]})
+ }
+ i++
+ j++
+ } else if a[i].Name < b[j].Name {
+ // a[i] is not present in b
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ } else {
+ // b[j] is not present in a
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+ }
+ for i < len(a) {
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ }
+ for j < len(b) {
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+
+ return diff
+}
diff --git a/cmd/diff_target_files/compare_test.go b/cmd/diff_target_files/compare_test.go
new file mode 100644
index 0000000..9d3f8a5
--- /dev/null
+++ b/cmd/diff_target_files/compare_test.go
@@ -0,0 +1,131 @@
+// Copyright 2019 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 main
+
+import (
+ "archive/zip"
+ "reflect"
+ "testing"
+)
+
+func TestDiffTargetFilesLists(t *testing.T) {
+ zipArtifactFile := func(name string, crc32 uint32, size uint64) *ZipArtifactFile {
+ return &ZipArtifactFile{
+ File: &zip.File{
+ FileHeader: zip.FileHeader{
+ Name: name,
+ CRC32: crc32,
+ UncompressedSize64: size,
+ },
+ },
+ }
+ }
+ x0 := zipArtifactFile("x", 0, 0)
+ x1 := zipArtifactFile("x", 1, 0)
+ x2 := zipArtifactFile("x", 0, 2)
+ y0 := zipArtifactFile("y", 0, 0)
+ //y1 := zipArtifactFile("y", 1, 0)
+ //y2 := zipArtifactFile("y", 1, 2)
+ z0 := zipArtifactFile("z", 0, 0)
+ z1 := zipArtifactFile("z", 1, 0)
+ //z2 := zipArtifactFile("z", 1, 2)
+
+ testCases := []struct {
+ name string
+ a, b []*ZipArtifactFile
+ diff zipDiff
+ }{
+ {
+ name: "same",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, nil},
+ },
+ {
+ name: "first only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{y0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{x0}, nil},
+ },
+ {
+ name: "middle only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{y0}, nil},
+ },
+ {
+ name: "last only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0},
+ diff: zipDiff{nil, []*ZipArtifactFile{z0}, nil},
+ },
+
+ {
+ name: "first only in b",
+ a: []*ZipArtifactFile{y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{x0}},
+ },
+ {
+ name: "middle only in b",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "last only in b",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{z0}},
+ },
+
+ {
+ name: "diff",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, nil, nil},
+ },
+ {
+ name: "diff plus unique last",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x1, z0},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, []*ZipArtifactFile{y0}, []*ZipArtifactFile{z0}},
+ },
+ {
+ name: "diff plus unique first",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{y0, z1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{z0, z1}}, []*ZipArtifactFile{x0}, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "diff size",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x2},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x2}}, nil, nil},
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ diff := diffTargetFilesLists(test.a, test.b)
+
+ if !reflect.DeepEqual(diff, test.diff) {
+
+ t.Errorf("diffTargetFilesLists = %v, %v, %v", diff.modified, diff.onlyInA, diff.onlyInB)
+ t.Errorf(" want %v, %v, %v", test.diff.modified, test.diff.onlyInA, test.diff.onlyInB)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/diff_target_files.go b/cmd/diff_target_files/diff_target_files.go
new file mode 100644
index 0000000..75bc8ee
--- /dev/null
+++ b/cmd/diff_target_files/diff_target_files.go
@@ -0,0 +1,82 @@
+// Copyright 2019 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 main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var (
+ whitelists = newMultiString("whitelist", "whitelist patterns in the form <pattern>[:<regex of line to ignore>]")
+ whitelistFiles = newMultiString("whitelist_file", "files containing whitelist definitions")
+
+ filters = newMultiString("filter", "filter patterns to apply to files in target-files.zip before comparing")
+)
+
+func newMultiString(name, usage string) *multiString {
+ var f multiString
+ flag.Var(&f, name, usage)
+ return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ flag.Parse()
+
+ if flag.NArg() != 2 {
+ fmt.Fprintf(os.Stderr, "Error, exactly two arguments are required\n")
+ os.Exit(1)
+ }
+
+ whitelists, err := parseWhitelists(*whitelists, *whitelistFiles)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error parsing whitelists: %v\n", err)
+ os.Exit(1)
+ }
+
+ priZip, err := NewLocalZipArtifact(flag.Arg(0))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(0), err)
+ os.Exit(1)
+ }
+ defer priZip.Close()
+
+ refZip, err := NewLocalZipArtifact(flag.Arg(1))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(1), err)
+ os.Exit(1)
+ }
+ defer refZip.Close()
+
+ diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, whitelists, *filters)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error comparing zip files: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Print(diff.String())
+
+ if len(diff.modified) > 0 || len(diff.onlyInA) > 0 || len(diff.onlyInB) > 0 {
+ fmt.Fprintln(os.Stderr, "differences found")
+ os.Exit(1)
+ }
+}
diff --git a/cmd/diff_target_files/glob.go b/cmd/diff_target_files/glob.go
new file mode 100644
index 0000000..ed91af7
--- /dev/null
+++ b/cmd/diff_target_files/glob.go
@@ -0,0 +1,81 @@
+// Copyright 2019 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 main
+
+import (
+ "errors"
+ "path/filepath"
+ "strings"
+)
+
+// Match returns true if name matches pattern using the same rules as filepath.Match, but supporting
+// recursive globs (**).
+func Match(pattern, name string) (bool, error) {
+ if filepath.Base(pattern) == "**" {
+ return false, errors.New("pattern has '**' as last path element")
+ }
+
+ patternDir := pattern[len(pattern)-1] == '/'
+ nameDir := name[len(name)-1] == '/'
+
+ if patternDir != nameDir {
+ return false, nil
+ }
+
+ if nameDir {
+ name = name[:len(name)-1]
+ pattern = pattern[:len(pattern)-1]
+ }
+
+ for {
+ var patternFile, nameFile string
+ pattern, patternFile = filepath.Dir(pattern), filepath.Base(pattern)
+
+ if patternFile == "**" {
+ if strings.Contains(pattern, "**") {
+ return false, errors.New("pattern contains multiple '**'")
+ }
+ // Test if the any prefix of name matches the part of the pattern before **
+ for {
+ if name == "." || name == "/" {
+ return name == pattern, nil
+ }
+ if match, err := filepath.Match(pattern, name); err != nil {
+ return false, err
+ } else if match {
+ return true, nil
+ }
+ name = filepath.Dir(name)
+ }
+ } else if strings.Contains(patternFile, "**") {
+ return false, errors.New("pattern contains other characters between '**' and path separator")
+ }
+
+ name, nameFile = filepath.Dir(name), filepath.Base(name)
+
+ if nameFile == "." && patternFile == "." {
+ return true, nil
+ } else if nameFile == "/" && patternFile == "/" {
+ return true, nil
+ } else if nameFile == "." || patternFile == "." || nameFile == "/" || patternFile == "/" {
+ return false, nil
+ }
+
+ match, err := filepath.Match(patternFile, nameFile)
+ if err != nil || !match {
+ return match, err
+ }
+ }
+}
diff --git a/cmd/diff_target_files/glob_test.go b/cmd/diff_target_files/glob_test.go
new file mode 100644
index 0000000..63df68d
--- /dev/null
+++ b/cmd/diff_target_files/glob_test.go
@@ -0,0 +1,158 @@
+// Copyright 2019 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 main
+
+import (
+ "testing"
+)
+
+func TestMatch(t *testing.T) {
+ testCases := []struct {
+ pattern, name string
+ match bool
+ }{
+ {"a/*", "b/", false},
+ {"a/*", "b/a", false},
+ {"a/*", "b/b/", false},
+ {"a/*", "b/b/c", false},
+ {"a/**/*", "b/", false},
+ {"a/**/*", "b/a", false},
+ {"a/**/*", "b/b/", false},
+ {"a/**/*", "b/b/c", false},
+
+ {"a/*", "a/", false},
+ {"a/*", "a/a", true},
+ {"a/*", "a/b/", false},
+ {"a/*", "a/b/c", false},
+
+ {"a/*/", "a/", false},
+ {"a/*/", "a/a", false},
+ {"a/*/", "a/b/", true},
+ {"a/*/", "a/b/c", false},
+
+ {"a/**/*", "a/", false},
+ {"a/**/*", "a/a", true},
+ {"a/**/*", "a/b/", false},
+ {"a/**/*", "a/b/c", true},
+
+ {"a/**/*/", "a/", false},
+ {"a/**/*/", "a/a", false},
+ {"a/**/*/", "a/b/", true},
+ {"a/**/*/", "a/b/c", false},
+
+ {"**/*", "a/", false},
+ {"**/*", "a/a", true},
+ {"**/*", "a/b/", false},
+ {"**/*", "a/b/c", true},
+
+ {"**/*/", "a/", true},
+ {"**/*/", "a/a", false},
+ {"**/*/", "a/b/", true},
+ {"**/*/", "a/b/c", false},
+
+ {`a/\*\*/\*`, `a/**/*`, true},
+ {`a/\*\*/\*`, `a/a/*`, false},
+ {`a/\*\*/\*`, `a/**/a`, false},
+ {`a/\*\*/\*`, `a/a/a`, false},
+
+ {`a/**/\*`, `a/**/*`, true},
+ {`a/**/\*`, `a/a/*`, true},
+ {`a/**/\*`, `a/**/a`, false},
+ {`a/**/\*`, `a/a/a`, false},
+
+ {`a/\*\*/*`, `a/**/*`, true},
+ {`a/\*\*/*`, `a/a/*`, false},
+ {`a/\*\*/*`, `a/**/a`, true},
+ {`a/\*\*/*`, `a/a/a`, false},
+
+ {`*/**/a`, `a/a/a`, true},
+ {`*/**/a`, `*/a/a`, true},
+ {`*/**/a`, `a/**/a`, true},
+ {`*/**/a`, `*/**/a`, true},
+
+ {`\*/\*\*/a`, `a/a/a`, false},
+ {`\*/\*\*/a`, `*/a/a`, false},
+ {`\*/\*\*/a`, `a/**/a`, false},
+ {`\*/\*\*/a`, `*/**/a`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/[a-c]`, `a/b`, true},
+ {`a/[abc]`, `a/b`, true},
+
+ {`a/\[abc]`, `a/b`, false},
+ {`a/\[abc]`, `a/[abc]`, true},
+
+ {`a/\[abc\]`, `a/b`, false},
+ {`a/\[abc\]`, `a/[abc]`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {"/a/*", "/a/", false},
+ {"/a/*", "/a/a", true},
+ {"/a/*", "/a/b/", false},
+ {"/a/*", "/a/b/c", false},
+
+ {"/a/*/", "/a/", false},
+ {"/a/*/", "/a/a", false},
+ {"/a/*/", "/a/b/", true},
+ {"/a/*/", "/a/b/c", false},
+
+ {"/a/**/*", "/a/", false},
+ {"/a/**/*", "/a/a", true},
+ {"/a/**/*", "/a/b/", false},
+ {"/a/**/*", "/a/b/c", true},
+
+ {"/**/*", "/a/", false},
+ {"/**/*", "/a/a", true},
+ {"/**/*", "/a/b/", false},
+ {"/**/*", "/a/b/c", true},
+
+ {"/**/*/", "/a/", true},
+ {"/**/*/", "/a/a", false},
+ {"/**/*/", "/a/b/", true},
+ {"/**/*/", "/a/b/c", false},
+
+ {`a`, `/a`, false},
+ {`/a`, `a`, false},
+ {`*`, `/a`, false},
+ {`/*`, `a`, false},
+ {`**/*`, `/a`, false},
+ {`/**/*`, `a`, false},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.pattern+","+test.name, func(t *testing.T) {
+ match, err := Match(test.pattern, test.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if match != test.match {
+ t.Errorf("want: %v, got %v", test.match, match)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/known_nondeterminism.whitelist b/cmd/diff_target_files/known_nondeterminism.whitelist
new file mode 100644
index 0000000..6d71403
--- /dev/null
+++ b/cmd/diff_target_files/known_nondeterminism.whitelist
@@ -0,0 +1,10 @@
+// List of files that are known to be non-deterministic, along with the
+// bug number to tracking fixing the non-determinism.
+[
+ {
+ "Paths": [
+ // b/120039850
+ "system/framework/oat/*/services.art"
+ ]
+ }
+]
diff --git a/cmd/diff_target_files/props.whitelist b/cmd/diff_target_files/props.whitelist
new file mode 100644
index 0000000..9245b8b
--- /dev/null
+++ b/cmd/diff_target_files/props.whitelist
@@ -0,0 +1,18 @@
+[
+ // Ignore date, version and hostname properties in build.prop and prop.default files.
+ {
+ "Paths": [
+ "**/build.prop",
+ "**/prop.default"
+ ],
+ "IgnoreMatchingLines": [
+ "ro\\..*build\\.date=.*",
+ "ro\\..*build\\.date\\.utc=.*",
+ "ro\\..*build\\.version\\.incremental=.*",
+ "ro\\..*build\\.fingerprint=.*",
+ "ro\\.build\\.display\\.id=.*",
+ "ro\\.build\\.description=.*",
+ "ro\\.build\\.host=.*"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
new file mode 100644
index 0000000..8705ca7
--- /dev/null
+++ b/cmd/diff_target_files/target_files.go
@@ -0,0 +1,86 @@
+// Copyright 2019 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 main
+
+import (
+ "fmt"
+ "strings"
+)
+
+const targetFilesPattern = "*-target_files-*.zip"
+
+var targetZipPartitions = []string{
+ "BOOT/RAMDISK/",
+ "BOOT/",
+ "DATA/",
+ "ODM/",
+ "OEM/",
+ "PRODUCT/",
+ "PRODUCT_SERVICES/",
+ "ROOT/",
+ "SYSTEM/",
+ "SYSTEM_OTHER/",
+ "VENDOR/",
+}
+
+var targetZipFilter = []string{
+ "IMAGES/",
+ "OTA/",
+ "META/",
+ "PREBUILT_IMAGES/",
+ "RADIO/",
+}
+
+func filterTargetZipFiles(files []*ZipArtifactFile, artifact string, patterns []string) ([]*ZipArtifactFile, error) {
+ var ret []*ZipArtifactFile
+outer:
+ for _, f := range files {
+ if f.FileInfo().IsDir() {
+ continue
+ }
+
+ if artifact == targetFilesPattern {
+ found := false
+ for _, p := range targetZipPartitions {
+ if strings.HasPrefix(f.Name, p) {
+ f.Name = strings.ToLower(p) + strings.TrimPrefix(f.Name, p)
+ found = true
+ }
+ }
+ for _, filter := range targetZipFilter {
+ if strings.HasPrefix(f.Name, filter) {
+ continue outer
+ }
+ }
+
+ if !found {
+ return nil, fmt.Errorf("unmatched prefix for %s", f.Name)
+ }
+ }
+
+ if patterns != nil {
+ for _, pattern := range patterns {
+ match, _ := Match(pattern, f.Name)
+ if match {
+ ret = append(ret, f)
+ }
+ }
+ } else {
+ ret = append(ret, f)
+ }
+ }
+
+ return ret, nil
+}
diff --git a/cmd/diff_target_files/whitelist.go b/cmd/diff_target_files/whitelist.go
new file mode 100644
index 0000000..f00fc1e
--- /dev/null
+++ b/cmd/diff_target_files/whitelist.go
@@ -0,0 +1,251 @@
+// Copyright 2019 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 main
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+type jsonWhitelist struct {
+ Paths []string
+ IgnoreMatchingLines []string
+}
+
+type whitelist struct {
+ path string
+ ignoreMatchingLines []string
+}
+
+func parseWhitelists(whitelists []string, whitelistFiles []string) ([]whitelist, error) {
+ var ret []whitelist
+
+ add := func(path string, ignoreMatchingLines []string) {
+ for _, x := range ret {
+ if x.path == path {
+ x.ignoreMatchingLines = append(x.ignoreMatchingLines, ignoreMatchingLines...)
+ return
+ }
+ }
+
+ ret = append(ret, whitelist{
+ path: path,
+ ignoreMatchingLines: ignoreMatchingLines,
+ })
+ }
+
+ for _, file := range whitelistFiles {
+ newWhitelists, err := parseWhitelistFile(file)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, w := range newWhitelists {
+ add(w.path, w.ignoreMatchingLines)
+ }
+ }
+
+ for _, s := range whitelists {
+ colon := strings.IndexRune(s, ':')
+ var ignoreMatchingLines []string
+ if colon >= 0 {
+ ignoreMatchingLines = []string{s[colon+1:]}
+ }
+ add(s, ignoreMatchingLines)
+ }
+
+ return ret, nil
+}
+
+func parseWhitelistFile(file string) ([]whitelist, error) {
+ r, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ d := json.NewDecoder(newJSONCommentStripper(r))
+
+ var jsonWhitelists []jsonWhitelist
+
+ err = d.Decode(&jsonWhitelists)
+
+ var whitelists []whitelist
+ for _, w := range jsonWhitelists {
+ for _, p := range w.Paths {
+ whitelists = append(whitelists, whitelist{
+ path: p,
+ ignoreMatchingLines: w.IgnoreMatchingLines,
+ })
+ }
+ }
+
+ return whitelists, err
+}
+
+func filterModifiedPaths(l [][2]*ZipArtifactFile, whitelists []whitelist) ([][2]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i][0].Name); err != nil {
+ return l, err
+ } else if match {
+ if match, err := diffIgnoringMatchingLines(l[i][0], l[i][1], w.ignoreMatchingLines); err != nil {
+ return l, err
+ } else if match || len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func filterNewPaths(l []*ZipArtifactFile, whitelists []whitelist) ([]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i].Name); err != nil {
+ return l, err
+ } else if match && len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func diffIgnoringMatchingLines(a *ZipArtifactFile, b *ZipArtifactFile, ignoreMatchingLines []string) (match bool, err error) {
+ lineMatchesIgnores := func(b []byte) (bool, error) {
+ for _, m := range ignoreMatchingLines {
+ if match, err := regexp.Match(m, b); err != nil {
+ return false, err
+ } else if match {
+ return match, nil
+ }
+ }
+ return false, nil
+ }
+
+ filter := func(z *ZipArtifactFile) ([]byte, error) {
+ var ret []byte
+
+ r, err := z.Open()
+ if err != nil {
+ return nil, err
+ }
+ s := bufio.NewScanner(r)
+
+ for s.Scan() {
+ if match, err := lineMatchesIgnores(s.Bytes()); err != nil {
+ return nil, err
+ } else if !match {
+ ret = append(ret, "\n"...)
+ ret = append(ret, s.Bytes()...)
+ }
+ }
+
+ return ret, nil
+ }
+
+ bufA, err := filter(a)
+ if err != nil {
+ return false, err
+ }
+ bufB, err := filter(b)
+ if err != nil {
+ return false, err
+ }
+
+ return bytes.Compare(bufA, bufB) == 0, nil
+}
+
+func applyWhitelists(diff zipDiff, whitelists []whitelist) (zipDiff, error) {
+ var err error
+
+ diff.modified, err = filterModifiedPaths(diff.modified, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInA, err = filterNewPaths(diff.onlyInA, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInB, err = filterNewPaths(diff.onlyInB, whitelists)
+ if err != nil {
+ return diff, err
+ }
+
+ return diff, nil
+}
+
+func newJSONCommentStripper(r io.Reader) *jsonCommentStripper {
+ return &jsonCommentStripper{
+ r: bufio.NewReader(r),
+ }
+}
+
+type jsonCommentStripper struct {
+ r *bufio.Reader
+ b []byte
+ err error
+}
+
+func (j *jsonCommentStripper) Read(buf []byte) (int, error) {
+ for len(j.b) == 0 {
+ if j.err != nil {
+ return 0, j.err
+ }
+
+ j.b, j.err = j.r.ReadBytes('\n')
+
+ if isComment(j.b) {
+ j.b = nil
+ }
+ }
+
+ n := copy(buf, j.b)
+ j.b = j.b[n:]
+ return n, nil
+}
+
+var commentPrefix = []byte("//")
+
+func isComment(b []byte) bool {
+ for len(b) > 0 && unicode.IsSpace(rune(b[0])) {
+ b = b[1:]
+ }
+ return bytes.HasPrefix(b, commentPrefix)
+}
diff --git a/cmd/diff_target_files/whitelist_test.go b/cmd/diff_target_files/whitelist_test.go
new file mode 100644
index 0000000..4b19fdd
--- /dev/null
+++ b/cmd/diff_target_files/whitelist_test.go
@@ -0,0 +1,126 @@
+// Copyright 2019 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 main
+
+import (
+ "archive/zip"
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func bytesToZipArtifactFile(name string, data []byte) *ZipArtifactFile {
+ buf := &bytes.Buffer{}
+ w := zip.NewWriter(buf)
+ f, err := w.Create(name)
+ if err != nil {
+ panic(err)
+ }
+ _, err = f.Write(data)
+ if err != nil {
+ panic(err)
+ }
+
+ w.Close()
+
+ r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ panic(err)
+ }
+
+ return &ZipArtifactFile{r.File[0]}
+}
+
+var f1a = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: bar
+c
+`))
+
+var f1b = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: baz
+c
+`))
+
+var f2 = bytesToZipArtifactFile("dir/f2", nil)
+
+func Test_applyWhitelists(t *testing.T) {
+ type args struct {
+ diff zipDiff
+ whitelists []whitelist
+ }
+ tests := []struct {
+ name string
+ args args
+ want zipDiff
+ wantErr bool
+ }{
+ {
+ name: "simple",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/f1"}},
+ },
+ want: zipDiff{
+ onlyInA: []*ZipArtifactFile{f2},
+ },
+ },
+ {
+ name: "glob",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "modified",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "matching lines",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
+ },
+ want: zipDiff{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := applyWhitelists(tt.args.diff, tt.args.whitelists)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("applyWhitelists() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("applyWhitelists() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/zip_artifact.go b/cmd/diff_target_files/zip_artifact.go
new file mode 100644
index 0000000..08ce889
--- /dev/null
+++ b/cmd/diff_target_files/zip_artifact.go
@@ -0,0 +1,174 @@
+// Copyright 2019 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 main
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+)
+
+// ZipArtifact represents a zip file that may be local or remote.
+type ZipArtifact interface {
+ // Files returns the list of files contained in the zip file.
+ Files() ([]*ZipArtifactFile, error)
+
+ // Close closes the zip file artifact.
+ Close()
+}
+
+// localZipArtifact is a handle to a local zip file artifact.
+type localZipArtifact struct {
+ zr *zip.ReadCloser
+ files []*ZipArtifactFile
+}
+
+// NewLocalZipArtifact returns a ZipArtifact for a local zip file..
+func NewLocalZipArtifact(name string) (ZipArtifact, error) {
+ zr, err := zip.OpenReader(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*ZipArtifactFile
+ for _, zf := range zr.File {
+ files = append(files, &ZipArtifactFile{zf})
+ }
+
+ return &localZipArtifact{
+ zr: zr,
+ files: files,
+ }, nil
+}
+
+// Files returns the list of files contained in the local zip file artifact.
+func (z *localZipArtifact) Files() ([]*ZipArtifactFile, error) {
+ return z.files, nil
+}
+
+// Close closes the buffered reader of the local zip file artifact.
+func (z *localZipArtifact) Close() {
+ z.zr.Close()
+}
+
+// ZipArtifactFile contains a zip.File handle to the data inside the remote *-target_files-*.zip
+// build artifact.
+type ZipArtifactFile struct {
+ *zip.File
+}
+
+// Extract begins extract a file from inside a ZipArtifact. It returns an
+// ExtractedZipArtifactFile handle.
+func (zf *ZipArtifactFile) Extract(ctx context.Context, dir string,
+ limiter chan bool) *ExtractedZipArtifactFile {
+
+ d := &ExtractedZipArtifactFile{
+ initCh: make(chan struct{}),
+ }
+
+ go func() {
+ defer close(d.initCh)
+ limiter <- true
+ defer func() { <-limiter }()
+
+ zr, err := zf.Open()
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer zr.Close()
+
+ crc := crc32.NewIEEE()
+ r := io.TeeReader(zr, crc)
+
+ if filepath.Clean(zf.Name) != zf.Name {
+ d.err = fmt.Errorf("invalid filename %q", zf.Name)
+ return
+ }
+ path := filepath.Join(dir, zf.Name)
+
+ err = os.MkdirAll(filepath.Dir(path), 0777)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Remove(path)
+ if err != nil && !os.IsNotExist(err) {
+ d.err = err
+ return
+ }
+
+ if zf.Mode().IsRegular() {
+ w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, zf.Mode())
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer w.Close()
+
+ _, err = io.Copy(w, r)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else if zf.Mode()&os.ModeSymlink != 0 {
+ target, err := ioutil.ReadAll(r)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Symlink(string(target), path)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else {
+ d.err = fmt.Errorf("unknown mode %q", zf.Mode())
+ return
+ }
+
+ if crc.Sum32() != zf.CRC32 {
+ d.err = fmt.Errorf("crc mismatch for %v", zf.Name)
+ return
+ }
+
+ d.path = path
+ }()
+
+ return d
+}
+
+// ExtractedZipArtifactFile is a handle to a downloaded file from a remoteZipArtifact. The download
+// may still be in progress, and will be complete with Path() returns.
+type ExtractedZipArtifactFile struct {
+ initCh chan struct{}
+ err error
+
+ path string
+}
+
+// Path returns the path to the downloaded file and any errors that occurred during the download.
+// It will block until the download is complete.
+func (d *ExtractedZipArtifactFile) Path() (string, error) {
+ <-d.initCh
+ return d.path, d.err
+}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 0af1886..4167edb 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -24,6 +24,7 @@
"path"
"path/filepath"
"strings"
+ "time"
)
var (
@@ -265,6 +266,15 @@
if err != nil {
return err
}
+
+ // Update the timestamp of the output file in case the tool wrote an old timestamp (for example, tar can extract
+ // files with old timestamps).
+ now := time.Now()
+ err = os.Chtimes(tempPath, now, now)
+ if err != nil {
+ return err
+ }
+
err = os.Rename(tempPath, destPath)
if err != nil {
return err
diff --git a/java/androidmk.go b/java/androidmk.go
index 5b4f738..908286a 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -95,16 +95,21 @@
}
}
+// Called for modules that are a component of a test suite.
+func testSuiteComponent(w io.Writer, test_suites []string) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+}
+
func (j *Test) AndroidMk() android.AndroidMkData {
data := j.Library.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(j.testProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(j.testProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, j.testProperties.Test_suites)
if j.testConfig != nil {
fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
}
@@ -115,6 +120,15 @@
return data
}
+func (j *TestHelperLibrary) AndroidMk() android.AndroidMkData {
+ data := j.Library.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, j.testHelperLibraryProperties.Test_suites)
+ })
+
+ return data
+}
+
func (prebuilt *Import) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
@@ -321,13 +335,7 @@
func (a *AndroidTest) AndroidMk() android.AndroidMkData {
data := a.AndroidApp.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(a.testProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(a.testProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, a.testProperties.Test_suites)
if a.testConfig != nil {
fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", a.testConfig.String())
}
@@ -340,13 +348,7 @@
func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
data := a.AndroidApp.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(a.appTestHelperAppProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(a.appTestHelperAppProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, a.appTestHelperAppProperties.Test_suites)
})
return data
diff --git a/java/app.go b/java/app.go
index b31f232..ab623e2 100644
--- a/java/app.go
+++ b/java/app.go
@@ -74,6 +74,11 @@
// Store dex files uncompressed in the APK and set the android:useEmbeddedDex="true" manifest attribute so that
// they are used from inside the APK at runtime.
Use_embedded_dex *bool
+
+ // Forces native libraries to always be packaged into the APK,
+ // Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
+ // True for android_test* modules.
+ AlwaysPackageNativeLibs bool `blueprint:"mutated"`
}
// android_app properties that can be overridden by override_android_app
@@ -81,6 +86,9 @@
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
// or an android_app_certificate module name in the form ":module".
Certificate *string
+
+ // the package name of this app. The package name in the manifest file is used if one was not given.
+ Package_name *string
}
type AndroidApp struct {
@@ -223,11 +231,12 @@
}
}
- // TODO: LOCAL_PACKAGE_OVERRIDES
- // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
-
manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
- if overridden {
+ if overridden || a.overridableAppProperties.Package_name != nil {
+ // The product override variable has a priority over the package_name property.
+ if !overridden {
+ manifestPackageName = *a.overridableAppProperties.Package_name
+ }
aaptLinkFlags = append(aaptLinkFlags, "--rename-manifest-package "+manifestPackageName)
}
@@ -281,7 +290,8 @@
func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
var jniJarFile android.WritablePath
if len(jniLibs) > 0 {
- embedJni := ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs)
+ embedJni := ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
+ a.appProperties.AlwaysPackageNativeLibs
if embedJni {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.shouldUncompressJNI(ctx))
@@ -503,6 +513,7 @@
module.Module.properties.Instrument = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
@@ -543,6 +554,7 @@
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
diff --git a/java/app_test.go b/java/app_test.go
index cf57c80..1f6297c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -16,6 +16,8 @@
import (
"android/soong/android"
+ "android/soong/cc"
+
"fmt"
"path/filepath"
"reflect"
@@ -537,43 +539,8 @@
}
}
-func TestJNI(t *testing.T) {
- ctx := testJava(t, `
- toolchain_library {
- name: "libcompiler_rt-extras",
- src: "",
- }
-
- toolchain_library {
- name: "libatomic",
- src: "",
- }
-
- toolchain_library {
- name: "libgcc",
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-aarch64-android",
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-arm-android",
- src: "",
- }
-
- cc_object {
- name: "crtbegin_so",
- stl: "none",
- }
-
- cc_object {
- name: "crtend_so",
- stl: "none",
- }
-
+func TestJNIABI(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
system_shared_libs: [],
@@ -615,13 +582,6 @@
}
`)
- // check the existence of the internal modules
- ctx.ModuleForTests("test", "android_common")
- ctx.ModuleForTests("test_first", "android_common")
- ctx.ModuleForTests("test_both", "android_common")
- ctx.ModuleForTests("test_32", "android_common")
- ctx.ModuleForTests("test_64", "android_common")
-
testCases := []struct {
name string
abis []string
@@ -652,6 +612,90 @@
}
}
+func TestJNIPackaging(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_app {
+ name: "app",
+ jni_libs: ["libjni"],
+ }
+
+ android_app {
+ name: "app_noembed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_app {
+ name: "app_embed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: true,
+ }
+
+ android_test {
+ name: "test",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_noembed",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_test_helper_app {
+ name: "test_helper",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test_helper_app {
+ name: "test_helper_noembed",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ packaged bool
+ compressed bool
+ }{
+ {"app", false, false},
+ {"app_noembed", false, false},
+ {"app_embed", true, false},
+ {"test", true, false},
+ {"test_noembed", true, true},
+ {"test_helper", true, false},
+ {"test_helper_noembed", true, true},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.MaybeOutput("jnilibs.zip")
+ if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
+ t.Errorf("expected jni packaged %v, got %v", w, g)
+ }
+
+ if jniLibZip.Rule != nil {
+ if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
+ t.Errorf("expected jni compressed %v, got %v", w, g)
+ }
+ }
+ })
+ }
+
+}
+
func TestCertificates(t *testing.T) {
testCases := []struct {
name string
@@ -833,6 +877,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ certificate: "expiredkey",
overrides: ["baz"],
}
@@ -846,6 +891,12 @@
name: "new_certificate",
certificate: "cert/new_cert",
}
+
+ override_android_app {
+ name: "baz",
+ base: "foo",
+ package_name: "org.dandroid.bp",
+ }
`)
expectedVariants := []struct {
@@ -854,18 +905,28 @@
apkPath string
signFlag string
overrides []string
+ aaptFlag string
}{
{
variantName: "android_common",
apkPath: "/target/product/test_device/system/app/foo/foo.apk",
- signFlag: "build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
overrides: []string{"baz"},
+ aaptFlag: "",
},
{
variantName: "bar_android_common",
apkPath: "/target/product/test_device/system/app/bar/bar.apk",
signFlag: "cert/new_cert.x509.pem cert/new_cert.pk8",
overrides: []string{"baz", "foo"},
+ aaptFlag: "",
+ },
+ {
+ variantName: "baz_android_common",
+ apkPath: "/target/product/test_device/system/app/baz/baz.apk",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
+ overrides: []string{"baz", "foo"},
+ aaptFlag: "--rename-manifest-package org.dandroid.bp",
},
}
for _, expected := range expectedVariants {
@@ -892,10 +953,18 @@
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
}
+ // Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidApp)
if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
expected.overrides, mod.appProperties.Overrides)
}
+
+ // Check the package renaming flag, if exists.
+ res := variant.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ if !strings.Contains(aapt2Flags, expected.aaptFlag) {
+ t.Errorf("package renaming flag, %q is missing in aapt2 link flags, %q", expected.aaptFlag, aapt2Flags)
+ }
}
}
diff --git a/java/java.go b/java/java.go
index 1fd0a9e..beee1a5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -41,6 +41,7 @@
android.RegisterModuleType("java_binary", BinaryFactory)
android.RegisterModuleType("java_binary_host", BinaryHostFactory)
android.RegisterModuleType("java_test", TestFactory)
+ android.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
android.RegisterModuleType("java_test_host", TestHostFactory)
android.RegisterModuleType("java_import", ImportFactory)
android.RegisterModuleType("java_import_host", ImportFactoryHost)
@@ -1536,6 +1537,12 @@
Data []string `android:"path"`
}
+type testHelperLibraryProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
type Test struct {
Library
@@ -1545,6 +1552,12 @@
data android.Paths
}
+type TestHelperLibrary struct {
+ Library
+
+ testHelperLibraryProperties testHelperLibraryProperties
+}
+
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -1552,6 +1565,10 @@
j.Library.GenerateAndroidBuildActions(ctx)
}
+func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.Library.GenerateAndroidBuildActions(ctx)
+}
+
// java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
// creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
//
@@ -1577,6 +1594,21 @@
return module
}
+// java_test_helper_library creates a java library and makes sure that it is added to the appropriate test suite.
+func TestHelperLibraryFactory() android.Module {
+ module := &TestHelperLibrary{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.testHelperLibraryProperties)
+
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
// java_test_host builds a and links sources into a `.jar` file for the host, and creates an `AndroidTest.xml` file to
// allow running the test with `atest` or a `TEST_MAPPING` file.
//
diff --git a/java/java_test.go b/java/java_test.go
index f466499..ec4c100 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -103,6 +103,7 @@
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("link", cc.LinkageMutator).Parallel()
ctx.BottomUp("begin", cc.BeginMutator).Parallel()
diff --git a/scripts/setup_go_workspace_for_soong.sh b/scripts/setup_go_workspace_for_soong.sh
index e2fb9fa..6374aae 100755
--- a/scripts/setup_go_workspace_for_soong.sh
+++ b/scripts/setup_go_workspace_for_soong.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
-# Copyright 2017 Google Inc. All rights reserved.
+# Copyright 2019 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.
@@ -15,23 +15,174 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#mounts the components of soong into a directory structure that Go tools and editors expect
+# Mounts the components of soong into a directory structure that Go tools
+# and editors expect.
-#move to the script's directory
-cd "$(dirname $0)"
-SCRIPT_PATH="$PWD"
-#find the root of the Repo checkout
-cd "${SCRIPT_PATH}"/../../..
-ANDROID_PATH="${PWD}"
-OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')" #if GOPATH contains multiple paths, use the first one
-
-if [ -z "${OUTPUT_PATH}" ]; then
- echo "Error; could not determine the desired location at which to create a Go-compatible workspace. Please update GOPATH to specify the desired destination directory"
+#####################################################################
+# Print the message to stderr with the prefix ERROR and abort this
+# script.
+#####################################################################
+function log_FATAL() {
+ echo "ERROR:" "$*" >&2
exit 1
-fi
+}
-function confirm() {
+#####################################################################
+# Print the message to stderr with the prefix WARN
+#####################################################################
+function log_WARN() {
+ echo "WARN:" "$*" >&2
+}
+
+
+#####################################################################
+# Print the message with the prefix INFO.
+#####################################################################
+function log_INFO() {
+ echo "INFO:" "$*"
+}
+
+
+#####################################################################
+# Find the root project directory of this repo. This is done by
+# finding the directory of where this script lives and then go up one
+# directory to check the ".repo" directory exist. If not, keep going
+# up until we find the ".repo" file or we reached to the filesystem
+# root. Project root directory is printed to stdout.
+#####################################################################
+function root_dir() (
+ local dir
+ if ! dir="$("${readlink}" -e $(dirname "$0"))"; then
+ log_FATAL "failed to read the script's current directory."
+ fi
+
+ dir=${dir}/../../..
+ if ! dir="$("${readlink}" -e "${dir}")"; then
+ log_FATAL "Cannot find the root project directory"
+ fi
+
+ echo "${dir}"
+)
+
+
+#####################################################################
+# executes a shell command by printing out to the screen first and
+# then evaluating the command.
+#####################################################################
+function execute() {
+ echo "$@"
+ eval "$@"
+}
+
+
+#####################################################################
+# Returns the source directory of a passed in path from BIND_PATHS
+# array.
+#####################################################################
+function bind_path_src_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/%|*/}"
+)
+
+
+#####################################################################
+# Returns the destination directory of a passed in path from
+# BIND_PATHS array.
+#####################################################################
+function bind_path_dst_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/#*|}"
+)
+
+
+#####################################################################
+# Executes the bindfs command in linux. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function linux_bind_dir() (
+ execute bindfs "$1" "$2"
+)
+
+#####################################################################
+# Executes the fusermount -u command in linux. Expects $1 to be the
+# destination directory.
+#####################################################################
+function linux_unbind_dir() (
+ execute fusermount -u "$1"
+)
+
+#####################################################################
+# Executes the bindfs command in darwin. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function darwin_bind_dir() (
+ execute bindfs -o allow_recursion -n "$1" "$2"
+)
+
+
+#####################################################################
+# Execute the umount command in darwin to unbind a directory. Expects
+# $1 to be the destination directory
+#####################################################################
+function darwin_unbind_dir() (
+ execute umount -f "$1"
+)
+
+
+#####################################################################
+# Bind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function bind_all() (
+ local src_dir
+ local dst_dir
+
+ for path in ${BIND_PATHS[@]}; do
+ src_dir=$(bind_path_src_dir "${path}")
+
+ dst_dir=$(bind_path_dst_dir "${path}")
+ mkdir -p "${dst_dir}"
+
+ "${bind_dir}" ${src_dir} "${dst_dir}"
+ done
+
+ echo
+ log_INFO "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Unbind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function unbind_all() (
+ local dst_dir
+ local exit_code=0
+
+ # need to go into reverse since several parent directory may have been
+ # first before the child one.
+ for (( i=${#BIND_PATHS[@]}-1; i>=0; i-- )); do
+ dst_dir=$(bind_path_dst_dir "${BIND_PATHS[$i]}")
+
+ # continue to unmount even one of them fails
+ if ! "${unbind_dir}" "${dst_dir}"; then
+ log_WARN "Failed to umount ${dst_dir}."
+ exit_code=1
+ fi
+ done
+
+ if [[ ${exit_code} -ne 0 ]]; then
+ exit ${exit_code}
+ fi
+
+ echo
+ log_INFO "Unmounted the GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Asks the user to create the GOPATH-compatible directory structure.
+#####################################################################
+function confirm() (
while true; do
echo "Will create GOPATH-compatible directory structure at ${OUTPUT_PATH}"
echo -n "Ok [Y/n]?"
@@ -42,48 +193,162 @@
if [ "${decision}" == "n" ]; then
return 1
else
- echo "Invalid choice ${decision}; choose either 'y' or 'n'"
+ log_WARN "Invalid choice ${decision}; choose either 'y' or 'n'"
fi
fi
done
+)
+
+
+#####################################################################
+# Help function.
+#####################################################################
+function help() (
+ cat <<EOF
+Mounts the components of soong into a directory structure that Go tools
+and editors expect.
+
+ --help
+ This help
+
+ --bind
+ Create the directory structure that Go tools and editors expect by
+ binding the one to aosp build directory.
+
+ --unbind
+ Reverse operation of bind.
+
+If no flags were specified, the --bind one is selected by default.
+EOF
+)
+
+
+#####################################################################
+# Parse the arguments passed in to this script.
+#####################################################################
+function parse_arguments() {
+ while [[ -n "$1" ]]; do
+ case "$1" in
+ --bind)
+ ACTION="bind"
+ shift
+ ;;
+ --unbind)
+ ACTION="unbind"
+ shift
+ ;;
+ --help )
+ help
+ shift
+ exit 0
+ ;;
+ *)
+ log_WARN "Unknown option: $1"
+ help
+ exit 1
+ ;;
+ esac
+ done
+
+ if [[ -z "${ACTION}" ]]; then
+ ACTION=bind
+ fi
}
-function bindAll() {
- bindOne "${ANDROID_PATH}/build/blueprint" "${OUTPUT_PATH}/src/github.com/google/blueprint"
- bindOne "${ANDROID_PATH}/build/soong" "${OUTPUT_PATH}/src/android/soong"
- bindOne "${ANDROID_PATH}/art/build" "${OUTPUT_PATH}/src/android/soong/art"
- bindOne "${ANDROID_PATH}/external/golang-protobuf" "${OUTPUT_PATH}/src/github.com/golang/protobuf"
- bindOne "${ANDROID_PATH}/external/llvm/soong" "${OUTPUT_PATH}/src/android/soong/llvm"
- bindOne "${ANDROID_PATH}/external/clang/soong" "${OUTPUT_PATH}/src/android/soong/clang"
- echo
- echo "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}"
-}
+#####################################################################
+# Verifies that a list of required binaries are installed in the
+# host in order to run this script.
+#####################################################################
+function check_exec_existence() (
+ function check() {
+ if ! hash "$1" &>/dev/null; then
+ log_FATAL "missing $1"
+ fi
+ }
-function bindOne() {
- #causes $newPath to mirror $existingPath
- existingPath="$1"
- newPath="$2"
- mkdir -p "$newPath"
- case $(uname -s) in
+ local bins
+ case "${os_type}" in
Darwin)
- echoAndDo bindfs -o allow_recursion -n "${existingPath}" "${newPath}"
+ bins=("bindfs" "greadlink")
;;
Linux)
- echoAndDo bindfs "${existingPath}" "${newPath}"
+ bins=("bindfs" "fusermount")
;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
esac
+
+ for bin in "${bins[@]}"; do
+ check "${bin}"
+ done
+)
+
+
+function main() {
+ parse_arguments "$@"
+
+ check_exec_existence
+
+ if [[ "${ACTION}" == "bind" ]]; then
+ if confirm; then
+ echo
+ bind_all
+ else
+ echo "skipping due to user request"
+ exit 1
+ fi
+ else
+ echo
+ unbind_all
+ fi
}
-function echoAndDo() {
- echo "$@"
- eval "$@"
-}
+readonly os_type="$(uname -s)"
+case "${os_type}" in
+ Darwin)
+ bind_dir=darwin_bind_dir
+ unbind_dir=darwin_unbind_dir
+ readlink=greadlink
+ ;;
+ Linux)
+ bind_dir=linux_bind_dir
+ unbind_dir=linux_unbind_dir
+ readlink=readlink
+ ;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
+esac
+readonly bind_dir
+readonly unbind_dir
+readonly readlink
-if confirm; then
- echo
- bindAll
-else
- echo "skipping due to user request"
- exit 1
+
+if ! ANDROID_PATH="$(root_dir)"; then
+ log_FATAL "failed to find the root of the repo checkout"
fi
+readonly ANDROID_PATH
+
+#if GOPATH contains multiple paths, use the first one
+if ! OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')"; then
+ log_FATAL "failed to extract the first GOPATH environment variable"
+fi
+readonly OUTPUT_PATH
+if [ -z "${OUTPUT_PATH}" ]; then
+ log_FATAL "Could not determine the desired location at which to create a" \
+ "Go-compatible workspace. Please update GOPATH to specify the" \
+ "desired destination directory."
+fi
+
+# Below are the paths to bind from src to dst. The paths are separated by |
+# where the left side is the source and the right side is destination.
+readonly BIND_PATHS=(
+ "${ANDROID_PATH}/build/blueprint|${OUTPUT_PATH}/src/github.com/google/blueprint"
+ "${ANDROID_PATH}/build/soong|${OUTPUT_PATH}/src/android/soong"
+ "${ANDROID_PATH}/art/build|${OUTPUT_PATH}/src/android/soong/art"
+ "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/github.com/golang/protobuf"
+ "${ANDROID_PATH}/external/llvm/soong|${OUTPUT_PATH}/src/android/soong/llvm"
+ "${ANDROID_PATH}/external/clang/soong|${OUTPUT_PATH}/src/android/soong/clang"
+)
+
+main "$@"
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 79b0f4e..a7aff59 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -73,6 +73,7 @@
})
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(cc.LibraryFactory))
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
@@ -271,6 +272,25 @@
soc_specific: true,
static_libs: ["sysprop-platform", "sysprop-vendor"],
}
+
+ cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "liblog",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ llndk_library {
+ name: "liblog",
+ symbol_file: "",
+ }
`)
for _, variant := range []string{