New AndroidMk authoring system based on entry map.
The new system collects all Android.mk variable assignments using a map
and writes them to io.Writer. Compared to the previous system, which
directly writes all entries to buffers, this new system is more robust
and test-friendly.
Test: Built without prebuilt_etc.go change and diffed the mk output.
Test: prebuilt_etc_test.go
Change-Id: Idd28443d129ff70053295015e69328a8fa3eca47
diff --git a/android/androidmk.go b/android/androidmk.go
index 42e1439..2a3748e 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -32,6 +32,8 @@
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
@@ -57,6 +59,200 @@
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
+ Host_required []string
+ Target_required []string
+
+ header bytes.Buffer
+ footer bytes.Buffer
+
+ AddCustomEntries func(name, prefix, moduleDir string, entries *AndroidMkEntries)
+
+ EntryMap map[string][]string
+ entryOrder []string
+}
+
+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...)
+ a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
+ a.Target_required = append(a.Target_required, amod.commonProperties.Target_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...)
+ a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
+ a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_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
+ }
+ }
+ blueprintDir := filepath.Dir(bpPath)
+ if a.AddCustomEntries != nil {
+ a.AddCustomEntries(name, prefix, blueprintDir, 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{}
}
@@ -159,6 +355,8 @@
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
}
@@ -178,37 +376,32 @@
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...)
- data.Host_required = append(data.Host_required, amod.commonProperties.Host_required...)
- data.Target_required = append(data.Target_required, amod.commonProperties.Target_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,
+ Host_required: data.Host_required,
+ Target_required: data.Target_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() {
@@ -227,112 +420,7 @@
}
}
- 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())
- WriteRequiredModulesSettings(&data.preamble, data)
-
- 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 {
@@ -362,14 +450,29 @@
fmt.Fprintln(w, "include "+data.Include)
}
-func WriteRequiredModulesSettings(w io.Writer, data AndroidMkData) {
- if len(data.Required) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
+func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ provider AndroidMkEntriesProvider) error {
+ if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
+ return nil
}
- if len(data.Host_required) > 0 {
- fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(data.Host_required, " "))
+
+ 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
}
- if len(data.Target_required) > 0 {
- fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(data.Target_required, " "))
- }
+
+ return !module.Enabled() ||
+ module.commonProperties.SkipInstall ||
+ // Make does not understand LinuxBionic
+ module.Os() == LinuxBionic
}