Handle restricting to device only builds

By default, most module types are only enabled for device. Converting
this behavior and host_supported/device_supported properties allows us
to skip building incompatible targets.

Test: soong tests
Test: bp2build.sh
Change-Id: If1da523b4cc8c4cbf2bb26da063d9923b662cc32
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 69703d1..383207f 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -196,6 +196,7 @@
 		"system/libziparchive":                               Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":                              Bp2BuildDefaultTrueRecursively,
 		"system/media/audio":                                 Bp2BuildDefaultTrueRecursively,
+		"system/memory/libion":                               Bp2BuildDefaultTrueRecursively,
 		"system/memory/libmemunreachable":                    Bp2BuildDefaultTrueRecursively,
 		"system/sepolicy/apex":                               Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":                               Bp2BuildDefaultTrueRecursively,
@@ -255,21 +256,23 @@
 
 	Bp2buildModuleAlwaysConvertList = []string{
 		// cc mainline modules
-		"libnativeloader-headers",
-		"libgui_bufferqueue_sources",
 		"code_coverage.policy",
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
 		"com.android.media.swcodec-ld.config.txt",
-		"libcodec2_headers",
-		"libcodec2_internal",
 		"com.android.media.swcodec-mediaswcodec.rc",
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
-		"libbluetooth-types-header",
 		"libaudioclient_aidl_conversion_util",
 		"libaudioutils_fixedfft",
+		"libbluetooth-types-header",
+		"libcodec2_headers",
+		"libcodec2_internal",
+		"libdmabufheap",
+		"libgui_bufferqueue_sources",
+		"libnativeloader-headers",
+		"libsync",
 
 		//external/avb
 		"avbtool",
diff --git a/android/module.go b/android/module.go
index 21d5a3d..8bbfd8a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1181,33 +1181,89 @@
 	archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
 
 	var enabledProperty bazel.BoolAttribute
-	if props.Enabled != nil {
-		enabledProperty.Value = props.Enabled
+
+	onlyAndroid := false
+	neitherHostNorDevice := false
+
+	osSupport := map[string]bool{}
+
+	// if the target is enabled and supports arch variance, determine the defaults based on the module
+	// type's host or device property and host_supported/device_supported properties
+	if mod.commonProperties.ArchSpecific {
+		moduleSupportsDevice := mod.DeviceSupported()
+		moduleSupportsHost := mod.HostSupported()
+		if moduleSupportsHost && !moduleSupportsDevice {
+			// for host only, we specify as unsupported on android rather than listing all host osSupport
+			// TODO(b/220874839): consider replacing this with a constraint that covers all host osSupport
+			// instead
+			enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
+		} else if moduleSupportsDevice && !moduleSupportsHost {
+			enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(true))
+			// specify as a positive to ensure any target-specific enabled can be resolved
+			// also save that a target is only android, as if there is only the positive restriction on
+			// android, it'll be dropped, so we may need to add it back later
+			onlyAndroid = true
+		} else if !moduleSupportsHost && !moduleSupportsDevice {
+			neitherHostNorDevice = true
+		}
+
+		for _, os := range OsTypeList() {
+			if os.Class == Host {
+				osSupport[os.Name] = moduleSupportsHost
+			} else if os.Class == Device {
+				osSupport[os.Name] = moduleSupportsDevice
+			}
+		}
+	}
+
+	if neitherHostNorDevice {
+		// we can't build this, disable
+		enabledProperty.Value = proptools.BoolPtr(false)
+	} else if props.Enabled != nil {
+		enabledProperty.SetValue(props.Enabled)
+		if !*props.Enabled {
+			for os, enabled := range osSupport {
+				if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, os); enabled && val != nil && *val {
+					// if this should be disabled by default, clear out any enabling we've done
+					enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, os, nil)
+				}
+			}
+		}
 	}
 
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*commonProperties); ok {
 				required.SetSelectValue(axis, config, depsToLabelList(archProps.Required).Value)
-				if archProps.Enabled != nil {
-					enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+				if !neitherHostNorDevice {
+					if archProps.Enabled != nil {
+						if axis != bazel.OsConfigurationAxis || osSupport[config] {
+							enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+						}
+					}
 				}
 			}
 		}
 	}
 
