Use packagepath and local_repository for mixed builds

This allows mixed builds to continue functioning even given the toplevel
WORKSPACE file containing toplevel_output_directories with out/.

Test: Manually verified on aosp_flame building libc.
Change-Id: I80fc4853421317e2d2c7f03d92d58286df1342ce
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index c87a945..a1ba8c9 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -195,6 +195,7 @@
 	cmdFlags := []string{"--bazelrc=build/bazel/common.bazelrc",
 		"--output_base=" + context.outputBase, command}
 	cmdFlags = append(cmdFlags, labels...)
+	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
@@ -211,6 +212,21 @@
 	}
 }
 
+// Returns the string contents of a workspace file that should be output
+// adjacent to the main bzl file and build file.
+// This workspace file allows, via local_repository rule, sourcetree-level
+// BUILD targets to be referenced via @sourceroot.
+func (context *bazelContext) workspaceFileContents() []byte {
+	formatString := `
+# This file is generated by soong_build. Do not edit.
+local_repository(
+    name = "sourceroot",
+    path = "%s",
+)
+`
+	return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+}
+
 func (context *bazelContext) mainBzlFileContents() []byte {
 	contents := `
 # This file is generated by soong_build. Do not edit.
@@ -225,6 +241,18 @@
 	return []byte(contents)
 }
 
+// Returns a "canonicalized" corresponding to the given sourcetree-level label.
+// This abstraction is required because a sourcetree label such as //foo/bar:baz
+// must be referenced via the local repository prefix, such as
+// @sourceroot//foo/bar:baz.
+func canonicalizeLabel(label string) string {
+	if strings.HasPrefix(label, "//") {
+		return "@sourceroot" + label
+	} else {
+		return "@sourceroot//" + label
+	}
+}
+
 func (context *bazelContext) mainBuildFileContents() []byte {
 	formatString := `
 # This file is generated by soong_build. Do not edit.
@@ -236,7 +264,7 @@
 `
 	var buildRootDeps []string = nil
 	for val, _ := range context.requests {
-		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", val.label))
+		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
 	}
 	buildRootDepsString := strings.Join(buildRootDeps, ",\n            ")
 
@@ -262,13 +290,19 @@
 	// TODO(cparsons): Sort by request type instead of assuming all requests
 	// are of GetAllFiles type.
 	for val, _ := range context.requests {
-		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", val.label))
+		buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
 	}
 	buildRootDepsString := strings.Join(buildRootDeps, ",\n  ")
 
 	return []byte(fmt.Sprintf(formatString, buildRootDepsString))
 }
 
+// Returns a workspace-relative path containing build-related metadata required
+// for interfacing with Bazel. Example: out/soong/bazel.
+func (context *bazelContext) intermediatesDir() string {
+	return filepath.Join(context.buildDir, "bazel")
+}
+
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
 func (context *bazelContext) InvokeBazel() error {
@@ -276,26 +310,38 @@
 
 	var cqueryOutput string
 	var err error
+
+	err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+	if err != nil {
+		return err
+	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.buildDir, "main.bzl")),
+		absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
 		context.mainBzlFileContents(), 0666)
 	if err != nil {
 		return err
 	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.buildDir, "BUILD.bazel")),
+		absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
 		context.mainBuildFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	cquery_file_relpath := filepath.Join(context.buildDir, "buildroot.cquery")
+	cquery_file_relpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
 	err = ioutil.WriteFile(
 		absolutePath(cquery_file_relpath),
 		context.cqueryStarlarkFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	buildroot_label := fmt.Sprintf("//%s:buildroot", context.buildDir)
+	workspace_file_relpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
+	err = ioutil.WriteFile(
+		absolutePath(workspace_file_relpath),
+		context.workspaceFileContents(), 0666)
+	if err != nil {
+		return err
+	}
+	buildroot_label := "//:buildroot"
 	cqueryOutput, err = context.issueBazelCommand("cquery",
 		[]string{fmt.Sprintf("deps(%s)", buildroot_label)},
 		"--output=starlark",
@@ -314,7 +360,7 @@
 	}
 
 	for val, _ := range context.requests {
-		if cqueryResult, ok := cqueryResults[val.label]; ok {
+		if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
 			context.results[val] = string(cqueryResult)
 		} else {
 			return fmt.Errorf("missing result for bazel target %s", val.label)