Merge "Improve realism of boot jar tests"
diff --git a/android/bazel.go b/android/bazel.go
index b54ae64..b3f9d88 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -135,45 +135,45 @@
 
 	// Per-module denylist to always opt modules out.
 	bp2buildModuleDoNotConvertList = []string{
-		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static
+		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static, 'map' file not found
 		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
 		"libc_jemalloc_wrapper",         // ruperts@, cc_library_static, depends on //external/jemalloc_new
-		"libc_bootstrap",                // ruperts@, cc_library_static
-		"libc_init_static",              // ruperts@, cc_library_static
-		"libc_init_dynamic",             // ruperts@, cc_library_static
-		"libc_tzcode",                   // ruperts@, cc_library_static
-		"libc_freebsd",                  // ruperts@, cc_library_static
-		"libc_freebsd_large_stack",      // ruperts@, cc_library_static
-		"libc_netbsd",                   // ruperts@, cc_library_static
-		"libc_openbsd_ndk",              // ruperts@, cc_library_static
-		"libc_openbsd_large_stack",      // ruperts@, cc_library_static
-		"libc_openbsd",                  // ruperts@, cc_library_static
-		"libc_gdtoa",                    // ruperts@, cc_library_static
-		"libc_fortify",                  // ruperts@, cc_library_static
-		"libc_bionic",                   // ruperts@, cc_library_static
+		"libc_bootstrap",                // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_init_static",              // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_init_dynamic",             // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_tzcode",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_freebsd",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_freebsd_large_stack",      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_netbsd",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_openbsd_ndk",              // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_openbsd_large_stack",      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_openbsd",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_gdtoa",                    // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_fortify",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_bionic",                   // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
-		"libc_bionic_systrace",          // ruperts@, cc_library_static
-		"libc_pthread",                  // ruperts@, cc_library_static
-		"libc_syscalls",                 // ruperts@, cc_library_static
-		"libc_aeabi",                    // ruperts@, cc_library_static
+		"libc_bionic_systrace",          // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_pthread",                  // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_syscalls",                 // ruperts@, cc_library_static, mutator panic cannot get direct dep syscalls-arm64.S of libc_syscalls
+		"libc_aeabi",                    // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_ndk",                      // ruperts@, cc_library_static, depends on //bionic/libm:libm
 		"libc_nopthread",                // ruperts@, cc_library_static, depends on //external/arm-optimized-routines
 		"libc_common",                   // ruperts@, cc_library_static, depends on //bionic/libc:libc_nopthread
-		"libc_static_dispatch",          // ruperts@, cc_library_static
-		"libc_dynamic_dispatch",         // ruperts@, cc_library_static
+		"libc_static_dispatch",          // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
+		"libc_dynamic_dispatch",         // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_common_static",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
 		"libc_common_shared",            // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libc_unwind_static",            // ruperts@, cc_library_static
+		"libc_unwind_static",            // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 		"libc_nomalloc",                 // ruperts@, cc_library_static, depends on //bionic/libc:libc_common
-		"libasync_safe",                 // ruperts@, cc_library_static
+		"libasync_safe",                 // ruperts@, cc_library_static, 'private/CachedProperty.h' file not found
 		"libc_malloc_debug_backtrace",   // ruperts@, cc_library_static, depends on //system/libbase
 		"libsystemproperties",           // ruperts@, cc_library_static, depends on //system/core/property_service/libpropertyinfoparser
-		"libdl_static",                  // ruperts@, cc_library_static
+		"libdl_static",                  // ruperts@, cc_library_static, 'private/CFIShadow.h' file not found
 		"liblinker_main",                // ruperts@, cc_library_static, depends on //system/libbase
 		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
 		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
-		"libbionic_tests_headers_posix", // ruperts@, cc_library_static
-		"libc_dns",                      // ruperts@, cc_library_static
+		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
+		"libc_dns",                      // ruperts@, cc_library_static, 'bionic/libc/async_safe' is a subpackage
 
 		"note_memtag_heap_async", // jingwen@, b/185079815, features.h includes not found
 		"note_memtag_heap_sync",  // jingwen@, b/185079815, features.h includes not found
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 97eec30..31c31fb 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -67,11 +67,7 @@
 
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
-	GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
-
-	// GetPrebuiltCcStaticLibraryFiles returns paths to prebuilt cc static libraries, and whether the
-	// results were available
-	GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool)
+	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool)
 
 	// ** End cquery methods
 
@@ -89,16 +85,24 @@
 	BuildStatementsToRegister() []bazel.BuildStatement
 }
 
