Merge "Revert "Preopt APEX system server jars.""
diff --git a/android/arch.go b/android/arch.go
index f7eb963..ddc082b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -631,8 +631,7 @@
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported.
 	// Skip creating native bridge variants for non-core modules.
-	if os == Android &&
-		!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
+	if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) {
 
 		var targets []Target
 		for _, t := range osTargets {
diff --git a/android/bazel.go b/android/bazel.go
index 22846e8..facd116 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -61,8 +61,8 @@
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
 	ConvertWithBp2build(ctx BazelConversionPathContext) bool
+	convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
-	ConvertedToBazel(ctx BazelConversionPathContext) bool
 }
 
 // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -312,9 +312,10 @@
 	if !ctx.Config().BazelContext.BazelEnabled() {
 		return false
 	}
-	if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 {
+	if !convertedToBazel(ctx, ctx.Module()) {
 		return false
 	}
+
 	if GenerateCcLibraryStaticOnly(ctx) {
 		// Don't use partially-converted cc_library targets in mixed builds,
 		// since mixed builds would generally rely on both static and shared
@@ -324,20 +325,33 @@
 	return !mixedBuildsDisabled[ctx.Module().Name()]
 }
 
+// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
+func convertedToBazel(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	b, ok := module.(Bazelable)
+	if !ok {
+		return false
+	}
+	return b.convertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
+}
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
 func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
-	if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
+	return b.convertWithBp2build(ctx, ctx.Module())
+}
+
+func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	if bp2buildModuleDoNotConvert[module.Name()] {
 		return false
 	}
 
 	// Ensure that the module type of this module has a bp2build converter. This
 	// prevents mixed builds from using auto-converted modules just by matching
 	// the package dir; it also has to have a bp2build mutator as well.
-	if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+	if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
 		return false
 	}
 
-	packagePath := ctx.ModuleDir()
+	packagePath := ctx.OtherModuleDir(module)
 	config := ctx.Config().bp2buildPackageConfig
 
 	// This is a tristate value: true, false, or unset.
@@ -408,9 +422,3 @@
 	}
 	return string(data[:]), nil
 }
-
-// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
-// or manually
-func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
-	return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
-}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index a4bd2ef..ccbc156 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -75,9 +75,10 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	ModuleFromName(name string) (blueprint.Module, bool)
 	Module() Module
-	ModuleType() string
+	OtherModuleType(m blueprint.Module) string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
+	AddUnconvertedBp2buildDep(string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
@@ -345,6 +346,9 @@
 	if m == nil {
 		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
 	}
+	if !convertedToBazel(ctx, m) {
+		ctx.AddUnconvertedBp2buildDep(dep)
+	}
 	otherLabel := bazelModuleLabel(ctx, m, tag)
 	label := bazelModuleLabel(ctx, ctx.Module(), "")
 	if isWholeLibs {
@@ -363,11 +367,10 @@
 
 func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
-	b, ok := module.(Bazelable)
-	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
-	if !ok || !b.ConvertedToBazel(ctx) {
+	if !convertedToBazel(ctx, module) {
 		return bp2buildModuleLabel(ctx, module)
 	}
+	b, _ := module.(Bazelable)
 	return b.GetBazelLabel(ctx, module)
 }
 
diff --git a/android/licenses.go b/android/licenses.go
index 7ee78c7..bcd85f9 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -48,7 +48,7 @@
 
 	// License modules, i.e. modules depended upon via a licensesTag, must be automatically added to
 	// any sdk/module_exports to which their referencing module is a member.
-	_ SdkMemberTypeDependencyTag = licensesTag
+	_ SdkMemberDependencyTag = licensesTag
 )
 
 // Describes the property provided by a module to reference applicable licenses.
diff --git a/android/module.go b/android/module.go
index dd6a25a..9532cbc 100644
--- a/android/module.go
+++ b/android/module.go
@@ -316,6 +316,9 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
+	AddUnconvertedBp2buildDep(dep string)
+
 	Target() Target
 	TargetPrimary() bool
 
@@ -496,6 +499,7 @@
 	IsConvertedByBp2build() bool
 	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
 	Bp2buildTargets() []bp2buildInfo
+	GetUnconvertedBp2buildDeps() []string
 
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -833,6 +837,10 @@
 	// supported as Soong handles some things within a single target that we may choose to split into
 	// multiple targets, e.g. renderscript, protos, yacc within a cc module.
 	Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
+
+	// UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
+	// Bazel
+	UnconvertedBp2buildDeps []string `blueprint:"mutated"`
 }
 
 type distProperties struct {
@@ -1212,6 +1220,18 @@
 	return m.commonProperties.Bp2buildInfo
 }
 
+// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel.
+func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) {
+	unconvertedDeps := &b.Module().base().commonProperties.UnconvertedBp2buildDeps
+	*unconvertedDeps = append(*unconvertedDeps, dep)
+}
+
+// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that
+// were not converted to Bazel.
+func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string {
+	return m.commonProperties.UnconvertedBp2buildDeps
+}
+
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
 	(*d)["Android"] = map[string]interface{}{}
 }
diff --git a/android/mutator.go b/android/mutator.go
index 20ec621..c6b6676 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -532,7 +532,6 @@
 		BazelProps: bazelProps,
 		Attrs:      attrs,
 	}
-
 	t.Module().base().addBp2buildInfo(info)
 }
 
diff --git a/android/sdk.go b/android/sdk.go
index b8f76c1..7c26fbf 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -374,9 +374,9 @@
 	Variants() []SdkAware
 }
 
-// SdkMemberTypeDependencyTag is the interface that a tag must implement in order to allow the
+// SdkMemberDependencyTag is the interface that a tag must implement in order to allow the
 // dependent module to be automatically added to the sdk.
-type SdkMemberTypeDependencyTag interface {
+type SdkMemberDependencyTag interface {
 	blueprint.DependencyTag
 
 	// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
@@ -401,34 +401,34 @@
 	ExportMember() bool
 }
 
-var _ SdkMemberTypeDependencyTag = (*sdkMemberTypeDependencyTag)(nil)
-var _ ReplaceSourceWithPrebuilt = (*sdkMemberTypeDependencyTag)(nil)
+var _ SdkMemberDependencyTag = (*sdkMemberDependencyTag)(nil)
+var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
 
-type sdkMemberTypeDependencyTag struct {
+type sdkMemberDependencyTag struct {
 	blueprint.BaseDependencyTag
 	memberType SdkMemberType
 	export     bool
 }
 
-func (t *sdkMemberTypeDependencyTag) SdkMemberType(_ Module) SdkMemberType {
+func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
 	return t.memberType
 }
 
-func (t *sdkMemberTypeDependencyTag) ExportMember() bool {
+func (t *sdkMemberDependencyTag) ExportMember() bool {
 	return t.export
 }
 
 // Prevent dependencies from the sdk/module_exports onto their members from being
 // replaced with a preferred prebuilt.
-func (t *sdkMemberTypeDependencyTag) ReplaceSourceWithPrebuilt() bool {
+func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return false
 }
 
-// DependencyTagForSdkMemberType creates an SdkMemberTypeDependencyTag that will cause any
+// DependencyTagForSdkMemberType creates an SdkMemberDependencyTag that will cause any
 // dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
 // (or not) as specified by the export parameter.
-func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberTypeDependencyTag {
-	return &sdkMemberTypeDependencyTag{memberType: memberType, export: export}
+func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberDependencyTag {
+	return &sdkMemberDependencyTag{memberType: memberType, export: export}
 }
 
 // Interface that must be implemented for every type that can be a member of an
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 06a7306..48b2945 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -19,6 +19,7 @@
 	"android/soong/bazel"
 	"fmt"
 	"os"
+	"strings"
 )
 
 // Codegen is the backend of bp2build. The code generator is responsible for
@@ -29,14 +30,22 @@
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(bp2buildDir)
 
-	buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true)
-	bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+	res, errs := GenerateBazelTargets(ctx, true)
+	if len(errs) > 0 {
+		errMsgs := make([]string, len(errs))
+		for i, err := range errs {
+			errMsgs[i] = fmt.Sprintf("%q", err)
+		}
+		fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
+		os.Exit(1)
+	}
+	bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer))
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.compatLayer))
 
