Merge "NDK library: remove remnants of abidw usage" into main
diff --git a/aconfig/init.go b/aconfig/init.go
index 7e283ea..37167aa 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -30,7 +30,7 @@
 				` ${declarations}` +
 				` ${values}` +
 				` --cache ${out}.tmp` +
-				` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 			//				` --build-id ${release_version}` +
 			CommandDeps: []string{
 				"${aconfig}",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 69bcd2e..d37dc02 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -121,6 +121,7 @@
 		"development/sdk":                             Bp2BuildDefaultTrueRecursively,
 
 		"external/aac":                           Bp2BuildDefaultTrueRecursively,
+		"external/abseil-cpp":                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":        Bp2BuildDefaultTrueRecursively,
 		"external/auto":                          Bp2BuildDefaultTrue,
 		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
@@ -189,7 +190,11 @@
 		"external/ow2-asm":                       Bp2BuildDefaultTrueRecursively,
 		"external/pcre":                          Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                      Bp2BuildDefaultTrueRecursively,
+		"external/python/pyyaml/lib/yaml":        Bp2BuildDefaultTrueRecursively,
 		"external/python/six":                    Bp2BuildDefaultTrueRecursively,
+		"external/python/jinja/src":              Bp2BuildDefaultTrueRecursively,
+		"external/python/markupsafe/src":         Bp2BuildDefaultTrueRecursively,
+		"external/python/setuptools":             Bp2BuildDefaultTrueRecursively,
 		"external/rappor":                        Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                         Bp2BuildDefaultTrueRecursively,
 		"external/selinux/checkpolicy":           Bp2BuildDefaultTrueRecursively,
@@ -813,6 +818,8 @@
 		"chre_flatbuffers",
 		"event_logger",
 		"hal_unit_tests",
+
+		"merge_annotation_zips_test",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -836,6 +843,10 @@
 	// the "prebuilt_" prefix to the name, so that it's differentiable from
 	// the source versions within Soong's module graph.
 	Bp2buildModuleDoNotConvertList = []string{
+		// TODO(b/263326760): Failed already.
+		"minijail_compiler_unittest",
+		"minijail_parser_unittest",
+
 		// Depends on unconverted libandroid, libgui
 		"dvr_buffer_queue-test",
 		"dvr_display-test",
@@ -1516,73 +1527,6 @@
 		"libartd-runtime-gtest",
 	}
 
-	MixedBuildsDisabledList = []string{
-		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
-
-		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
-
-		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
-		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
-
-		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
-
-		// Depends on libprotobuf-cpp-*
-		"libadb_pairing_connection",
-		"libadb_pairing_connection_static",
-		"libadb_pairing_server", "libadb_pairing_server_static",
-
-		// java_import[_host] issues
-		// tradefed prebuilts depend on libprotobuf
-		"prebuilt_tradefed",
-		"prebuilt_tradefed-test-framework",
-		// handcrafted BUILD.bazel files in //prebuilts/...
-		"prebuilt_r8lib-prebuilt",
-		"prebuilt_sdk-core-lambda-stubs",
-		"prebuilt_android-support-collections-nodeps",
-		"prebuilt_android-arch-core-common-nodeps",
-		"prebuilt_android-arch-lifecycle-common-java8-nodeps",
-		"prebuilt_android-arch-lifecycle-common-nodeps",
-		"prebuilt_android-support-annotations-nodeps",
-		"prebuilt_android-arch-paging-common-nodeps",
-		"prebuilt_android-arch-room-common-nodeps",
-		// TODO(b/217750501) exclude_dirs property not supported
-		"prebuilt_kotlin-reflect",
-		"prebuilt_kotlin-stdlib",
-		"prebuilt_kotlin-stdlib-jdk7",
-		"prebuilt_kotlin-stdlib-jdk8",
-		"prebuilt_kotlin-test",
-		// TODO(b/217750501) exclude_files property not supported
-		"prebuilt_currysrc_org.eclipse",
-
-		// TODO(b/266459895): re-enable libunwindstack
-		"libunwindstack",
-		"libunwindstack_stdout_log",
-		"libunwindstack_no_dex",
-		"libunwindstack_utils",
-		"unwind_reg_info",
-		"libunwindstack_local",
-		"unwind_for_offline",
-		"unwind",
-		"unwind_info",
-		"unwind_symbols",
-		"libEGL",
-		"libGLESv2",
-		"libc_malloc_debug",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_hidl@1.2",
-		"libfdtrack",
-		"libgui",
-		"libgui_bufferqueue_static",
-		"libmedia_codecserviceregistrant",
-		"libstagefright_bufferqueue_helper_novndk",
-		"libutils_test",
-		"libutilscallstack",
-		"mediaswcodec",
-	}
-
 	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
 	// in either prod mode or staging mode.
 	ProdMixedBuildsEnabledList = []string{
@@ -1643,4 +1587,14 @@
 		"art_":        DEFAULT_PRIORITIZED_WEIGHT,
 		"ndk_library": DEFAULT_PRIORITIZED_WEIGHT,
 	}
+
+	BazelSandwichTargets = []struct {
+		Label string
+		Host  bool
+	}{
+		{
+			Label: "//build/bazel/examples/partitions:system_image",
+			Host:  false,
+		},
+	}
 )
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 94bc88b..fd4b5ef 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -186,6 +186,8 @@
 
 	// Returns the depsets defined in Bazel's aquery response.
 	AqueryDepsets() []bazel.AqueryDepset
+
+	QueueBazelSandwichCqueryRequests(config Config) error
 }
 
 type bazelRunner interface {
@@ -264,6 +266,10 @@
 	m.BazelRequests[key] = true
 }
 
+func (m MockBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
 	result, ok := m.LabelToOutputFiles[label]
 	if !ok {
@@ -424,6 +430,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
 	panic("unimplemented")
 }
@@ -1042,6 +1052,50 @@
 	allBazelCommands = []bazelCommand{aqueryCmd, cqueryCmd, buildCmd}
 )
 
+func GetBazelSandwichCqueryRequests(config Config) ([]cqueryKey, error) {
+	result := make([]cqueryKey, 0, len(allowlists.BazelSandwichTargets))
+	// Note that bazel "targets" are different from soong "targets", the bazel targets are
+	// synonymous with soong modules, and soong targets are a configuration a module is built in.
+	for _, target := range allowlists.BazelSandwichTargets {
+		var soongTarget Target
+		if target.Host {
+			soongTarget = config.BuildOSTarget
+		} else {
+			soongTarget = config.AndroidCommonTarget
+			if soongTarget.Os.Class != Device {
+				// kernel-build-tools seems to set the AndroidCommonTarget to a linux host
+				// target for some reason, disable device builds in that case.
+				continue
+			}
+		}
+
+		result = append(result, cqueryKey{
+			label:       target.Label,
+			requestType: cquery.GetOutputFiles,
+			configKey: configKey{
+				arch:   soongTarget.Arch.String(),
+				osType: soongTarget.Os,
+			},
+		})
+	}
+	return result, nil
+}
+
+// QueueBazelSandwichCqueryRequests queues cquery requests for all the bazel labels in
+// bazel_sandwich_targets. These will later be given phony targets so that they can be built on the
+// command line.
+func (context *mixedBuildBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	requests, err := GetBazelSandwichCqueryRequests(config)
+	if err != nil {
+		return err
+	}
+	for _, request := range requests {
+		context.QueueBazelRequest(request.label, request.requestType, request.configKey)
+	}
+
+	return nil
+}
+
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
 func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
@@ -1255,6 +1309,11 @@
 
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
+	rel, err := filepath.Rel(ctx.Config().OutDir(), executionRoot)
+	if err != nil {
+		ctx.Errorf("%s", err.Error())
+	}
+	dotdotsToOutRoot := strings.Repeat("../", strings.Count(rel, "/")+1)
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
 		// nil build statements are a valid case where we do not create an action because it is
 		// unnecessary or handled by other processing
@@ -1286,7 +1345,8 @@
 					})
 				}
 			}
-			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset)
+			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset, dotdotsToOutRoot)
+
 			desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
 			rule.Build(fmt.Sprintf("bazel %d", index), desc)
 			continue
@@ -1331,6 +1391,24 @@
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
 	}
+
+	// Create phony targets for all the bazel sandwich output files
+	requests, err := GetBazelSandwichCqueryRequests(ctx.Config())
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+	for _, request := range requests {
+		files, err := ctx.Config().BazelContext.GetOutputFiles(request.label, request.configKey)
+		if err != nil {
+			ctx.Errorf(err.Error())
+		}
+		filesAsPaths := make([]Path, 0, len(files))
+		for _, file := range files {
+			filesAsPaths = append(filesAsPaths, PathForBazelOut(ctx, file))
+		}
+		ctx.Phony("bazel_sandwich", filesAsPaths...)
+	}
+	ctx.Phony("checkbuild", PathForPhony(ctx, "bazel_sandwich"))
 }
 
 // Returns a out dir path for a sandboxed mixed build action
@@ -1344,7 +1422,7 @@
 }
 
 // Register bazel-owned build statements (obtained from the aquery invocation).
-func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset) {
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset, dotdotsToOutRoot string) {
 	// executionRoot is the action cwd.
 	if buildStatement.ShouldRunInSbox {
 		// mkdir -p ensures that the directory exists when run via sbox
@@ -1367,14 +1445,17 @@
 		cmd.Flag(pair.Key + "=" + pair.Value)
 	}
 
+	command := buildStatement.Command
+	command = strings.ReplaceAll(command, "{DOTDOTS_TO_OUTPUT_ROOT}", dotdotsToOutRoot)
+
 	// The actual Bazel action.
-	if len(buildStatement.Command) > 16*1024 {
+	if len(command) > 16*1024 {
 		commandFile := PathForBazelOut(ctx, buildStatement.OutputPaths[0]+".sh")
-		WriteFileRule(ctx, commandFile, buildStatement.Command)
+		WriteFileRule(ctx, commandFile, command)
 
 		cmd.Text("bash").Text(buildStatement.OutputPaths[0] + ".sh").Implicit(commandFile)
 	} else {
-		cmd.Text(buildStatement.Command)
+		cmd.Text(command)
 	}
 
 	for _, outputPath := range buildStatement.OutputPaths {
@@ -1403,6 +1484,9 @@
 			cmd.Implicit(PathForPhony(ctx, otherDepsetName))
 		}
 	}
+	for _, implicitPath := range buildStatement.ImplicitDeps {
+		cmd.Implicit(PathForArbitraryOutput(ctx, implicitPath))
+	}
 
 	if depfile := buildStatement.Depfile; depfile != nil {
 		// The paths in depfile are relative to `executionRoot`.
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index e08a471..9a3c8fc 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -181,7 +181,7 @@
 
 		cmd := RuleBuilderCommand{}
 		ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
-		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
+		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
 		if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
 			t.Errorf("expected: [%s], actual: [%s]", expected, actual)
 		}
@@ -224,7 +224,7 @@
 
 	cmd := RuleBuilderCommand{}
 	ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
-	createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
+	createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
 	// Assert that the output is generated in an intermediate directory
 	// fe05bcdcdc4928012781a5f1a2a77cbb5398e106 is the sha1 checksum of "one"
 	if actual, expected := cmd.outputs[0].String(), "out/soong/mixed_build_sbox_intermediates/fe05bcdcdc4928012781a5f1a2a77cbb5398e106/test/exec_root/one"; expected != actual {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 8956a18..7568543 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -124,6 +124,7 @@
 		labels.Includes = []bazel.Label{}
 		return labels
 	}
+	modules = FirstUniqueStrings(modules)
 	for _, module := range modules {
 		bpText := module
 		if m := SrcIsModule(module); m == "" {
diff --git a/android/paths.go b/android/paths.go
index e16cb37..325a953 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1029,16 +1029,16 @@
 	return p
 }
 
+func (p basePath) RelativeToTop() Path {
+	ensureTestOnly()
+	return p
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
 }
 
-func (p SourcePath) RelativeToTop() Path {
-	ensureTestOnly()
-	return p
-}
-
 var _ Path = SourcePath{}
 
 func (p SourcePath) withRel(rel string) SourcePath {
@@ -1126,6 +1126,16 @@
 	return path
 }
 
+// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
+// the path is relative to the root of the output folder, not the out/soong folder.
+func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
+	p, err := validatePath(pathComponents...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
+}
+
 // MaybeExistentPathForSource joins the provided path components and validates that the result
 // neither escapes the source dir nor is in the out dir.
 // It does not validate whether the path exists.
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 2c080a1..d77d59a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -17,15 +17,15 @@
 import (
 	"crypto/sha256"
 	"encoding/base64"
+	"encoding/json"
 	"fmt"
 	"path/filepath"
+	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 	"reflect"
 	"sort"
 	"strings"
 	"sync"
 
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
-
 	"github.com/google/blueprint/metrics"
 	"github.com/google/blueprint/proptools"
 	"google.golang.org/protobuf/proto"
@@ -119,6 +119,10 @@
 	// If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
 	// and run the mixed build action there
 	ShouldRunInSbox bool
+	// A list of files to add as implicit deps to the outputs of this BuildStatement.
+	// Unlike most properties in BuildStatement, these paths must be relative to the root of
+	// the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
+	ImplicitDeps []string
 }
 
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -581,6 +585,72 @@
 	}, nil
 }
 
+type bazelSandwichJson struct {
+	Target         string   `json:"target"`
+	DependOnTarget *bool    `json:"depend_on_target,omitempty"`
+	ImplicitDeps   []string `json:"implicit_deps"`
+}
+
+func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+	if err != nil {
+		return nil, err
+	}
+	if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
+		return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
+	}
+	target := actionEntry.UnresolvedSymlinkTarget
+	if target == "" {
+		return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
+	}
+	if filepath.Clean(target) != target {
+		return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
+	}
+	if strings.HasPrefix(target, "/") {
+		return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
+	}
+
+	out := outputPaths[0]
+	outDir := filepath.Dir(out)
+	var implicitDeps []string
+	if strings.HasPrefix(target, "bazel_sandwich:") {
+		j := bazelSandwichJson{}
+		err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
+		if err != nil {
+			return nil, err
+		}
+		if proptools.BoolDefault(j.DependOnTarget, true) {
+			implicitDeps = append(implicitDeps, j.Target)
+		}
+		implicitDeps = append(implicitDeps, j.ImplicitDeps...)
+		dotDotsToReachCwd := ""
+		if outDir != "." {
+			dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
+		}
+		target = proptools.ShellEscapeIncludingSpaces(j.Target)
+		target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
+	} else {
+		target = proptools.ShellEscapeIncludingSpaces(target)
+	}
+
+	outDir = proptools.ShellEscapeIncludingSpaces(outDir)
+	out = proptools.ShellEscapeIncludingSpaces(out)
+	// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
+	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
+	symlinkPaths := outputPaths[:]
+
+	buildStatement := &BuildStatement{
+		Command:      command,
+		Depfile:      depfile,
+		OutputPaths:  outputPaths,
+		Env:          actionEntry.EnvironmentVariables,
+		Mnemonic:     actionEntry.Mnemonic,
+		SymlinkPaths: symlinkPaths,
+		ImplicitDeps: implicitDeps,
+	}
+	return buildStatement, nil
+}
+
 func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -690,6 +760,8 @@
 		return a.fileWriteActionBuildStatement(actionEntry)
 	case "SymlinkTree":
 		return a.symlinkTreeActionBuildStatement(actionEntry)
+	case "UnresolvedSymlink":
+		return a.unresolvedSymlinkActionBuildStatement(actionEntry)
 	}
 
 	if len(actionEntry.Arguments) < 1 {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 19a584f..32c87a0 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -357,9 +357,11 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 1; len(actual) != expected {
 		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+		return
 	}
 
 	bs := actual[0]
@@ -544,6 +546,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -756,9 +759,11 @@
 	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 2; len(actualBuildStatements) != expected {
 		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
+		return
 	}
 
 	expectedDepsetFiles := [][]string{
@@ -859,6 +864,7 @@
 
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -907,6 +913,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1017,6 +1024,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1088,6 +1096,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1126,6 +1135,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1136,6 +1146,126 @@
 	}, actual)
 }
 
+func TestUnresolvedSymlink(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "symlink/target"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwich(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		ImplicitDeps: []string{"target/product/emulator_x86_64/system"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		// Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here
+		ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"},
+	}}, actual)
+}
+
 func assertError(t *testing.T, err error, expected string) {
 	t.Helper()
 	if err == nil {
@@ -1201,6 +1331,9 @@
 	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
 		return "SymlinkPaths"
 	}
+	if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) {
+		return "ImplicitDeps"
+	}
 	if first.Depfile != second.Depfile {
 		return "Depfile"
 	}
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index f56e6d8..2513af8 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -97,6 +97,7 @@
 android_product(
     name = "mixed_builds_product-{VARIANT}",
     soong_variables = _soong_variables,
+    extra_constraints = ["@//build/bazel/platforms:mixed_builds"],
 )
 `)),
 		newFile(
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 3de5213..09a9d04 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -744,10 +744,12 @@
 				targets, targetErrs = generateBazelTargetsGoPackage(bpCtx, glib, nameToGoLibMap)
 				errs = append(errs, targetErrs...)
 				metrics.IncrementRuleClassCount("go_library")
+				metrics.AddConvertedModule(glib, "go_library", dir)
 			} else if gbin, ok := m.(*bootstrap.GoBinary); ok {
 				targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap)
 				errs = append(errs, targetErrs...)
 				metrics.IncrementRuleClassCount("go_binary")