-// A context object which tracks queued requests that need to be made to Bazel,
-// and their results after the requests have been made.
-type bazelContext struct {
+type bazelRunner interface {
+	issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
+}
+
+type bazelPaths struct {
 	homeDir      string
 	bazelPath    string
 	outputBase   string
 	workspaceDir string
 	buildDir     string
 	metricsDir   string
+}
 
+// A context object which tracks queued requests that need to be made to Bazel,
+// and their results after the requests have been made.
+type bazelContext struct {
+	bazelRunner
+	paths        *bazelPaths
 	requests     map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
 	requestMutex sync.Mutex         // requests can be written in parallel
 
@@ -119,9 +123,8 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles                 map[string][]string
-	LabelToOutputFilesAndCcObjectFiles map[string]cquery.GetOutputFilesAndCcObjectFiles_Result
-	LabelToCcStaticLibraryFiles        map[string][]string
+	LabelToOutputFiles map[string][]string
+	LabelToCcInfo      map[string]cquery.CcInfo
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
@@ -129,13 +132,8 @@
 	return result, ok
 }
 
-func (m MockBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	result, ok := m.LabelToOutputFilesAndCcObjectFiles[label]
-	return result.OutputFiles, result.CcObjectFiles, ok
-}
-
-func (m MockBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := m.LabelToCcStaticLibraryFiles[label]
+func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool) {
+	result, ok := m.LabelToCcInfo[label]
 	return result, ok
 }
 
@@ -165,39 +163,21 @@
 	return ret, ok
 }
 
-func (bazelCtx *bazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	var outputFiles []string
-	var ccObjects []string
-
-	result, ok := bazelCtx.cquery(label, cquery.GetOutputFilesAndCcObjectFiles, archType)
-	if ok {
-		bazelOutput := strings.TrimSpace(result)
-		returnResult := cquery.GetOutputFilesAndCcObjectFiles.ParseResult(bazelOutput)
-		outputFiles = returnResult.OutputFiles
-		ccObjects = returnResult.CcObjectFiles
-	}
-
-	return outputFiles, ccObjects, ok
-}
-
-// GetPrebuiltCcStaticLibraryFiles returns a slice of prebuilt static libraries for the given
-// label/archType if there are query results; otherwise, it enqueues the query and returns false.
-func (bazelCtx *bazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := bazelCtx.cquery(label, cquery.GetPrebuiltCcStaticLibraryFiles, archType)
+func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool) {
+	result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
 	if !ok {
-		return nil, false
+		return cquery.CcInfo{}, ok
 	}
 
 	bazelOutput := strings.TrimSpace(result)
-	ret := cquery.GetPrebuiltCcStaticLibraryFiles.ParseResult(bazelOutput)
-	return ret, ok
+	return cquery.GetCcInfo.ParseResult(bazelOutput), ok
 }
 
 func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
+func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool) {
 	panic("unimplemented")
 }
 
@@ -228,42 +208,56 @@
 		return noopBazelContext{}, nil
 	}
 
-	bazelCtx := bazelContext{buildDir: c.buildDir, requests: make(map[cqueryKey]bool)}
+	p, err := bazelPathsFromConfig(c)
+	if err != nil {
+		return nil, err
+	}
+	return &bazelContext{
+		bazelRunner: &builtinBazelRunner{},
+		paths:       p,
+		requests:    make(map[cqueryKey]bool),
+	}, nil
+}
+
+func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
+	p := bazelPaths{
+		buildDir: c.buildDir,
+	}
 	missingEnvVars := []string{}
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
-		bazelCtx.homeDir = c.Getenv("BAZEL_HOME")
+		p.homeDir = c.Getenv("BAZEL_HOME")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
 	}
 	if len(c.Getenv("BAZEL_PATH")) > 1 {
-		bazelCtx.bazelPath = c.Getenv("BAZEL_PATH")
+		p.bazelPath = c.Getenv("BAZEL_PATH")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
 	}
 	if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
-		bazelCtx.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
+		p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
 	}
 	if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
-		bazelCtx.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
+		p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
 	}
 	if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
-		bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
+		p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
 	} else {
 		missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
 	}
 	if len(missingEnvVars) > 0 {
 		return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
 	} else {
-		return &bazelCtx, nil
+		return &p, nil
 	}
 }
 