-	return metrics
+	return res.metrics
 }
 
 // Get the output directory and create it if it doesn't exist.
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index f652a35..10e2329 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -153,10 +153,11 @@
 }
 
 type CodegenContext struct {
-	config         android.Config
-	context        android.Context
-	mode           CodegenMode
-	additionalDeps []string
+	config                  android.Config
+	context                 android.Context
+	mode                    CodegenMode
+	additionalDeps          []string
+	unconvertedDepMode unconvertedDepsMode
 }
 
 func (c *CodegenContext) Mode() CodegenMode {
@@ -181,6 +182,16 @@
 	QueryView
 )
 
+type unconvertedDepsMode int
+
+const (
+	// Include a warning in conversion metrics about converted modules with unconverted direct deps
+	warnUnconvertedDeps unconvertedDepsMode = iota
+	// Error and fail conversion if encountering a module with unconverted direct deps
+	// Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED`
+	errorModulesUnconvertedDeps
+)
+
 func (mode CodegenMode) String() string {
 	switch mode {
 	case Bp2Build:
@@ -211,10 +222,15 @@
 // NewCodegenContext creates a wrapper context that conforms to PathContext for
 // writing BUILD files in the output directory.
 func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+	var unconvertedDeps unconvertedDepsMode
+	if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") {
+		unconvertedDeps = errorModulesUnconvertedDeps
+	}
 	return &CodegenContext{
-		context: context,
-		config:  config,
-		mode:    mode,
+		context:            context,
+		config:             config,
+		mode:               mode,
+		unconvertedDepMode: unconvertedDeps,
 	}
 }
 
@@ -230,7 +246,17 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) {
+type conversionResults struct {
+	buildFileToTargets map[string]BazelTargets
+	metrics            CodegenMetrics
+	compatLayer        CodegenCompatLayer
+}
+
+func (r conversionResults) BuildDirToTargets() map[string]BazelTargets {
+	return r.buildFileToTargets
+}
+
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
 	buildFileToAppend := make(map[string]bool)
 
@@ -245,6 +271,8 @@
 
 	dirs := make(map[string]bool)
 
+	var errs []error
+
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
@@ -266,13 +294,24 @@
 				}
 				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
 				if err != nil {
-					panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					errs = append(errs, fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					return
 				}
 				targets = append(targets, t)
 				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
 				// something more targeted based on the rule type and target
 				buildFileToAppend[pathToBuildFile] = true
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
+					msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
+					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+						metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
+					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+						metrics.TotalModuleCount += 1
+						errs = append(errs, fmt.Errorf(msg))
+						return
+					}
+				}
 				targets = generateBazelTargets(bpCtx, aModule)
 				for _, t := range targets {
 					if t.name == m.Name() {
@@ -295,11 +334,17 @@
 			t := generateSoongModuleTarget(bpCtx, m)
 			targets = append(targets, t)
 		default:
-			panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			return
 		}
 
 		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
 	})
+
+	if len(errs) > 0 {
+		return conversionResults{}, errs
+	}
+
 	if generateFilegroups {
 		// Add a filegroup target that exposes all sources in the subtree of this package
 		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
@@ -312,7 +357,11 @@
 		}
 	}
 
-	return buildFileToTargets, metrics, compatLayer
+	return conversionResults{
+		buildFileToTargets: buildFileToTargets,
+		metrics:            metrics,
+		compatLayer:        compatLayer,
+	}, errs
 }
 
 func getBazelPackagePath(b android.Bazelable) string {
@@ -575,6 +624,19 @@
 			// Ignore zero-valued fields
 			continue
 		}
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			if field.Type.Kind() == reflect.Ptr {
+				fieldValue = fieldValue.Elem()
+			}
+			if fieldValue.Type().Kind() == reflect.Struct {
+				propsToMerge := extractStructProperties(fieldValue, indent)
+				for prop, value := range propsToMerge {
+					ret[prop] = value
+				}
+				continue
+			}
+		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
 		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index ecea6b2..e904627 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 	"strings"
 	"testing"
 )