+				metrics.AddConvertedModule(gbin, "go_binary", dir)
 			} else {
 				metrics.AddUnconvertedModule(m, moduleType, dir, android.UnconvertedReason{
 					ReasonType: int(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED),
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index dd612ce..f9b3eac 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -21,6 +21,7 @@
 var VndkMustUseVendorVariantList = []string{
 	"android.hardware.nfc@1.2",
 	"libbinder",
+	"libdumpstateutil",
 	"libcrypto",
 	"libexpat",
 	"libgatekeeper",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 5ea84bc..62b3333 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -121,6 +121,10 @@
 	defer ctx.EventHandler.End("mixed_build")
 
 	bazelHook := func() error {
+		err := ctx.Config().BazelContext.QueueBazelSandwichCqueryRequests(ctx.Config())
+		if err != nil {
+			return err
+		}
 		return ctx.Config().BazelContext.InvokeBazel(ctx.Config(), ctx)
 	}
 	ctx.SetBeforePrepareBuildActionsHook(bazelHook)
diff --git a/python/bp2build.go b/python/bp2build.go
index cd3f2a1..60cabc4 100644
--- a/python/bp2build.go
+++ b/python/bp2build.go
@@ -223,7 +223,8 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		// Use the native py_binary rule.
-		Rule_class: "py_test",
+		Rule_class:        "py_test",
+		Bzl_load_location: "//build/bazel/rules/python:py_test.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/rust/rust.go b/rust/rust.go
index 05fceee..dab3532 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1306,12 +1306,17 @@
 				}
 			}
 			linkObject := ccDep.OutputFile()
-			linkPath := linkPathFromFilePath(linkObject.Path())
-
 			if !linkObject.Valid() {
-				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				if !ctx.Config().AllowMissingDependencies() {
+					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				} else {
+					ctx.AddMissingDependencies([]string{depName})
+				}
+				return
 			}
 
+			linkPath := linkPathFromFilePath(linkObject.Path())
+
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
@@ -1356,6 +1361,14 @@
 				// Re-get linkObject as ChooseStubOrImpl actually tells us which
 				// object (either from stub or non-stub) to use.
 				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+				if !linkObject.Valid() {
+					if !ctx.Config().AllowMissingDependencies() {
+						ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+					} else {
+						ctx.AddMissingDependencies([]string{depName})
+					}
+					return
+				}
 				linkPath = linkPathFromFilePath(linkObject.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 48f654e..6b9ff8b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -22,7 +22,5 @@
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
 
-"$TOP/build/soong/tests/sbom_test.sh"
-
 "$TOP/build/bazel/ci/b_test.sh"