1package cquery 2 3import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7) 8 9var ( 10 GetOutputFiles = &getOutputFilesRequestType{} 11 GetPythonBinary = &getPythonBinaryRequestType{} 12 GetCcInfo = &getCcInfoType{} 13 GetApexInfo = &getApexInfoType{} 14 GetCcUnstrippedInfo = &getCcUnstrippedInfoType{} 15) 16 17type CcAndroidMkInfo struct { 18 LocalStaticLibs []string 19 LocalWholeStaticLibs []string 20 LocalSharedLibs []string 21} 22 23type CcInfo struct { 24 CcAndroidMkInfo 25 OutputFiles []string 26 CcObjectFiles []string 27 CcSharedLibraryFiles []string 28 CcStaticLibraryFiles []string 29 Includes []string 30 SystemIncludes []string 31 Headers []string 32 // Archives owned by the current target (not by its dependencies). These will 33 // be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles, 34 // but general cc_library will also have dynamic libraries in output files). 35 RootStaticArchives []string 36 // Dynamic libraries (.so files) created by the current target. These will 37 // be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles, 38 // but general cc_library will also have dynamic libraries in output files). 39 RootDynamicLibraries []string 40 TidyFiles []string 41 TocFile string 42 UnstrippedOutput string 43 AbiDiffFiles []string 44} 45 46type getOutputFilesRequestType struct{} 47 48type getPythonBinaryRequestType struct{} 49 50// Name returns a string name for this request type. Such request type names must be unique, 51// and must only consist of alphanumeric characters. 52func (g getOutputFilesRequestType) Name() string { 53 return "getOutputFiles" 54} 55 56// StarlarkFunctionBody returns a starlark function body to process this request type. 57// The returned string is the body of a Starlark function which obtains 58// all request-relevant information about a target and returns a string containing 59// this information. 60// The function should have the following properties: 61// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). 62// - The return value must be a string. 63// - The function body should not be indented outside of its own scope. 64func (g getOutputFilesRequestType) StarlarkFunctionBody() string { 65 return "return ', '.join([f.path for f in target.files.to_list()])" 66} 67 68// ParseResult returns a value obtained by parsing the result of the request's Starlark function. 69// The given rawString must correspond to the string output which was created by evaluating the 70// Starlark given in StarlarkFunctionBody. 71func (g getOutputFilesRequestType) ParseResult(rawString string) []string { 72 return splitOrEmpty(rawString, ", ") 73} 74 75// Name returns a string name for this request type. Such request type names must be unique, 76// and must only consist of alphanumeric characters. 77func (g getPythonBinaryRequestType) Name() string { 78 return "getPythonBinary" 79} 80 81// StarlarkFunctionBody returns a starlark function body to process this request type. 82// The returned string is the body of a Starlark function which obtains 83// all request-relevant information about a target and returns a string containing 84// this information. 85// The function should have the following properties: 86// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). 87// - The return value must be a string. 88// - The function body should not be indented outside of its own scope. 89func (g getPythonBinaryRequestType) StarlarkFunctionBody() string { 90 return "return providers(target)['FilesToRunProvider'].executable.path" 91} 92 93// ParseResult returns a value obtained by parsing the result of the request's Starlark function. 94// The given rawString must correspond to the string output which was created by evaluating the 95// Starlark given in StarlarkFunctionBody. 96func (g getPythonBinaryRequestType) ParseResult(rawString string) string { 97 return rawString 98} 99 100type getCcInfoType struct{} 101 102// Name returns a string name for this request type. Such request type names must be unique, 103// and must only consist of alphanumeric characters. 104func (g getCcInfoType) Name() string { 105 return "getCcInfo" 106} 107 108// StarlarkFunctionBody returns a starlark function body to process this request type. 109// The returned string is the body of a Starlark function which obtains 110// all request-relevant information about a target and returns a string containing 111// this information. 112// The function should have the following properties: 113// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). 114// - The return value must be a string. 115// - The function body should not be indented outside of its own scope. 116func (g getCcInfoType) StarlarkFunctionBody() string { 117 return ` 118outputFiles = [f.path for f in target.files.to_list()] 119p = providers(target) 120cc_info = p.get("CcInfo") 121if not cc_info: 122 fail("%s did not provide CcInfo" % id_string) 123 124includes = cc_info.compilation_context.includes.to_list() 125system_includes = cc_info.compilation_context.system_includes.to_list() 126headers = [f.path for f in cc_info.compilation_context.headers.to_list()] 127 128ccObjectFiles = [] 129staticLibraries = [] 130rootStaticArchives = [] 131linker_inputs = cc_info.linking_context.linker_inputs.to_list() 132 133static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo" 134if static_info_tag in p: 135 static_info = p[static_info_tag] 136 ccObjectFiles = [f.path for f in static_info.objects] 137 rootStaticArchives = [static_info.root_static_archive.path] 138else: 139 for linker_input in linker_inputs: 140 for library in linker_input.libraries: 141 for object in library.objects: 142 ccObjectFiles += [object.path] 143 if library.static_library: 144 staticLibraries.append(library.static_library.path) 145 if linker_input.owner == target.label: 146 rootStaticArchives.append(library.static_library.path) 147 148sharedLibraries = [] 149rootSharedLibraries = [] 150 151shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo" 152stubs_tag = "//build/bazel/rules/cc:cc_stub_library.bzl%CcStubInfo" 153unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo" 154unstripped = "" 155 156if shared_info_tag in p: 157 shared_info = p[shared_info_tag] 158 path = shared_info.output_file.path 159 sharedLibraries.append(path) 160 rootSharedLibraries += [path] 161 unstripped = path 162 if unstripped_tag in p: 163 unstripped = p[unstripped_tag].unstripped.path 164elif stubs_tag in p: 165 rootSharedLibraries.extend([f.path for f in target.files.to_list()]) 166else: 167 for linker_input in linker_inputs: 168 for library in linker_input.libraries: 169 if library.dynamic_library: 170 path = library.dynamic_library.path 171 sharedLibraries.append(path) 172 if linker_input.owner == target.label: 173 rootSharedLibraries.append(path) 174 175toc_file = "" 176toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo" 177if toc_file_tag in p: 178 toc_file = p[toc_file_tag].toc.path 179else: 180 # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization 181 pass 182 183tidy_files = [] 184clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo") 185if clang_tidy_info: 186 tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()] 187 188abi_diff_files = [] 189abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo") 190if abi_diff_info: 191 abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()] 192 193local_static_libs = [] 194local_whole_static_libs = [] 195local_shared_libs = [] 196androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo" 197if androidmk_tag in p: 198 androidmk_info = p[androidmk_tag] 199 local_static_libs = androidmk_info.local_static_libs 200 local_whole_static_libs = androidmk_info.local_whole_static_libs 201 local_shared_libs = androidmk_info.local_shared_libs 202 203return json.encode({ 204 "OutputFiles": outputFiles, 205 "CcObjectFiles": ccObjectFiles, 206 "CcSharedLibraryFiles": sharedLibraries, 207 "CcStaticLibraryFiles": staticLibraries, 208 "Includes": includes, 209 "SystemIncludes": system_includes, 210 "Headers": headers, 211 "RootStaticArchives": rootStaticArchives, 212 "RootDynamicLibraries": rootSharedLibraries, 213 "TidyFiles": [t for t in tidy_files], 214 "TocFile": toc_file, 215 "UnstrippedOutput": unstripped, 216 "AbiDiffFiles": abi_diff_files, 217 "LocalStaticLibs": [l for l in local_static_libs], 218 "LocalWholeStaticLibs": [l for l in local_whole_static_libs], 219 "LocalSharedLibs": [l for l in local_shared_libs], 220})` 221 222} 223 224// ParseResult returns a value obtained by parsing the result of the request's Starlark function. 225// The given rawString must correspond to the string output which was created by evaluating the 226// Starlark given in StarlarkFunctionBody. 227func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { 228 var ccInfo CcInfo 229 if err := parseJson(rawString, &ccInfo); err != nil { 230 return ccInfo, err 231 } 232 return ccInfo, nil 233} 234 235// Query Bazel for the artifacts generated by the apex modules. 236type getApexInfoType struct{} 237 238// Name returns a string name for this request type. Such request type names must be unique, 239// and must only consist of alphanumeric characters. 240func (g getApexInfoType) Name() string { 241 return "getApexInfo" 242} 243 244// StarlarkFunctionBody returns a starlark function body to process this request type. 245// The returned string is the body of a Starlark function which obtains 246// all request-relevant information about a target and returns a string containing 247// this information. The function should have the following properties: 248// - The arguments are `target` (a configured target) and `id_string` (the label + configuration). 249// - The return value must be a string. 250// - The function body should not be indented outside of its own scope. 251func (g getApexInfoType) StarlarkFunctionBody() string { 252 return ` 253info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexInfo") 254if not info: 255 fail("%s did not provide ApexInfo" % id_string) 256bundle_key_info = info.bundle_key_info 257container_key_info = info.container_key_info 258 259signed_compressed_output = "" # no .capex if the apex is not compressible, cannot be None as it needs to be json encoded. 260if info.signed_compressed_output: 261 signed_compressed_output = info.signed_compressed_output.path 262 263mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo") 264if not mk_info: 265 fail("%s did not provide ApexMkInfo" % id_string) 266 267tidy_files = [] 268clang_tidy_info = providers(target).get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo") 269if clang_tidy_info: 270 tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()] 271 272return json.encode({ 273 "signed_output": info.signed_output.path, 274 "signed_compressed_output": signed_compressed_output, 275 "unsigned_output": info.unsigned_output.path, 276 "provides_native_libs": [str(lib) for lib in info.provides_native_libs], 277 "requires_native_libs": [str(lib) for lib in info.requires_native_libs], 278 "bundle_key_info": [bundle_key_info.public_key.path, bundle_key_info.private_key.path], 279 "container_key_info": [container_key_info.pem.path, container_key_info.pk8.path, container_key_info.key_name], 280 "package_name": info.package_name, 281 "symbols_used_by_apex": info.symbols_used_by_apex.path, 282 "java_symbols_used_by_apex": info.java_symbols_used_by_apex.path, 283 "backing_libs": info.backing_libs.path, 284 "bundle_file": info.base_with_config_zip.path, 285 "installed_files": info.installed_files.path, 286 "make_modules_to_install": mk_info.make_modules_to_install, 287 "files_info": mk_info.files_info, 288 "tidy_files": [t for t in tidy_files], 289})` 290} 291 292type ApexInfo struct { 293 // From the ApexInfo provider 294 SignedOutput string `json:"signed_output"` 295 SignedCompressedOutput string `json:"signed_compressed_output"` 296 UnsignedOutput string `json:"unsigned_output"` 297 ProvidesLibs []string `json:"provides_native_libs"` 298 RequiresLibs []string `json:"requires_native_libs"` 299 BundleKeyInfo []string `json:"bundle_key_info"` 300 ContainerKeyInfo []string `json:"container_key_info"` 301 PackageName string `json:"package_name"` 302 SymbolsUsedByApex string `json:"symbols_used_by_apex"` 303 JavaSymbolsUsedByApex string `json:"java_symbols_used_by_apex"` 304 BackingLibs string `json:"backing_libs"` 305 BundleFile string `json:"bundle_file"` 306 InstalledFiles string `json:"installed_files"` 307 TidyFiles []string `json:"tidy_files"` 308 309 // From the ApexMkInfo provider 310 MakeModulesToInstall []string `json:"make_modules_to_install"` 311 PayloadFilesInfo []map[string]string `json:"files_info"` 312} 313 314// ParseResult returns a value obtained by parsing the result of the request's Starlark function. 315// The given rawString must correspond to the string output which was created by evaluating the 316// Starlark given in StarlarkFunctionBody. 317func (g getApexInfoType) ParseResult(rawString string) (ApexInfo, error) { 318 var info ApexInfo 319 err := parseJson(rawString, &info) 320 return info, err 321} 322 323// getCcUnstrippedInfoType implements cqueryRequest interface. It handles the 324// interaction with `bazel cquery` to retrieve CcUnstrippedInfo provided 325// by the` cc_binary` and `cc_shared_library` rules. 326type getCcUnstrippedInfoType struct{} 327 328func (g getCcUnstrippedInfoType) Name() string { 329 return "getCcUnstrippedInfo" 330} 331 332func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string { 333 return ` 334p = providers(target) 335output_path = target.files.to_list()[0].path 336 337unstripped = output_path 338unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo" 339if unstripped_tag in p: 340 unstripped_info = p[unstripped_tag] 341 unstripped = unstripped_info.unstripped[0].files.to_list()[0].path 342 343local_static_libs = [] 344local_whole_static_libs = [] 345local_shared_libs = [] 346androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo" 347if androidmk_tag in p: 348 androidmk_info = p[androidmk_tag] 349 local_static_libs = androidmk_info.local_static_libs 350 local_whole_static_libs = androidmk_info.local_whole_static_libs 351 local_shared_libs = androidmk_info.local_shared_libs 352 353tidy_files = [] 354clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo") 355if clang_tidy_info: 356 tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()] 357 358return json.encode({ 359 "OutputFile": output_path, 360 "UnstrippedOutput": unstripped, 361 "LocalStaticLibs": [l for l in local_static_libs], 362 "LocalWholeStaticLibs": [l for l in local_whole_static_libs], 363 "LocalSharedLibs": [l for l in local_shared_libs], 364 "TidyFiles": [t for t in tidy_files], 365}) 366` 367} 368 369// ParseResult returns a value obtained by parsing the result of the request's Starlark function. 370// The given rawString must correspond to the string output which was created by evaluating the 371// Starlark given in StarlarkFunctionBody. 372func (g getCcUnstrippedInfoType) ParseResult(rawString string) (CcUnstrippedInfo, error) { 373 var info CcUnstrippedInfo 374 err := parseJson(rawString, &info) 375 return info, err 376} 377 378type CcUnstrippedInfo struct { 379 CcAndroidMkInfo 380 OutputFile string 381 UnstrippedOutput string 382 TidyFiles []string 383} 384 385// splitOrEmpty is a modification of strings.Split() that returns an empty list 386// if the given string is empty. 387func splitOrEmpty(s string, sep string) []string { 388 if len(s) < 1 { 389 return []string{} 390 } else { 391 return strings.Split(s, sep) 392 } 393} 394 395// parseJson decodes json string into the fields of the receiver. 396// Unknown attribute name causes panic. 397func parseJson(jsonString string, info interface{}) error { 398 decoder := json.NewDecoder(strings.NewReader(jsonString)) 399 decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests 400 err := decoder.Decode(info) 401 if err != nil { 402 return fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err) 403 } 404 return nil 405} 406