kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/verifier_test/cc_indexer_test.bzl (about) 1 """Rules for testing the c++ indexer""" 2 3 # 4 # Copyright 2017 The Kythe Authors. All rights reserved. 5 # 6 # Licensed under the Apache License, Version 2.0 (the "License"); 7 # you may not use this file except in compliance with the License. 8 # You may obtain a copy of the License at 9 # 10 # http://www.apache.org/licenses/LICENSE-2.0 11 # 12 # Unless required by applicable law or agreed to in writing, software 13 # distributed under the License is distributed on an "AS IS" BASIS, 14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 # See the License for the specific language governing permissions and 16 # limitations under the License. 17 # 18 19 load("@rules_proto//proto:defs.bzl", "ProtoInfo") 20 load( 21 "@bazel_tools//tools/build_defs/cc:action_names.bzl", 22 "CPP_COMPILE_ACTION_NAME", 23 ) 24 load("//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain") 25 load("@bazel_skylib//lib:paths.bzl", "paths") 26 load("@bazel_skylib//lib:shell.bzl", "shell") 27 load( 28 ":verifier_test.bzl", 29 "KytheEntryProducerInfo", 30 "KytheVerifierSources", 31 "extract", 32 "verifier_test", 33 ) 34 load( 35 "@io_kythe//kythe/cxx/extractor:toolchain.bzl", 36 "CXX_EXTRACTOR_TOOLCHAINS", 37 "CxxExtractorToolchainInfo", 38 "find_extractor_toolchains", 39 ) 40 41 UNSUPPORTED_FEATURES = [ 42 "thin_lto", 43 "module_maps", 44 "use_header_modules", 45 "fdo_instrument", 46 "fdo_optimize", 47 ] 48 49 CxxCompilationUnits = provider( 50 doc = "A bundle of pre-extracted Kythe CompilationUnits for C++.", 51 fields = { 52 "files": "Depset of .kzip files.", 53 }, 54 ) 55 56 _VERIFIER_FLAGS = { 57 "check_for_singletons": False, 58 "convert_marked_source": False, 59 "goal_prefix": "//-", 60 "ignore_dups": False, 61 "ignore_code_conflicts": False, 62 "use_fast_solver": False, 63 "show_protos": False, 64 "show_goals": False, 65 "show_anchors": False, 66 } 67 68 _INDEXER_FLAGS = { 69 "experimental_alias_template_instantiations": False, 70 "experimental_drop_cpp_fwd_decl_docs": False, 71 "experimental_drop_instantiation_independent_data": False, 72 "experimental_drop_objc_fwd_class_docs": False, 73 "experimental_usr_byte_size": 0, 74 "emit_anchor_scopes": False, 75 "emit_usr_corpus": False, 76 "template_instance_exclude_path_pattern": "", 77 "fail_on_unimplemented_builtin": True, 78 "ignore_unimplemented": False, 79 "index_template_instantiations": True, 80 "ibuild_config": "", 81 "use_compilation_corpus_as_default": False, 82 "record_call_directness": False, 83 "experimental_analysis_exclude_path_pattern": "", 84 "experimental_record_variable_init_types": False, 85 "experimental_record_c_symbols": False, 86 } 87 88 def _compiler_options(ctx, extractor_toolchain, copts, cc_info): 89 """Returns the list of compiler flags from the C++ toolchain.""" 90 cc_toolchain = extractor_toolchain.cc_toolchain 91 feature_configuration = cc_common.configure_features( 92 ctx = ctx, 93 cc_toolchain = cc_toolchain, 94 requested_features = ctx.features, 95 unsupported_features = ctx.disabled_features + UNSUPPORTED_FEATURES, 96 ) 97 98 variables = cc_common.create_compile_variables( 99 feature_configuration = feature_configuration, 100 cc_toolchain = cc_toolchain, 101 user_compile_flags = copts, 102 include_directories = cc_info.compilation_context.includes, 103 quote_include_directories = cc_info.compilation_context.quote_includes, 104 system_include_directories = cc_info.compilation_context.system_includes, 105 ) 106 107 # TODO(schroederc): use memory-efficient Args, when available 108 args = ctx.actions.args() 109 args.add("--with_executable", extractor_toolchain.compiler_executable) 110 args.add_all(cc_common.get_memory_inefficient_command_line( 111 feature_configuration = feature_configuration, 112 action_name = CPP_COMPILE_ACTION_NAME, 113 variables = variables, 114 )) 115 116 env = cc_common.get_environment_variables( 117 feature_configuration = feature_configuration, 118 action_name = CPP_COMPILE_ACTION_NAME, 119 variables = variables, 120 ) 121 return args, env 122 123 def _compile_and_link(ctx, cc_info_providers, sources, headers): 124 cc_toolchain = find_cpp_toolchain(ctx) 125 feature_configuration = cc_common.configure_features( 126 ctx = ctx, 127 cc_toolchain = cc_toolchain, 128 requested_features = ctx.features, 129 unsupported_features = ctx.disabled_features + UNSUPPORTED_FEATURES, 130 ) 131 compile_ctx, compile_outs = cc_common.compile( 132 name = ctx.label.name, 133 actions = ctx.actions, 134 cc_toolchain = cc_toolchain, 135 srcs = sources, 136 public_hdrs = headers, 137 feature_configuration = feature_configuration, 138 compilation_contexts = [provider.compilation_context for provider in cc_info_providers], 139 ) 140 linking_ctx, linking_out = cc_common.create_linking_context_from_compilation_outputs( 141 name = ctx.label.name + "_transitive_library", 142 actions = ctx.actions, 143 feature_configuration = feature_configuration, 144 cc_toolchain = cc_toolchain, 145 compilation_outputs = compile_outs, 146 linking_contexts = [provider.linking_context for provider in cc_info_providers], 147 ) 148 return struct( 149 compilation_context = compile_ctx, 150 linking_context = linking_ctx, 151 transitive_library_file = linking_out, 152 ) 153 154 def _flag(name, typename, value): 155 if value == None: # Omit None flags. 156 return None 157 158 if type(value) != typename: 159 fail("Invalid value for %s: %s; expected %s, found %s" % ( 160 name, 161 value, 162 typename, 163 type(value), 164 )) 165 if typename == "bool": 166 value = str(value).lower() 167 return "--%s=%s" % (name, value) 168 169 def _flags(values, defaults): 170 return [ 171 flag 172 for flag in [ 173 _flag(name, type(default), values.pop(name, default)) 174 for name, default in defaults.items() 175 ] 176 if flag != None 177 ] 178 179 def _split_flags(kwargs): 180 flags = struct( 181 indexer = _flags(kwargs, _INDEXER_FLAGS), 182 verifier = _flags(kwargs, _VERIFIER_FLAGS), 183 ) 184 if kwargs: 185 fail("Unrecognized verifier flags: %s" % (kwargs.keys(),)) 186 return flags 187 188 def _fix_path_for_generated_file(path): 189 virtual_imports = "/_virtual_imports/" 190 if virtual_imports in path: 191 return path.split(virtual_imports)[1].split("/", 1)[1] 192 else: 193 return path 194 195 def _generate_files(ctx, files, extensions): 196 return [ 197 ctx.actions.declare_file( 198 paths.replace_extension( 199 paths.relativize(_fix_path_for_generated_file(f.short_path), ctx.label.package), 200 extension, 201 ), 202 ) 203 for f in files 204 for extension in extensions 205 ] 206 207 def _format_path_and_short_path(f): 208 return "-I{0}={1}".format(_fix_path_for_generated_file(f.short_path), f.path) 209 210 def _get_short_path(f): 211 return _fix_path_for_generated_file(f.short_path) 212 213 _KytheProtoInfo = provider() 214 215 def _cc_kythe_proto_library_aspect_impl(target, ctx): 216 sources = _generate_files(ctx, target[ProtoInfo].direct_sources, [".pb.cc"]) 217 if ctx.attr.enable_proto_static_reflection: 218 headers = _generate_files(ctx, target[ProtoInfo].direct_sources, [".pb.h", ".proto.h", ".proto.static_reflection.h"]) 219 else: 220 headers = _generate_files(ctx, target[ProtoInfo].direct_sources, [".pb.h", ".proto.h"]) 221 args = ctx.actions.args() 222 args.add("--plugin=protoc-gen-PLUGIN=" + ctx.executable._plugin.path) 223 if ctx.attr.enable_proto_static_reflection: 224 args.add("--PLUGIN_out=proto_h,proto_static_reflection_h:" + ctx.bin_dir.path + "/") 225 else: 226 args.add("--PLUGIN_out=proto_h:" + ctx.bin_dir.path + "/") 227 args.add_all(target[ProtoInfo].transitive_sources, map_each = _format_path_and_short_path) 228 args.add_all(target[ProtoInfo].direct_sources, map_each = _get_short_path) 229 ctx.actions.run( 230 arguments = [args], 231 outputs = sources + headers, 232 inputs = target[ProtoInfo].transitive_sources, 233 executable = ctx.executable._protoc, 234 tools = [ctx.executable._plugin], 235 mnemonic = "GenerateKytheCCProto", 236 ) 237 cc_info_providers = [lib[CcInfo] for lib in [target, ctx.attr._runtime] if CcInfo in lib] 238 cc_context = _compile_and_link(ctx, cc_info_providers, headers = headers, sources = sources) 239 return [ 240 _KytheProtoInfo(files = depset(sources + headers)), 241 CcInfo( 242 compilation_context = cc_context.compilation_context, 243 linking_context = cc_context.linking_context, 244 ), 245 ] 246 247 _cc_kythe_proto_library_aspect = aspect( 248 attr_aspects = ["deps"], 249 attrs = { 250 "_protoc": attr.label( 251 default = Label("@com_google_protobuf//:protoc"), 252 executable = True, 253 cfg = "exec", 254 ), 255 "_plugin": attr.label( 256 default = Label("//kythe/cxx/tools:proto_metadata_plugin"), 257 executable = True, 258 cfg = "exec", 259 ), 260 "_runtime": attr.label( 261 default = Label("@com_google_protobuf//:protobuf"), 262 cfg = "target", 263 ), 264 # Do not add references, temporary attribute for find_cpp_toolchain. 265 "_cc_toolchain": attr.label( 266 default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), 267 ), 268 "enable_proto_static_reflection": attr.bool( 269 default = False, 270 doc = "Emit and capture generated code for proto static reflection", 271 ), 272 }, 273 fragments = ["cpp"], 274 toolchains = use_cpp_toolchain(), 275 implementation = _cc_kythe_proto_library_aspect_impl, 276 ) 277 278 def _cc_kythe_proto_library(ctx): 279 files = [dep[_KytheProtoInfo].files for dep in ctx.attr.deps] 280 return [ 281 ctx.attr.deps[0][CcInfo], 282 DefaultInfo(files = depset([], transitive = files)), 283 ] 284 285 cc_kythe_proto_library = rule( 286 attrs = { 287 "deps": attr.label_list( 288 providers = [ProtoInfo], 289 aspects = [_cc_kythe_proto_library_aspect], 290 ), 291 "enable_proto_static_reflection": attr.bool( 292 default = False, 293 doc = "Emit and capture generated code for proto static reflection", 294 ), 295 }, 296 implementation = _cc_kythe_proto_library, 297 ) 298 299 def _cc_write_kzip_impl(ctx): 300 extractor_toolchain, runtimes_deps = find_extractor_toolchains(ctx) 301 cpp = extractor_toolchain.cc_toolchain 302 cc_info = cc_common.merge_cc_infos(cc_infos = [ 303 src[CcInfo] 304 for src in ctx.attr.srcs + ctx.attr.deps + runtimes_deps 305 if CcInfo in src 306 ]) 307 opts, cc_env = _compiler_options(ctx, extractor_toolchain, ctx.attr.opts, cc_info) 308 if ctx.attr.corpus: 309 env = {"KYTHE_CORPUS": ctx.attr.corpus} 310 env.update(cc_env) 311 else: 312 env = cc_env 313 314 outputs = depset([ 315 extract( 316 srcs = depset([src]), 317 ctx = ctx, 318 env = env, 319 extractor = extractor_toolchain.extractor_binary, 320 kzip = ctx.actions.declare_file("{}/{}.kzip".format(ctx.label.name, src.basename)), 321 opts = opts, 322 vnames_config = ctx.file.vnames_config, 323 deps = depset( 324 direct = ctx.files.srcs, 325 transitive = [ 326 depset(ctx.files.deps), 327 cc_info.compilation_context.headers, 328 cpp.all_files, 329 ], 330 ), 331 ) 332 for src in ctx.files.srcs 333 if not src.path.endswith(".h") # Don't extract headers. 334 ]) 335 outputs = depset(transitive = [outputs] + [ 336 dep[CxxCompilationUnits].files 337 for dep in ctx.attr.deps 338 if CxxCompilationUnits in dep 339 ]) 340 return [ 341 DefaultInfo(files = outputs), 342 CxxCompilationUnits(files = outputs), 343 KytheVerifierSources(files = depset(ctx.files.srcs)), 344 ] 345 346 cc_write_kzip = rule( 347 attrs = { 348 "srcs": attr.label_list( 349 doc = "A list of C++ source files to extract.", 350 mandatory = True, 351 allow_empty = False, 352 allow_files = [ 353 ".cc", 354 ".c", 355 ".h", 356 ], 357 ), 358 "copts": attr.string_list( 359 doc = """Options which are required to compile/index the sources. 360 361 These will be included in the resulting .kzip CompilationUnits. 362 """, 363 ), 364 "opts": attr.string_list( 365 doc = "Options which will be passed to the extractor as arguments.", 366 ), 367 "corpus": attr.string( 368 doc = "The compilation unit corpus to use.", 369 default = "", 370 ), 371 "vnames_config": attr.label( 372 doc = "vnames_config file to be used by the extractor.", 373 default = Label("//external:vnames_config"), 374 allow_single_file = [".json"], 375 ), 376 "deps": attr.label_list( 377 doc = """Files which are required by the extracted sources. 378 379 Additionally, targets providing CxxCompilationUnits 380 may be used for dependencies which are required for an eventual 381 Kythe index, but should not be extracted here. 382 """, 383 allow_files = True, 384 providers = [ 385 [CcInfo], 386 [CxxCompilationUnits], 387 ], 388 ), 389 "_cxx_extractor_toolchain": attr.label( 390 doc = "Fallback cxx_extractor_toolchain to use.", 391 default = Label("@io_kythe//kythe/cxx/extractor:cxx_extractor_default"), 392 providers = [CxxExtractorToolchainInfo], 393 ), 394 }, 395 doc = """cc_write_kzip extracts srcs into CompilationUnits. 396 397 Each file in srcs will be extracted into a separate .kzip file, based on the name 398 of the source. 399 """, 400 fragments = ["cpp"], 401 toolchains = CXX_EXTRACTOR_TOOLCHAINS, 402 implementation = _cc_write_kzip_impl, 403 ) 404 405 def _extract_bundle_impl(ctx): 406 bundle = ctx.actions.declare_directory(ctx.label.name + "_unbundled") 407 ctx.actions.run( 408 inputs = [ctx.file.src], 409 tools = [ctx.executable.unbundle], 410 outputs = [bundle], 411 mnemonic = "Unbundle", 412 executable = ctx.executable.unbundle, 413 arguments = [ctx.file.src.path, bundle.path], 414 ) 415 ctx.actions.run_shell( 416 inputs = [ 417 ctx.file.vnames_config, 418 bundle, 419 ], 420 tools = [ctx.executable.extractor], 421 outputs = [ctx.outputs.kzip], 422 mnemonic = "ExtractBundle", 423 env = { 424 "KYTHE_OUTPUT_FILE": ctx.outputs.kzip.path, 425 "KYTHE_ROOT_DIRECTORY": ".", 426 "KYTHE_VNAMES": ctx.file.vnames_config.path, 427 }, 428 arguments = [ 429 ctx.executable.extractor.path, 430 bundle.path, 431 ] + ctx.attr.opts, 432 command = "\"$1\" -c \"${@:2}\" $(cat \"${2}/cflags\") \"${2}/test_bundle/test.cc\"", 433 ) 434 435 # TODO(shahms): Allow directly specifying the unbundled sources as verifier sources, 436 # rather than relying on --use_file_nodes. 437 # Possibly, just use the bundled source directly as the verifier doesn't actually 438 # care about the expanded source. 439 # Bazel makes it hard to use a glob here. 440 return [CxxCompilationUnits(files = depset([ctx.outputs.kzip]))] 441 442 cc_extract_bundle = rule( 443 attrs = { 444 "src": attr.label( 445 doc = "Label of the bundled test to extract.", 446 mandatory = True, 447 allow_single_file = True, 448 ), 449 "extractor": attr.label( 450 default = Label("//kythe/cxx/extractor:cxx_extractor"), 451 executable = True, 452 cfg = "exec", 453 ), 454 "opts": attr.string_list( 455 doc = "Additional arguments to pass to the extractor.", 456 ), 457 "unbundle": attr.label( 458 default = Label("//tools/build_rules/verifier_test:unbundle"), 459 executable = True, 460 cfg = "exec", 461 ), 462 "vnames_config": attr.label( 463 default = Label("//kythe/cxx/indexer/cxx/testdata:test_vnames.json"), 464 allow_single_file = True, 465 ), 466 }, 467 doc = "Extracts a bundled C++ indexer test into a .kzip file.", 468 outputs = {"kzip": "%{name}.kzip"}, 469 implementation = _extract_bundle_impl, 470 ) 471 472 def _bazel_extract_kzip_impl(ctx): 473 # TODO(shahms): This is a hack as we get both executable 474 # and .sh from files.scripts but only want the "executable" one. 475 # Unlike `attr.label`, `attr.label_list` lacks an `executable` argument. 476 # Excluding "is_source" files may be overly aggressive, but effective. 477 scripts = [s for s in ctx.files.scripts if not s.is_source] 478 ctx.actions.run( 479 inputs = [ 480 ctx.file.vnames_config, 481 ctx.file.data, 482 ] + scripts + ctx.files.srcs, 483 tools = [ctx.executable.extractor], 484 outputs = [ctx.outputs.kzip], 485 mnemonic = "BazelExtractKZip", 486 executable = ctx.executable.extractor, 487 arguments = [ 488 ctx.file.data.path, 489 ctx.outputs.kzip.path, 490 ctx.file.vnames_config.path, 491 ] + [script.path for script in scripts], 492 ) 493 return [ 494 KytheVerifierSources(files = depset(ctx.files.srcs)), 495 CxxCompilationUnits(files = depset([ctx.outputs.kzip])), 496 ] 497 498 # TODO(shahms): Clean up the bazel extraction rules. 499 _bazel_extract_kzip = rule( 500 attrs = { 501 "srcs": attr.label_list( 502 doc = "Source files to provide via KytheVerifierSources.", 503 allow_files = True, 504 ), 505 "data": attr.label( 506 doc = "The .xa extra action to extract.", 507 # TODO(shahms): This should be the "src" which is extracted. 508 mandatory = True, 509 allow_single_file = [".xa"], 510 ), 511 "extractor": attr.label( 512 default = Label("//kythe/cxx/extractor:cxx_extractor_bazel"), 513 executable = True, 514 cfg = "exec", 515 ), 516 "scripts": attr.label_list( 517 cfg = "exec", 518 allow_files = True, 519 ), 520 "vnames_config": attr.label( 521 default = Label("//external:vnames_config"), 522 allow_single_file = True, 523 ), 524 }, 525 doc = "Extracts a Bazel extra action binary proto file into a .kzip.", 526 outputs = {"kzip": "%{name}.kzip"}, 527 implementation = _bazel_extract_kzip_impl, 528 ) 529 530 def _expand_as_rootpath(ctx, option): 531 # Replace $(location X) and $(execpath X) with $(rootpath X). 532 return ctx.expand_location( 533 option.replace("$(location ", "$(rootpath ").replace("$(execpath ", "$(rootpath "), 534 ) 535 536 def _cc_index_source(ctx, src, test_runners): 537 entries = ctx.actions.declare_file( 538 ctx.label.name + "/" + src.basename + ".entries", 539 ) 540 ctx.actions.run( 541 mnemonic = "CcIndexSource", 542 outputs = [entries], 543 inputs = ctx.files.srcs + ctx.files.deps, 544 tools = [ctx.executable.indexer], 545 executable = ctx.executable.indexer, 546 arguments = [ctx.expand_location(o) for o in ctx.attr.opts] + [ 547 "-i", 548 src.path, 549 "-o", 550 entries.path, 551 "--", 552 "-c", 553 ] + [ctx.expand_location(o) for o in ctx.attr.copts], 554 ) 555 test_runners.append(_make_test_runner( 556 ctx, 557 src, 558 {}, 559 arguments = [_expand_as_rootpath(ctx, o) for o in ctx.attr.opts] + [ 560 "-i", 561 src.short_path, 562 "--", 563 "-c", 564 ] + [_expand_as_rootpath(ctx, o) for o in ctx.attr.copts], 565 )) 566 return entries 567 568 def _cc_index_compilation(ctx, compilation, test_runners): 569 if ctx.attr.copts: 570 print("Ignoring compiler options:", ctx.attr.copts) 571 entries = ctx.actions.declare_file( 572 ctx.label.name + "/" + compilation.basename + ".entries", 573 ) 574 ctx.actions.run( 575 mnemonic = "CcIndexCompilation", 576 outputs = [entries], 577 inputs = [compilation] + ctx.files.deps, 578 tools = [ctx.executable.indexer], 579 executable = ctx.executable.indexer, 580 arguments = [ctx.expand_location(o) for o in ctx.attr.opts] + [ 581 "-o", 582 entries.path, 583 compilation.path, 584 ], 585 ) 586 test_runners.append(_make_test_runner( 587 ctx, 588 compilation, 589 {}, 590 arguments = [_expand_as_rootpath(ctx, o) for o in ctx.attr.opts] + [ 591 compilation.short_path, 592 ], 593 )) 594 return entries 595 596 def _cc_index_single_file(ctx, input, test_runners): 597 if input.extension == "kzip": 598 return _cc_index_compilation(ctx, input, test_runners) 599 elif input.extension in ("c", "cc", "m"): 600 return _cc_index_source(ctx, input, test_runners) 601 fail("Cannot index input file: %s" % (input,)) 602 603 def _make_test_runner(ctx, source, env, arguments): 604 output = ctx.actions.declare_file(ctx.label.name + source.basename.replace(".", "_") + "_test_runner") 605 ctx.actions.expand_template( 606 output = output, 607 is_executable = True, 608 template = ctx.file._test_template, 609 substitutions = { 610 "@INDEXER@": shell.quote(ctx.executable.test_indexer.short_path), 611 "@ENV@": "\n".join([ 612 shell.quote("{key}={value}".format(key = key, value = value)) 613 for key, value in env.items() 614 ]), 615 "@ARGS@": "\n".join([ 616 shell.quote(a) 617 for a in arguments 618 ]), 619 }, 620 ) 621 return output 622 623 def _cc_index_impl(ctx): 624 test_runners = [] 625 intermediates = [ 626 _cc_index_single_file(ctx, src, test_runners) 627 for src in ctx.files.srcs 628 if src.extension in ("m", "c", "cc", "kzip") 629 ] 630 additional_kzips = [ 631 kzip 632 for dep in ctx.attr.deps 633 if CxxCompilationUnits in dep 634 for kzip in dep[CxxCompilationUnits].files 635 if kzip not in ctx.files.deps 636 ] 637 638 intermediates += [ 639 _cc_index_compilation(ctx, kzip, test_runners) 640 for kzip in additional_kzips 641 ] 642 643 ctx.actions.run_shell( 644 outputs = [ctx.outputs.entries], 645 inputs = intermediates, 646 command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") | gzip -c > "${@:${#@}}"', 647 mnemonic = "CompressEntries", 648 arguments = ["cat"] + [i.path for i in intermediates] + [ctx.outputs.entries.path], 649 ) 650 651 sources = [depset([src for src in ctx.files.srcs if src.extension != "kzip"])] 652 for dep in ctx.attr.srcs: 653 if KytheVerifierSources in dep: 654 sources.append(dep[KytheVerifierSources].files) 655 656 return [ 657 KytheVerifierSources(files = depset(transitive = sources)), 658 KytheEntryProducerInfo( 659 executables = test_runners, 660 runfiles = ctx.runfiles( 661 files = (test_runners + 662 ctx.files.deps + 663 ctx.files.srcs + 664 additional_kzips), 665 ).merge(ctx.attr.test_indexer[DefaultInfo].default_runfiles), 666 ), 667 ] 668 669 # TODO(shahms): Support cc_library deps, along with cc toolchain support. 670 # TODO(shahms): Split objc_index into a separate rule. 671 cc_index = rule( 672 attrs = { 673 # .cc/.h files, added to KytheVerifierSources provider, but not transitively. 674 # CxxCompilationUnits, which may also include sources. 675 "srcs": attr.label_list( 676 doc = "C++/ObjC source files or extracted .kzip files to index.", 677 allow_files = [ 678 ".cc", 679 ".c", 680 ".h", 681 ".m", # Objective-C is supported by the indexer as well. 682 ".kzip", 683 ], 684 ), 685 "copts": attr.string_list( 686 doc = "Options to pass to the compiler while indexing.", 687 ), 688 "opts": attr.string_list( 689 doc = "Options to pass to the indexer.", 690 ), 691 "deps": attr.label_list( 692 doc = "Files required to index srcs.", 693 # .meta files, .h files 694 allow_files = [ 695 ".h", 696 ".meta", # Cross language metadata files. 697 ".claim", # Static claim files. 698 ], 699 ), 700 "indexer": attr.label( 701 default = Label("//kythe/cxx/indexer/cxx:indexer"), 702 executable = True, 703 cfg = "exec", 704 ), 705 "test_indexer": attr.label( 706 default = Label("//kythe/cxx/indexer/cxx:indexer"), 707 executable = True, 708 cfg = "target", 709 ), 710 "_test_template": attr.label( 711 default = Label("//tools/build_rules/verifier_test:indexer.sh.in"), 712 allow_single_file = True, 713 ), 714 }, 715 doc = """Produces a Kythe index from the C++ source files. 716 717 Files in `srcs` and `deps` will be indexed, files in `srcs` will 718 additionally be included in the provided KytheVerifierSources. 719 KytheEntries dependencies will be transitively included in the index. 720 """, 721 outputs = { 722 "entries": "%{name}.entries.gz", 723 }, 724 implementation = _cc_index_impl, 725 ) 726 727 def _indexer_test( 728 name, 729 srcs, 730 copts, 731 deps = [], 732 tags = [], 733 size = "small", 734 target_compatible_with = [], 735 bundled = False, 736 expect_fail_verify = False, 737 indexer = None, 738 **kwargs): 739 flags = _split_flags(kwargs) 740 goals = srcs 741 if bundled: 742 if len(srcs) != 1: 743 fail("Bundled indexer tests require exactly one src!") 744 cc_extract_bundle( 745 name = name + "_kzip", 746 testonly = True, 747 src = srcs[0], 748 opts = copts, 749 target_compatible_with = target_compatible_with, 750 tags = tags, 751 ) 752 753 # Verifier sources come from file nodes. 754 srcs = [":" + name + "_kzip"] 755 goals = [] 756 indexer_args = {} 757 if indexer != None: 758 # Obnoxiously, we have to duplicate these attributes so that 759 # they both have the proper configuration. 760 indexer_args = { 761 "indexer": indexer, 762 "test_indexer": indexer, 763 } 764 765 cc_index( 766 name = name + "_entries", 767 testonly = True, 768 srcs = srcs, 769 copts = copts if not bundled else [], 770 opts = (["-claim_unknown=false"] if bundled else []) + flags.indexer, 771 target_compatible_with = target_compatible_with, 772 tags = tags, 773 deps = deps, 774 **indexer_args 775 ) 776 verifier_test( 777 name = name, 778 size = size, 779 srcs = goals, 780 deps = [":" + name + "_entries"], 781 expect_success = not expect_fail_verify, 782 opts = flags.verifier, 783 target_compatible_with = target_compatible_with, 784 tags = tags, 785 ) 786 787 # If a test is expected to pass on OSX but not on linux, you can set 788 # target_compatible_with=["@platforms//os:osx"]. This causes the test to be skipped on linux and it 789 # causes the actual test to execute on OSX. 790 def cc_indexer_test( 791 name, 792 srcs, 793 deps = [], 794 tags = [], 795 size = "small", 796 target_compatible_with = [], 797 std = "c++11", 798 bundled = False, 799 expect_fail_verify = False, 800 copts = [], 801 indexer = None, 802 **kwargs): 803 """C++ indexer test rule. 804 805 Args: 806 name: The name of the test rule. 807 srcs: Source files to index and run the verifier. 808 deps: Sources, compilation units or entries which should be present 809 in the index or are required to index the sources. 810 std: The C++ standard to use for the test. 811 bundled: True if this test is a "bundled" C++ test and must be extracted. 812 expect_fail_verify: True if this test is expected to fail. 813 convert_marked_source: Whether the verifier should convert marked source. 814 ignore_dups: Whether the verifier should ignore duplicate nodes. 815 check_for_singletons: Whether the verifier should check for singleton facts. 816 goal_prefix: The comment prefix the verifier should use for goals. 817 fail_on_unimplemented_builtin: Whether the indexer should fail on 818 unimplemented builtins. 819 ignore_unimplemented: Whether the indexer should continue after encountering 820 an unimplemented construct. 821 index_template_instantiations: Whether the indexer should index template 822 instantiations. 823 emit_usr_corpus: Whether the indexer should emit corpora for USRs. 824 experimental_alias_template_instantiations: Whether the indexer should alias 825 template instantiations. 826 experimental_drop_instantiation_independent_data: Whether the indexer should 827 drop extraneous instantiation independent data. 828 experimental_usr_byte_size: How many bytes of a USR to use. 829 ignore_code_conflicts: Ignore conflicting /kythe/code facts during verification. 830 use_fast_solver: Use the fast solver. 831 """ 832 _indexer_test( 833 name = name, 834 srcs = srcs, 835 deps = deps, 836 tags = tags, 837 size = size, 838 copts = ["-std=" + std] + copts, 839 target_compatible_with = target_compatible_with, 840 bundled = bundled, 841 expect_fail_verify = expect_fail_verify, 842 indexer = indexer, 843 **kwargs 844 ) 845 846 def objc_indexer_test( 847 name, 848 srcs, 849 deps = [], 850 tags = [], 851 size = "small", 852 target_compatible_with = [], 853 bundled = False, 854 expect_fail_verify = False, 855 **kwargs): 856 """Objective C indexer test rule. 857 858 Args: 859 name: The name of the test rule. 860 srcs: Source files to index and run the verifier. 861 deps: Sources, compilation units or entries which should be present 862 in the index or are required to index the sources. 863 bundled: True if this test is a "bundled" C++ test and must be extracted. 864 expect_fail_verify: True if this test is expected to fail. 865 convert_marked_source: Whether the verifier should convert marked source. 866 ignore_dups: Whether the verifier should ignore duplicate nodes. 867 check_for_singletons: Whether the verifier should check for singleton facts. 868 goal_prefix: The comment prefix the verifier should use for goals. 869 fail_on_unimplemented_builtin: Whether the indexer should fail on 870 unimplemented builtins. 871 ignore_unimplemented: Whether the indexer should continue after encountering 872 an unimplemented construct. 873 index_template_instantiations: Whether the indexer should index template 874 instantiations. 875 experimental_alias_template_instantiations: Whether the indexer should alias 876 template instantiations. 877 experimental_drop_instantiation_independent_data: Whether the indexer should 878 drop extraneous instantiation independent data. 879 experimental_usr_byte_size: How many bytes of a USR to use. 880 """ 881 _indexer_test( 882 name = name, 883 srcs = srcs, 884 deps = deps, 885 tags = tags, 886 size = size, 887 # Newer ObjC features are only enabled on the "modern" runtime. 888 copts = ["-fblocks", "-fobjc-runtime=macosx"], 889 target_compatible_with = target_compatible_with, 890 bundled = bundled, 891 expect_fail_verify = expect_fail_verify, 892 **kwargs 893 ) 894 895 def objc_bazel_extractor_test( 896 name, 897 src, 898 data, 899 size = "small", 900 tags = [], 901 target_compatible_with = []): 902 """Objective C Bazel extractor test. 903 904 Args: 905 src: The source file to use with the verifier. 906 data: The extracted .xa protocol buffer to index. 907 """ 908 _bazel_extract_kzip( 909 name = name + "_kzip", 910 testonly = True, 911 srcs = [src], 912 data = data, 913 extractor = "//kythe/cxx/extractor:objc_extractor_bazel", 914 target_compatible_with = target_compatible_with, 915 scripts = [ 916 "//third_party/bazel:get_devdir", 917 "//third_party/bazel:get_sdkroot", 918 ], 919 tags = tags, 920 ) 921 cc_index( 922 name = name + "_entries", 923 testonly = True, 924 srcs = [":" + name + "_kzip"], 925 target_compatible_with = target_compatible_with, 926 tags = tags, 927 ) 928 return verifier_test( 929 name = name, 930 size = size, 931 srcs = [src], 932 deps = [":" + name + "_entries"], 933 opts = ["--ignore_dups"], 934 target_compatible_with = target_compatible_with, 935 tags = tags, 936 ) 937 938 def cc_bazel_extractor_test(name, src, data, size = "small", tags = []): 939 """C++ Bazel extractor test. 940 941 Args: 942 src: The source file to use with the verifier. 943 data: The extracted .xa protocol buffer to index. 944 """ 945 _bazel_extract_kzip( 946 name = name + "_kzip", 947 testonly = True, 948 srcs = [src], 949 data = data, 950 tags = tags, 951 ) 952 cc_index( 953 name = name + "_entries", 954 testonly = True, 955 srcs = [":" + name + "_kzip"], 956 tags = tags, 957 ) 958 return verifier_test( 959 name = name, 960 size = size, 961 srcs = [src], 962 deps = [":" + name + "_entries"], 963 opts = ["--ignore_dups"], 964 tags = tags, 965 ) 966 967 def cc_extractor_test( 968 name, 969 srcs, 970 deps = [], 971 data = [], 972 size = "small", 973 std = "c++11", 974 tags = [], 975 target_compatible_with = []): 976 """C++ verifier test on an extracted source file.""" 977 args = ["-std=" + std, "-c"] 978 cc_write_kzip( 979 name = name + "_kzip", 980 testonly = True, 981 srcs = srcs, 982 opts = args, 983 target_compatible_with = target_compatible_with, 984 tags = tags, 985 deps = data, 986 ) 987 cc_index( 988 name = name + "_entries", 989 testonly = True, 990 srcs = [":" + name + "_kzip"], 991 opts = ["--ignore_unimplemented"], 992 target_compatible_with = target_compatible_with, 993 tags = tags, 994 deps = data, 995 ) 996 return verifier_test( 997 name = name, 998 size = size, 999 srcs = srcs, 1000 opts = ["--ignore_dups", "--ignore_code_conflicts"], 1001 target_compatible_with = target_compatible_with, 1002 tags = tags, 1003 deps = deps + [":" + name + "_entries"], 1004 )