-	if enabledPropertyOverrides.Value != nil {
-		enabledProperty.Value = enabledPropertyOverrides.Value
-	}
-	for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
-		configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
-		for cfg, val := range configToBools {
-			enabledProperty.SetSelectValue(axis, cfg, &val)
+	if !neitherHostNorDevice {
+		if enabledPropertyOverrides.Value != nil {
+			enabledProperty.Value = enabledPropertyOverrides.Value
+		}
+		for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
+			configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
+			for cfg, val := range configToBools {
+				if axis != bazel.OsConfigurationAxis || osSupport[cfg] {
+					enabledProperty.SetSelectValue(axis, cfg, &val)
+				}
+			}
 		}
 	}
 
 	productConfigEnabledLabels := []bazel.Label{}
-	if !proptools.BoolDefault(enabledProperty.Value, true) {
+	// TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we
+	// should handle it correctly
+	if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice {
 		// If the module is not enabled by default, then we can check if a
 		// product variable enables it
 		productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
@@ -1224,11 +1280,6 @@
 		productConfigEnabledLabels, nil,
 	})
 
-	moduleSupportsDevice := mod.commonProperties.HostOrDeviceSupported&deviceSupported == deviceSupported
-	if mod.commonProperties.HostOrDeviceSupported != NeitherHostNorDeviceSupported && !moduleSupportsDevice {
-		enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
-	}
-
 	platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
 		bazel.LabelList{[]bazel.Label{bazel.Label{Label: "@platforms//:incompatible"}}, nil},
 		bazel.LabelList{[]bazel.Label{}, nil})
@@ -1236,6 +1287,13 @@
 		ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
 	}
 
+	// if android is the only arch/os enabled, then add a restriction to only be compatible with android
+	if platformEnabledAttribute.IsNil() && onlyAndroid {
+		l := bazel.LabelAttribute{}
+		l.SetValue(bazel.Label{Label: bazel.OsConfigurationAxis.SelectKey(Android.Name)})
+		platformEnabledAttribute.Add(&l)
+	}
+
 	data.Append(required)
 
 	constraints := constraintAttributes{}
diff --git a/bazel/properties.go b/bazel/properties.go
index f956031..7a6caf8 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -409,6 +409,11 @@
 	return false
 }
 
+// SetValue sets value for the no config axis
+func (ba *BoolAttribute) SetValue(value *bool) {
+	ba.SetSelectValue(NoConfigAxis, "", value)
+}
+
 // SetSelectValue sets value for the given axis/config.
 func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
 	axis.validateConfig(config)
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 035a352..173b4e4 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -42,7 +42,7 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("android_app_certificate", "com.android.apogee.cert", attrNameToString{
+			makeBazelTargetNoRestrictions("android_app_certificate", "com.android.apogee.cert", attrNameToString{
 				"certificate": `"chamber_of_secrets_dir"`,
 			}),
 		}})
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 1d949901..dfa96a2 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -42,7 +42,7 @@
         private_key: "com.android.apogee.pem",
 }
 `,
-		expectedBazelTargets: []string{makeBazelTarget("apex_key", "com.android.apogee.key", attrNameToString{
+		expectedBazelTargets: []string{makeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", attrNameToString{
 			"private_key": `"com.android.apogee.pem"`,
 			"public_key":  `"com.android.apogee.avbpubkey"`,
 		}),
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 0f6e139..19209f6 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -201,7 +201,7 @@
 			config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 			ctx := android.NewTestContext(config)
 
-			ctx.RegisterModuleType("custom", customModuleFactory)
+			ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 			ctx.Register()
 
 			_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -501,6 +501,215 @@
 	}
 }
 
+func TestBp2buildHostAndDevice(t *testing.T) {
+	testCases := []bp2buildTestCase{
+		{
+			description:                "host and device, device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device, both",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+			},
+		},
+		{
+			description:                "host and device, host explicitly disabled",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device, neither",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, neither, cannot override with product_var",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		product_variables: { unbundled_build: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, both, disabled overrided with product_var",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		device_supported: true,
+		enabled: false,
+		product_variables: { unbundled_build: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["//build/bazel/product_variables:unbundled_build"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, neither, cannot override with arch enabled",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		arch: { x86: { enabled: true } },
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+		{
+			description:                "host and device, host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: true,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostSupported,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryDeviceSupported,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device default, default",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{}),
+			},
+		},
+		{
+			description:                "host and device default, device only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.DeviceSupported),
+			},
+		},
+		{
+			description:                "host and device default, host only",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetHostOrDevice("custom", "foo", attrNameToString{}, android.HostSupported),
+			},
+		},
+		{
+			description:                "host and device default, neither",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDeviceDefault,
+			blueprint: `custom {
+		name: "foo",
+		host_supported: false,
+		device_supported: false,
+		bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTargetNoRestrictions("custom", "foo", attrNameToString{
+					"target_compatible_with": `["@platforms//:incompatible"]`,
+				}),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			runBp2BuildTestCaseSimple(t, tc)
+		})
+	}
+}
+
 func TestLoadStatements(t *testing.T) {
 	testCases := []struct {
 		bazelTargets           BazelTargets
@@ -610,6 +819,7 @@
 		{
 			bp: `custom {
     name: "bar",
+    host_supported: true,
     one_to_many_prop: true,
     bazel_module: { bp2build_available: true  },
 }`,
@@ -634,7 +844,7 @@
 	for _, testCase := range testCases {
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType("custom", customModuleFactory)
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -680,7 +890,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 			},
 		},
 		{
@@ -693,7 +903,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 			},
 		},
 		{
@@ -706,7 +916,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a",
         "b",
@@ -725,7 +935,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `["b"]`,
 				}),
 			},
