Add multilib.prefer32.deps to packaging base

The property is used to prefer the 32-bit variant of a dep over the
64-bit variant. If 64-bit variant is the only available one, it is
depended on.

This will be used to include 32-bit preferred modules like drmserver and
mediaserver in filesystem modules.

Bug: N/A
Test: go test ./... under soong/android

Change-Id: Ic7185eb2044c9987f8d1e9b6cf7f0dbd235cf04c
diff --git a/android/packaging.go b/android/packaging.go
index a2b8755..ae412e1 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -155,11 +155,12 @@
 }
 
 type packagingMultilibProperties struct {
-	First  depsProperty `android:"arch_variant"`
-	Common depsProperty `android:"arch_variant"`
-	Lib32  depsProperty `android:"arch_variant"`
-	Lib64  depsProperty `android:"arch_variant"`
-	Both   depsProperty `android:"arch_variant"`
+	First    depsProperty `android:"arch_variant"`
+	Common   depsProperty `android:"arch_variant"`
+	Lib32    depsProperty `android:"arch_variant"`
+	Lib64    depsProperty `android:"arch_variant"`
+	Both     depsProperty `android:"arch_variant"`
+	Prefer32 depsProperty `android:"arch_variant"`
 }
 
 type packagingArchProperties struct {
@@ -198,8 +199,20 @@
 		ret = append(ret, get(p.properties.Deps)...)
 	} else if arch.Multilib == "lib32" {
 		ret = append(ret, get(p.properties.Multilib.Lib32.Deps)...)
+		// multilib.prefer32.deps are added for lib32 only when they support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
 	} else if arch.Multilib == "lib64" {
 		ret = append(ret, get(p.properties.Multilib.Lib64.Deps)...)
+		// multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
 	} else if arch == Common {
 		ret = append(ret, get(p.properties.Multilib.Common.Deps)...)
 	}
@@ -246,7 +259,7 @@
 	return FirstUniqueStrings(ret)
 }
 
-func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
+func getSupportedTargets(ctx BaseModuleContext) []Target {
 	var ret []Target
 	// The current and the common OS targets are always supported
 	ret = append(ret, ctx.Target())
@@ -258,6 +271,28 @@
 	return ret
 }
 
+// getLib32Target returns the 32-bit target from the list of targets this module supports. If this
+// module doesn't support 32-bit target, nil is returned.
+func getLib32Target(ctx BaseModuleContext) *Target {
+	for _, t := range getSupportedTargets(ctx) {
+		if t.Arch.ArchType.Multilib == "lib32" {
+			return &t
+		}
+	}
+	return nil
+}
+
+// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists.
+func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool {
+	t := getLib32Target(ctx)
+	if t == nil {
+		// This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit
+		// or not.
+		return false
+	}
+	return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep)
+}
+
 // PackagingItem is a marker interface for dependency tags.
 // Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
 type PackagingItem interface {
@@ -278,7 +313,7 @@
 
 // See PackageModule.AddDeps
 func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
-	for _, t := range p.getSupportedTargets(ctx) {
+	for _, t := range getSupportedTargets(ctx) {
 		for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
 			if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
 				continue
@@ -292,7 +327,7 @@
 	m := make(map[string]PackagingSpec)
 
 	var arches []ArchType
-	for _, target := range p.getSupportedTargets(ctx) {
+	for _, target := range getSupportedTargets(ctx) {
 		arches = append(arches, target.Arch.ArchType)
 	}
 
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 0570ec5..89df8ef 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"fmt"
+	"strings"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -584,3 +586,69 @@
 		runPackagingTest(t, config, bp, tc.expected)
 	}
 }
+
+func TestPrefer32Deps(t *testing.T) {
+	bpTemplate := `
+		component {
+			name: "foo",
+			compile_multilib: "both", // not needed but for clarity
+		}
+
+		component {
+			name: "foo_32only",
+			compile_multilib: "prefer32",
+		}
+
+		component {
+			name: "foo_64only",
+			compile_multilib: "64",
+		}
+
+		package_module {
+			name: "package",
+			compile_multilib: "%COMPILE_MULTILIB%",
+			multilib: {
+				prefer32: {
+					deps: %DEPS%,
+				},
+			},
+		}
+	`
+
+	testcases := []struct {
+		compileMultilib string
+		deps            []string
+		expected        []string
+	}{
+		{
+			compileMultilib: "first",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "64",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "32",
+			deps:            []string{"foo", "foo_32only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only"},
+		},
+		{
+			compileMultilib: "both",
+			deps:            []string{"foo", "foo_32only", "foo_64only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only", "lib64/foo_64only"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			multiTarget:                true,
+			depsCollectFirstTargetOnly: true,
+		}
+		bp := strings.Replace(bpTemplate, "%COMPILE_MULTILIB%", tc.compileMultilib, -1)
+		bp = strings.Replace(bp, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
+		fmt.Printf("bp = %s\n", bp)
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}