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
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index e81086d..47ed7dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -144,6 +144,9 @@
 	// 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 e35b531..fcc2f07 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -7,10 +7,11 @@
 )
 
 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 @@
 	// 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 @@
 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 @@
 	"Headers": headers,
 	"RootStaticArchives": rootStaticArchives,
 	"RootDynamicLibraries": rootSharedLibraries,
-	"TocFile": toc_file
+	"TocFile": toc_file,
+	"UnstrippedOutput": unstripped,
 })`
 
 }
@@ -237,6 +245,47 @@
 	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 afe478b..0f51cc0 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -165,3 +165,31 @@
 		}
 	}
 }
+
+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 69cf4ac..a6d7507 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -577,25 +577,20 @@
 
 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 cba5974..db6fb3a 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 @@
 	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 @@
 	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 77f686e..a590b22 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -885,7 +885,7 @@
 	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 6d5eda2..2bc9967 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -259,6 +259,7 @@
 				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 @@
 	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)