1#!/usr/bin/env python 2# Copyright 2020 The gRPC Authors 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import subprocess 17import yaml 18import xml.etree.ElementTree as ET 19import os 20import sys 21import build_cleaner 22 23_ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..')) 24os.chdir(_ROOT) 25 26 27def _bazel_query_xml_tree(query): 28 """Get xml output of bazel query invocation, parsed as XML tree""" 29 output = subprocess.check_output( 30 ['tools/bazel', 'query', '--noimplicit_deps', '--output', 'xml', query]) 31 return ET.fromstring(output) 32 33 34def _rule_dict_from_xml_node(rule_xml_node): 35 result = { 36 'class': rule_xml_node.attrib.get('class'), 37 'name': rule_xml_node.attrib.get('name'), 38 'srcs': [], 39 'hdrs': [], 40 'deps': [], 41 'data': [], 42 'tags': [], 43 'args': [], 44 'generator_function': None, 45 'size': None, 46 'flaky': False, 47 } 48 for child in rule_xml_node: 49 # all the metadata we want is stored under "list" tags 50 if child.tag == 'list': 51 list_name = child.attrib['name'] 52 if list_name in ['srcs', 'hdrs', 'deps', 'data', 'tags', 'args']: 53 result[list_name] += [item.attrib['value'] for item in child] 54 if child.tag == 'string': 55 string_name = child.attrib['name'] 56 if string_name in ['generator_function', 'size']: 57 result[string_name] = child.attrib['value'] 58 if child.tag == 'boolean': 59 bool_name = child.attrib['name'] 60 if bool_name in ['flaky']: 61 result[bool_name] = child.attrib['value'] == 'true' 62 return result 63 64 65def _extract_rules_from_bazel_xml(xml_tree): 66 result = {} 67 for child in xml_tree: 68 if child.tag == 'rule': 69 rule_dict = _rule_dict_from_xml_node(child) 70 rule_clazz = rule_dict['class'] 71 rule_name = rule_dict['name'] 72 if rule_clazz in [ 73 'cc_library', 'cc_binary', 'cc_test', 'cc_proto_library', 74 'proto_library' 75 ]: 76 if rule_name in result: 77 raise Exception('Rule %s already present' % rule_name) 78 result[rule_name] = rule_dict 79 return result 80 81 82def _get_bazel_label(target_name): 83 if ':' in target_name: 84 return '//%s' % target_name 85 else: 86 return '//:%s' % target_name 87 88 89def _extract_source_file_path(label): 90 """Gets relative path to source file from bazel deps listing""" 91 if label.startswith('//'): 92 label = label[len('//'):] 93 # labels in form //:src/core/lib/surface/call_test_only.h 94 if label.startswith(':'): 95 label = label[len(':'):] 96 # labels in form //test/core/util:port.cc 97 label = label.replace(':', '/') 98 return label 99 100 101def _extract_public_headers(bazel_rule): 102 """Gets list of public headers from a bazel rule""" 103 result = [] 104 for dep in bazel_rule['hdrs']: 105 if dep.startswith('//:include/') and dep.endswith('.h'): 106 result.append(_extract_source_file_path(dep)) 107 return list(sorted(result)) 108 109 110def _extract_nonpublic_headers(bazel_rule): 111 """Gets list of non-public headers from a bazel rule""" 112 result = [] 113 for dep in bazel_rule['hdrs']: 114 if dep.startswith('//') and not dep.startswith( 115 '//:include/') and dep.endswith('.h'): 116 result.append(_extract_source_file_path(dep)) 117 return list(sorted(result)) 118 119 120def _extract_sources(bazel_rule): 121 """Gets list of source files from a bazel rule""" 122 result = [] 123 for dep in bazel_rule['srcs']: 124 if dep.startswith('//') and (dep.endswith('.cc') or dep.endswith('.c') 125 or dep.endswith('.proto')): 126 result.append(_extract_source_file_path(dep)) 127 return list(sorted(result)) 128 129 130def _extract_deps(bazel_rule): 131 """Gets list of deps from from a bazel rule""" 132 return list(sorted(bazel_rule['deps'])) 133 134 135def _create_target_from_bazel_rule(target_name, bazel_rules): 136 # extract the deps from bazel 137 bazel_rule = bazel_rules[_get_bazel_label(target_name)] 138 result = { 139 'name': target_name, 140 '_PUBLIC_HEADERS_BAZEL': _extract_public_headers(bazel_rule), 141 '_HEADERS_BAZEL': _extract_nonpublic_headers(bazel_rule), 142 '_SRC_BAZEL': _extract_sources(bazel_rule), 143 '_DEPS_BAZEL': _extract_deps(bazel_rule), 144 } 145 return result 146 147 148def _sort_by_build_order(lib_names, lib_dict, deps_key_name, verbose=False): 149 """Sort library names to form correct build order. Use metadata from lib_dict""" 150 # we find correct build order by performing a topological sort 151 # expected output: if library B depends on A, A should be listed first 152 153 # all libs that are not in the dictionary are considered external. 154 external_deps = list( 155 sorted(filter(lambda lib_name: lib_name not in lib_dict, lib_names))) 156 if verbose: 157 print('topo_ordering ' + str(lib_names)) 158 print(' external_deps ' + str(external_deps)) 159 160 result = list(external_deps) # external deps will be listed first 161 while len(result) < len(lib_names): 162 more_results = [] 163 for lib in lib_names: 164 if lib not in result: 165 dep_set = set(lib_dict[lib].get(deps_key_name, [])) 166 dep_set = dep_set.intersection(lib_names) 167 # if lib only depends on what's already built, add it to the results 168 if not dep_set.difference(set(result)): 169 more_results.append(lib) 170 if not more_results: 171 raise Exception( 172 'Cannot sort topologically, there seems to be a cyclic dependency' 173 ) 174 if verbose: 175 print(' adding ' + str(more_results)) 176 result = result + list( 177 sorted(more_results 178 )) # when build order doesn't matter, sort lexicographically 179 return result 180 181 182# TODO(jtattermusch): deduplicate with transitive_dependencies.py (which has a slightly different logic) 183def _populate_transitive_deps(bazel_rules): 184 """Add 'transitive_deps' field for each of the rules""" 185 transitive_deps = {} 186 for rule_name in bazel_rules.iterkeys(): 187 transitive_deps[rule_name] = set(bazel_rules[rule_name]['deps']) 188 189 while True: 190 deps_added = 0 191 for rule_name in bazel_rules.iterkeys(): 192 old_deps = transitive_deps[rule_name] 193 new_deps = set(old_deps) 194 for dep_name in old_deps: 195 new_deps.update(transitive_deps.get(dep_name, set())) 196 deps_added += len(new_deps) - len(old_deps) 197 transitive_deps[rule_name] = new_deps 198 # if none of the transitive dep sets has changed, we're done 199 if deps_added == 0: 200 break 201 202 for rule_name, bazel_rule in bazel_rules.iteritems(): 203 bazel_rule['transitive_deps'] = list(sorted(transitive_deps[rule_name])) 204 205 206def _external_dep_name_from_bazel_dependency(bazel_dep): 207 """Returns name of dependency if external bazel dependency is provided or None""" 208 if bazel_dep.startswith('@com_google_absl//'): 209 # special case for add dependency on one of the absl libraries (there is not just one absl library) 210 prefixlen = len('@com_google_absl//') 211 return bazel_dep[prefixlen:] 212 elif bazel_dep == '//external:upb_lib': 213 return 'upb' 214 elif bazel_dep == '//external:benchmark': 215 return 'benchmark' 216 else: 217 # all the other external deps such as gflags, protobuf, cares, zlib 218 # don't need to be listed explicitly, they are handled automatically 219 # by the build system (make, cmake) 220 return None 221 222 223def _expand_intermediate_deps(target_dict, public_dep_names, bazel_rules): 224 # Some of the libraries defined by bazel won't be exposed in build.yaml 225 # We call these "intermediate" dependencies. This method expands 226 # the intermediate deps for given target (populates library's 227 # headers, sources and dicts as if the intermediate dependency never existed) 228 229 # use this dictionary to translate from bazel labels to dep names 230 bazel_label_to_dep_name = {} 231 for dep_name in public_dep_names: 232 bazel_label_to_dep_name[_get_bazel_label(dep_name)] = dep_name 233 234 target_name = target_dict['name'] 235 bazel_deps = target_dict['_DEPS_BAZEL'] 236 237 # initial values 238 public_headers = set(target_dict['_PUBLIC_HEADERS_BAZEL']) 239 headers = set(target_dict['_HEADERS_BAZEL']) 240 src = set(target_dict['_SRC_BAZEL']) 241 deps = set() 242 243 expansion_blacklist = set() 244 to_expand = set(bazel_deps) 245 while to_expand: 246 247 # start with the last dependency to be built 248 build_order = _sort_by_build_order(list(to_expand), bazel_rules, 249 'transitive_deps') 250 251 bazel_dep = build_order[-1] 252 to_expand.remove(bazel_dep) 253 254 is_public = bazel_dep in bazel_label_to_dep_name 255 external_dep_name_maybe = _external_dep_name_from_bazel_dependency( 256 bazel_dep) 257 258 if is_public: 259 # this is not an intermediate dependency we so we add it 260 # to the list of public dependencies to the list, in the right format 261 deps.add(bazel_label_to_dep_name[bazel_dep]) 262 263 # we do not want to expand any intermediate libraries that are already included 264 # by the dependency we just added 265 expansion_blacklist.update( 266 bazel_rules[bazel_dep]['transitive_deps']) 267 268 elif external_dep_name_maybe: 269 deps.add(external_dep_name_maybe) 270 271 elif bazel_dep.startswith( 272 '//external:') or not bazel_dep.startswith('//'): 273 # all the other external deps can be skipped 274 pass 275 276 elif bazel_dep in expansion_blacklist: 277 # do not expand if a public dependency that depends on this has already been expanded 278 pass 279 280 else: 281 if bazel_dep in bazel_rules: 282 # this is an intermediate library, expand it 283 public_headers.update( 284 _extract_public_headers(bazel_rules[bazel_dep])) 285 headers.update( 286 _extract_nonpublic_headers(bazel_rules[bazel_dep])) 287 src.update(_extract_sources(bazel_rules[bazel_dep])) 288 289 new_deps = _extract_deps(bazel_rules[bazel_dep]) 290 to_expand.update(new_deps) 291 else: 292 raise Exception(bazel_dep + ' not in bazel_rules') 293 294 # make the 'deps' field transitive, but only list non-intermediate deps and selected external deps 295 bazel_transitive_deps = bazel_rules[_get_bazel_label( 296 target_name)]['transitive_deps'] 297 for transitive_bazel_dep in bazel_transitive_deps: 298 public_name = bazel_label_to_dep_name.get(transitive_bazel_dep, None) 299 if public_name: 300 deps.add(public_name) 301 external_dep_name_maybe = _external_dep_name_from_bazel_dependency( 302 transitive_bazel_dep) 303 if external_dep_name_maybe: 304 # expanding all absl libraries is technically correct but creates too much noise 305 if not external_dep_name_maybe.startswith('absl'): 306 deps.add(external_dep_name_maybe) 307 308 target_dict['public_headers'] = list(sorted(public_headers)) 309 target_dict['headers'] = list(sorted(headers)) 310 target_dict['src'] = list(sorted(src)) 311 target_dict['deps'] = list(sorted(deps)) 312 313 314def _generate_build_metadata(build_extra_metadata, bazel_rules): 315 lib_names = build_extra_metadata.keys() 316 result = {} 317 318 for lib_name in lib_names: 319 lib_dict = _create_target_from_bazel_rule(lib_name, bazel_rules) 320 321 _expand_intermediate_deps(lib_dict, lib_names, bazel_rules) 322 323 # populate extra properties from build metadata 324 lib_dict.update(build_extra_metadata.get(lib_name, {})) 325 326 # store to results 327 result[lib_name] = lib_dict 328 329 # rename some targets to something else 330 # this needs to be made after we're done with most of processing logic 331 # otherwise the already-renamed libraries will have different names than expected 332 for lib_name in lib_names: 333 to_name = build_extra_metadata.get(lib_name, {}).get('_RENAME', None) 334 if to_name: 335 # store lib under the new name and also change its 'name' property 336 if to_name in result: 337 raise Exception('Cannot rename target ' + lib_name + ', ' + 338 to_name + ' already exists.') 339 lib_dict = result.pop(lib_name) 340 lib_dict['name'] = to_name 341 result[to_name] = lib_dict 342 343 # dep names need to be updated as well 344 for lib_dict_to_update in result.values(): 345 lib_dict_to_update['deps'] = list( 346 map(lambda dep: to_name if dep == lib_name else dep, 347 lib_dict_to_update['deps'])) 348 349 # make sure deps are listed in reverse topological order (e.g. "grpc gpr" and not "gpr grpc") 350 for lib_dict in result.itervalues(): 351 lib_dict['deps'] = list( 352 reversed(_sort_by_build_order(lib_dict['deps'], result, 'deps'))) 353 354 return result 355 356 357def _convert_to_build_yaml_like(lib_dict): 358 lib_names = list( 359 filter( 360 lambda lib_name: lib_dict[lib_name].get('_TYPE', 'library') == 361 'library', lib_dict.keys())) 362 target_names = list( 363 filter( 364 lambda lib_name: lib_dict[lib_name].get('_TYPE', 'library') == 365 'target', lib_dict.keys())) 366 test_names = list( 367 filter( 368 lambda lib_name: lib_dict[lib_name].get('_TYPE', 'library') == 369 'test', lib_dict.keys())) 370 371 # list libraries and targets in predefined order 372 lib_list = list(map(lambda lib_name: lib_dict[lib_name], lib_names)) 373 target_list = list(map(lambda lib_name: lib_dict[lib_name], target_names)) 374 test_list = list(map(lambda lib_name: lib_dict[lib_name], test_names)) 375 376 # get rid of temporary private fields prefixed with "_" and some other useless fields 377 for lib in lib_list: 378 for field_to_remove in filter(lambda k: k.startswith('_'), lib.keys()): 379 lib.pop(field_to_remove, None) 380 for target in target_list: 381 for field_to_remove in filter(lambda k: k.startswith('_'), 382 target.keys()): 383 target.pop(field_to_remove, None) 384 target.pop('public_headers', 385 None) # public headers make no sense for targets 386 for test in test_list: 387 for field_to_remove in filter(lambda k: k.startswith('_'), test.keys()): 388 test.pop(field_to_remove, None) 389 test.pop('public_headers', 390 None) # public headers make no sense for tests 391 392 build_yaml_like = { 393 'libs': lib_list, 394 'filegroups': [], 395 'targets': target_list, 396 'tests': test_list, 397 } 398 return build_yaml_like 399 400 401def _extract_cc_tests(bazel_rules): 402 """Gets list of cc_test tests from bazel rules""" 403 result = [] 404 for bazel_rule in bazel_rules.itervalues(): 405 if bazel_rule['class'] == 'cc_test': 406 test_name = bazel_rule['name'] 407 if test_name.startswith('//'): 408 prefixlen = len('//') 409 result.append(test_name[prefixlen:]) 410 return list(sorted(result)) 411 412 413def _filter_cc_tests(tests): 414 """Filters out tests that we don't want or we cannot build them reasonably""" 415 416 # most qps tests are autogenerated, we are fine without them 417 tests = list( 418 filter(lambda test: not test.startswith('test/cpp/qps:'), tests)) 419 420 # we have trouble with census dependency outside of bazel 421 tests = list( 422 filter(lambda test: not test.startswith('test/cpp/ext/filters/census:'), 423 tests)) 424 tests = list( 425 filter( 426 lambda test: not test.startswith( 427 'test/cpp/microbenchmarks:bm_opencensus_plugin'), tests)) 428 429 # missing opencensus/stats/stats.h 430 tests = list( 431 filter( 432 lambda test: not test.startswith( 433 'test/cpp/end2end:server_load_reporting_end2end_test'), tests)) 434 tests = list( 435 filter( 436 lambda test: not test.startswith( 437 'test/cpp/server/load_reporter:lb_load_reporter_test'), tests)) 438 439 # The test uses --running_under_bazel cmdline argument 440 # To avoid the trouble needing to adjust it, we just skip the test 441 tests = list( 442 filter( 443 lambda test: not test.startswith( 444 'test/cpp/naming:resolver_component_tests_runner_invoker'), 445 tests)) 446 447 # the test requires 'client_crash_test_server' to be built 448 tests = list( 449 filter( 450 lambda test: not test.startswith('test/cpp/end2end:time_change_test' 451 ), tests)) 452 453 # the test requires 'client_crash_test_server' to be built 454 tests = list( 455 filter( 456 lambda test: not test.startswith( 457 'test/cpp/end2end:client_crash_test'), tests)) 458 459 # the test requires 'server_crash_test_client' to be built 460 tests = list( 461 filter( 462 lambda test: not test.startswith( 463 'test/cpp/end2end:server_crash_test'), tests)) 464 465 # test never existed under build.yaml and it fails -> skip it 466 tests = list( 467 filter( 468 lambda test: not test.startswith( 469 'test/core/tsi:ssl_session_cache_test'), tests)) 470 471 return tests 472 473 474def _generate_build_extra_metadata_for_tests(tests, bazel_rules): 475 test_metadata = {} 476 for test in tests: 477 test_dict = {'build': 'test', '_TYPE': 'target'} 478 479 bazel_rule = bazel_rules[_get_bazel_label(test)] 480 481 bazel_tags = bazel_rule['tags'] 482 if 'manual' in bazel_tags: 483 # don't run the tests marked as "manual" 484 test_dict['run'] = False 485 486 if bazel_rule['flaky']: 487 # don't run tests that are marked as "flaky" under bazel 488 # because that would only add noise for the run_tests.py tests 489 # and seeing more failures for tests that we already know are flaky 490 # doesn't really help anything 491 test_dict['run'] = False 492 493 if 'no_uses_polling' in bazel_tags: 494 test_dict['uses_polling'] = False 495 496 if 'grpc_fuzzer' == bazel_rule['generator_function']: 497 # currently we hand-list fuzzers instead of generating them automatically 498 # because there's no way to obtain maxlen property from bazel BUILD file. 499 print('skipping fuzzer ' + test) 500 continue 501 502 # if any tags that restrict platform compatibility are present, 503 # generate the "platforms" field accordingly 504 # TODO(jtattermusch): there is also a "no_linux" tag, but we cannot take 505 # it into account as it is applied by grpc_cc_test when poller expansion 506 # is made (for tests where uses_polling=True). So for now, we just 507 # assume all tests are compatible with linux and ignore the "no_linux" tag 508 # completely. 509 known_platform_tags = set(['no_windows', 'no_mac']) 510 if set(bazel_tags).intersection(known_platform_tags): 511 platforms = [] 512 # assume all tests are compatible with linux and posix 513 platforms.append('linux') 514 platforms.append( 515 'posix') # there is no posix-specific tag in bazel BUILD 516 if not 'no_mac' in bazel_tags: 517 platforms.append('mac') 518 if not 'no_windows' in bazel_tags: 519 platforms.append('windows') 520 test_dict['platforms'] = platforms 521 522 if '//external:benchmark' in bazel_rule['transitive_deps']: 523 test_dict['benchmark'] = True 524 test_dict['defaults'] = 'benchmark' 525 526 cmdline_args = bazel_rule['args'] 527 if cmdline_args: 528 test_dict['args'] = list(cmdline_args) 529 530 uses_gtest = '//external:gtest' in bazel_rule['transitive_deps'] 531 if uses_gtest: 532 test_dict['gtest'] = True 533 534 if test.startswith('test/cpp') or uses_gtest: 535 test_dict['language'] = 'c++' 536 537 elif test.startswith('test/core'): 538 test_dict['language'] = 'c' 539 else: 540 raise Exception('wrong test' + test) 541 542 # short test name without the path. 543 # There can be name collisions, but we will resolve them later 544 simple_test_name = os.path.basename(_extract_source_file_path(test)) 545 test_dict['_RENAME'] = simple_test_name 546 547 test_metadata[test] = test_dict 548 549 # detect duplicate test names 550 tests_by_simple_name = {} 551 for test_name, test_dict in test_metadata.iteritems(): 552 simple_test_name = test_dict['_RENAME'] 553 if not simple_test_name in tests_by_simple_name: 554 tests_by_simple_name[simple_test_name] = [] 555 tests_by_simple_name[simple_test_name].append(test_name) 556 557 # choose alternative names for tests with a name collision 558 for collision_list in tests_by_simple_name.itervalues(): 559 if len(collision_list) > 1: 560 for test_name in collision_list: 561 long_name = test_name.replace('/', '_').replace(':', '_') 562 print( 563 'short name of "%s" collides with another test, renaming to %s' 564 % (test_name, long_name)) 565 test_metadata[test_name]['_RENAME'] = long_name 566 567 return test_metadata 568 569 570# extra metadata that will be used to construct build.yaml 571# there are mostly extra properties that we weren't able to obtain from the bazel build 572# _TYPE: whether this is library, target or test 573# _RENAME: whether this target should be renamed to a different name (to match expectations of make and cmake builds) 574# NOTE: secure is 'check' by default, so setting secure = False below does matter 575_BUILD_EXTRA_METADATA = { 576 'third_party/address_sorting:address_sorting': { 577 'language': 'c', 578 'build': 'all', 579 'secure': False, 580 '_RENAME': 'address_sorting' 581 }, 582 'gpr': { 583 'language': 'c', 584 'build': 'all', 585 'secure': False 586 }, 587 'grpc': { 588 'language': 'c', 589 'build': 'all', 590 'baselib': True, 591 'secure': True, 592 'deps_linkage': 'static', 593 'dll': True, 594 'generate_plugin_registry': True 595 }, 596 'grpc++': { 597 'language': 'c++', 598 'build': 'all', 599 'baselib': True, 600 'dll': True 601 }, 602 'grpc++_alts': { 603 'language': 'c++', 604 'build': 'all', 605 'baselib': True 606 }, 607 'grpc++_error_details': { 608 'language': 'c++', 609 'build': 'all' 610 }, 611 'grpc++_reflection': { 612 'language': 'c++', 613 'build': 'all' 614 }, 615 'grpc++_unsecure': { 616 'language': 'c++', 617 'build': 'all', 618 'baselib': True, 619 'secure': False, 620 'dll': True 621 }, 622 # TODO(jtattermusch): do we need to set grpc_csharp_ext's LDFLAGS for wrapping memcpy in the same way as in build.yaml? 623 'grpc_csharp_ext': { 624 'language': 'c', 625 'build': 'all', 626 'deps_linkage': 'static', 627 'dll': 'only' 628 }, 629 'grpc_unsecure': { 630 'language': 'c', 631 'build': 'all', 632 'baselib': True, 633 'secure': False, 634 'deps_linkage': 'static', 635 'dll': True, 636 'generate_plugin_registry': True 637 }, 638 'grpcpp_channelz': { 639 'language': 'c++', 640 'build': 'all' 641 }, 642 'grpc++_test': { 643 'language': 'c++', 644 'build': 'private', 645 }, 646 'src/compiler:grpc_plugin_support': { 647 'language': 'c++', 648 'build': 'protoc', 649 'secure': False, 650 '_RENAME': 'grpc_plugin_support' 651 }, 652 'src/compiler:grpc_cpp_plugin': { 653 'language': 'c++', 654 'build': 'protoc', 655 'secure': False, 656 '_TYPE': 'target', 657 '_RENAME': 'grpc_cpp_plugin' 658 }, 659 'src/compiler:grpc_csharp_plugin': { 660 'language': 'c++', 661 'build': 'protoc', 662 'secure': False, 663 '_TYPE': 'target', 664 '_RENAME': 'grpc_csharp_plugin' 665 }, 666 'src/compiler:grpc_node_plugin': { 667 'language': 'c++', 668 'build': 'protoc', 669 'secure': False, 670 '_TYPE': 'target', 671 '_RENAME': 'grpc_node_plugin' 672 }, 673 'src/compiler:grpc_objective_c_plugin': { 674 'language': 'c++', 675 'build': 'protoc', 676 'secure': False, 677 '_TYPE': 'target', 678 '_RENAME': 'grpc_objective_c_plugin' 679 }, 680 'src/compiler:grpc_php_plugin': { 681 'language': 'c++', 682 'build': 'protoc', 683 'secure': False, 684 '_TYPE': 'target', 685 '_RENAME': 'grpc_php_plugin' 686 }, 687 'src/compiler:grpc_python_plugin': { 688 'language': 'c++', 689 'build': 'protoc', 690 'secure': False, 691 '_TYPE': 'target', 692 '_RENAME': 'grpc_python_plugin' 693 }, 694 'src/compiler:grpc_ruby_plugin': { 695 'language': 'c++', 696 'build': 'protoc', 697 'secure': False, 698 '_TYPE': 'target', 699 '_RENAME': 'grpc_ruby_plugin' 700 }, 701 702 # TODO(jtattermusch): consider adding grpc++_core_stats 703 704 # test support libraries 705 'test/core/util:grpc_test_util': { 706 'language': 'c', 707 'build': 'private', 708 '_RENAME': 'grpc_test_util' 709 }, 710 'test/core/util:grpc_test_util_unsecure': { 711 'language': 'c', 712 'build': 'private', 713 'secure': False, 714 '_RENAME': 'grpc_test_util_unsecure' 715 }, 716 # TODO(jtattermusch): consider adding grpc++_test_util_unsecure - it doesn't seem to be used by bazel build (don't forget to set secure: False) 717 'test/cpp/util:test_config': { 718 'language': 'c++', 719 'build': 'private', 720 '_RENAME': 'grpc++_test_config' 721 }, 722 'test/cpp/util:test_util': { 723 'language': 'c++', 724 'build': 'private', 725 '_RENAME': 'grpc++_test_util' 726 }, 727 728 # end2end test support libraries 729 'test/core/end2end:end2end_tests': { 730 'language': 'c', 731 'build': 'private', 732 'secure': True, 733 '_RENAME': 'end2end_tests' 734 }, 735 'test/core/end2end:end2end_nosec_tests': { 736 'language': 'c', 737 'build': 'private', 738 'secure': False, 739 '_RENAME': 'end2end_nosec_tests' 740 }, 741 742 # benchmark support libraries 743 'test/cpp/microbenchmarks:helpers': { 744 'language': 'c++', 745 'build': 'test', 746 'defaults': 'benchmark', 747 '_RENAME': 'benchmark_helpers' 748 }, 749 'test/cpp/interop:interop_client': { 750 'language': 'c++', 751 'build': 'test', 752 'run': False, 753 '_TYPE': 'target', 754 '_RENAME': 'interop_client' 755 }, 756 'test/cpp/interop:interop_server': { 757 'language': 'c++', 758 'build': 'test', 759 'run': False, 760 '_TYPE': 'target', 761 '_RENAME': 'interop_server' 762 }, 763 'test/cpp/interop:xds_interop_client': { 764 'language': 'c++', 765 'build': 'test', 766 'run': False, 767 '_TYPE': 'target', 768 '_RENAME': 'xds_interop_client' 769 }, 770 'test/cpp/interop:xds_interop_server': { 771 'language': 'c++', 772 'build': 'test', 773 'run': False, 774 '_TYPE': 'target', 775 '_RENAME': 'xds_interop_server' 776 }, 777 'test/cpp/interop:http2_client': { 778 'language': 'c++', 779 'build': 'test', 780 'run': False, 781 '_TYPE': 'target', 782 '_RENAME': 'http2_client' 783 }, 784 'test/cpp/qps:qps_json_driver': { 785 'language': 'c++', 786 'build': 'test', 787 'run': False, 788 '_TYPE': 'target', 789 '_RENAME': 'qps_json_driver' 790 }, 791 'test/cpp/qps:qps_worker': { 792 'language': 'c++', 793 'build': 'test', 794 'run': False, 795 '_TYPE': 'target', 796 '_RENAME': 'qps_worker' 797 }, 798 'test/cpp/util:grpc_cli': { 799 'language': 'c++', 800 'build': 'test', 801 'run': False, 802 '_TYPE': 'target', 803 '_RENAME': 'grpc_cli' 804 }, 805 806 # TODO(jtattermusch): create_jwt and verify_jwt breaks distribtests because it depends on grpc_test_utils and thus requires tests to be built 807 # For now it's ok to disable them as these binaries aren't very useful anyway. 808 #'test/core/security:create_jwt': { 'language': 'c', 'build': 'tool', '_TYPE': 'target', '_RENAME': 'grpc_create_jwt' }, 809 #'test/core/security:verify_jwt': { 'language': 'c', 'build': 'tool', '_TYPE': 'target', '_RENAME': 'grpc_verify_jwt' }, 810 811 # TODO(jtattermusch): add remaining tools such as grpc_print_google_default_creds_token (they are not used by bazel build) 812 813 # Fuzzers 814 'test/core/security:alts_credentials_fuzzer': { 815 'language': 'c++', 816 'build': 'fuzzer', 817 'corpus_dirs': ['test/core/security/corpus/alts_credentials_corpus'], 818 'maxlen': 2048, 819 '_TYPE': 'target', 820 '_RENAME': 'alts_credentials_fuzzer' 821 }, 822 'test/core/end2end/fuzzers:client_fuzzer': { 823 'language': 'c++', 824 'build': 'fuzzer', 825 'corpus_dirs': ['test/core/end2end/fuzzers/client_fuzzer_corpus'], 826 'maxlen': 2048, 827 'dict': 'test/core/end2end/fuzzers/hpack.dictionary', 828 '_TYPE': 'target', 829 '_RENAME': 'client_fuzzer' 830 }, 831 'test/core/transport/chttp2:hpack_parser_fuzzer': { 832 'language': 'c++', 833 'build': 'fuzzer', 834 'corpus_dirs': ['test/core/transport/chttp2/hpack_parser_corpus'], 835 'maxlen': 512, 836 'dict': 'test/core/end2end/fuzzers/hpack.dictionary', 837 '_TYPE': 'target', 838 '_RENAME': 'hpack_parser_fuzzer_test' 839 }, 840 'test/core/http:request_fuzzer': { 841 'language': 'c++', 842 'build': 'fuzzer', 843 'corpus_dirs': ['test/core/http/request_corpus'], 844 'maxlen': 2048, 845 '_TYPE': 'target', 846 '_RENAME': 'http_request_fuzzer_test' 847 }, 848 'test/core/http:response_fuzzer': { 849 'language': 'c++', 850 'build': 'fuzzer', 851 'corpus_dirs': ['test/core/http/response_corpus'], 852 'maxlen': 2048, 853 '_TYPE': 'target', 854 '_RENAME': 'http_response_fuzzer_test' 855 }, 856 'test/core/json:json_fuzzer': { 857 'language': 'c++', 858 'build': 'fuzzer', 859 'corpus_dirs': ['test/core/json/corpus'], 860 'maxlen': 512, 861 '_TYPE': 'target', 862 '_RENAME': 'json_fuzzer_test' 863 }, 864 'test/core/nanopb:fuzzer_response': { 865 'language': 'c++', 866 'build': 'fuzzer', 867 'corpus_dirs': ['test/core/nanopb/corpus_response'], 868 'maxlen': 128, 869 '_TYPE': 'target', 870 '_RENAME': 'nanopb_fuzzer_response_test' 871 }, 872 'test/core/nanopb:fuzzer_serverlist': { 873 'language': 'c++', 874 'build': 'fuzzer', 875 'corpus_dirs': ['test/core/nanopb/corpus_serverlist'], 876 'maxlen': 128, 877 '_TYPE': 'target', 878 '_RENAME': 'nanopb_fuzzer_serverlist_test' 879 }, 880 'test/core/slice:percent_decode_fuzzer': { 881 'language': 'c++', 882 'build': 'fuzzer', 883 'corpus_dirs': ['test/core/slice/percent_decode_corpus'], 884 'maxlen': 32, 885 '_TYPE': 'target', 886 '_RENAME': 'percent_decode_fuzzer' 887 }, 888 'test/core/slice:percent_encode_fuzzer': { 889 'language': 'c++', 890 'build': 'fuzzer', 891 'corpus_dirs': ['test/core/slice/percent_encode_corpus'], 892 'maxlen': 32, 893 '_TYPE': 'target', 894 '_RENAME': 'percent_encode_fuzzer' 895 }, 896 'test/core/end2end/fuzzers:server_fuzzer': { 897 'language': 'c++', 898 'build': 'fuzzer', 899 'corpus_dirs': ['test/core/end2end/fuzzers/server_fuzzer_corpus'], 900 'maxlen': 2048, 901 'dict': 'test/core/end2end/fuzzers/hpack.dictionary', 902 '_TYPE': 'target', 903 '_RENAME': 'server_fuzzer' 904 }, 905 'test/core/security:ssl_server_fuzzer': { 906 'language': 'c++', 907 'build': 'fuzzer', 908 'corpus_dirs': ['test/core/security/corpus/ssl_server_corpus'], 909 'maxlen': 2048, 910 '_TYPE': 'target', 911 '_RENAME': 'ssl_server_fuzzer' 912 }, 913 'test/core/client_channel:uri_fuzzer_test': { 914 'language': 'c++', 915 'build': 'fuzzer', 916 'corpus_dirs': ['test/core/client_channel/uri_corpus'], 917 'maxlen': 128, 918 '_TYPE': 'target', 919 '_RENAME': 'uri_fuzzer_test' 920 }, 921 922 # TODO(jtattermusch): these fuzzers had no build.yaml equivalent 923 # test/core/compression:message_compress_fuzzer 924 # test/core/compression:message_decompress_fuzzer 925 # test/core/compression:stream_compression_fuzzer 926 # test/core/compression:stream_decompression_fuzzer 927 # test/core/slice:b64_decode_fuzzer 928 # test/core/slice:b64_encode_fuzzer 929} 930 931# We need a complete picture of all the targets and dependencies we're interested in 932# so we run multiple bazel queries and merge the results. 933_BAZEL_DEPS_QUERIES = [ 934 'deps("//test/...")', 935 'deps("//:all")', 936 'deps("//src/compiler/...")', 937 'deps("//src/proto/...")', 938] 939 940bazel_rules = {} 941for query in _BAZEL_DEPS_QUERIES: 942 bazel_rules.update( 943 _extract_rules_from_bazel_xml(_bazel_query_xml_tree(query))) 944 945_populate_transitive_deps(bazel_rules) 946 947tests = _filter_cc_tests(_extract_cc_tests(bazel_rules)) 948test_metadata = _generate_build_extra_metadata_for_tests(tests, bazel_rules) 949 950all_metadata = {} 951all_metadata.update(_BUILD_EXTRA_METADATA) 952all_metadata.update(test_metadata) 953 954all_targets_dict = _generate_build_metadata(all_metadata, bazel_rules) 955build_yaml_like = _convert_to_build_yaml_like(all_targets_dict) 956 957# if a test uses source files from src/ directly, it's a little bit suspicious 958for tgt in build_yaml_like['targets']: 959 if tgt['build'] == 'test': 960 for src in tgt['src']: 961 if src.startswith('src/') and not src.endswith('.proto'): 962 print('source file from under "src/" tree used in test ' + 963 tgt['name'] + ': ' + src) 964 965build_yaml_string = build_cleaner.cleaned_build_yaml_dict_as_string( 966 build_yaml_like) 967with open('build_autogenerated.yaml', 'w') as file: 968 file.write(build_yaml_string) 969