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
 }
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 3cadaeb..bec24c7 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -14,10 +14,7 @@
 
 package android
 
-import (
-	"fmt"
-	"io"
-)
+import "strconv"
 
 // TODO(jungw): Now that it handles more than the ones in etc/, consider renaming this file.
 
@@ -134,37 +131,25 @@
 	})
 }
 
-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())
-			WriteRequiredModulesSettings(w, data)
+func (p *PrebuiltEtc) AndroidMkEntries() AndroidMkEntries {
+	nameSuffix := ""
+	if p.inRecovery() && !p.onlyInRecovery() {
+		nameSuffix = ".recovery"
+	}
+	return AndroidMkEntries{
+		Class:      "ETC",
+		SubName:    nameSuffix,
+		OutputFile: OptionalPathForPath(p.outputFilePath),
+		AddCustomEntries: func(name, prefix, moduleDir string, 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 {
-				fmt.Fprint(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=")
 				for _, path := range *p.additionalDependencies {
-					fmt.Fprint(w, " "+path.String())
+					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 fbdbc97..08700ae 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,49 +137,37 @@
 }
 
 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"],
+			host_required: ["hostModA", "hostModB"],
+			target_required: ["targetModA"],
 		}
 	`)
 
-	data := AndroidMkData{}
-	data.Required = append(data.Required, "modA", "moduleB")
-	data.Host_required = append(data.Host_required, "hostModA", "hostModB")
-	data.Target_required = append(data.Target_required, "targetModA")
-
-	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",
-		"LOCAL_HOST_REQUIRED_MODULES":   "hostModA hostModB",
-		"LOCAL_TARGET_REQUIRED_MODULES": "targetModA",
+	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"},
+		"LOCAL_HOST_REQUIRED_MODULES":   {"hostModA", "hostModB"},
+		"LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"},
 	}
 
 	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/testing.go b/android/testing.go
index aee6855..c0db75e 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -371,3 +371,14 @@
 		}
 	}
 }
+
+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/java/androidmk.go b/java/androidmk.go
index d2e0f2e..304b1c4 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -38,7 +38,15 @@
 		}
 		fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
 		fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
-		android.WriteRequiredModulesSettings(w, data)
+		if len(data.Required) > 0 {
+			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
+		}
+		if len(data.Host_required) > 0 {
+			fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES :=", strings.Join(data.Host_required, " "))
+		}
+		if len(data.Target_required) > 0 {
+			fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=", strings.Join(data.Target_required, " "))
+		}
 		fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
 	}
 }