From 808d84c45d8504dddbbe58b908a77924e1f2fca5 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Tue, 9 Mar 2021 20:43:32 -0500 Subject: [PATCH] mixed builds for cc_static_library without deps Test: Manual mixed builds testing of `libc` target with manually migrated "libc_nopthread" and "libc_init_dynamic". Change-Id: If7d67e95eca9899271b1eeb662c7c2e571f64afa --- android/bazel_handler.go | 74 ++++++++++++++++++++++++++++++++++------ cc/library.go | 44 ++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 10 deletions(-) diff --git a/android/bazel_handler.go b/android/bazel_handler.go index 9cd9fadfe..667584072 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -37,6 +37,7 @@ type CqueryRequestType int const ( getAllFiles CqueryRequestType = iota getCcObjectFiles + getAllFilesAndCcObjectFiles ) // Map key to describe bazel cquery requests. @@ -58,7 +59,9 @@ type BazelContext interface { // Retrieves these files from Bazel's CcInfo provider. GetCcObjectFiles(label string, archType ArchType) ([]string, bool) - // TODO(cparsons): Other cquery-related methods should be added here. + // Returns the results of GetAllFiles and GetCcObjectFiles in a single query (in that order). + GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) + // ** End cquery methods // Issues commands to Bazel to receive results for all cquery requests @@ -116,6 +119,11 @@ func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]s return result, ok } +func (m MockBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) { + result, ok := m.AllFiles[label] + return result, result, ok +} + func (m MockBazelContext) InvokeBazel() error { panic("unimplemented") } @@ -154,6 +162,22 @@ func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) } } +func (bazelCtx *bazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) { + var allFiles []string + var ccObjects []string + + result, ok := bazelCtx.cquery(label, getAllFilesAndCcObjectFiles, archType) + if ok { + bazelOutput := strings.TrimSpace(result) + splitString := strings.Split(bazelOutput, "|") + allFilesString := splitString[0] + ccObjectsString := splitString[1] + allFiles = strings.Split(allFilesString, ", ") + ccObjects = strings.Split(ccObjectsString, ", ") + } + return allFiles, ccObjects, ok +} + func (n noopBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) { panic("unimplemented") } @@ -162,6 +186,10 @@ func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]s panic("unimplemented") } +func (n noopBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) { + panic("unimplemented") +} + func (n noopBazelContext) InvokeBazel() error { panic("unimplemented") } @@ -253,8 +281,12 @@ func pwdPrefix() string { return "" } +// 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, - extraFlags ...string) (string, error) { + extraFlags ...string) (string, string, error) { cmdFlags := []string{"--output_base=" + context.outputBase, command} cmdFlags = append(cmdFlags, labels...) @@ -281,9 +313,10 @@ func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command st bazelCmd.Stderr = stderr if output, err := bazelCmd.Output(); err != nil { - return "", fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr) + return "", string(stderr.Bytes()), + fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr) } else { - return string(output), nil + return string(output), string(stderr.Bytes()), nil } } @@ -452,6 +485,11 @@ phony_root(name = "phonyroot", strings.Join(deps_arm, ",\n "))) } +// Returns the file contents of the buildroot.cquery file that should be used for the cquery +// expression in order to obtain information about buildroot and its dependencies. +// The contents of this file depend on the bazelContext's requests; requests are enumerated +// and grouped by their request type. The data retrieved for each label depends on its +// request type. func (context *bazelContext) cqueryStarlarkFileContents() []byte { formatString := ` # This file is generated by soong_build. Do not edit. @@ -463,6 +501,13 @@ getCcObjectFilesLabels = { %s } +getAllFilesAndCcObjectFilesLabels = { + %s +} + +def get_all_files(target): + return [f.path for f in target.files.to_list()] + def get_cc_object_files(target): result = [] linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list() @@ -492,9 +537,11 @@ def get_arch(target): def format(target): id_string = str(target.label) + "|" + get_arch(target) if id_string in getAllFilesLabels: - return id_string + ">>" + ', '.join([f.path for f in target.files.to_list()]) + return id_string + ">>" + ', '.join(get_all_files(target)) elif id_string in getCcObjectFilesLabels: return id_string + ">>" + ', '.join(get_cc_object_files(target)) + elif id_string in getAllFilesAndCcObjectFilesLabels: + return id_string + ">>" + ', '.join(get_all_files(target)) + "|" + ', '.join(get_cc_object_files(target)) else: # This target was not requested via cquery, and thus must be a dependency # of a requested target. @@ -502,6 +549,7 @@ def format(target): ` var getAllFilesDeps []string = nil var getCcObjectFilesDeps []string = nil + var getAllFilesAndCcObjectFilesDeps []string = nil for val, _ := range context.requests { labelWithArch := getCqueryId(val) @@ -511,12 +559,16 @@ def format(target): getAllFilesDeps = append(getAllFilesDeps, mapEntryString) case getCcObjectFiles: getCcObjectFilesDeps = append(getCcObjectFilesDeps, mapEntryString) + case getAllFilesAndCcObjectFiles: + getAllFilesAndCcObjectFilesDeps = append(getAllFilesAndCcObjectFilesDeps, mapEntryString) } } getAllFilesDepsString := strings.Join(getAllFilesDeps, ",\n ") getCcObjectFilesDepsString := strings.Join(getCcObjectFilesDeps, ",\n ") + getAllFilesAndCcObjectFilesDepsString := strings.Join(getAllFilesAndCcObjectFilesDeps, ",\n ") - return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString)) + return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString, + getAllFilesAndCcObjectFilesDepsString)) } // Returns a workspace-relative path containing build-related metadata required @@ -531,6 +583,7 @@ func (context *bazelContext) InvokeBazel() error { context.results = make(map[cqueryKey]string) var cqueryOutput string + var cqueryErr string var err error intermediatesDirPath := absolutePath(context.intermediatesDir()) @@ -568,7 +621,7 @@ func (context *bazelContext) InvokeBazel() error { return err } buildrootLabel := "//:buildroot" - cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery", + cqueryOutput, cqueryErr, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery", []string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)}, "--output=starlark", "--starlark:file="+cqueryFileRelpath) @@ -595,7 +648,8 @@ func (context *bazelContext) InvokeBazel() error { if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok { context.results[val] = string(cqueryResult) } else { - return fmt.Errorf("missing result for bazel target %s. query output: [%s]", getCqueryId(val), cqueryOutput) + return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]", + getCqueryId(val), cqueryOutput, cqueryErr) } } @@ -603,7 +657,7 @@ func (context *bazelContext) InvokeBazel() error { // // TODO(cparsons): Use --target_pattern_file to avoid command line limits. var aqueryOutput string - aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery", + 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. @@ -621,7 +675,7 @@ func (context *bazelContext) InvokeBazel() error { // 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", + _, _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build", []string{"//:phonyroot"}) if err != nil { diff --git a/cc/library.go b/cc/library.go index 0e6e10764..6a3b87693 100644 --- a/cc/library.go +++ b/cc/library.go @@ -230,6 +230,7 @@ func LibraryStaticFactory() android.Module { module, library := NewLibrary(android.HostAndDeviceSupported) library.BuildOnlyStatic() module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType} + module.bazelHandler = &staticLibraryBazelHandler{module: module} return module.Init() } @@ -406,6 +407,49 @@ type libraryDecorator struct { collectedSnapshotHeaders android.Paths } +type staticLibraryBazelHandler struct { + bazelHandler + + module *Module +} + +func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { + bazelCtx := ctx.Config().BazelContext + outputPaths, objPaths, ok := bazelCtx.GetAllFilesAndCcObjectFiles(label, ctx.Arch().ArchType) + if ok { + if len(outputPaths) != 1 { + // TODO(cparsons): This is actually expected behavior for static libraries with no srcs. + // We should support this. + ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, objPaths) + return false + } + outputFilePath := android.PathForBazelOut(ctx, outputPaths[0]) + handler.module.outputFile = android.OptionalPathForPath(outputFilePath) + + objFiles := make(android.Paths, len(objPaths)) + for i, objPath := range objPaths { + objFiles[i] = android.PathForBazelOut(ctx, objPath) + } + objects := Objects{ + objFiles: objFiles, + } + + ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{ + StaticLibrary: outputFilePath, + ReuseObjects: objects, + Objects: objects, + + // TODO(cparsons): Include transitive static libraries in this provider to support + // static libraries with deps. + TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL). + Direct(outputFilePath). + Build(), + }) + handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0])) + } + return ok +} + // collectHeadersForSnapshot collects all exported headers from library. // It globs header files in the source tree for exported include directories, // and tracks generated header files separately.