From edd16668c646921b190c7cac3275dc181cc1a1e8 Mon Sep 17 00:00:00 2001 From: Sasha Smundak Date: Fri, 7 Oct 2022 14:44:50 -0700 Subject: [PATCH] Add CcUnstrippedInfo provider and use it in mixed builds The build uses unstripped binary/shared library to extract function signatures, so for each each target of this kind Bazel should return its unstripped version, too. Fixes: 220164721 Test: treehugger Change-Id: Id5f6143340519bf2ae98791a9e981d1306bb08d1 --- android/bazel_handler.go | 22 ++++++++++++ bazel/cquery/request_type.go | 59 ++++++++++++++++++++++++++++--- bazel/cquery/request_type_test.go | 28 +++++++++++++++ cc/binary.go | 13 +++---- cc/binary_test.go | 10 ++++-- cc/library.go | 2 +- cc/library_test.go | 2 ++ 7 files changed, 118 insertions(+), 18 deletions(-) diff --git a/android/bazel_handler.go b/android/bazel_handler.go index e81086d7d..47ed7dd75 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -144,6 +144,9 @@ type BazelContext interface { // Returns the results of the GetApexInfo query (including output files) GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error) + // Returns the results of the GetCcUnstrippedInfo query + GetCcUnstrippedInfo(label string, cfgkey configKey) (cquery.CcUnstrippedInfo, error) + // ** end Cquery Results Retrieval Functions // Issues commands to Bazel to receive results for all cquery requests @@ -223,6 +226,7 @@ type MockBazelContext struct { LabelToCcInfo map[string]cquery.CcInfo LabelToPythonBinary map[string]string LabelToApexInfo map[string]cquery.ApexCqueryInfo + LabelToCcBinary map[string]cquery.CcUnstrippedInfo } func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { @@ -248,6 +252,11 @@ func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryI panic("unimplemented") } +func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) { + result, _ := m.LabelToCcBinary[label] + return result, nil +} + func (m MockBazelContext) InvokeBazel(_ Config) error { panic("unimplemented") } @@ -311,6 +320,14 @@ func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquer return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key) } +func (bazelCtx *bazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) { + key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey) + if rawString, ok := bazelCtx.results[key]; ok { + return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString)), nil + } + return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key) +} + func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) { panic("unimplemented") } @@ -331,6 +348,11 @@ func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryI panic("unimplemented") } +func (n noopBazelContext) GetCcUnstrippedInfo(_ string, _ configKey) (cquery.CcUnstrippedInfo, error) { + //TODO implement me + panic("implement me") +} + func (n noopBazelContext) InvokeBazel(_ Config) error { panic("unimplemented") } diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index e35b531eb..fcc2f07b1 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -7,10 +7,11 @@ import ( ) var ( - GetOutputFiles = &getOutputFilesRequestType{} - GetPythonBinary = &getPythonBinaryRequestType{} - GetCcInfo = &getCcInfoType{} - GetApexInfo = &getApexInfoType{} + GetOutputFiles = &getOutputFilesRequestType{} + GetPythonBinary = &getPythonBinaryRequestType{} + GetCcInfo = &getCcInfoType{} + GetApexInfo = &getApexInfoType{} + GetCcUnstrippedInfo = &getCcUnstippedInfoType{} ) type CcInfo struct { @@ -30,6 +31,7 @@ type CcInfo struct { // but general cc_library will also have dynamic libraries in output files). RootDynamicLibraries []string TocFile string + UnstrippedOutput string } type getOutputFilesRequestType struct{} @@ -135,12 +137,17 @@ sharedLibraries = [] rootSharedLibraries = [] shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo" +unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo" +unstripped = "" if shared_info_tag in providers(target): shared_info = providers(target)[shared_info_tag] path = shared_info.output_file.path sharedLibraries.append(path) rootSharedLibraries += [path] + unstripped = path + if unstripped_tag in providers(target): + unstripped = providers(target)[unstripped_tag].unstripped.path else: for linker_input in linker_inputs: for library in linker_input.libraries: @@ -168,7 +175,8 @@ return json_encode({ "Headers": headers, "RootStaticArchives": rootStaticArchives, "RootDynamicLibraries": rootSharedLibraries, - "TocFile": toc_file + "TocFile": toc_file, + "UnstrippedOutput": unstripped, })` } @@ -237,6 +245,47 @@ func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo { return info } +// getCcUnstrippedInfoType implements cqueryRequest interface. It handles the +// interaction with `bazel cquery` to retrieve CcUnstrippedInfo provided +// by the` cc_binary` and `cc_shared_library` rules. +type getCcUnstippedInfoType struct{} + +func (g getCcUnstippedInfoType) Name() string { + return "getCcUnstrippedInfo" +} + +func (g getCcUnstippedInfoType) StarlarkFunctionBody() string { + return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo" +p = providers(target) +output_path = target.files.to_list()[0].path +unstripped = output_path +if unstripped_tag in p: + unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path +return json_encode({ + "OutputFile": output_path, + "UnstrippedOutput": unstripped, +}) +` +} + +// 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 getCcUnstippedInfoType) ParseResult(rawString string) CcUnstrippedInfo { + var info CcUnstrippedInfo + decoder := json.NewDecoder(strings.NewReader(rawString)) + decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests + if err := decoder.Decode(&info); err != nil { + panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err)) + } + return info +} + +type CcUnstrippedInfo struct { + OutputFile string + UnstrippedOutput string +} + // splitOrEmpty is a modification of strings.Split() that returns an empty list // if the given string is empty. func splitOrEmpty(s string, sep string) []string { diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index afe478ba0..0f51cc040 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -165,3 +165,31 @@ func TestGetApexInfoParseResults(t *testing.T) { } } } + +func TestGetCcUnstrippedParseResults(t *testing.T) { + testCases := []struct { + description string + input string + expectedOutput CcUnstrippedInfo + }{ + { + description: "no result", + input: "{}", + expectedOutput: CcUnstrippedInfo{}, + }, + { + description: "one result", + input: `{"OutputFile":"myapp", "UnstrippedOutput":"myapp_unstripped"}`, + expectedOutput: CcUnstrippedInfo{ + OutputFile: "myapp", + UnstrippedOutput: "myapp_unstripped", + }, + }, + } + for _, tc := range testCases { + actualOutput := GetCcUnstrippedInfo.ParseResult(tc.input) + if !reflect.DeepEqual(tc.expectedOutput, actualOutput) { + t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput) + } + } +} diff --git a/cc/binary.go b/cc/binary.go index 69cf4ac38..a6d750726 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -577,25 +577,20 @@ var _ BazelHandler = (*ccBinaryBazelHandler)(nil) func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) { bazelCtx := ctx.Config().BazelContext - bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx)) + bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx)) } func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) { bazelCtx := ctx.Config().BazelContext - filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx)) + info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx)) if err != nil { ctx.ModuleErrorf(err.Error()) return } - if len(filePaths) != 1 { - ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths) - return - } - outputFilePath := android.PathForBazelOut(ctx, filePaths[0]) + outputFilePath := android.PathForBazelOut(ctx, info.OutputFile) handler.module.outputFile = android.OptionalPathForPath(outputFilePath) - // TODO(b/220164721): We need to decide if we should return the stripped as the unstripped. - handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath + handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput) } func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes { diff --git a/cc/binary_test.go b/cc/binary_test.go index cba5974fb..db6fb3a3e 100644 --- a/cc/binary_test.go +++ b/cc/binary_test.go @@ -15,6 +15,7 @@ package cc import ( + "android/soong/bazel/cquery" "testing" "android/soong/android" @@ -30,8 +31,11 @@ cc_binary { config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) config.BazelContext = android.MockBazelContext{ OutputBaseDir: "outputbase", - LabelToOutputFiles: map[string][]string{ - "//foo/bar:bar": []string{"foo"}, + LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{ + "//foo/bar:bar": cquery.CcUnstrippedInfo{ + OutputFile: "foo", + UnstrippedOutput: "foo.unstripped", + }, }, } ctx := testCcWithConfig(t, config) @@ -46,7 +50,7 @@ cc_binary { android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile() - expectedUnStrippedFile := "outputbase/execroot/__main__/foo" + expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped" android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String()) } diff --git a/cc/library.go b/cc/library.go index 77f686e04..a590b223b 100644 --- a/cc/library.go +++ b/cc/library.go @@ -885,7 +885,7 @@ func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx androi outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0]) handler.module.outputFile = android.OptionalPathForPath(outputFilePath) - handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath + handler.module.linker.(*libraryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, ccInfo.UnstrippedOutput) var tocFile android.OptionalPath if len(ccInfo.TocFile) > 0 { diff --git a/cc/library_test.go b/cc/library_test.go index 6d5eda2d8..2bc99677e 100644 --- a/cc/library_test.go +++ b/cc/library_test.go @@ -259,6 +259,7 @@ cc_library { SystemIncludes: []string{"system_include"}, Headers: []string{"foo.h"}, RootDynamicLibraries: []string{"foo.so"}, + UnstrippedOutput: "foo_unstripped.so", }, "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{ CcObjectFiles: []string{"foo.o"}, @@ -294,6 +295,7 @@ cc_library { expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"} android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) + android.AssertStringEquals(t, "unstripped shared library", "outputbase/execroot/__main__/foo_unstripped.so", sharedFoo.(*Module).linker.unstrippedOutputFilePath().String()) flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo) android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs) android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)