Merge "Add support for multiple os types"
diff --git a/android/arch.go b/android/arch.go
index 922548e..e440486 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -596,7 +596,7 @@
 }()
 
 var (
-	osTypeList      []OsType
+	OsTypeList      []OsType
 	commonTargetMap = make(map[string]Target)
 
 	NoOsType    OsType
@@ -672,7 +672,7 @@
 
 		DefaultDisabled: defDisabled,
 	}
-	osTypeList = append(osTypeList, os)
+	OsTypeList = append(OsTypeList, os)
 
 	if _, found := commonTargetMap[name]; found {
 		panic(fmt.Errorf("Found Os type duplicate during OsType registration: %q", name))
@@ -684,7 +684,7 @@
 }
 
 func osByName(name string) OsType {
-	for _, os := range osTypeList {
+	for _, os := range OsTypeList {
 		if os.Name == name {
 			return os
 		}
@@ -750,7 +750,7 @@
 
 	var moduleOSList []OsType
 
-	for _, os := range osTypeList {
+	for _, os := range OsTypeList {
 		supportedClass := false
 		for _, osClass := range osClasses {
 			if os.Class == osClass {
@@ -1071,7 +1071,7 @@
 			"Arm_on_x86",
 			"Arm_on_x86_64",
 		}
-		for _, os := range osTypeList {
+		for _, os := range OsTypeList {
 			targets = append(targets, os.Field)
 
 			for _, archType := range osArchTypeMap[os] {
diff --git a/android/sdk.go b/android/sdk.go
index f28c392..969e21a 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -448,10 +448,29 @@
 
 // Base structure for all implementations of SdkMemberProperties.
 //
-// Contains common properties that apply across many different member types.
+// Contains common properties that apply across many different member types. These
+// are not affected by the optimization to extract common values.
 type SdkMemberPropertiesBase struct {
 	// The setting to use for the compile_multilib property.
 	Compile_multilib string
+
+	// The number of unique os types supported by the member variants.
+	Os_count int
+
+	// The os type for which these properties refer.
+	Os OsType
+}
+
+// The os prefix to use for any file paths in the sdk.
+//
+// Is an empty string if the member only provides variants for a single os type, otherwise
+// is the OsType.Name.
+func (b *SdkMemberPropertiesBase) OsPrefix() string {
+	if b.Os_count == 1 {
+		return ""
+	} else {
+		return b.Os.Name
+	}
 }
 
 func (b *SdkMemberPropertiesBase) Base() *SdkMemberPropertiesBase {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index eddf5b8..fc9b89e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -78,7 +78,7 @@
 
 // path to the native binary. Relative to <sdk_root>/<api_dir>
 func nativeBinaryPathFor(lib nativeBinaryInfoProperties) string {
-	return filepath.Join(lib.archType,
+	return filepath.Join(lib.OsPrefix(), lib.archType,
 		nativeBinaryDir, lib.outputFile.Base())
 }
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index a262697..656df69 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -254,7 +254,7 @@
 
 // path to the native library. Relative to <sdk_root>/<api_dir>
 func nativeLibraryPathFor(lib *nativeLibInfoProperties) string {
-	return filepath.Join(lib.archType,
+	return filepath.Join(lib.OsPrefix(), lib.archType,
 		nativeStubDir, lib.outputFile.Base())
 }
 
diff --git a/cc/testing.go b/cc/testing.go
index a22763a..b8a7eab 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -34,7 +34,7 @@
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
 }
 
-func GatherRequiredDepsForTest(os android.OsType) string {
+func GatherRequiredDepsForTest(oses ...android.OsType) string {
 	ret := `
 		toolchain_library {
 			name: "libatomic",
@@ -341,8 +341,9 @@
 		}
 	`
 
-	if os == android.Fuchsia {
-		ret += `
+	for _, os := range oses {
+		if os == android.Fuchsia {
+			ret += `
 		cc_library {
 			name: "libbioniccompat",
 			stl: "none",
@@ -352,6 +353,22 @@
 			stl: "none",
 		}
 		`
+		}
+		if os == android.Windows {
+			ret += `
+		toolchain_library {
+			name: "libwinpthread",
+			host_supported: true,
+			enabled: false,
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+			src: "",
+		}
+		`
+		}
 	}
 	return ret
 }
diff --git a/java/java.go b/java/java.go
index c651317..d0b5adf 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1879,12 +1879,12 @@
 )
 
 // path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
-func sdkSnapshotFilePathForJar(name string) string {
-	return sdkSnapshotFilePathForMember(name, jarFileSuffix)
+func sdkSnapshotFilePathForJar(osPrefix, name string) string {
+	return sdkSnapshotFilePathForMember(osPrefix, name, jarFileSuffix)
 }
 
-func sdkSnapshotFilePathForMember(name string, suffix string) string {
-	return filepath.Join(javaDir, name+suffix)
+func sdkSnapshotFilePathForMember(osPrefix, name string, suffix string) string {
+	return filepath.Join(javaDir, osPrefix, name+suffix)
 }
 
 type librarySdkMemberType struct {
@@ -1931,7 +1931,7 @@
 func (p *librarySdkMemberProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
 	if p.jarToExport != nil {
 		exportedJar := p.jarToExport
-		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.library.Name())
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), p.library.Name())
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 		for _, dir := range p.library.AidlIncludeDirs() {
@@ -2137,10 +2137,10 @@
 
 func (p *testSdkMemberProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) {
 	if p.jarToExport != nil {
-		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.test.Name())
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), p.test.Name())
 		builder.CopyToSnapshot(p.jarToExport, snapshotRelativeJavaLibPath)
 
-		snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(p.test.Name(), testConfigSuffix)
+		snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(p.OsPrefix(), p.test.Name(), testConfigSuffix)
 		builder.CopyToSnapshot(p.test.testConfig, snapshotRelativeTestConfigPath)
 
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
@@ -2348,7 +2348,7 @@
 //
 
 type ImportProperties struct {
-	Jars []string `android:"path"`
+	Jars []string `android:"path,arch_variant"`
 
 	Sdk_version *string
 
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 5366edf..11bc902 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -25,11 +25,13 @@
 	t.Helper()
 
 	fs := map[string][]byte{
-		"Test.cpp":                  nil,
-		"include/Test.h":            nil,
-		"arm64/include/Arm64Test.h": nil,
-		"libfoo.so":                 nil,
-		"aidl/foo/bar/Test.aidl":    nil,
+		"Test.cpp":                      nil,
+		"include/Test.h":                nil,
+		"include-android/AndroidTest.h": nil,
+		"include-host/HostTest.h":       nil,
+		"arm64/include/Arm64Test.h":     nil,
+		"libfoo.so":                     nil,
+		"aidl/foo/bar/Test.aidl":        nil,
 	}
 	return testSdkWithFs(t, bp, fs)
 }
@@ -401,6 +403,108 @@
 	)
 }
 
+func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			native_binaries: ["mynativebinary"],
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+
+		cc_binary {
+			name: "mynativebinary",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			compile_multilib: "both",
+			system_shared_libs: [],
+			stl: "none",
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "myexports_mynativebinary@current",
+    sdk_member_name: "mynativebinary",
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/bin/mynativebinary"],
+        },
+        windows: {
+            compile_multilib: "64",
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "mynativebinary",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc: {
+            compile_multilib: "both",
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/bin/mynativebinary"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/bin/mynativebinary"],
+        },
+        windows: {
+            compile_multilib: "64",
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/bin/mynativebinary.exe"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["myexports_mynativebinary@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativebinary/linux_glibc_x86_64/mynativebinary -> linux_glibc/x86_64/bin/mynativebinary
+.intermediates/mynativebinary/linux_glibc_x86/mynativebinary -> linux_glibc/x86/bin/mynativebinary
+.intermediates/mynativebinary/windows_x86_64/mynativebinary.exe -> windows/x86_64/bin/mynativebinary.exe
+`),
+	)
+}
+
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
@@ -588,6 +692,99 @@
 	)
 }
 
+func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			device_supported: false,
+			host_supported: true,
+			native_shared_libs: ["mynativelib"],
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			device_supported: false,
+			host_supported: true,
+			srcs: [
+				"Test.cpp",
+			],
+			system_shared_libs: [],
+			stl: "none",
+			target: {
+				windows: {
+					enabled: true,
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/lib/mynativelib.dll"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    target: {
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mynativelib.so"],
+        },
+        windows_x86_64: {
+            srcs: ["windows/x86_64/lib/mynativelib.dll"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    device_supported: false,
+    host_supported: true,
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> linux_glibc/x86_64/lib/mynativelib.so
+.intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> linux_glibc/x86/lib/mynativelib.so
+.intermediates/mynativelib/windows_x86_64_shared/mynativelib.dll -> windows/x86_64/lib/mynativelib.dll
+`),
+	)
+}
+
 func TestSnapshotWithCcStaticLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		module_exports {
@@ -958,3 +1155,83 @@
 `),
 	)
 }
+
+func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			host_supported: true,
+			native_header_libs: ["mynativeheaders"],
+		}
+
+		cc_library_headers {
+			name: "mynativeheaders",
+			host_supported: true,
+			system_shared_libs: [],
+			stl: "none",
+			export_system_include_dirs: ["include"],
+			target: {
+				android: {
+					export_include_dirs: ["include-android"],
+				},
+				host: {
+					export_include_dirs: ["include-host"],
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_headers {
+    name: "mysdk_mynativeheaders@current",
+    sdk_member_name: "mynativeheaders",
+    host_supported: true,
+    export_system_include_dirs: ["include/include"],
+    target: {
+        android: {
+            export_include_dirs: ["include/include-android"],
+        },
+        linux_glibc: {
+            export_include_dirs: ["include/include-host"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+cc_prebuilt_library_headers {
+    name: "mynativeheaders",
+    prefer: false,
+    host_supported: true,
+    export_system_include_dirs: ["include/include"],
+    target: {
+        android: {
+            export_include_dirs: ["include/include-android"],
+        },
+        linux_glibc: {
+            export_include_dirs: ["include/include-host"],
+        },
+    },
+    stl: "none",
+    system_shared_libs: [],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    host_supported: true,
+    native_header_libs: ["mysdk_mynativeheaders@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+include-android/AndroidTest.h -> include/include-android/AndroidTest.h
+include-host/HostTest.h -> include/include-host/HostTest.h
+`),
+	)
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 9046eec..45da1f9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -230,6 +230,72 @@
 	)
 }
 
+func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			host_supported: true,
+			java_header_libs: ["myjavalib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			host_supported: true,
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    host_supported: true,
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar -> java/android/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+`),
+	)
+}
+
 func TestSnapshotWithJavaImplLibrary(t *testing.T) {
 	result := testSdkWithJava(t, `
 		module_exports {
diff --git a/sdk/testing.go b/sdk/testing.go
index 41333cd..464c3ca 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -40,7 +40,7 @@
 			name: "myapex.cert",
 			certificate: "myapex",
 		}
-	` + cc.GatherRequiredDepsForTest(android.Android)
+	` + cc.GatherRequiredDepsForTest(android.Android, android.Windows)
 
 	mockFS := map[string][]byte{
 		"build/make/target/product/security":         nil,
diff --git a/sdk/update.go b/sdk/update.go
index 84b6c18..282a7a4 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -660,6 +661,9 @@
 
 	// The list of arch type specific info for this os type.
 	archTypes []*archTypeSpecificInfo
+
+	// True if the member has common arch variants for this os type.
+	commonArch bool
 }
 
 type archTypeSpecificInfo struct {
@@ -672,75 +676,199 @@
 
 	memberType := member.memberType
 
-	// Group the properties for each variant by arch type.
-	osInfo := &osTypeSpecificInfo{}
-	osInfo.Properties = memberType.CreateVariantPropertiesStruct()
+	// Group the variants by os type.
+	variantsByOsType := make(map[android.OsType][]android.SdkAware)
 	variants := member.Variants()
-	commonArch := false
 	for _, variant := range variants {
-		var properties android.SdkMemberProperties
-
-		// Get the info associated with the arch type inside the os info.
-		archType := variant.Target().Arch.ArchType
-
-		if archType.Name == "common" {
-			// The arch type is common so populate the common properties directly.
-			properties = osInfo.Properties
-
-			commonArch = true
-		} else {
-			archInfo := &archTypeSpecificInfo{archType: archType}
-			properties = memberType.CreateVariantPropertiesStruct()
-			archInfo.Properties = properties
-
-			osInfo.archTypes = append(osInfo.archTypes, archInfo)
-		}
-
-		properties.PopulateFromVariant(variant)
+		osType := variant.Target().Os
+		variantsByOsType[osType] = append(variantsByOsType[osType], variant)
 	}
 
-	if commonArch {
-		if len(variants) != 1 {
-			panic("Expected to only have 1 variant when arch type is common but found " + string(len(variants)))
-		}
-	} else {
-		var archProperties []android.SdkMemberProperties
-		for _, archInfo := range osInfo.archTypes {
-			archProperties = append(archProperties, archInfo.Properties)
-		}
+	osCount := len(variantsByOsType)
+	createVariantPropertiesStruct := func(os android.OsType) android.SdkMemberProperties {
+		properties := memberType.CreateVariantPropertiesStruct()
+		base := properties.Base()
+		base.Os_count = osCount
+		base.Os = os
+		return properties
+	}
 
-		extractCommonProperties(osInfo.Properties, archProperties)
+	osTypeToInfo := make(map[android.OsType]*osTypeSpecificInfo)
 
-		// Choose setting for compile_multilib that is appropriate for the arch variants supplied.
-		var multilib string
-		archVariantCount := len(osInfo.archTypes)
-		if archVariantCount == 2 {
-			multilib = "both"
-		} else if archVariantCount == 1 {
-			if strings.HasSuffix(osInfo.archTypes[0].archType.Name, "64") {
-				multilib = "64"
+	// The set of properties that are common across all architectures and os types.
+	commonProperties := createVariantPropertiesStruct(android.CommonOS)
+
+	// The list of property structures which are os type specific but common across
+	// architectures within that os type.
+	var osSpecificPropertiesList []android.SdkMemberProperties
+
+	for osType, osTypeVariants := range variantsByOsType {
+		// Group the properties for each variant by arch type within the os.
+		osInfo := &osTypeSpecificInfo{}
+		osTypeToInfo[osType] = osInfo
+
+		// Create a structure into which properties common across the architectures in
+		// this os type will be stored. Add it to the list of os type specific yet
+		// architecture independent properties structs.
+		osInfo.Properties = createVariantPropertiesStruct(osType)
+		osSpecificPropertiesList = append(osSpecificPropertiesList, osInfo.Properties)
+
+		commonArch := false
+		for _, variant := range osTypeVariants {
+			var properties android.SdkMemberProperties
+
+			// Get the info associated with the arch type inside the os info.
+			archType := variant.Target().Arch.ArchType
+
+			if archType.Name == "common" {
+				// The arch type is common so populate the common properties directly.
+				properties = osInfo.Properties
+
+				commonArch = true
 			} else {
-				multilib = "32"
+				archInfo := &archTypeSpecificInfo{archType: archType}
+				properties = createVariantPropertiesStruct(osType)
+				archInfo.Properties = properties
+
+				osInfo.archTypes = append(osInfo.archTypes, archInfo)
 			}
+
+			properties.PopulateFromVariant(variant)
 		}
 
-		osInfo.Properties.Base().Compile_multilib = multilib
+		if commonArch {
+			if len(osTypeVariants) != 1 {
+				panic("Expected to only have 1 variant when arch type is common but found " + string(len(variants)))
+			}
+		} else {
+			var archPropertiesList []android.SdkMemberProperties
+			for _, archInfo := range osInfo.archTypes {
+				archPropertiesList = append(archPropertiesList, archInfo.Properties)
+			}
+
+			extractCommonProperties(osInfo.Properties, archPropertiesList)
+
+			// Choose setting for compile_multilib that is appropriate for the arch variants supplied.
+			var multilib string
+			archVariantCount := len(osInfo.archTypes)
+			if archVariantCount == 2 {
+				multilib = "both"
+			} else if archVariantCount == 1 {
+				if strings.HasSuffix(osInfo.archTypes[0].archType.Name, "64") {
+					multilib = "64"
+				} else {
+					multilib = "32"
+				}
+			}
+
+			osInfo.commonArch = commonArch
+			osInfo.Properties.Base().Compile_multilib = multilib
+		}
 	}
 
-	osInfo.Properties.AddToPropertySet(sdkModuleContext, builder, bpModule)
+	// Extract properties which are common across all architectures and os types.
+	extractCommonProperties(commonProperties, osSpecificPropertiesList)
 
-	if !commonArch {
-		archPropertySet := bpModule.AddPropertySet("arch")
-		for _, av := range osInfo.archTypes {
-			archTypePropertySet := archPropertySet.AddPropertySet(av.archType.Name)
+	// Add the common properties to the module.
+	commonProperties.AddToPropertySet(sdkModuleContext, builder, bpModule)
 
-			av.Properties.AddToPropertySet(sdkModuleContext, builder, archTypePropertySet)
+	// Create a target property set into which target specific properties can be
+	// added.
+	targetPropertySet := bpModule.AddPropertySet("target")
+
+	// Iterate over the os types in a fixed order.
+	for _, osType := range s.getPossibleOsTypes() {
+		osInfo := osTypeToInfo[osType]
+		if osInfo == nil {
+			continue
+		}
+
+		var osPropertySet android.BpPropertySet
+		var archOsPrefix string
+		if len(osTypeToInfo) == 1 {
+			// There is only one os type present in the variants sp don't bother
+			// with adding target specific properties.
+
+			// Create a structure that looks like:
+			// module_type {
+			//   name: "...",
+			//   ...
+			//   <common properties>
+			//   ...
+			//   <single os type specific properties>
+			//
+			//   arch: {
+			//     <arch specific sections>
+			//   }
+			//
+			osPropertySet = bpModule
+
+			// Arch specific properties need to be added to an arch specific section
+			// within arch.
+			archOsPrefix = ""
+		} else {
+			// Create a structure that looks like:
+			// module_type {
+			//   name: "...",
+			//   ...
+			//   <common properties>
+			//   ...
+			//   target: {
+			//     <arch independent os specific sections, e.g. android>
+			//     ...
+			//     <arch and os specific sections, e.g. android_x86>
+			//   }
+			//
+			osPropertySet = targetPropertySet.AddPropertySet(osType.Name)
+
+			// Arch specific properties need to be added to an os and arch specific
+			// section prefixed with <os>_.
+			archOsPrefix = osType.Name + "_"
+		}
+
+		osInfo.Properties.AddToPropertySet(sdkModuleContext, builder, osPropertySet)
+		if !osInfo.commonArch {
+			// Either add the arch specific sections into the target or arch sections
+			// depending on whether they will also be os specific.
+			var archPropertySet android.BpPropertySet
+			if archOsPrefix == "" {
+				archPropertySet = osPropertySet.AddPropertySet("arch")
+			} else {
+				archPropertySet = targetPropertySet
+			}
+
+			// Add arch (and possibly os) specific sections for each set of
+			// arch (and possibly os) specific properties.
+			for _, av := range osInfo.archTypes {
+				archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + av.archType.Name)
+
+				av.Properties.AddToPropertySet(sdkModuleContext, builder, archTypePropertySet)
+			}
 		}
 	}
 
 	memberType.FinalizeModule(sdkModuleContext, builder, member, bpModule)
 }
 
+// Compute the list of possible os types that this sdk could support.
+func (s *sdk) getPossibleOsTypes() []android.OsType {
+	var osTypes []android.OsType
+	for _, osType := range android.OsTypeList {
+		if s.DeviceSupported() {
+			if osType.Class == android.Device && osType != android.Fuchsia {
+				osTypes = append(osTypes, osType)
+			}
+		}
+		if s.HostSupported() {
+			if osType.Class == android.Host || osType.Class == android.HostCross {
+				osTypes = append(osTypes, osType)
+			}
+		}
+	}
+	sort.SliceStable(osTypes, func(i, j int) bool { return osTypes[i].Name < osTypes[j].Name })
+	return osTypes
+}
+
 // Extract common properties from a slice of property structures of the same type.
 //
 // All the property structures must be of the same type.
@@ -766,6 +894,11 @@
 		var commonValue *reflect.Value
 		sliceValue := reflect.ValueOf(inputPropertiesSlice)
 
+		field := propertiesStructType.Field(f)
+		if field.Name == "SdkMemberPropertiesBase" {
+			continue
+		}
+
 		for i := 0; i < sliceValue.Len(); i++ {
 			structValue := sliceValue.Index(i).Elem().Elem()
 			fieldValue := structValue.Field(f)