-func (context *bazelContext) BazelMetricsDir() string {
-	return context.metricsDir
+func (p *bazelPaths) BazelMetricsDir() string {
+	return p.metricsDir
 }
 
 func (context *bazelContext) BazelEnabled() bool {
@@ -296,17 +290,40 @@
 	return ""
 }
 
+type bazelCommand struct {
+	command string
+	// query or label
+	expression string
+}
+
+type mockBazelRunner struct {
+	bazelCommandResults map[bazelCommand]string
+	commands            []bazelCommand
+}
+
+func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
+	runName bazel.RunName,
+	command bazelCommand,
+	extraFlags ...string) (string, string, error) {
+	r.commands = append(r.commands, command)
+	if ret, ok := r.bazelCommandResults[command]; ok {
+		return ret, "", nil
+	}
+	return "", "", nil
+}
+
+type builtinBazelRunner struct{}
+
 // Issues the given bazel command with given build label and additional flags.
 // Returns (stdout, stderr, error). The first and second return values are strings
 // containing the stdout and stderr of the run command, and an error is returned if
 // the invocation returned an error code.
-func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
+func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
-
-	cmdFlags := []string{"--output_base=" + context.outputBase, command}
-	cmdFlags = append(cmdFlags, labels...)
-	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
-	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
+	cmdFlags := []string{"--output_base=" + paths.outputBase, command.command}
+	cmdFlags = append(cmdFlags, command.expression)
+	cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+paths.intermediatesDir())
+	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
 
 	// Set default platforms to canonicalized values for mixed builds requests.
 	// If these are set in the bazelrc, they will have values that are
@@ -328,9 +345,9 @@
 	cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
 	cmdFlags = append(cmdFlags, extraFlags...)
 