@@ -740,7 +950,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "other/a.txt",
         "other/b.txt",
@@ -772,7 +982,7 @@
 				"other/file":         "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "b.txt",
@@ -801,7 +1011,7 @@
 }`,
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "//other:foo",
         "c",
@@ -884,7 +1094,7 @@
 		{
 			description:                "generates more than 1 target if needed",
 			moduleTypeUnderTest:        "custom",
-			moduleTypeUnderTestFactory: customModuleFactory,
+			moduleTypeUnderTestFactory: customModuleFactoryHostAndDevice,
 			bp: `custom {
     name: "foo",
     one_to_many_prop: true,
@@ -1092,7 +1302,7 @@
 				"other/BUILD.bazel": `// definition for fg_bar`,
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{}),
 				`// definition for fg_bar`,
 			},
 		},
@@ -1118,7 +1328,7 @@
         },
     }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
+				makeBazelTargetNoRestrictions("filegroup", "fg_bar", map[string]string{}),
 				`// BUILD file`,
 			},
 		},
@@ -1203,7 +1413,7 @@
 				"dir/f.txt":      "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "b.txt",
@@ -1234,7 +1444,7 @@
 				"dir/subdir/f.txt":      "",
 			},
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"srcs": `[
         "a.txt",
         "//dir/subdir:e.txt",
@@ -1265,7 +1475,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"data": `[":reqd"]`,
 				}),
 			},
@@ -1337,7 +1547,7 @@
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{
-				makeBazelTarget("filegroup", "fg_foo", map[string]string{
+				makeBazelTargetNoRestrictions("filegroup", "fg_foo", map[string]string{
 					"data": `[":reqd"]`,
 				}),
 			},
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 30be997..4794269 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -34,10 +34,11 @@
 	attrs attrNameToString
 }
 
-func generateBazelTargetsForTest(targets []testBazelTarget) []string {
+func generateBazelTargetsForTest(targets []testBazelTarget, hod android.HostOrDeviceSupported) []string {
 	ret := make([]string, 0, len(targets))
 	for _, t := range targets {
-		ret = append(ret, makeBazelTarget(t.typ, t.name, t.attrs))
+		attrs := t.attrs.clone()
+		ret = append(ret, makeBazelTargetHostOrDevice(t.typ, t.name, attrs, hod))
 	}
 	return ret
 }
@@ -65,42 +66,33 @@
 	runCcHostBinaryTestCase(t, tc)
 }
 
-func runCcBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
 	t.Helper()
 	moduleTypeUnderTest := "cc_binary"
-	testCase := bp2buildTestCase{
-		expectedBazelTargets:       generateBazelTargetsForTest(tc.targets),
-		moduleTypeUnderTest:        moduleTypeUnderTest,
-		moduleTypeUnderTestFactory: cc.BinaryFactory,
-		description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
-		blueprint:                  binaryReplacer.Replace(tc.blueprint),
-	}
-	t.Run(testCase.description, func(t *testing.T) {
+
+	description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+	t.Run(description, func(t *testing.T) {
 		t.Helper()
-		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
+			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets, android.DeviceSupported),
+			moduleTypeUnderTest:        moduleTypeUnderTest,
+			moduleTypeUnderTestFactory: cc.BinaryFactory,
+			description:                description,
+			blueprint:                  binaryReplacer.Replace(testCase.blueprint),
+		})
 	})
 }
 
-func runCcHostBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
+func runCcHostBinaryTestCase(t *testing.T, testCase ccBinaryBp2buildTestCase) {
 	t.Helper()
-	testCase := tc
-	for i, tar := range testCase.targets {
-		switch tar.typ {
-		case "cc_binary", "proto_library", "cc_lite_proto_library", "genlex":
-			tar.attrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-		testCase.targets[i] = tar
-	}
 	moduleTypeUnderTest := "cc_binary_host"
-	t.Run(testCase.description, func(t *testing.T) {
+	description := fmt.Sprintf("%s %s", moduleTypeUnderTest, testCase.description)
+	t.Run(description, func(t *testing.T) {
 		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
-			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets),
+			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets, android.HostSupported),
 			moduleTypeUnderTest:        moduleTypeUnderTest,
 			moduleTypeUnderTestFactory: cc.BinaryHostFactory,
-			description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
+			description:                description,
 			blueprint:                  hostBinaryReplacer.Replace(testCase.blueprint),
 		})
 	})
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index a0bc9e7..b7de452 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2278,6 +2278,7 @@
 		blueprint: soongCcProtoPreamble + `cc_library {
 	name: "foo",
 	srcs: ["foo.cpp"],
+	host_supported: true,
 	target: {
 		darwin: {
 			enabled: false,
@@ -2313,6 +2314,7 @@
 	name: "foo",
 	srcs: ["foo.cpp"],
   enabled: false,
+	host_supported: true,
 	target: {
 		darwin: {
 			enabled: true,
@@ -2377,6 +2379,7 @@
 		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcProtoPreamble + `cc_library {
 	name: "foo",
+	host_supported: true,
 	srcs: ["foo.cpp"],
 	shared: {
 		enabled: false
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 9244b99..4504892 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -56,6 +56,7 @@
 		moduleType string
 		factory    android.ModuleFactory
 		genDir     string
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -66,16 +67,19 @@
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
 			genDir:     "$(RULEDIR)",
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
 			genDir:     "$(RULEDIR)",
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
 			genDir:     "$(RULEDIR)",
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -104,15 +108,8 @@
 			"tools": `[":foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -131,6 +128,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -139,14 +137,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -183,18 +184,9 @@
 			"srcs": `["foo_tool.in"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			compatibilityAttrs := `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-			fooAttrs["target_compatible_with"] = compatibilityAttrs
-			fooToolsAttrs["target_compatible_with"] = compatibilityAttrs
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", fooAttrs),
-			makeBazelTarget("genrule", "foo.tools", fooToolsAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", fooAttrs, tc.hod),
+			makeBazelTargetHostOrDevice("genrule", "foo.tools", fooToolsAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -213,6 +205,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -221,14 +214,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -249,15 +245,8 @@
 			"tools": `["//other:foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -277,6 +266,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -285,14 +275,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -313,15 +306,8 @@
 			"tools": `["//other:foo.tool"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -341,6 +327,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -349,14 +336,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -380,15 +370,8 @@
     ]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -408,6 +391,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -416,14 +400,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -447,15 +434,8 @@
     ]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -475,6 +455,7 @@
 	testCases := []struct {
 		moduleType string
 		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
 	}{
 		{
 			moduleType: "genrule",
@@ -483,14 +464,17 @@
 		{
 			moduleType: "cc_genrule",
 			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule",
 			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
 		},
 		{
 			moduleType: "java_genrule_host",
 			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
 		},
 	}
 
@@ -509,15 +493,8 @@
 			"srcs": `["foo.in"]`,
 		}
 
-		if tc.moduleType == "java_genrule_host" {
-			moduleAttrs["target_compatible_with"] = `select({
-        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
-        "//conditions:default": [],
-    })`
-		}
-
 		expectedBazelTargets := []string{
-			makeBazelTarget("genrule", "foo", moduleAttrs),
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
 		}
 
 		t.Run(tc.moduleType, func(t *testing.T) {
@@ -549,7 +526,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd":  `"do-something $(SRCS) $(OUTS)"`,
 					"outs": `["out"]`,
 					"srcs": `["in1"]`,
@@ -574,7 +551,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd": `"do-something $(SRCS) $(OUTS)"`,
 					"outs": `[
         "out-from-defaults",
@@ -607,7 +584,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd":  `"cp $(SRCS) $(OUTS)"`,
 					"outs": `["out"]`,
 					"srcs": `["in1"]`,
@@ -644,7 +621,7 @@
 }
 `,
 			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "gen", attrNameToString{
+				makeBazelTargetNoRestrictions("genrule", "gen", attrNameToString{
 					"cmd": `"cmd1 $(SRCS) $(OUTS)"`,
 					"outs": `[
         "out-from-3",
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index b1e1fb2..8460cae 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -49,6 +49,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -94,6 +95,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -141,6 +143,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		board: {
 			soc_a: {
@@ -200,6 +203,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		feature1: {
 			conditions_default: {
@@ -268,6 +272,7 @@
 custom_cc_library_static {
 	name: "foo",
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 	soong_config_variables: {
 		board: {
 			soc_a: {
@@ -356,6 +361,7 @@
 	name: "lib",
 	defaults: ["foo_defaults_2"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -429,12 +435,14 @@
 	name: "lib",
 	defaults: ["foo_defaults", "bar_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 
 cc_library_static {
 	name: "lib2",
 	defaults: ["bar_defaults", "foo_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -550,6 +558,7 @@
 	name: "lib",
 	defaults: ["foo_defaults", "qux_defaults"],
 	bazel_module: { bp2build_available: true },
+	host_supported: true,
 }
 `
 
@@ -615,6 +624,7 @@
 library_linking_strategy_cc_defaults {
     name: "library_linking_strategy_merged_defaults",
     defaults: ["library_linking_strategy_lib_a_defaults"],
+    host_supported: true,
     soong_config_variables: {
         library_linking_strategy: {
             prefer_static: {
@@ -714,6 +724,7 @@
 
 cc_binary {
     name: "library_linking_strategy_sample_binary",
+    host_supported: true,
     srcs: ["library_linking_strategy.cc"],
     defaults: ["library_linking_strategy_sample_defaults"],
 }`
@@ -800,6 +811,7 @@
 
 cc_binary {
     name: "alphabet_binary",
+    host_supported: true,
     srcs: ["main.cc"],
     defaults: ["alphabet_sample_cc_defaults"],
 }`
@@ -861,6 +873,7 @@
 cc_binary {
     name: "alphabet_binary",
     srcs: ["main.cc"],
+    host_supported: true,
     defaults: ["alphabet_sample_cc_defaults"],
     enabled: false,
     arch: {
@@ -958,6 +971,7 @@
 
 alphabet_cc_defaults {
     name: "alphabet_sample_cc_defaults",
+    host_supported: true,
     soong_config_variables: {
         special_build: {
             enabled: true,
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 9341495..580bac4 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -213,12 +213,36 @@
 	return module
 }
 
-func customModuleFactory() android.Module {
+func customModuleFactoryHostAndDevice() android.Module {
 	m := customModuleFactoryBase()
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth)
 	return m
 }
 
+func customModuleFactoryDeviceSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryHostSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryHostAndDeviceDefault() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibBoth)
+	return m
+}
+
+func customModuleFactoryNeitherHostNorDeviceSupported() android.Module {
+	m := customModuleFactoryBase()
+	android.InitAndroidArchModule(m, android.NeitherHostNorDeviceSupported, android.MultilibBoth)
+	return m
+}
+
 type testProps struct {
 	Test_prop struct {
 		Test_string_prop string
@@ -355,7 +379,7 @@
 }
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
-	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 	ctx.RegisterForBazelConversion()
 }
 
@@ -369,7 +393,29 @@
 
 type attrNameToString map[string]string
 
-func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+func (a attrNameToString) clone() attrNameToString {
+	newAttrs := make(attrNameToString, len(a))
+	for k, v := range a {
+		newAttrs[k] = v
+	}
+	return newAttrs
+}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that can be host or
+// device specific, or independent of host/device.
+func makeBazelTargetHostOrDevice(typ, name string, attrs attrNameToString, hod android.HostOrDeviceSupported) string {
+	if _, ok := attrs["target_compatible_with"]; !ok {
+		switch hod {
+		case android.HostSupported:
+			attrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		case android.DeviceSupported:
+			attrs["target_compatible_with"] = `["//build/bazel/platforms/os:android"]`
+		}
+	}
+
 	attrStrings := make([]string, 0, len(attrs)+1)
 	attrStrings = append(attrStrings, fmt.Sprintf(`    name = "%s",`, name))
 	for _, k := range android.SortedStringKeys(attrs) {
@@ -379,3 +425,16 @@
 %s
 )`, typ, strings.Join(attrStrings, "\n"))
 }
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that does not add a
+// target_compatible_with.  This is useful for module types like filegroup and genrule that arch not
+// arch variant
+func makeBazelTargetNoRestrictions(typ, name string, attrs attrNameToString) string {
+	return makeBazelTargetHostOrDevice(typ, name, attrs, android.HostAndDeviceDefault)
+}
+
+// makeBazelTargetNoRestrictions returns bazel target build file definition that is device specific
+// as this is the most common default in Soong.
+func makeBazelTarget(typ, name string, attrs attrNameToString) string {
+	return makeBazelTargetHostOrDevice(typ, name, attrs, android.DeviceSupported)
+}