summaryrefslogtreecommitdiff
path: root/android/androidmk.go
diff options
context:
space:
mode:
Diffstat (limited to 'android/androidmk.go')
-rw-r--r--android/androidmk.go607
1 files changed, 578 insertions, 29 deletions
diff --git a/android/androidmk.go b/android/androidmk.go
index 6426835af..cac2cfe47 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -34,7 +34,6 @@ import (
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -502,6 +501,7 @@ type fillInEntriesContext interface {
otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
ModuleType(module blueprint.Module) string
OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+ HasMutatorFinished(mutatorName string) bool
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -806,15 +806,19 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs
// Additional cases here require review for correct license propagation to make.
var err error
- switch x := mod.(type) {
- case AndroidMkDataProvider:
- err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
- case bootstrap.GoBinaryTool:
- err = translateGoBinaryModule(ctx, w, mod, x)
- case AndroidMkEntriesProvider:
- err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
- default:
- // Not exported to make so no make variables to set.
+
+ if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok {
+ androidMkEntriesInfos := info.(*AndroidMkProviderInfo)
+ err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos)
+ } else {
+ switch x := mod.(type) {
+ case AndroidMkDataProvider:
+ err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
+ case AndroidMkEntriesProvider:
+ err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
+ default:
+ // Not exported to make so no make variables to set.
+ }
}
if err != nil {
@@ -824,23 +828,6 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs
return err
}
-// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
-// m by making them phony targets.
-func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
- goBinary bootstrap.GoBinaryTool) error {
-
- name := ctx.ModuleName(mod)
- fmt.Fprintln(w, ".PHONY:", name)
- fmt.Fprintln(w, name+":", goBinary.InstallPath())
- fmt.Fprintln(w, "")
- // Assuming no rules in make include go binaries in distributables.
- // If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
- // In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
- // `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
-
- return nil
-}
-
func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
// Get the preamble content through AndroidMkEntries logic.
data.Entries = AndroidMkEntries{
@@ -913,6 +900,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *
case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
case "*apex.apexBundle": // license properties written
case "*bpf.bpf": // license properties written (both for module and objs)
+ case "*libbpf_prog.libbpfProg": // license properties written (both for module and objs)
case "*genrule.Module": // writes non-custom before adding .phony
case "*java.SystemModules": // doesn't go through base_rules
case "*java.systemModulesImport": // doesn't go through base_rules
@@ -982,11 +970,11 @@ func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleIn
return nil
}
-func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
+func ShouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module Module) bool {
return shouldSkipAndroidMkProcessing(ctx, module.base())
}
-func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool {
+func shouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, 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?
@@ -1062,3 +1050,564 @@ func AndroidMkEmitAssignList(w io.Writer, varName string, lists ...[]string) {
}
fmt.Fprintln(w)
}
+
+type AndroidMkProviderInfo struct {
+ PrimaryInfo AndroidMkInfo
+ ExtraInfo []AndroidMkInfo
+}
+
+type AndroidMkInfo struct {
+ // Android.mk class string, e.g. EXECUTABLES, JAVA_LIBRARIES, ETC
+ Class string
+ // Optional suffix to append to the module name. Useful when a module wants to return multiple
+ // AndroidMkEntries objects. For example, when a java_library returns an additional entry for
+ // its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
+ // different name than the parent's.
+ SubName string
+ // If set, this value overrides the base module name. SubName is still appended.
+ OverrideName string
+ // Dist files to output
+ DistFiles TaggedDistFiles
+ // The output file for Kati to process and/or install. If absent, the module is skipped.
+ OutputFile OptionalPath
+ // If true, the module is skipped and does not appear on the final Android-<product name>.mk
+ // file. Useful when a module needs to be skipped conditionally.
+ Disabled bool
+ // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk
+ // If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
+ Include string
+ // Required modules that need to be built and included in the final build output when building
+ // this module.
+ Required []string
+ // Required host modules that need to be built and included in the final build output when
+ // building this module.
+ Host_required []string
+ // Required device modules that need to be built and included in the final build output when
+ // building this module.
+ Target_required []string
+
+ HeaderStrings []string
+ FooterStrings []string
+
+ // A map that holds the up-to-date Make variable values. Can be accessed from tests.
+ EntryMap map[string][]string
+ // A list of EntryMap keys in insertion order. This serves a few purposes:
+ // 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
+ // the outputted Android-*.mk file may change even though there have been no content changes.
+ // 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
+ // without worrying about the variables being mixed up in the actual mk file.
+ // 3. Makes troubleshooting and spotting errors easier.
+ EntryOrder []string
+}
+
+// TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
+var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
+
+func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+ mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
+ if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+ return nil
+ }
+
+ // Deep copy the provider info since we need to modify the info later
+ info := deepCopyAndroidMkProviderInfo(providerInfo)
+
+ aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
+
+ // Any new or special cases here need review to verify correct propagation of license information.
+ info.PrimaryInfo.fillInEntries(ctx, mod)
+ info.PrimaryInfo.write(w)
+ if len(info.ExtraInfo) > 0 {
+ for _, ei := range info.ExtraInfo {
+ ei.fillInEntries(ctx, mod)
+ ei.write(w)
+ }
+ }
+
+ if !info.PrimaryInfo.disabled() {
+ if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+ *moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+ }
+ }
+
+ return nil
+}
+
+// Utility funcs to manipulate Android.mk variable entries.
+
+// SetString sets a Make variable with the given name to the given value.
+func (a *AndroidMkInfo) SetString(name, value string) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = []string{value}
+}
+
+// SetPath sets a Make variable with the given name to the given path string.
+func (a *AndroidMkInfo) SetPath(name string, path Path) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = []string{path.String()}
+}
+
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) SetOptionalPath(name string, path OptionalPath) {
+ if path.Valid() {
+ a.SetPath(name, path.Path())
+ }
+}
+
+// AddPath appends the given path string to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPath(name string, path Path) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = append(a.EntryMap[name], path.String())
+}
+
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) AddOptionalPath(name string, path OptionalPath) {
+ if path.Valid() {
+ a.AddPath(name, path.Path())
+ }
+}
+
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
+func (a *AndroidMkInfo) SetPaths(name string, paths Paths) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = paths.Strings()
+}
+
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
+func (a *AndroidMkInfo) SetOptionalPaths(name string, paths Paths) {
+ if len(paths) > 0 {
+ a.SetPaths(name, paths)
+ }
+}
+
+// AddPaths appends the given path strings to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPaths(name string, paths Paths) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
+}
+
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
+func (a *AndroidMkInfo) SetBoolIfTrue(name string, flag bool) {
+ if flag {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ a.EntryMap[name] = []string{"true"}
+ }
+}
+
+// SetBool sets a Make variable with the given name to if the given bool flag value.
+func (a *AndroidMkInfo) SetBool(name string, flag bool) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.EntryOrder = append(a.EntryOrder, name)
+ }
+ if flag {
+ a.EntryMap[name] = []string{"true"}
+ } else {
+ a.EntryMap[name] = []string{"false"}
+ }
+}
+
+// AddStrings appends the given strings to a Make variable with the given name.
+func (a *AndroidMkInfo) 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...)
+}
+
+// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
+// for partial MTS and MCTS test suites.
+func (a *AndroidMkInfo) AddCompatibilityTestSuites(suites ...string) {
+ // M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+ // To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite,
+ // we add the full test suite to our list.
+ if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+ suites = append(suites, "mts")
+ }
+ if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
+ suites = append(suites, "mcts")
+ }
+ a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
+}
+
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+ helperInfo := AndroidMkInfo{
+ EntryMap: make(map[string][]string),
+ }
+
+ amod := mod.(Module)
+ base := amod.base()
+ name := base.BaseModuleName()
+ if a.OverrideName != "" {
+ name = a.OverrideName
+ }
+
+ if a.Include == "" {
+ a.Include = "$(BUILD_PREBUILT)"
+ }
+ a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
+ a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
+ a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
+ a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
+
+ for _, distString := range a.GetDistForGoals(ctx, mod) {
+ a.HeaderStrings = append(a.HeaderStrings, distString)
+ }
+
+ a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS) # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
+
+ // Collect make variable assignment entries.
+ helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
+ helperInfo.SetString("LOCAL_MODULE", name+a.SubName)
+ helperInfo.SetString("LOCAL_MODULE_CLASS", a.Class)
+ helperInfo.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
+ helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
+ helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
+ helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+ helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+
+ // If the install rule was generated by Soong tell Make about it.
+ info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+ if len(info.KatiInstalls) > 0 {
+ // Assume the primary install file is last since it probably needs to depend on any other
+ // installed files. If that is not the case we can add a method to specify the primary
+ // installed file.
+ helperInfo.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
+ helperInfo.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
+ helperInfo.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
+ } else {
+ // Soong may not have generated the install rule also when `no_full_install: true`.
+ // Mark this module as uninstallable in order to prevent Make from creating an
+ // install rule there.
+ helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
+ }
+
+ if len(info.TestData) > 0 {
+ helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
+ }
+
+ if am, ok := mod.(ApexModule); ok {
+ helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
+ }
+
+ archStr := base.Arch().ArchType.String()
+ host := false
+ switch base.Os().Class {
+ case Host:
+ if base.Target().HostCross {
+ // Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+ if base.Arch().ArchType != Common {
+ helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+ }
+ } else {
+ // Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+ if base.Arch().ArchType != Common {
+ helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+ }
+ }
+ host = true
+ case Device:
+ // Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
+ if base.Arch().ArchType != Common {
+ if base.Target().NativeBridge {
+ hostArchStr := base.Target().NativeBridgeHostArchName
+ if hostArchStr != "" {
+ helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
+ }
+ } else {
+ helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
+ }
+ }
+
+ if !base.InVendorRamdisk() {
+ helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
+ }
+ if len(info.VintfFragmentsPaths) > 0 {
+ helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
+ }
+ helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
+ if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
+ helperInfo.SetString("LOCAL_VENDOR_MODULE", "true")
+ }
+ helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
+ helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
+ helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
+ if base.commonProperties.Owner != nil {
+ helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
+ }
+ }
+
+ if host {
+ makeOs := base.Os().String()
+ if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
+ makeOs = "linux"
+ }
+ helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
+ helperInfo.SetString("LOCAL_IS_HOST_MODULE", "true")
+ }
+
+ prefix := ""
+ if base.ArchSpecific() {
+ switch base.Os().Class {
+ case Host:
+ if base.Target().HostCross {
+ prefix = "HOST_CROSS_"
+ } else {
+ prefix = "HOST_"
+ }
+ case Device:
+ prefix = "TARGET_"
+
+ }
+
+ if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
+ prefix = "2ND_" + prefix
+ }
+ }
+
+ if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
+ helperInfo.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
+ }
+
+ if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+ helperInfo.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
+ }
+
+ a.mergeEntries(&helperInfo)
+
+ // Write to footer.
+ a.FooterStrings = append([]string{"include " + a.Include}, a.FooterStrings...)
+}
+
+// This method merges the entries to helperInfo, then replaces a's EntryMap and
+// EntryOrder with helperInfo's
+func (a *AndroidMkInfo) mergeEntries(helperInfo *AndroidMkInfo) {
+ for _, extraEntry := range a.EntryOrder {
+ if v, ok := helperInfo.EntryMap[extraEntry]; ok {
+ v = append(v, a.EntryMap[extraEntry]...)
+ } else {
+ helperInfo.EntryMap[extraEntry] = a.EntryMap[extraEntry]
+ helperInfo.EntryOrder = append(helperInfo.EntryOrder, extraEntry)
+ }
+ }
+ a.EntryOrder = helperInfo.EntryOrder
+ a.EntryMap = helperInfo.EntryMap
+}
+
+func (a *AndroidMkInfo) disabled() bool {
+ return a.Disabled || !a.OutputFile.Valid()
+}
+
+// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
+func (a *AndroidMkInfo) write(w io.Writer) {
+ if a.disabled() {
+ return
+ }
+
+ combinedHeaderString := strings.Join(a.HeaderStrings, "\n")
+ combinedFooterString := strings.Join(a.FooterStrings, "\n")
+ w.Write([]byte(combinedHeaderString))
+ for _, name := range a.EntryOrder {
+ AndroidMkEmitAssignList(w, name, a.EntryMap[name])
+ }
+ w.Write([]byte(combinedFooterString))
+}
+
+// Compute the list of Make strings to declare phony goals and dist-for-goals
+// calls from the module's dist and dists properties.
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string {
+ distContributions := a.getDistContributions(ctx, mod)
+ if distContributions == nil {
+ return nil
+ }
+
+ return generateDistContributionsForMake(distContributions)
+}
+
+// Compute the contributions that the module makes to the dist.
+func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions {
+ amod := mod.(Module).base()
+ name := amod.BaseModuleName()
+
+ // Collate the set of associated tag/paths available for copying to the dist.
+ // Start with an empty (nil) set.
+ var availableTaggedDists TaggedDistFiles
+
+ // Then merge in any that are provided explicitly by the module.
+ if a.DistFiles != nil {
+ // Merge the DistFiles into the set.
+ availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
+ }
+
+ // If no paths have been provided for the DefaultDistTag and the output file is
+ // valid then add that as the default dist path.
+ if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
+ availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
+ }
+
+ info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+ // If the distFiles created by GenerateTaggedDistFiles contains paths for the
+ // DefaultDistTag then that takes priority so delete any existing paths.
+ if _, ok := info.DistFiles[DefaultDistTag]; ok {
+ delete(availableTaggedDists, DefaultDistTag)
+ }
+
+ // Finally, merge the distFiles created by GenerateTaggedDistFiles.
+ availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
+
+ if len(availableTaggedDists) == 0 {
+ // Nothing dist-able for this module.
+ return nil
+ }
+
+ // Collate the contributions this module makes to the dist.
+ distContributions := &distContributions{}
+
+ if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+ distContributions.licenseMetadataFile = info.LicenseMetadataFile
+ }
+
+ // Iterate over this module's dist structs, merged from the dist and dists properties.
+ for _, dist := range amod.Dists() {
+ // Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
+ goals := strings.Join(dist.Targets, " ")
+
+ // Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
+ var tag string
+ if dist.Tag == nil {
+ // If the dist struct does not specify a tag, use the default output files tag.
+ tag = DefaultDistTag
+ } else {
+ tag = *dist.Tag
+ }
+
+ // Get the paths of the output files to be dist'd, represented by the tag.
+ // Can be an empty list.
+ tagPaths := availableTaggedDists[tag]
+ if len(tagPaths) == 0 {
+ // Nothing to dist for this tag, continue to the next dist.
+ continue
+ }
+
+ if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
+ errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
+ "file for %q goals tag %q in module %s. The list of dist files, " +
+ "which should have a single element, is:\n%s"
+ panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
+ }
+
+ copiesForGoals := distContributions.getCopiesForGoals(goals)
+
+ // Iterate over each path adding a copy instruction to copiesForGoals
+ for _, path := range tagPaths {
+ // It's possible that the Path is nil from errant modules. Be defensive here.
+ if path == nil {
+ tagName := "default" // for error message readability
+ if dist.Tag != nil {
+ tagName = *dist.Tag
+ }
+ panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
+ }
+
+ dest := filepath.Base(path.String())
+
+ if dist.Dest != nil {
+ var err error
+ if dest, err = validateSafePath(*dist.Dest); err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ ext := filepath.Ext(dest)
+ suffix := ""
+ if dist.Suffix != nil {
+ suffix = *dist.Suffix
+ }
+
+ productString := ""
+ if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
+ productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+ }
+
+ if suffix != "" || productString != "" {
+ dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
+ }
+
+ if dist.Dir != nil {
+ var err error
+ if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ copiesForGoals.addCopyInstruction(path, dest)
+ }
+ }
+
+ return distContributions
+}
+
+func deepCopyAndroidMkProviderInfo(providerInfo *AndroidMkProviderInfo) AndroidMkProviderInfo {
+ info := AndroidMkProviderInfo{
+ PrimaryInfo: deepCopyAndroidMkInfo(&providerInfo.PrimaryInfo),
+ }
+ if len(providerInfo.ExtraInfo) > 0 {
+ for _, i := range providerInfo.ExtraInfo {
+ info.ExtraInfo = append(info.ExtraInfo, deepCopyAndroidMkInfo(&i))
+ }
+ }
+ return info
+}
+
+func deepCopyAndroidMkInfo(mkinfo *AndroidMkInfo) AndroidMkInfo {
+ info := AndroidMkInfo{
+ Class: mkinfo.Class,
+ SubName: mkinfo.SubName,
+ OverrideName: mkinfo.OverrideName,
+ // There is no modification on DistFiles or OutputFile, so no need to
+ // make their deep copy.
+ DistFiles: mkinfo.DistFiles,
+ OutputFile: mkinfo.OutputFile,
+ Disabled: mkinfo.Disabled,
+ Include: mkinfo.Include,
+ Required: deepCopyStringSlice(mkinfo.Required),
+ Host_required: deepCopyStringSlice(mkinfo.Host_required),
+ Target_required: deepCopyStringSlice(mkinfo.Target_required),
+ HeaderStrings: deepCopyStringSlice(mkinfo.HeaderStrings),
+ FooterStrings: deepCopyStringSlice(mkinfo.FooterStrings),
+ EntryOrder: deepCopyStringSlice(mkinfo.EntryOrder),
+ }
+ info.EntryMap = make(map[string][]string)
+ for k, v := range mkinfo.EntryMap {
+ info.EntryMap[k] = deepCopyStringSlice(v)
+ }
+
+ return info
+}
+
+func deepCopyStringSlice(original []string) []string {
+ result := make([]string, len(original))
+ copy(result, original)
+ return result
+}