-	bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
-	bazelCmd.Dir = context.workspaceDir
-	bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix(),
+	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
+	bazelCmd.Dir = paths.workspaceDir
+	bazelCmd.Env = append(os.Environ(), "HOME="+paths.homeDir, pwdPrefix(),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
@@ -367,7 +384,7 @@
     path = "%[1]s/build/bazel/bazel_skylib",
 )
 `
-	return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+	return []byte(fmt.Sprintf(formatString, context.paths.workspaceDir))
 }
 
 func (context *bazelContext) mainBzlFileContents() []byte {
@@ -577,8 +594,8 @@
 
 // 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")
+func (p *bazelPaths) intermediatesDir() string {
+	return filepath.Join(p.buildDir, "bazel")
 }
 
 // Issues commands to Bazel to receive results for all cquery requests
@@ -590,7 +607,7 @@
 	var cqueryErr string
 	var err error
 
-	intermediatesDirPath := absolutePath(context.intermediatesDir())
+	intermediatesDirPath := absolutePath(context.paths.intermediatesDir())
 	if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
 		err = os.Mkdir(intermediatesDirPath, 0777)
 	}
@@ -599,38 +616,38 @@
 		return err
 	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
+		filepath.Join(intermediatesDirPath, "main.bzl"),
 		context.mainBzlFileContents(), 0666)
 	if err != nil {
 		return err
 	}
 	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
+		filepath.Join(intermediatesDirPath, "BUILD.bazel"),
 		context.mainBuildFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
+	cqueryFileRelpath := filepath.Join(context.paths.intermediatesDir(), "buildroot.cquery")
 	err = ioutil.WriteFile(
 		absolutePath(cqueryFileRelpath),
 		context.cqueryStarlarkFileContents(), 0666)
 	if err != nil {
 		return err
 	}
-	workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
 	err = ioutil.WriteFile(
-		absolutePath(workspaceFileRelpath),
+		filepath.Join(intermediatesDirPath, "WORKSPACE.bazel"),
 		context.workspaceFileContents(), 0666)
 	if err != nil {
 		return err
 	}
 	buildrootLabel := "//:buildroot"
-	cqueryOutput, cqueryErr, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
-		[]string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
+	cqueryOutput, cqueryErr, err = context.issueBazelCommand(
+		context.paths,
+		bazel.CqueryBuildRootRunName,
+		bazelCommand{"cquery", fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
 		"--output=starlark",
 		"--starlark:file="+cqueryFileRelpath)
-	err = ioutil.WriteFile(
-		absolutePath(filepath.Join(context.intermediatesDir(), "cquery.out")),
+	err = ioutil.WriteFile(filepath.Join(intermediatesDirPath, "cquery.out"),
 		[]byte(cqueryOutput), 0666)
 	if err != nil {
 		return err
@@ -661,11 +678,13 @@
 	//
 	// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
 	var aqueryOutput string
-	aqueryOutput, _, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
-		[]string{fmt.Sprintf("deps(%s)", buildrootLabel),
-			// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
-			// proto sources, which would add a number of unnecessary dependencies.
-			"--output=jsonproto"})
+	aqueryOutput, _, err = context.issueBazelCommand(
+		context.paths,
+		bazel.AqueryBuildRootRunName,
+		bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
+		// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+		// proto sources, which would add a number of unnecessary dependencies.
+		"--output=jsonproto")
 
 	if err != nil {
 		return err
@@ -679,8 +698,10 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
-		[]string{"//:phonyroot"})
+	_, _, err = context.issueBazelCommand(
+		context.paths,
+		bazel.BazelBuildPhonyRootRunName,
+		bazelCommand{"build", "//:phonyroot"})
 
 	if err != nil {
 		return err
@@ -696,7 +717,7 @@
 }
 
 func (context *bazelContext) OutputBase() string {
-	return context.outputBase
+	return context.paths.outputBase
 }
 
 // Singleton used for registering BUILD file ninja dependencies (needed
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
new file mode 100644
index 0000000..85f701f
--- /dev/null
+++ b/android/bazel_handler_test.go
@@ -0,0 +1,118 @@
+package android
+
+import (
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func TestRequestResultsAfterInvokeBazel(t *testing.T) {
+	label := "//foo:bar"
+	arch := Arm64
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+		bazelCommand{command: "cquery", expression: "kind(rule, deps(//:buildroot))"}: `@sourceroot//foo:bar|arm64>>out/foo/bar.txt`,
+	})
+	g, ok := bazelContext.GetOutputFiles(label, arch)
+	if ok {
+		t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
+	}
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+	g, ok = bazelContext.GetOutputFiles(label, arch)
+	if !ok {
+		t.Errorf("Expected cquery results after running InvokeBazel(), but got none")
+	} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+		t.Errorf("Expected output %s, got %s", w, g)
+	}
+}
+
+func TestInvokeBazelWritesBazelFiles(t *testing.T) {
+	bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "main.bzl")); os.IsNotExist(err) {
+		t.Errorf("Expected main.bzl to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating main.bzl %s", err)
+	}
+
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "BUILD.bazel")); os.IsNotExist(err) {
+		t.Errorf("Expected BUILD.bazel to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating BUILD.bazel %s", err)
+	}
+
+	if _, err := os.Stat(filepath.Join(baseDir, "bazel", "WORKSPACE.bazel")); os.IsNotExist(err) {
+		t.Errorf("Expected WORKSPACE.bazel to exist, but it does not")
+	} else if err != nil {
+		t.Errorf("Unexpected error stating WORKSPACE.bazel %s", err)
+	}
+}
+
+func TestInvokeBazelPopulatesBuildStatements(t *testing.T) {
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
+		bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}: `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "x",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [1],
+    "primaryOutputId": 1
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "two"
+  }]
+}`,
+	})
+	err := bazelContext.InvokeBazel()
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+	}
+
+	got := bazelContext.BuildStatementsToRegister()
+	if want := 1; len(got) != want {
+		t.Errorf("Expected %d registered build statements, got %#v", want, got)
+	}
+}
+
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+	t.Helper()
+	p := bazelPaths{
+		buildDir:     t.TempDir(),
+		outputBase:   "outputbase",
+		workspaceDir: "workspace_dir",
+	}
+	aqueryCommand := bazelCommand{command: "aquery", expression: "deps(//:buildroot)"}
+	if _, exists := bazelCommandResults[aqueryCommand]; !exists {
+		bazelCommandResults[aqueryCommand] = "{}\n"
+	}
+	runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
+	return &bazelContext{
+		bazelRunner: runner,
+		paths:       &p,
+		requests:    map[cqueryKey]bool{},
+	}, p.buildDir
+}
diff --git a/android/paths.go b/android/paths.go
index 37b04dd..df12228 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1101,11 +1101,12 @@
 		// a single file.
 		files, err = gctx.GlobWithDeps(path.String(), nil)
 	} else {
-		var deps []string
+		var result pathtools.GlobResult
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
-		ctx.AddNinjaFileDeps(deps...)
+		result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
+		ctx.AddNinjaFileDeps(result.Deps...)
+		files = result.Matches
 	}
 
 	if err != nil {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index b88da2f..7bd12a8 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -5,14 +5,14 @@
 )
 
 var (
-	GetOutputFiles                  = &getOutputFilesRequestType{}
-	GetOutputFilesAndCcObjectFiles  = &getOutputFilesAndCcObjectFilesType{}
-	GetPrebuiltCcStaticLibraryFiles = &getPrebuiltCcStaticLibraryFiles{}
+	GetOutputFiles = &getOutputFilesRequestType{}
+	GetCcInfo      = &getCcInfoType{}
 )
 
