aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/androidmk.go363
-rw-r--r--android/arch.go4
-rw-r--r--android/bootjar.go117
-rw-r--r--android/config.go8
-rw-r--r--android/module.go17
-rw-r--r--android/namespace.go12
-rw-r--r--android/namespace_test.go43
-rw-r--r--android/paths.go53
-rw-r--r--android/paths_test.go5
-rw-r--r--android/prebuilt.go53
-rw-r--r--android/prebuilt_etc.go57
-rw-r--r--android/prebuilt_etc_test.go46
-rw-r--r--android/sh_binary.go75
-rw-r--r--android/sh_binary_test.go79
-rw-r--r--android/testing.go11
-rw-r--r--android/variable.go21
16 files changed, 740 insertions, 224 deletions
diff --git a/android/androidmk.go b/android/androidmk.go
index bd49e4c6..4a968f5e 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -32,6 +32,8 @@ func init() {
RegisterSingletonType("androidmk", AndroidMkSingleton)
}
+// Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to
+// use the Custom function.
type AndroidMkDataProvider interface {
AndroidMk() AndroidMkData
BaseModuleName() string
@@ -55,6 +57,195 @@ type AndroidMkData struct {
type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
+// Allows modules to customize their Android*.mk output.
+type AndroidMkEntriesProvider interface {
+ AndroidMkEntries() AndroidMkEntries
+ BaseModuleName() string
+}
+
+type AndroidMkEntries struct {
+ Class string
+ SubName string
+ DistFile OptionalPath
+ OutputFile OptionalPath
+ Disabled bool
+ Include string
+ Required []string
+
+ header bytes.Buffer
+ footer bytes.Buffer
+
+ ExtraEntries []AndroidMkExtraEntriesFunc
+
+ EntryMap map[string][]string
+ entryOrder []string
+}
+
+type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+
+func (a *AndroidMkEntries) SetString(name, value string) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.entryOrder = append(a.entryOrder, name)
+ }
+ a.EntryMap[name] = []string{value}
+}
+
+func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
+ if flag {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.entryOrder = append(a.entryOrder, name)
+ }
+ a.EntryMap[name] = []string{"true"}
+ }
+}
+
+func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
+ if len(value) == 0 {
+ return
+ }
+ if _, ok := a.EntryMap[name]; !ok {
+ a.entryOrder = append(a.entryOrder, name)
+ }
+ a.EntryMap[name] = append(a.EntryMap[name], value...)
+}
+
+func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
+ a.EntryMap = make(map[string][]string)
+ amod := mod.(Module).base()
+ name := amod.BaseModuleName()
+
+ if a.Include == "" {
+ a.Include = "$(BUILD_PREBUILT)"
+ }
+ a.Required = append(a.Required, amod.commonProperties.Required...)
+
+ // Fill in the header part.
+ if len(amod.commonProperties.Dist.Targets) > 0 {
+ distFile := a.DistFile
+ if !distFile.Valid() {
+ distFile = a.OutputFile
+ }
+ if distFile.Valid() {
+ dest := filepath.Base(distFile.String())
+
+ if amod.commonProperties.Dist.Dest != nil {
+ var err error
+ if dest, err = validateSafePath(*amod.commonProperties.Dist.Dest); err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ if amod.commonProperties.Dist.Suffix != nil {
+ ext := filepath.Ext(dest)
+ suffix := *amod.commonProperties.Dist.Suffix
+ dest = strings.TrimSuffix(dest, ext) + suffix + ext
+ }
+
+ if amod.commonProperties.Dist.Dir != nil {
+ var err error
+ if dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest); err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
+ fmt.Fprintln(&a.header, ".PHONY:", goals)
+ fmt.Fprintf(&a.header, "$(call dist-for-goals,%s,%s:%s)\n",
+ goals, distFile.String(), dest)
+ }
+ }
+
+ fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
+
+ // Collect make variable assignment entries.
+ a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
+ a.SetString("LOCAL_MODULE", name+a.SubName)
+ a.SetString("LOCAL_MODULE_CLASS", a.Class)
+ a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
+ a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
+
+ archStr := amod.Arch().ArchType.String()
+ host := false
+ switch amod.Os().Class {
+ case Host:
+ // Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+ if archStr != "common" {
+ a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+ }
+ host = true
+ case HostCross:
+ // Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+ if archStr != "common" {
+ a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+ }
+ host = true
+ case Device:
+ // Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
+ if archStr != "common" {
+ a.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
+ }
+
+ a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
+ a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
+ a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
+ if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
+ a.SetString("LOCAL_VENDOR_MODULE", "true")
+ }
+ a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
+ a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
+ a.SetBoolIfTrue("LOCAL_PRODUCT_SERVICES_MODULE", Bool(amod.commonProperties.Product_services_specific))
+ if amod.commonProperties.Owner != nil {
+ a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
+ }
+ }
+
+ if amod.noticeFile.Valid() {
+ a.SetString("LOCAL_NOTICE_FILE", amod.noticeFile.String())
+ }
+
+ if host {
+ makeOs := amod.Os().String()
+ if amod.Os() == Linux || amod.Os() == LinuxBionic {
+ makeOs = "linux"
+ }
+ a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
+ a.SetString("LOCAL_IS_HOST_MODULE", "true")
+ }
+
+ prefix := ""
+ if amod.ArchSpecific() {
+ switch amod.Os().Class {
+ case Host:
+ prefix = "HOST_"
+ case HostCross:
+ prefix = "HOST_CROSS_"
+ case Device:
+ prefix = "TARGET_"
+
+ }
+
+ if amod.Arch().ArchType != config.Targets[amod.Os()][0].Arch.ArchType {
+ prefix = "2ND_" + prefix
+ }
+ }
+ for _, extra := range a.ExtraEntries {
+ extra(a)
+ }
+
+ // Write to footer.
+ fmt.Fprintln(&a.footer, "include "+a.Include)
+}
+
+func (a *AndroidMkEntries) write(w io.Writer) {
+ w.Write(a.header.Bytes())
+ for _, name := range a.entryOrder {
+ fmt.Fprintln(w, name+" := "+strings.Join(a.EntryMap[name], " "))
+ }
+ w.Write(a.footer.Bytes())
+}
+
func AndroidMkSingleton() Singleton {
return &androidMkSingleton{}
}
@@ -157,6 +348,8 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.M
return translateAndroidModule(ctx, w, mod, x)
case bootstrap.GoBinaryTool:
return translateGoBinaryModule(ctx, w, mod, x)
+ case AndroidMkEntriesProvider:
+ return translateAndroidMkEntriesModule(ctx, w, mod, x)
default:
return nil
}
@@ -176,35 +369,30 @@ func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Mo
func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
provider AndroidMkDataProvider) error {
- name := provider.BaseModuleName()
amod := mod.(Module).base()
-
- if !amod.Enabled() {
- return nil
- }
-
- if amod.commonProperties.SkipInstall {
- return nil
- }
-
- if !amod.commonProperties.NamespaceExportedToMake {
- // TODO(jeffrygaston) do we want to validate that there are no modules being
- // exported to Kati that depend on this module?
+ if shouldSkipAndroidMkProcessing(amod) {
return nil
}
data := provider.AndroidMk()
-
if data.Include == "" {
data.Include = "$(BUILD_PREBUILT)"
}
- data.Required = append(data.Required, amod.commonProperties.Required...)
-
- // Make does not understand LinuxBionic
- if amod.Os() == LinuxBionic {
- return nil
+ // Get the preamble content through AndroidMkEntries logic.
+ entries := AndroidMkEntries{
+ Class: data.Class,
+ SubName: data.SubName,
+ DistFile: data.DistFile,
+ OutputFile: data.OutputFile,
+ Disabled: data.Disabled,
+ Include: data.Include,
+ Required: data.Required,
}
+ entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
+ // preamble doesn't need the footer content.
+ entries.footer = bytes.Buffer{}
+ entries.write(&data.preamble)
prefix := ""
if amod.ArchSpecific() {
@@ -223,115 +411,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Mod
}
}
- if len(amod.commonProperties.Dist.Targets) > 0 {
- distFile := data.DistFile
- if !distFile.Valid() {
- distFile = data.OutputFile
- }
- if distFile.Valid() {
- dest := filepath.Base(distFile.String())
-
- if amod.commonProperties.Dist.Dest != nil {
- var err error
- dest, err = validateSafePath(*amod.commonProperties.Dist.Dest)
- if err != nil {
- // This was checked in ModuleBase.GenerateBuildActions
- panic(err)
- }
- }
-
- if amod.commonProperties.Dist.Suffix != nil {
- ext := filepath.Ext(dest)
- suffix := *amod.commonProperties.Dist.Suffix
- dest = strings.TrimSuffix(dest, ext) + suffix + ext
- }
-
- if amod.commonProperties.Dist.Dir != nil {
- var err error
- dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest)
- if err != nil {
- // This was checked in ModuleBase.GenerateBuildActions
- panic(err)
- }
- }
-
- goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
- fmt.Fprintln(&data.preamble, ".PHONY:", goals)
- fmt.Fprintf(&data.preamble, "$(call dist-for-goals,%s,%s:%s)\n",
- goals, distFile.String(), dest)
- }
- }
-
- fmt.Fprintln(&data.preamble, "\ninclude $(CLEAR_VARS)")
- fmt.Fprintln(&data.preamble, "LOCAL_PATH :=", filepath.Dir(ctx.BlueprintFile(mod)))
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE :=", name+data.SubName)
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_CLASS :=", data.Class)
- fmt.Fprintln(&data.preamble, "LOCAL_PREBUILT_MODULE_FILE :=", data.OutputFile.String())
-
- if len(data.Required) > 0 {
- fmt.Fprintln(&data.preamble, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
- }
-
- archStr := amod.Arch().ArchType.String()
- host := false
- switch amod.Os().Class {
- case Host:
- // Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
- if archStr != "common" {
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_HOST_ARCH :=", archStr)
- }
- host = true
- case HostCross:
- // Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
- if archStr != "common" {
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
- }
- host = true
- case Device:
- // Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
- if archStr != "common" {
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
- }
-
- if len(amod.commonProperties.Init_rc) > 0 {
- fmt.Fprintln(&data.preamble, "LOCAL_INIT_RC := ", strings.Join(amod.commonProperties.Init_rc, " "))
- }
- if len(amod.commonProperties.Vintf_fragments) > 0 {
- fmt.Fprintln(&data.preamble, "LOCAL_VINTF_FRAGMENTS := ", strings.Join(amod.commonProperties.Vintf_fragments, " "))
- }
- if Bool(amod.commonProperties.Proprietary) {
- fmt.Fprintln(&data.preamble, "LOCAL_PROPRIETARY_MODULE := true")
- }
- 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_PRODUCT_MODULE := true")
- }
- if Bool(amod.commonProperties.Product_services_specific) {
- fmt.Fprintln(&data.preamble, "LOCAL_PRODUCT_SERVICES_MODULE := true")
- }
- if amod.commonProperties.Owner != nil {
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_OWNER :=", *amod.commonProperties.Owner)
- }
- }
-
- if amod.noticeFile.Valid() {
- fmt.Fprintln(&data.preamble, "LOCAL_NOTICE_FILE :=", amod.noticeFile.String())
- }
-
- if host {
- makeOs := amod.Os().String()
- if amod.Os() == Linux || amod.Os() == LinuxBionic {
- makeOs = "linux"
- }
- fmt.Fprintln(&data.preamble, "LOCAL_MODULE_HOST_OS :=", makeOs)
- fmt.Fprintln(&data.preamble, "LOCAL_IS_HOST_MODULE := true")
- }
-
+ name := provider.BaseModuleName()
blueprintDir := filepath.Dir(ctx.BlueprintFile(mod))
if data.Custom != nil {
@@ -360,3 +440,30 @@ func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
fmt.Fprintln(w, "include "+data.Include)
}
+
+func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ provider AndroidMkEntriesProvider) error {
+ if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
+ return nil
+ }
+
+ entries := provider.AndroidMkEntries()
+ entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
+
+ entries.write(w)
+
+ return nil
+}
+
+func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
+ if !module.commonProperties.NamespaceExportedToMake {
+ // TODO(jeffrygaston) do we want to validate that there are no modules being
+ // exported to Kati that depend on this module?
+ return true
+ }
+
+ return !module.Enabled() ||
+ module.commonProperties.SkipInstall ||
+ // Make does not understand LinuxBionic
+ module.Os() == LinuxBionic
+}
diff --git a/android/arch.go b/android/arch.go
index 957a659c..6e8c4bdb 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -557,6 +557,10 @@ func newArch(name, multilib string) ArchType {
return archType
}
+func ArchTypeList() []ArchType {
+ return append([]ArchType(nil), archTypeList...)
+}
+
func (a ArchType) String() string {
return a.Name
}
diff --git a/android/bootjar.go b/android/bootjar.go
new file mode 100644
index 00000000..ea04dc8f
--- /dev/null
+++ b/android/bootjar.go
@@ -0,0 +1,117 @@
+// Copyright (C) 2019 The LineageOS Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "strings"
+)
+
+// Keys are bootjar name, value is whether or not
+// we've marked a module as being a provider for it.
+var jarMap map[string]bool
+
+func init() {
+ PreArchMutators(RegisterBootJarMutators)
+}
+
+// Note: registration function is structured this way so that it can be included
+// from soong module tests.
+func RegisterBootJarMutators(ctx RegisterMutatorsContext) {
+ // Note: can't use Parallel() since we touch global jarMap
+ ctx.TopDown("bootjar_exportednamespace", bootJarMutatorExportedNamespace)
+ ctx.TopDown("bootjar_anynamespace", bootJarMutatorAnyNamespace)
+}
+
+func mutatorInit(mctx TopDownMutatorContext) {
+ // Did we init already ?
+ if jarMap != nil {
+ return
+ }
+
+ jarMap = make(map[string]bool)
+ for _, moduleName := range mctx.Config().BootJars() {
+ jarMap[moduleName] = false
+ }
+}
+
+// Mark modules in soong exported namespace as providing a boot jar.
+func bootJarMutatorExportedNamespace(mctx TopDownMutatorContext) {
+ bootJarMutator(mctx, true)
+}
+
+// Mark modules in any namespace (incl root) as providing a boot jar.
+func bootJarMutatorAnyNamespace(mctx TopDownMutatorContext) {
+ bootJarMutator(mctx, false)
+}
+
+func bootJarMutator(mctx TopDownMutatorContext, requireExportedNamespace bool) {
+ mutatorInit(mctx)
+
+ module, ok := mctx.Module().(Module)
+ if !ok {
+ // Not a proper module
+ return
+ }
+
+ // Does this module produce a dex jar ?
+ if _, ok := module.(interface{ DexJar() Path }); !ok {
+ // No
+ return
+ }
+
+ // If jarMap is empty we must be running in a test so
+ // set boot jar provide to true for all modules.
+ if len(jarMap) == 0 {
+ module.base().commonProperties.BootJarProvider = true
+ return
+ }
+
+ name := mctx.ModuleName()
+ dir := mctx.ModuleDir()
+
+ // Special treatment for hiddenapi modules - create extra
+ // jarMap entries if needed.
+ baseName := strings.TrimSuffix(name, "-hiddenapi")
+ if name != baseName {
+ _, baseIsBootJar := jarMap[baseName]
+ _, alreadyExists := jarMap[name]
+ if baseIsBootJar && !alreadyExists {
+ // This is a hidden api module whose base name exists in the boot jar list
+ // and we've not visited it before. Create a map entry for it.
+ jarMap[name] = false
+ }
+ }
+
+ // Does this module match the name of a boot jar ?
+ if found, exists := jarMap[name]; !exists || found {
+ // No
+ return
+ }
+
+ if requireExportedNamespace {
+ for _, n := range mctx.Config().ExportedNamespaces() {
+ if strings.HasPrefix(dir, n) {
+ jarMap[name] = true
+ module.base().commonProperties.BootJarProvider = true
+ break
+ }
+ }
+ } else {
+ jarMap[name] = true
+ module.base().commonProperties.BootJarProvider = true
+ }
+
+ return
+}
diff --git a/android/config.go b/android/config.go
index d1db87b2..7c604a42 100644
--- a/android/config.go
+++ b/android/config.go
@@ -584,7 +584,7 @@ func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
if defaultCert != "" {
return PathForSource(ctx, filepath.Dir(defaultCert))
} else {
- return PathForSource(ctx, "build/target/product/security")
+ return PathForSource(ctx, "build/make/target/product/security")
}
}
@@ -601,7 +601,7 @@ func (c *config) DefaultAppCertificate(ctx PathContext) (pem, key SourcePath) {
func (c *config) ApexKeyDir(ctx ModuleContext) SourcePath {
// TODO(b/121224311): define another variable such as TARGET_APEX_KEY_OVERRIDE
defaultCert := String(c.productVariables.DefaultAppCertificate)
- if defaultCert == "" || filepath.Dir(defaultCert) == "build/target/product/security" {
+ if defaultCert == "" || filepath.Dir(defaultCert) == "build/make/target/product/security" {
// When defaultCert is unset or is set to the testkeys path, use the APEX keys
// that is under the module dir
return pathForModuleSrc(ctx)
@@ -882,6 +882,10 @@ func (c *deviceConfig) DeviceKernelHeaderDirs() []string {
return c.config.productVariables.DeviceKernelHeaders
}
+func (c *deviceConfig) SpecificCameraParametersLibrary() string {
+ return String(c.config.productVariables.Lineage.Specific_camera_parameter_library)
+}
+
func (c *deviceConfig) NativeCoverageEnabled() bool {
return Bool(c.config.productVariables.NativeCoverage)
}
diff --git a/android/module.go b/android/module.go
index 6a796227..17ddb050 100644
--- a/android/module.go
+++ b/android/module.go
@@ -129,6 +129,7 @@ type ModuleContext interface {
AddMissingDependencies(deps []string)
InstallInData() bool
+ InstallInTestcases() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
@@ -185,6 +186,7 @@ type Module interface {
Enabled() bool
Target() Target
InstallInData() bool
+ InstallInTestcases() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
SkipInstall()
@@ -302,6 +304,9 @@ type commonProperties struct {
SkipInstall bool `blueprint:"mutated"`
NamespaceExportedToMake bool `blueprint:"mutated"`
+
+ // Whether this module provides a boot jar
+ BootJarProvider bool `blueprint:"mutated"`
}
type hostAndDeviceProperties struct {
@@ -519,6 +524,10 @@ func (a *ModuleBase) Name() string {
return String(a.nameProperties.Name)
}
+func (a *ModuleBase) BootJarProvider() bool {
+ return a.commonProperties.BootJarProvider
+}
+
// BaseModuleName returns the name of the module as specified in the blueprints file.
func (a *ModuleBase) BaseModuleName() string {
return String(a.nameProperties.Name)
@@ -656,6 +665,10 @@ func (p *ModuleBase) InstallInData() bool {
return false
}
+func (p *ModuleBase) InstallInTestcases() bool {
+ return false
+}
+
func (p *ModuleBase) InstallInSanitizerDir() bool {
return false
}
@@ -1252,6 +1265,10 @@ func (a *androidModuleContext) InstallInData() bool {
return a.module.InstallInData()
}
+func (a *androidModuleContext) InstallInTestcases() bool {
+ return a.module.InstallInTestcases()
+}
+
func (a *androidModuleContext) InstallInSanitizerDir() bool {
return a.module.InstallInSanitizerDir()
}
diff --git a/android/namespace.go b/android/namespace.go
index 50bdcba7..a2bd0242 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -228,6 +228,11 @@ func (r *NameResolver) parseFullyQualifiedName(name string) (namespaceName strin
}
func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
+ if sourceNamespace.visibleNamespaces == nil {
+ // When handling dependencies before namespaceMutator, assume they are non-Soong Blueprint modules and give
+ // access to all namespaces.
+ return r.sortedNamespaces.sortedItems()
+ }
return sourceNamespace.visibleNamespaces
}
@@ -265,7 +270,12 @@ func (r *NameResolver) FindNamespaceImports(namespace *Namespace) (err error) {
for _, name := range namespace.importedNamespaceNames {
imp, ok := r.namespaceAt(name)
if !ok {
- return fmt.Errorf("namespace %v does not exist", name)
+ if (name != "all") {
+ return fmt.Errorf("namespace %v does not exist", name)
+ } else {
+ namespace.visibleNamespaces = make([]*Namespace, 0, 2+len(namespace.importedNamespaceNames))
+ return nil
+ }
}
namespace.visibleNamespaces = append(namespace.visibleNamespaces, imp)
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 9a791a53..ec392dfc 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -93,6 +93,28 @@ func TestImplicitlyImportRootNamespace(t *testing.T) {
// setupTest will report any errors
}
+func TestDependingOnBlueprintModuleInRootNamespace(t *testing.T) {
+ _ = setupTest(t,
+ map[string]string{
+ ".": `
+ blueprint_test_module {
+ name: "a",
+ }
+ `,
+ "dir1": `
+ soong_namespace {
+ }
+ blueprint_test_module {
+ name: "b",
+ deps: ["a"],
+ }
+ `,
+ },
+ )
+
+ // setupTest will report any errors
+}
+
func TestDependingOnModuleInImportedNamespace(t *testing.T) {
ctx := setupTest(t,
map[string]string{
@@ -625,6 +647,7 @@ func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error)
ctx.MockFileSystem(bps)
ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
+ ctx.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
ctx.PreArchMutators(RegisterNamespaceMutator)
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("rename", renameMutator)
@@ -649,6 +672,7 @@ func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error)
}
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
+ t.Helper()
ctx, errs := setupTestExpectErrs(bps)
FailIfErrored(t, errs)
return ctx
@@ -726,3 +750,22 @@ func newTestModule() Module {
InitAndroidModule(m)
return m
}
+
+type blueprintTestModule struct {
+ blueprint.SimpleName
+ properties struct {
+ Deps []string
+ }
+}
+
+func (b *blueprintTestModule) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+ return b.properties.Deps
+}
+
+func (b *blueprintTestModule) GenerateBuildActions(blueprint.ModuleContext) {
+}
+
+func newBlueprintTestModule() (blueprint.Module, []interface{}) {
+ m := &blueprintTestModule{}
+ return m, []interface{}{&m.properties, &m.SimpleName.Properties}
+}
diff --git a/android/paths.go b/android/paths.go
index 0f20b844..edddce90 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -46,6 +46,7 @@ type ModuleInstallPathContext interface {
androidBaseContext
InstallInData() bool
+ InstallInTestcases() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
}
@@ -644,6 +645,31 @@ func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error
return ret, nil
}
+// pathForSourceRelaxed creates a SourcePath from pathComponents, but does not check that it exists.
+// It differs from pathForSource in that the path is allowed to exist outside of the PathContext.
+func pathForSourceRelaxed(ctx PathContext, pathComponents ...string) (SourcePath, error) {
+ p := filepath.Join(pathComponents...)
+ ret := SourcePath{basePath{p, ctx.Config(), ""}}
+
+ abs, err := filepath.Abs(ret.String())
+ if err != nil {
+ return ret, err
+ }
+ buildroot, err := filepath.Abs(ctx.Config().buildDir)
+ if err != nil {
+ return ret, err
+ }
+ if strings.HasPrefix(abs, buildroot) {
+ return ret, fmt.Errorf("source path %s is in output", abs)
+ }
+
+ if pathtools.IsGlob(ret.String()) {
+ return ret, fmt.Errorf("path may not contain a glob: %s", ret.String())
+ }
+
+ return ret, nil
+}
+
// existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
// path does not exist.
func existsWithDependencies(ctx PathContext, path SourcePath) (exists bool, err error) {
@@ -697,6 +723,31 @@ func PathForSource(ctx PathContext, pathComponents ...string) SourcePath {
return path
}
+// PathForSourceRelaxed joins the provided path components. Unlike PathForSource,
+// the result is allowed to exist outside of the source dir.
+// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
+func PathForSourceRelaxed(ctx PathContext, pathComponents ...string) SourcePath {
+ path, err := pathForSourceRelaxed(ctx, pathComponents...)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
+ exists, err := existsWithDependencies(ctx, path)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+ if !exists {
+ modCtx.AddMissingDependencies([]string{path.String()})
+ }
+ } else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
+ reportPathErrorf(ctx, "%s: %s", path, err.Error())
+ } else if !exists {
+ reportPathErrorf(ctx, "source path %s does not exist", path)
+ }
+ return path
+}
+
// ExistentPathForSource returns an OptionalPath with the SourcePath if the
// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
// so that the ninja file will be regenerated if the state of the path changes.
@@ -1117,6 +1168,8 @@ func modulePartition(ctx ModuleInstallPathContext) string {
var partition string
if ctx.InstallInData() {
partition = "data"
+ } else if ctx.InstallInTestcases() {
+ partition = "testcases"
} else if ctx.InstallInRecovery() {
// the layout of recovery partion is the same as that of system partition
partition = "recovery/root/system"
diff --git a/android/paths_test.go b/android/paths_test.go
index b52d7133..c956a795 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -203,6 +203,7 @@ type moduleInstallPathContextImpl struct {
androidBaseContextImpl
inData bool
+ inTestcases bool
inSanitizerDir bool
inRecovery bool
}
@@ -221,6 +222,10 @@ func (m moduleInstallPathContextImpl) InstallInData() bool {
return m.inData
}
+func (m moduleInstallPathContextImpl) InstallInTestcases() bool {
+ return m.inTestcases
+}
+
func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
return m.inSanitizerDir
}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 3be10f72..48d3b68b 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -16,6 +16,7 @@ package android
import (
"fmt"
+ "reflect"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -43,13 +44,21 @@ type Prebuilt struct {
properties PrebuiltProperties
module Module
srcs *[]string
- src *string
+
+ // Metadata for single source Prebuilt modules.
+ srcProps reflect.Value
+ srcField reflect.StructField
}
func (p *Prebuilt) Name(name string) string {
return "prebuilt_" + name
}
+// The below source-related functions and the srcs, src fields are based on an assumption that
+// prebuilt modules have a static source property at the moment. Currently there is only one
+// exception, android_app_import, which chooses a source file depending on the product's DPI
+// preference configs. We'll want to add native support for dynamic source cases if we end up having
+// more modules like this.
func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
if p.srcs != nil {
if len(*p.srcs) == 0 {
@@ -66,11 +75,16 @@ func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
// sources.
return PathForModuleSrc(ctx, (*p.srcs)[0])
} else {
- if proptools.String(p.src) == "" {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
+ if !p.srcProps.IsValid() {
+ ctx.ModuleErrorf("prebuilt source was not set")
+ }
+ src := p.getSingleSourceFieldValue()
+ if src == "" {
+ ctx.PropertyErrorf(proptools.FieldNameForProperty(p.srcField.Name),
+ "missing prebuilt source file")
return nil
}
- return PathForModuleSrc(ctx, *p.src)
+ return PathForModuleSrc(ctx, src)
}
}
@@ -84,10 +98,12 @@ func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
p.srcs = srcs
}
-func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
p := module.Prebuilt()
module.AddProperties(&p.properties)
- p.src = src
+ p.srcProps = reflect.ValueOf(srcProps).Elem()
+ p.srcField, _ = p.srcProps.Type().FieldByName(srcField)
+ p.checkSingleSourceProperties()
}
type PrebuiltInterface interface {
@@ -124,7 +140,7 @@ func PrebuiltMutator(ctx BottomUpMutatorContext) {
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
- if p.srcs == nil && p.src == nil {
+ if p.srcs == nil && !p.srcProps.IsValid() {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
@@ -167,7 +183,7 @@ func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
return false
}
- if p.src != nil && *p.src == "" {
+ if p.srcProps.IsValid() && p.getSingleSourceFieldValue() == "" {
return false
}
@@ -182,3 +198,24 @@ func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
func (p *Prebuilt) SourceExists() bool {
return p.properties.SourceExists
}
+
+func (p *Prebuilt) checkSingleSourceProperties() {
+ if !p.srcProps.IsValid() || p.srcField.Name == "" {
+ panic(fmt.Errorf("invalid single source prebuilt %+v", p))
+ }
+
+ if p.srcProps.Kind() != reflect.Struct && p.srcProps.Kind() != reflect.Interface {
+ panic(fmt.Errorf("invalid single source prebuilt %+v", p.srcProps))
+ }
+}
+
+func (p *Prebuilt) getSingleSourceFieldValue() string {
+ value := p.srcProps.FieldByIndex(p.srcField.Index)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+ if value.Kind() != reflect.String {
+ panic(fmt.Errorf("prebuilt src field %q should be a string or a pointer to one", p.srcField.Name))
+ }
+ return value.String()
+}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 8f23d78e..f35e3c76 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -14,11 +14,7 @@
package android
-import (
- "fmt"
- "io"
- "strings"
-)
+import "strconv"
// TODO(jungw): Now that it handles more than the ones in etc/, consider renaming this file.
@@ -135,38 +131,27 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) {
})
}
-func (p *PrebuiltEtc) AndroidMk() AndroidMkData {
- return AndroidMkData{
- Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
- nameSuffix := ""
- if p.inRecovery() && !p.onlyInRecovery() {
- nameSuffix = ".recovery"
- }
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
- fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
- fmt.Fprintln(w, "LOCAL_MODULE :=", name+nameSuffix)
- fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
- if p.commonProperties.Owner != nil {
- fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", *p.commonProperties.Owner)
- }
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional")
- if p.Host() {
- fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
- }
- fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", p.outputFilePath.String())
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
- fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", p.outputFilePath.Base())
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !p.Installable())
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
- fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", p.Arch().ArchType.String())
- if p.additionalDependencies != nil {
- fmt.Fprint(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=")
- for _, path := range *p.additionalDependencies {
- fmt.Fprint(w, " "+path.String())
+func (p *PrebuiltEtc) AndroidMkEntries() AndroidMkEntries {
+ nameSuffix := ""
+ if p.inRecovery() && !p.onlyInRecovery() {
+ nameSuffix = ".recovery"
+ }
+ return AndroidMkEntries{
+ Class: "ETC",
+ SubName: nameSuffix,
+ OutputFile: OptionalPathForPath(p.outputFilePath),
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_TAGS", "optional")
+ entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+ entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
+ if p.additionalDependencies != nil {
+ for _, path := range *p.additionalDependencies {
+ entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+ }
}
- fmt.Fprintln(w, "")
- }
- fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+ },
},
}
}
diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go
index e0ade7e5..ee6e990e 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -15,12 +15,10 @@
package android
import (
- "bufio"
- "bytes"
"io/ioutil"
"os"
"path/filepath"
- "strings"
+ "reflect"
"testing"
)
@@ -139,45 +137,33 @@ func TestPrebuiltEtcGlob(t *testing.T) {
}
func TestPrebuiltEtcAndroidMk(t *testing.T) {
- ctx, _ := testPrebuiltEtc(t, `
+ ctx, config := testPrebuiltEtc(t, `
prebuilt_etc {
name: "foo",
src: "foo.conf",
owner: "abc",
filename_from_src: true,
+ required: ["modA", "moduleB"],
}
`)
- data := AndroidMkData{}
- data.Required = append(data.Required, "modA", "moduleB")
-
- expected := map[string]string{
- "LOCAL_MODULE": "foo",
- "LOCAL_MODULE_CLASS": "ETC",
- "LOCAL_MODULE_OWNER": "abc",
- "LOCAL_INSTALLED_MODULE_STEM": "foo.conf",
- "LOCAL_REQUIRED_MODULES": "modA moduleB",
+ expected := map[string][]string{
+ "LOCAL_MODULE": {"foo"},
+ "LOCAL_MODULE_CLASS": {"ETC"},
+ "LOCAL_MODULE_OWNER": {"abc"},
+ "LOCAL_INSTALLED_MODULE_STEM": {"foo.conf"},
+ "LOCAL_REQUIRED_MODULES": {"modA", "moduleB"},
}
mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
- buf := &bytes.Buffer{}
- mod.AndroidMk().Custom(buf, "foo", "", "", data)
- for k, expected := range expected {
- found := false
- scanner := bufio.NewScanner(bytes.NewReader(buf.Bytes()))
- for scanner.Scan() {
- line := scanner.Text()
- tok := strings.Split(line, " := ")
- if tok[0] == k {
- found = true
- if tok[1] != expected {
- t.Errorf("Incorrect %s '%s', expected '%s'", k, tok[1], expected)
- }
+ entries := AndroidMkEntriesForTest(t, config, "", mod)
+ for k, expectedValue := range expected {
+ if value, ok := entries.EntryMap[k]; ok {
+ if !reflect.DeepEqual(value, expectedValue) {
+ t.Errorf("Incorrect %s '%s', expected '%s'", k, value, expectedValue)
}
- }
-
- if !found {
- t.Errorf("No %s defined, saw %s", k, buf.String())
+ } else {
+ t.Errorf("No %s defined, saw %q", k, entries.EntryMap)
}
}
}
diff --git a/android/sh_binary.go b/android/sh_binary.go
index cf415c56..ba0c8be7 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -16,7 +16,6 @@ package android
import (
"fmt"
- "io"
"strings"
)
@@ -30,6 +29,7 @@ func init() {
RegisterModuleType("sh_binary", ShBinaryFactory)
RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
RegisterModuleType("sh_test", ShTestFactory)
+ RegisterModuleType("sh_test_host", ShTestHostFactory)
}
type shBinaryProperties struct {
@@ -58,6 +58,10 @@ type TestProperties struct {
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
Test_config *string `android:"arch_variant"`
+
+ // list of files or filegroup modules that provide data that should be installed alongside
+ // the test.
+ Data []string `android:"path,arch_variant"`
}
type ShBinary struct {
@@ -73,6 +77,8 @@ type ShTest struct {
ShBinary
testProperties TestProperties
+
+ data Paths
}
func (s *ShBinary) DepsMutator(ctx BottomUpMutatorContext) {
@@ -122,30 +128,54 @@ func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
})
}
-func (s *ShBinary) AndroidMk() AndroidMkData {
- return AndroidMkData{
+func (s *ShBinary) AndroidMkEntries() AndroidMkEntries {
+ return AndroidMkEntries{
Class: "EXECUTABLES",
OutputFile: OptionalPathForPath(s.outputFilePath),
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
- Extra: []AndroidMkExtraFunc{
- func(w io.Writer, outputFile Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", String(s.properties.Sub_dir))
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX :=")
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", s.outputFilePath.Rel())
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ s.customAndroidMkEntries(entries)
},
},
}
}
-func (s *ShTest) AndroidMk() AndroidMkData {
- data := s.ShBinary.AndroidMk()
- data.Class = "NATIVE_TESTS"
- data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(s.testProperties.Test_suites, " "))
- fmt.Fprintln(w, "LOCAL_TEST_CONFIG :=", String(s.testProperties.Test_config))
- })
- return data
+func (s *ShBinary) customAndroidMkEntries(entries *AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_RELATIVE_PATH", String(s.properties.Sub_dir))
+ entries.SetString("LOCAL_MODULE_SUFFIX", "")
+ entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
+}
+
+func (s *ShTest) GenerateAndroidBuildActions(ctx ModuleContext) {
+ s.ShBinary.GenerateAndroidBuildActions(ctx)
+
+ s.data = PathsForModuleSrc(ctx, s.testProperties.Data)
+}
+
+func (s *ShTest) AndroidMkEntries() AndroidMkEntries {
+ return AndroidMkEntries{
+ Class: "NATIVE_TESTS",
+ OutputFile: OptionalPathForPath(s.outputFilePath),
+ Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ s.customAndroidMkEntries(entries)
+
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
+ entries.SetString("LOCAL_TEST_CONFIG", String(s.testProperties.Test_config))
+ for _, d := range s.data {
+ rel := d.Rel()
+ path := d.String()
+ if !strings.HasSuffix(path, rel) {
+ panic(fmt.Errorf("path %q does not end with %q", path, rel))
+ }
+ path = strings.TrimSuffix(path, rel)
+ entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
+ }
+ },
+ },
+ }
}
func InitShBinaryModule(s *ShBinary) {
@@ -170,6 +200,7 @@ func ShBinaryHostFactory() Module {
return module
}
+// sh_test defines a shell script based test module.
func ShTestFactory() Module {
module := &ShTest{}
InitShBinaryModule(&module.ShBinary)
@@ -178,3 +209,13 @@ func ShTestFactory() Module {
InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
return module
}
+
+// sh_test_host defines a shell script based test module that runs on a host.
+func ShTestHostFactory() Module {
+ module := &ShTest{}
+ InitShBinaryModule(&module.ShBinary)
+ module.AddProperties(&module.testProperties)
+
+ InitAndroidArchModule(module, HostSupported, MultilibFirst)
+ return module
+}
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
new file mode 100644
index 00000000..67360830
--- /dev/null
+++ b/android/sh_binary_test.go
@@ -0,0 +1,79 @@
+package android
+
+import (
+ "io/ioutil"
+ "os"
+ "reflect"
+ "testing"
+)
+
+func testShBinary(t *testing.T, bp string) (*TestContext, Config) {
+ buildDir, err := ioutil.TempDir("", "soong_sh_binary_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ config := TestArchConfig(buildDir, nil)
+
+ ctx := NewTestArchContext()
+ ctx.RegisterModuleType("sh_test", ModuleFactoryAdaptor(ShTestFactory))
+ ctx.RegisterModuleType("sh_test_host", ModuleFactoryAdaptor(ShTestHostFactory))
+ ctx.Register()
+ mockFiles := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "test.sh": nil,
+ "testdata/data1": nil,
+ "testdata/sub/data2": nil,
+ }
+ ctx.MockFileSystem(mockFiles)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ return ctx, config
+}
+
+func TestShTestTestData(t *testing.T) {
+ ctx, config := testShBinary(t, `
+ sh_test {
+ name: "foo",
+ src: "test.sh",
+ filename: "test.sh",
+ data: [
+ "testdata/data1",
+ "testdata/sub/data2",
+ ],
+ }
+ `)
+
+ mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+
+ entries := AndroidMkEntriesForTest(t, config, "", mod)
+ expected := []string{":testdata/data1", ":testdata/sub/data2"}
+ actual := entries.EntryMap["LOCAL_TEST_DATA"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected test data expected: %q, actual: %q", expected, actual)
+ }
+}
+
+func TestShTestHost(t *testing.T) {
+ ctx, _ := testShBinary(t, `
+ sh_test_host {
+ name: "foo",
+ src: "test.sh",
+ filename: "test.sh",
+ data: [
+ "testdata/data1",
+ "testdata/sub/data2",
+ ],
+ }
+ `)
+
+ buildOS := BuildOs.String()
+ mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+ if !mod.Host() {
+ t.Errorf("host bit is not set for a sh_test_host module.")
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 0ec5af58..ab3c69cf 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -369,3 +369,14 @@ func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
}
}
}
+
+func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkEntries {
+ var p AndroidMkEntriesProvider
+ var ok bool
+ if p, ok = mod.(AndroidMkEntriesProvider); !ok {
+ t.Errorf("module does not implmement AndroidMkEntriesProvider: " + mod.Name())
+ }
+ entries := p.AndroidMkEntries()
+ entries.fillInEntries(config, bpPath, mod)
+ return entries
+}
diff --git a/android/variable.go b/android/variable.go
index e643c0eb..56fdcc18 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -20,6 +20,8 @@ import (
"runtime"
"strings"
+ "lineage/soong/android"
+
"github.com/google/blueprint/proptools"
)
@@ -124,6 +126,9 @@ type variableProperties struct {
Static_libs []string
Srcs []string
}
+
+ // include Lineage variables
+ Lineage android.Product_variables
} `android:"arch_variant"`
}
@@ -287,6 +292,9 @@ type productVariables struct {
ProductHiddenAPIStubsTest []string `json:",omitempty"`
TargetFSConfigGen []string `json:",omitempty"`
+
+ // include Lineage variables
+ Lineage android.ProductVariables
}
func boolPtr(v bool) *bool {
@@ -346,7 +354,13 @@ func variableMutator(mctx BottomUpMutatorContext) {
a := module.base()
variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
+ valStruct := reflect.ValueOf(mctx.Config().productVariables)
+ doVariableMutation(mctx, a, variableValues, zeroValues, valStruct)
+}
+
+func doVariableMutation(mctx BottomUpMutatorContext, a *ModuleBase, variableValues reflect.Value, zeroValues reflect.Value,
+ valStruct reflect.Value) {
for i := 0; i < variableValues.NumField(); i++ {
variableValue := variableValues.Field(i)
zeroValue := zeroValues.Field(i)
@@ -354,8 +368,11 @@ func variableMutator(mctx BottomUpMutatorContext) {
property := "product_variables." + proptools.PropertyNameForField(name)
// Check that the variable was set for the product
- val := reflect.ValueOf(mctx.Config().productVariables).FieldByName(name)
- if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
+ val := valStruct.FieldByName(name)
+ if val.IsValid() && val.Kind() == reflect.Struct {
+ doVariableMutation(mctx, a, variableValue, zeroValue, val)
+ continue
+ } else if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
continue
}