From 9aa3ab1f3f748f1d0790729712860b6ba83d9a4a Mon Sep 17 00:00:00 2001 From: Jaewoong Jung Date: Wed, 3 Apr 2019 15:47:29 -0700 Subject: [PATCH] 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 --- android/androidmk.go | 375 ++++++++++++++++++++++------------- android/prebuilt_etc.go | 47 ++--- android/prebuilt_etc_test.go | 54 ++--- android/testing.go | 11 + java/androidmk.go | 10 +- 5 files changed, 295 insertions(+), 202 deletions(-) diff --git a/android/androidmk.go b/android/androidmk.go index 42e143951..2a3748e3a 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 @@ -57,6 +59,200 @@ 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 + 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 @@ 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 } @@ -178,37 +376,32 @@ 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...) - 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 @@ 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()) - 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 @@ func WriteAndroidMkData(w io.Writer, data AndroidMkData) { 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, " ")) - } - 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, " ")) +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/prebuilt_etc.go b/android/prebuilt_etc.go index 3cadaeb22..bec24c777 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) 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()) - 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 fbdbc970b..08700ae56 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 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"], + 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 aee68550b..c0db75ecc 100644 --- a/android/testing.go +++ b/android/testing.go @@ -371,3 +371,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/java/androidmk.go b/java/androidmk.go index d2e0f2e63..304b1c42d 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -38,7 +38,15 @@ func (library *Library) AndroidMkHostDex(w io.Writer, name string, data android. } 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") } }