-type GetOutputFilesAndCcObjectFiles_Result struct {
-	OutputFiles   []string
-	CcObjectFiles []string
+type CcInfo struct {
+	OutputFiles          []string
+	CcObjectFiles        []string
+	CcStaticLibraryFiles []string
 }
 
 type getOutputFilesRequestType struct{}
@@ -42,12 +42,12 @@
 	return strings.Split(rawString, ", ")
 }
 
-type getOutputFilesAndCcObjectFilesType struct{}
+type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
-func (g getOutputFilesAndCcObjectFilesType) Name() string {
-	return "getOutputFilesAndCcObjectFiles"
+func (g getCcInfoType) Name() string {
+	return "getCcInfo"
 }
 
 // StarlarkFunctionBody returns a starlark function body to process this request type.
@@ -58,61 +58,49 @@
 //   - `target` is the only parameter to this function (a configured target).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
-func (g getOutputFilesAndCcObjectFilesType) StarlarkFunctionBody() string {
+func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
 
 ccObjectFiles = []
+staticLibraries = []
 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
   for library in linker_input.libraries:
     for object in library.objects:
       ccObjectFiles += [object.path]
-return ', '.join(outputFiles) + "|" + ', '.join(ccObjectFiles)`
+    if library.static_library:
+      staticLibraries.append(library.static_library.path)
+
+returns = [
+  outputFiles,
+  staticLibraries,
+  ccObjectFiles,
+]
+
+return "|".join([", ".join(r) for r in returns])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
-func (g getOutputFilesAndCcObjectFilesType) ParseResult(rawString string) GetOutputFilesAndCcObjectFiles_Result {
+func (g getCcInfoType) ParseResult(rawString string) CcInfo {
 	var outputFiles []string
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
 	outputFilesString := splitString[0]
-	ccObjectsString := splitString[1]
+	ccStaticLibrariesString := splitString[1]
+	ccObjectsString := splitString[2]
 	outputFiles = splitOrEmpty(outputFilesString, ", ")
+	ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
 	ccObjects = splitOrEmpty(ccObjectsString, ", ")
-	return GetOutputFilesAndCcObjectFiles_Result{outputFiles, ccObjects}
-}
-
-type getPrebuiltCcStaticLibraryFiles struct{}
-
-// Name returns the name of the starlark function to get prebuilt cc static library files
-func (g getPrebuiltCcStaticLibraryFiles) Name() string {
-	return "getPrebuiltCcStaticLibraryFiles"
-}
-
-// StarlarkFunctionBody returns the unindented body of a starlark function for extracting the static
-// library paths from a cc_import module.
-func (g getPrebuiltCcStaticLibraryFiles) StarlarkFunctionBody() string {
-	return `
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
-
-static_libraries = []
-for linker_input in linker_inputs:
-  for library in linker_input.libraries:
-    static_libraries.append(library.static_library.path)
-
-return ', '.join(static_libraries)`
-}
-
-// ParseResult returns a slice of bazel output paths to static libraries if any exist for the given
-// rawString corresponding to the string output which was created by evaluating the
-// StarlarkFunctionBody.
-func (g getPrebuiltCcStaticLibraryFiles) ParseResult(rawString string) []string {
-	return strings.Split(rawString, ", ")
+	return CcInfo{
+		OutputFiles:          outputFiles,
+		CcObjectFiles:        ccObjects,
+		CcStaticLibraryFiles: ccStaticLibraries,
+	}
 }
 
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
diff --git a/cc/library.go b/cc/library.go
index 738b45f..cb0aebf 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -483,7 +483,9 @@
 
 func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	outputPaths, objPaths, ok := bazelCtx.GetOutputFilesAndCcObjectFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	outputPaths := ccInfo.OutputFiles
+	objPaths := ccInfo.CcObjectFiles
 	if !ok {
 		return ok
 	}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 3829b1e..7857432 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -329,7 +329,8 @@
 
 func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	staticLibs, ok := bazelCtx.GetPrebuiltCcStaticLibraryFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	staticLibs := ccInfo.CcStaticLibraryFiles
 	if !ok {
 		return false
 	}
diff --git a/zip/zip.go b/zip/zip.go
index a6490d4..84e974b 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -292,11 +292,11 @@
 				continue
 			}
 
-			globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+			result, err := z.fs.Glob(s, nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			if len(globbed) == 0 {
+			if len(result.Matches) == 0 {
 				err := &os.PathError{
 					Op:   "lstat",
 					Path: s,
@@ -308,7 +308,7 @@
 					return err
 				}
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		if fa.GlobDir != "" {
 			if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
@@ -336,11 +336,11 @@
 					return err
 				}
 			}
-			globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+			result, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		for _, src := range srcs {
 			err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)