HostCross is an attribute of a Target, not OsType

A host target is considered as being cross-compiled when the target
can't run natively on the build machine. For example, linux_glibc/x86_64
is a non-cross target on a standard x86/Linux machine, but is a cross
host on Mac. Previously, whether cross or not was a static attribute of
an OsType. For example, Windows was always considered as cross host,
while linux_bionic was not. This becomes a problem when we support more
host targets like linux_bionic/arm64 which should be cross-host on
standard x86/Linux machines.

This change removes HostCross from the OsClass type and instead adds a
property HostCross to the Target type. When a target is being added, it
is initialized to true when the target can't run natively on the current
build machine.

Bug: 168086242
Test: m
Change-Id: Ic37c8db918873ddf324c86b12b5412952b0f2be2
diff --git a/android/androidmk.go b/android/androidmk.go
index fafbfd6..e90e5f0 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -304,15 +304,16 @@
 	host := false
 	switch amod.Os().Class {
 	case Host:
-		// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
-		if amod.Arch().ArchType != Common {
-			a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
-		}
-		host = true
-	case HostCross:
-		// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-		if amod.Arch().ArchType != Common {
-			a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+		if amod.Target().HostCross {
+			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+			if amod.Arch().ArchType != Common {
+				a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+			}
+		} else {
+			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+			if amod.Arch().ArchType != Common {
+				a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+			}
 		}
 		host = true
 	case Device:
@@ -359,9 +360,11 @@
 	if amod.ArchSpecific() {
 		switch amod.Os().Class {
 		case Host:
-			prefix = "HOST_"
-		case HostCross:
-			prefix = "HOST_CROSS_"
+			if amod.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
 		case Device:
 			prefix = "TARGET_"
 
@@ -563,9 +566,11 @@
 	if amod.ArchSpecific() {
 		switch amod.Os().Class {
 		case Host:
-			prefix = "HOST_"
-		case HostCross:
-			prefix = "HOST_CROSS_"
+			if amod.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
 		case Device:
 			prefix = "TARGET_"
 
diff --git a/android/arch.go b/android/arch.go
index 4141138..6552570 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -578,7 +578,7 @@
 	Linux       = NewOsType("linux_glibc", Host, false)
 	Darwin      = NewOsType("darwin", Host, false)
 	LinuxBionic = NewOsType("linux_bionic", Host, false)
-	Windows     = NewOsType("windows", HostCross, true)
+	Windows     = NewOsType("windows", Host, true)
 	Android     = NewOsType("android", Device, false)
 	Fuchsia     = NewOsType("fuchsia", Device, false)
 
@@ -609,7 +609,6 @@
 	Generic OsClass = iota
 	Device
 	Host
-	HostCross
 )
 
 func (class OsClass) String() string {
@@ -620,8 +619,6 @@
 		return "device"
 	case Host:
 		return "host"
-	case HostCross:
-		return "host cross"
 	default:
 		panic(fmt.Errorf("unknown class %d", class))
 	}
@@ -681,6 +678,11 @@
 	NativeBridge             NativeBridgeSupport
 	NativeBridgeHostArchName string
 	NativeBridgeRelativePath string
+
+	// HostCross is true when the target cannot run natively on the current build host.
+	// For example, linux_glibc_x86 returns true on a regular x86/i686/Linux machines, but returns false
+	// on Mac (different OS), or on 64-bit only i686/Linux machines (unsupported arch).
+	HostCross bool
 }
 
 func (target Target) String() string {
@@ -739,26 +741,15 @@
 		return
 	}
 
-	osClasses := base.OsClassSupported()
-
 	var moduleOSList []OsType
 
 	for _, os := range OsTypeList {
-		supportedClass := false
-		for _, osClass := range osClasses {
-			if os.Class == osClass {
-				supportedClass = true
+		for _, t := range mctx.Config().Targets[os] {
+			if base.supportsTarget(t, mctx.Config()) {
+				moduleOSList = append(moduleOSList, os)
+				break
 			}
 		}
-		if !supportedClass {
-			continue
-		}
-
-		if len(mctx.Config().Targets[os]) == 0 {
-			continue
-		}
-
-		moduleOSList = append(moduleOSList, os)
 	}
 
 	if len(moduleOSList) == 0 {
@@ -913,7 +904,7 @@
 
 	prefer32 := false
 	if base.prefer32 != nil {
-		prefer32 = base.prefer32(mctx, base, os.Class)
+		prefer32 = base.prefer32(mctx, base, os)
 	}
 
 	multilib, extraMultilib := decodeMultilib(base, os.Class)
@@ -964,7 +955,7 @@
 	switch class {
 	case Device:
 		multilib = String(base.commonProperties.Target.Android.Compile_multilib)
-	case Host, HostCross:
+	case Host:
 		multilib = String(base.commonProperties.Target.Host.Compile_multilib)
 	}
 	if multilib == "" {
@@ -1240,7 +1231,7 @@
 			//         key: value,
 			//     },
 			// },
-			if os.Class == Host || os.Class == HostCross {
+			if os.Class == Host {
 				field := "Host"
 				prefix := "target.host"
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -1280,7 +1271,7 @@
 			prefix := "target." + os.Name
 			m.appendProperties(ctx, genProps, targetProp, field, prefix)
 
-			if (os.Class == Host || os.Class == HostCross) && os != Windows {
+			if os.Class == Host && os != Windows {
 				field := "Not_windows"
 				prefix := "target.not_windows"
 				m.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -1508,6 +1499,36 @@
 			nativeBridgeRelativePathStr = arch.ArchType.String()
 		}
 
+		// A target is considered as HostCross if it's a host target which can't run natively on
+		// the currently configured build machine (either because the OS is different or because of
+		// the unsupported arch)
+		hostCross := false
+		if os.Class == Host {
+			var osSupported bool
+			if os == BuildOs {
+				osSupported = true
+			} else if BuildOs.Linux() && os.Linux() {
+				// LinuxBionic and Linux are compatible
+				osSupported = true
+			} else {
+				osSupported = false
+			}
+
+			var archSupported bool
+			if arch.ArchType == Common {
+				archSupported = true
+			} else if arch.ArchType.Name == *variables.HostArch {
+				archSupported = true
+			} else if variables.HostSecondaryArch != nil && arch.ArchType.Name == *variables.HostSecondaryArch {
+				archSupported = true
+			} else {
+				archSupported = false
+			}
+			if !osSupported || !archSupported {
+				hostCross = true
+			}
+		}
+
 		targets[os] = append(targets[os],
 			Target{
 				Os:                       os,
@@ -1515,6 +1536,7 @@
 				NativeBridge:             nativeBridgeEnabled,
 				NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
 				NativeBridgeRelativePath: nativeBridgeRelativePathStr,
+				HostCross:                hostCross,
 			})
 	}
 
diff --git a/android/config.go b/android/config.go
index 1c06e8c..f8c8821 100644
--- a/android/config.go
+++ b/android/config.go
@@ -260,10 +260,10 @@
 	config := testConfig.config
 
 	config.Targets[Android] = []Target{
-		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
-		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
-		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
+		{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+		{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
+		{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false},
+		{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false},
 	}
 
 	return testConfig
@@ -275,10 +275,10 @@
 
 	config.Targets = map[OsType][]Target{
 		Fuchsia: []Target{
-			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
 		},
 	}
 
@@ -292,12 +292,12 @@
 
 	config.Targets = map[OsType][]Target{
 		Android: []Target{
-			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
-			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
+			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
 		},
 		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
-			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", ""},
+			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
 		},
 	}
 
diff --git a/android/module.go b/android/module.go
index 5bc7a17..e1b98f6 100644
--- a/android/module.go
+++ b/android/module.go
@@ -923,7 +923,7 @@
 	initRcPaths         Paths
 	vintfFragmentsPaths Paths
 
-	prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
+	prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool
 }
 
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
@@ -950,7 +950,7 @@
 	return m.variables
 }
 
-func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool) {
+func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool) {
 	m.prefer32 = prefer32
 }
 
@@ -1046,7 +1046,7 @@
 }
 
 func (m *ModuleBase) Host() bool {
-	return m.Os().Class == Host || m.Os().Class == HostCross
+	return m.Os().Class == Host
 }
 
 func (m *ModuleBase) Device() bool {
@@ -1066,28 +1066,28 @@
 	return m.commonProperties.CommonOSVariant
 }
 
-func (m *ModuleBase) OsClassSupported() []OsClass {
+func (m *ModuleBase) supportsTarget(target Target, config Config) bool {
 	switch m.commonProperties.HostOrDeviceSupported {
 	case HostSupported:
-		return []OsClass{Host, HostCross}
+		return target.Os.Class == Host
 	case HostSupportedNoCross:
-		return []OsClass{Host}
+		return target.Os.Class == Host && !target.HostCross
 	case DeviceSupported:
-		return []OsClass{Device}
+		return target.Os.Class == Device
 	case HostAndDeviceSupported, HostAndDeviceDefault:
-		var supported []OsClass
+		supported := false
 		if Bool(m.hostAndDeviceProperties.Host_supported) ||
 			(m.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
 				m.hostAndDeviceProperties.Host_supported == nil) {
-			supported = append(supported, Host, HostCross)
+			supported = supported || target.Os.Class == Host
 		}
 		if m.hostAndDeviceProperties.Device_supported == nil ||
 			*m.hostAndDeviceProperties.Device_supported {
-			supported = append(supported, Device)
+			supported = supported || target.Os.Class == Device
 		}
 		return supported
 	default:
-		return nil
+		return false
 	}
 }
 
@@ -2075,7 +2075,7 @@
 }
 
 func (b *baseModuleContext) Host() bool {
-	return b.os.Class == Host || b.os.Class == HostCross
+	return b.os.Class == Host
 }
 
 func (b *baseModuleContext) Device() bool {
@@ -2535,30 +2535,36 @@
 	}
 
 	// Create (host|host-cross|target)-<OS> phony rules to build a reduced checkbuild.
-	osDeps := map[OsType]Paths{}
+	type osAndCross struct {
+		os        OsType
+		hostCross bool
+	}
+	osDeps := map[osAndCross]Paths{}
 	ctx.VisitAllModules(func(module Module) {
 		if module.Enabled() {
-			os := module.Target().Os
-			osDeps[os] = append(osDeps[os], module.base().checkbuildFiles...)
+			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
+			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
 		}
 	})
 
 	osClass := make(map[string]Paths)
-	for os, deps := range osDeps {
+	for key, deps := range osDeps {
 		var className string
 
-		switch os.Class {
+		switch key.os.Class {
 		case Host:
-			className = "host"
-		case HostCross:
-			className = "host-cross"
+			if key.hostCross {
+				className = "host-cross"
+			} else {
+				className = "host"
+			}
 		case Device:
 			className = "target"
 		default:
 			continue
 		}
 
-		name := className + "-" + os.Name
+		name := className + "-" + key.os.Name
 		osClass[className] = append(osClass[className], PathForPhony(ctx, name))
 
 		ctx.Phony(name, deps...)
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 6c3cd9e..854395e 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -285,7 +285,7 @@
 				t.Errorf("windows is assumed to be disabled by default")
 			}
 			config.config.Targets[Windows] = []Target{
-				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
 			}
 
 			ctx := NewTestArchContext()
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 1b53a67..c4fe3a3 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -157,13 +157,14 @@
 			host := false
 			switch fi.module.Target().Os.Class {
 			case android.Host:
-				if fi.module.Target().Arch.ArchType != android.Common {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
-				}
-				host = true
-			case android.HostCross:
-				if fi.module.Target().Arch.ArchType != android.Common {
-					fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+				if fi.module.Target().HostCross {
+					if fi.module.Target().Arch.ArchType != android.Common {
+						fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+					}
+				} else {
+					if fi.module.Target().Arch.ArchType != android.Common {
+						fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+					}
 				}
 				host = true
 			case android.Device:
diff --git a/cc/cc.go b/cc/cc.go
index 6584380..d0d6427 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -950,9 +950,9 @@
 		c.AddProperties(feature.props()...)
 	}
 
-	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
+	c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, os android.OsType) bool {
 		// Windows builds always prefer 32-bit
-		return class == android.HostCross
+		return os == android.Windows
 	})
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
 	android.InitApexModule(c)
diff --git a/java/app.go b/java/app.go
index ae7373f..9ffb6b4 100755
--- a/java/app.go
+++ b/java/app.go
@@ -678,7 +678,7 @@
 		seenModules[child] = true
 
 		// Skip host modules.
-		if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
+		if child.Target().Os.Class == android.Host {
 			return false
 		}
 
diff --git a/python/binary.go b/python/binary.go
index 5a74926..1d2400e 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -79,7 +79,7 @@
 }
 
 func PythonBinaryHostFactory() android.Module {
-	module, _ := NewBinary(android.HostSupportedNoCross)
+	module, _ := NewBinary(android.HostSupported)
 
 	return module.Init()
 }
diff --git a/python/library.go b/python/library.go
index 65c1352..0c8d613 100644
--- a/python/library.go
+++ b/python/library.go
@@ -26,7 +26,7 @@
 }
 
 func PythonLibraryHostFactory() android.Module {
-	module := newModule(android.HostSupportedNoCross, android.MultilibFirst)
+	module := newModule(android.HostSupported, android.MultilibFirst)
 
 	return module.Init()
 }
diff --git a/sdk/testing.go b/sdk/testing.go
index b53558d..e57f1f7 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -68,14 +68,14 @@
 	// Add windows as a default disable OS to test behavior when some OS variants
 	// are disabled.
 	config.Targets[android.Windows] = []android.Target{
-		{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+		{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
 	}
 
 	for _, extraOsType := range extraOsTypes {
 		switch extraOsType {
 		case android.LinuxBionic:
 			config.Targets[android.LinuxBionic] = []android.Target{
-				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+				{android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", false},
 			}
 		}
 	}
@@ -89,7 +89,7 @@
 	android.RegisterAndroidMkBuildComponents(ctx)
 	android.SetInMakeForTests(config)
 	config.Targets[android.CommonOS] = []android.Target{
-		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", ""},
+		{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
 	}
 
 	// from android package
diff --git a/sdk/update.go b/sdk/update.go
index 537ab13..3c4309c 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -371,8 +371,7 @@
 			osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
 
 			// Enable the variant explicitly when we've disabled it by default on host.
-			if hasHostOsDependentMember &&
-				(osType.Class == android.Host || osType.Class == android.HostCross) {
+			if hasHostOsDependentMember && osType.Class == android.Host {
 				osPropertySet.AddProperty("enabled", true)
 			}
 
@@ -731,7 +730,7 @@
 
 	for _, variant := range member.Variants() {
 		osClass := variant.Target().Os.Class
-		if osClass == android.Host || osClass == android.HostCross {
+		if osClass == android.Host {
 			hostSupported = true
 		} else if osClass == android.Device {
 			deviceSupported = true
@@ -1061,8 +1060,7 @@
 		archPropertySet = targetPropertySet
 
 		// Enable the variant explicitly when we've disabled it by default on host.
-		if ctx.memberType.IsHostOsDependent() &&
-			(osType.Class == android.Host || osType.Class == android.HostCross) {
+		if ctx.memberType.IsHostOsDependent() && osType.Class == android.Host {
 			osPropertySet.AddProperty("enabled", true)
 		}
 
@@ -1086,7 +1084,7 @@
 
 func (osInfo *osTypeSpecificInfo) isHostVariant() bool {
 	osClass := osInfo.osType.Class
-	return osClass == android.Host || osClass == android.HostCross
+	return osClass == android.Host
 }
 
 var _ isHostVariant = (*osTypeSpecificInfo)(nil)
@@ -1323,7 +1321,7 @@
 			}
 		}
 		if s.HostSupported() {
-			if osType.Class == android.Host || osType.Class == android.HostCross {
+			if osType.Class == android.Host {
 				osTypes = append(osTypes, osType)
 			}
 		}