@@ -199,7 +200,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 				t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 			}
@@ -322,6 +324,30 @@
 )`,
 			},
 		},
+		{
+			blueprint: `custom {
+    name: "embedded_props",
+    embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "embedded_props",
+    embedded_attr = "abc",
+)`,
+			},
+		},
+		{
+			blueprint: `custom {
+    name: "ptr_to_embedded_props",
+    other_embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "ptr_to_embedded_props",
+    other_embedded_attr = "abc",
+)`,
+			},
+		},
 	}
 
 	dir := "."
@@ -341,7 +367,8 @@
 		}
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
@@ -502,7 +529,8 @@
 		android.FailIfErrored(t, errs)
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
 			t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
 		}
@@ -679,59 +707,38 @@
 				"other/Android.bp": `filegroup {
     name: "foo",
     srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
+}`,
+			},
+		},
+		{
+			description:                        "depends_on_other_unconverted_module_error",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			unconvertedDepsMode:                errorModulesUnconvertedDeps,
+			blueprint: `filegroup {
+    name: "foobar",
+    srcs: [
+        ":foo",
+        "c",
+    ],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf(`"foobar" depends on unconverted modules: foo`),
+			filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
 }`,
 			},
 		},
 	}
 
-	dir := "."
 	for _, testCase := range testCases {
-		fs := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			fs[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if errored(t, testCase, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, testCase, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
+		t.Run(testCase.description, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase)
+		})
 	}
 }
 
@@ -809,7 +816,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
 				t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
 			}
@@ -921,7 +929,8 @@
 
 		// For each directory, test that the expected number of generated targets is correct.
 		for dir, expectedCount := range testCase.expectedCount {
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != expectedCount {
 				t.Fatalf(
 					"%s: Expected %d bazel target for %s package, got %d",
@@ -1064,7 +1073,9 @@
 			if testCase.dir != "" {
 				checkDir = testCase.dir
 			}
-			bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+			android.FailIfErrored(t, err)
 			bazelTargets.sort()
 			actualCount := len(bazelTargets)
 			expectedCount := len(testCase.expectedBazelTargets)
@@ -1185,7 +1196,9 @@
 		if testCase.dir != "" {
 			checkDir = testCase.dir
 		}
-		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
 		} else {
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index f2f6b01..992cc1c 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -160,8 +160,15 @@
 		if shouldSkipStructField(field) {
 			continue
 		}
-
-		properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+		subProps := extractPropertyDescriptions(field.Name, field.Type)
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			for _, prop := range subProps {
+				properties = append(properties, prop.properties...)
+			}
+		} else {
+			properties = append(properties, subProps...)
+		}
 	}
 	return properties
 }
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 9e0c0a1..1e78c0e 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -92,6 +92,7 @@
         # bazel_module end
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -99,6 +100,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -118,6 +120,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -125,6 +128,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -144,6 +148,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -151,6 +156,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index a991180..f3bc1ba 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -267,7 +267,8 @@
 		}
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
 		} else {
@@ -461,7 +462,8 @@
 		android.FailIfErrored(t, errs)
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != 1 {
 			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
 		}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 65b06c6..645ef2d 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -3,6 +3,7 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"strings"
 )
 
 // Simple metrics struct to collect information about a Blueprint to BUILD
@@ -16,6 +17,8 @@
 
 	// Total number of handcrafted targets
 	handCraftedTargetCount int
+
+	moduleWithUnconvertedDepsMsgs []string
 }
 
 // Print the codegen metrics to stdout.
@@ -27,8 +30,10 @@
 		generatedTargetCount += count
 	}
 	fmt.Printf(
-		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n",
+		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s",
 		generatedTargetCount,
 		metrics.handCraftedTargetCount,
-		metrics.TotalModuleCount)
+		metrics.TotalModuleCount,
+		len(metrics.moduleWithUnconvertedDepsMsgs),
+		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 3ebe63d..74084b1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -39,9 +39,8 @@
 func checkError(t *testing.T, errs []error, expectedErr error) bool {
 	t.Helper()
 
-	// expectedErr is not nil, find it in the list of errors
 	if len(errs) != 1 {
-		t.Errorf("Expected only 1 error, got %d: %q", len(errs), errs)
+		return false
 	}
 	if errs[0].Error() == expectedErr.Error() {
 		return true
@@ -83,6 +82,7 @@
 	filesystem                         map[string]string
 	dir                                string
 	expectedErr                        error
+	unconvertedDepsMode                unconvertedDepsMode
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -126,7 +126,13 @@
 		checkDir = tc.dir
 	}
 	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-	bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+	codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
+	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
+	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
+		return
+	} else {
+		android.FailIfErrored(t, errs)
+	}
 	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target, got %d; %v",
 			tc.description, expectedCount, actualCount, bazelTargets)
@@ -148,7 +154,18 @@
 	Nested_prop string
 }
 
+type EmbeddedProps struct {
+	Embedded_prop string
+}
+
+type OtherEmbeddedProps struct {
+	Other_embedded_prop string
+}
+
 type customProps struct {
+	EmbeddedProps
+	*OtherEmbeddedProps
+
 	Bool_prop     bool
 	Bool_ptr_prop *bool
 	// Ensure that properties tagged `blueprint:mutated` are omitted
@@ -246,7 +263,17 @@
 	return m
 }
 
+type EmbeddedAttr struct {
+	Embedded_attr string
+}
+
+type OtherEmbeddedAttr struct {
+	Other_embedded_attr string
+}
+
 type customBazelModuleAttributes struct {
+	EmbeddedAttr
+	*OtherEmbeddedAttr
 	String_prop      string
 	String_list_prop []string
 	Arch_paths       bazel.LabelListAttribute
@@ -275,6 +302,10 @@
 			String_list_prop: m.props.String_list_prop,
 			Arch_paths:       paths,
 		}
+		attrs.Embedded_attr = m.props.Embedded_prop
+		if m.props.OtherEmbeddedProps != nil {
+			attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
+		}
 
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class: "custom",
@@ -316,10 +347,10 @@
 }
 
 // Helper method for tests to easily access the targets in a dir.
-func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
+func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
-	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
-	return buildFileToTargets[dir]
+	res, err := GenerateBazelTargets(codegenCtx, false)
+	return res.buildFileToTargets[dir], err
 }
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 98e27c6..d63ded5 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -27,12 +27,12 @@
 	os.RemoveAll(bazelQueryViewDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	// Ignore metrics reporting and compat layers for queryview, since queryview
-	// is already a full-repo conversion and can use data from bazel query
-	// directly.
-	buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true)
+	res, err := bp2build.GenerateBazelTargets(ctx, true)
+	if err != nil {
+		panic(err)
+	}
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
+	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f7561b4..b13e2db 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -89,7 +89,7 @@
 
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
-var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
+var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
 var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 8e39f40..51cd501 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -218,7 +218,7 @@
 var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
 var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
 var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
-var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
+var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{}
 
 // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
 // needed to produce the hidden API monolithic stub flags file.
diff --git a/sdk/member_type.go b/sdk/member_type.go
index ee27c86..9aab61d 100644
--- a/sdk/member_type.go
+++ b/sdk/member_type.go
@@ -35,7 +35,7 @@
 
 	// the dependency tag used for items in this list that can be used to determine the memberType
 	// for a resolved dependency.
-	dependencyTag android.SdkMemberTypeDependencyTag
+	dependencyTag android.SdkMemberDependencyTag
 }
 
 func (p *sdkMemberTypeListProperty) propertyName() string {
diff --git a/sdk/update.go b/sdk/update.go
index 96a6e69..a998f42 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -185,7 +185,7 @@
 	s.multilibUsages = multilibNone
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
-		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
+		if memberTag, ok := tag.(android.SdkMemberDependencyTag); ok {
 			memberType := memberTag.SdkMemberType(child)
 
 			// If a nil SdkMemberType was returned then this module should not be added to the sdk.