1COPYRIGHT = """\ 2/* 3 * Copyright 2020 Intel Corporation 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25""" 26 27import argparse 28import math 29import os 30 31from mako.template import Template 32 33# Mesa-local imports must be declared in meson variable 34# '{file_without_suffix}_depend_files'. 35from vk_entrypoints import get_entrypoints_from_xml 36 37# We generate a static hash table for entry point lookup 38# (vkGetProcAddress). We use a linear congruential generator for our hash 39# function and a power-of-two size table. The prime numbers are determined 40# experimentally. 41 42TEMPLATE_H = Template(COPYRIGHT + """\ 43/* This file generated from ${filename}, don't edit directly. */ 44 45#ifndef VK_DISPATCH_TABLE_H 46#define VK_DISPATCH_TABLE_H 47 48#include "vulkan/vulkan.h" 49#include "vulkan/vk_android_native_buffer.h" 50 51#include "vk_extensions.h" 52 53/* Windows api conflict */ 54#ifdef _WIN32 55#include <windows.h> 56#ifdef CreateSemaphore 57#undef CreateSemaphore 58#endif 59#ifdef CreateEvent 60#undef CreateEvent 61#endif 62#endif 63 64#ifdef __cplusplus 65extern "C" { 66#endif 67 68#ifdef _MSC_VER 69VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void); 70#endif 71 72<%def name="dispatch_table(entrypoints)"> 73% for e in entrypoints: 74 % if e.alias: 75 <% continue %> 76 % endif 77 % if e.guard is not None: 78#ifdef ${e.guard} 79 % endif 80 % if e.aliases: 81 union { 82 PFN_vk${e.name} ${e.name}; 83 % for a in e.aliases: 84 PFN_vk${a.name} ${a.name}; 85 % endfor 86 }; 87 % else: 88 PFN_vk${e.name} ${e.name}; 89 % endif 90 % if e.guard is not None: 91#else 92 % if e.aliases: 93 union { 94 PFN_vkVoidFunction ${e.name}; 95 % for a in e.aliases: 96 PFN_vkVoidFunction ${a.name}; 97 % endfor 98 }; 99 % else: 100 PFN_vkVoidFunction ${e.name}; 101 % endif 102#endif 103 % endif 104% endfor 105</%def> 106 107<%def name="entrypoint_table(type, entrypoints)"> 108struct vk_${type}_entrypoint_table { 109% for e in entrypoints: 110 % if e.guard is not None: 111#ifdef ${e.guard} 112 % endif 113 PFN_vk${e.name} ${e.name}; 114 % if e.guard is not None: 115#else 116 PFN_vkVoidFunction ${e.name}; 117# endif 118 % endif 119% endfor 120}; 121</%def> 122 123struct vk_instance_dispatch_table { 124 ${dispatch_table(instance_entrypoints)} 125}; 126 127struct vk_physical_device_dispatch_table { 128 ${dispatch_table(physical_device_entrypoints)} 129}; 130 131struct vk_device_dispatch_table { 132 ${dispatch_table(device_entrypoints)} 133}; 134 135struct vk_dispatch_table { 136 union { 137 struct { 138 struct vk_instance_dispatch_table instance; 139 struct vk_physical_device_dispatch_table physical_device; 140 struct vk_device_dispatch_table device; 141 }; 142 143 struct { 144 ${dispatch_table(instance_entrypoints)} 145 ${dispatch_table(physical_device_entrypoints)} 146 ${dispatch_table(device_entrypoints)} 147 }; 148 }; 149}; 150 151${entrypoint_table('instance', instance_entrypoints)} 152${entrypoint_table('physical_device', physical_device_entrypoints)} 153${entrypoint_table('device', device_entrypoints)} 154 155void 156vk_instance_dispatch_table_load(struct vk_instance_dispatch_table *table, 157 PFN_vkGetInstanceProcAddr gpa, 158 VkInstance instance); 159void 160vk_physical_device_dispatch_table_load(struct vk_physical_device_dispatch_table *table, 161 PFN_vkGetInstanceProcAddr gpa, 162 VkInstance instance); 163void 164vk_device_dispatch_table_load(struct vk_device_dispatch_table *table, 165 PFN_vkGetDeviceProcAddr gpa, 166 VkDevice device); 167 168void vk_instance_dispatch_table_from_entrypoints( 169 struct vk_instance_dispatch_table *dispatch_table, 170 const struct vk_instance_entrypoint_table *entrypoint_table, 171 bool overwrite); 172 173void vk_physical_device_dispatch_table_from_entrypoints( 174 struct vk_physical_device_dispatch_table *dispatch_table, 175 const struct vk_physical_device_entrypoint_table *entrypoint_table, 176 bool overwrite); 177 178void vk_device_dispatch_table_from_entrypoints( 179 struct vk_device_dispatch_table *dispatch_table, 180 const struct vk_device_entrypoint_table *entrypoint_table, 181 bool overwrite); 182 183PFN_vkVoidFunction 184vk_instance_dispatch_table_get(const struct vk_instance_dispatch_table *table, 185 const char *name); 186 187PFN_vkVoidFunction 188vk_physical_device_dispatch_table_get(const struct vk_physical_device_dispatch_table *table, 189 const char *name); 190 191PFN_vkVoidFunction 192vk_device_dispatch_table_get(const struct vk_device_dispatch_table *table, 193 const char *name); 194 195PFN_vkVoidFunction 196vk_instance_dispatch_table_get_if_supported( 197 const struct vk_instance_dispatch_table *table, 198 const char *name, 199 uint32_t core_version, 200 const struct vk_instance_extension_table *instance_exts); 201 202PFN_vkVoidFunction 203vk_physical_device_dispatch_table_get_if_supported( 204 const struct vk_physical_device_dispatch_table *table, 205 const char *name, 206 uint32_t core_version, 207 const struct vk_instance_extension_table *instance_exts); 208 209PFN_vkVoidFunction 210vk_device_dispatch_table_get_if_supported( 211 const struct vk_device_dispatch_table *table, 212 const char *name, 213 uint32_t core_version, 214 const struct vk_instance_extension_table *instance_exts, 215 const struct vk_device_extension_table *device_exts); 216 217#ifdef __cplusplus 218} 219#endif 220 221#endif /* VK_DISPATCH_TABLE_H */ 222""") 223 224TEMPLATE_C = Template(COPYRIGHT + """\ 225/* This file generated from ${filename}, don't edit directly. */ 226 227#include "vk_dispatch_table.h" 228 229#include "util/macros.h" 230#include "string.h" 231 232<%def name="load_dispatch_table(type, VkType, ProcAddr, entrypoints)"> 233void 234vk_${type}_dispatch_table_load(struct vk_${type}_dispatch_table *table, 235 PFN_vk${ProcAddr} gpa, 236 ${VkType} obj) 237{ 238% if type != 'physical_device': 239 table->${ProcAddr} = gpa; 240% endif 241% for e in entrypoints: 242 % if e.alias or e.name == '${ProcAddr}': 243 <% continue %> 244 % endif 245 % if e.guard is not None: 246#ifdef ${e.guard} 247 % endif 248 table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${e.name}"); 249 % for a in e.aliases: 250 if (table->${e.name} == NULL) { 251 table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${a.name}"); 252 } 253 % endfor 254 % if e.guard is not None: 255#endif 256 % endif 257% endfor 258} 259</%def> 260 261${load_dispatch_table('instance', 'VkInstance', 'GetInstanceProcAddr', 262 instance_entrypoints)} 263 264${load_dispatch_table('physical_device', 'VkInstance', 'GetInstanceProcAddr', 265 physical_device_entrypoints)} 266 267${load_dispatch_table('device', 'VkDevice', 'GetDeviceProcAddr', 268 device_entrypoints)} 269 270 271struct string_map_entry { 272 uint32_t name; 273 uint32_t hash; 274 uint32_t num; 275}; 276 277/* We use a big string constant to avoid lots of reloctions from the entry 278 * point table to lots of little strings. The entries in the entry point table 279 * store the index into this big string. 280 */ 281 282<%def name="strmap(strmap, prefix)"> 283static const char ${prefix}_strings[] = 284% for s in strmap.sorted_strings: 285 "${s.string}\\0" 286% endfor 287; 288 289static const struct string_map_entry ${prefix}_string_map_entries[] = { 290% for s in strmap.sorted_strings: 291 { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */ 292% endfor 293}; 294 295/* Hash table stats: 296 * size ${len(strmap.sorted_strings)} entries 297 * collisions entries: 298% for i in range(10): 299 * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]} 300% endfor 301 */ 302 303#define none 0xffff 304static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = { 305% for e in strmap.mapping: 306 ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' }, 307% endfor 308}; 309 310static int 311${prefix}_string_map_lookup(const char *str) 312{ 313 static const uint32_t prime_factor = ${strmap.prime_factor}; 314 static const uint32_t prime_step = ${strmap.prime_step}; 315 const struct string_map_entry *e; 316 uint32_t hash, h; 317 uint16_t i; 318 const char *p; 319 320 hash = 0; 321 for (p = str; *p; p++) 322 hash = hash * prime_factor + *p; 323 324 h = hash; 325 while (1) { 326 i = ${prefix}_string_map[h & ${strmap.hash_mask}]; 327 if (i == none) 328 return -1; 329 e = &${prefix}_string_map_entries[i]; 330 if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0) 331 return e->num; 332 h += prime_step; 333 } 334 335 return -1; 336} 337</%def> 338 339${strmap(instance_strmap, 'instance')} 340${strmap(physical_device_strmap, 'physical_device')} 341${strmap(device_strmap, 'device')} 342 343<% assert len(instance_entrypoints) < 2**8 %> 344static const uint8_t instance_compaction_table[] = { 345% for e in instance_entrypoints: 346 ${e.disp_table_index}, 347% endfor 348}; 349 350<% assert len(physical_device_entrypoints) < 2**8 %> 351static const uint8_t physical_device_compaction_table[] = { 352% for e in physical_device_entrypoints: 353 ${e.disp_table_index}, 354% endfor 355}; 356 357<% assert len(device_entrypoints) < 2**16 %> 358static const uint16_t device_compaction_table[] = { 359% for e in device_entrypoints: 360 ${e.disp_table_index}, 361% endfor 362}; 363 364static bool 365vk_instance_entrypoint_is_enabled(int index, uint32_t core_version, 366 const struct vk_instance_extension_table *instance) 367{ 368 switch (index) { 369% for e in instance_entrypoints: 370 case ${e.entry_table_index}: 371 /* ${e.name} */ 372 % if e.core_version: 373 return ${e.core_version.c_vk_version()} <= core_version; 374 % elif e.extensions: 375 % for ext in e.extensions: 376 % if ext.type == 'instance': 377 if (instance->${ext.name[3:]}) return true; 378 % else: 379 /* All device extensions are considered enabled at the instance level */ 380 return true; 381 % endif 382 % endfor 383 return false; 384 % else: 385 return true; 386 % endif 387% endfor 388 default: 389 return false; 390 } 391} 392 393/** Return true if the core version or extension in which the given entrypoint 394 * is defined is enabled. 395 * 396 * If device is NULL, all device extensions are considered enabled. 397 */ 398static bool 399vk_physical_device_entrypoint_is_enabled(int index, uint32_t core_version, 400 const struct vk_instance_extension_table *instance) 401{ 402 switch (index) { 403% for e in physical_device_entrypoints: 404 case ${e.entry_table_index}: 405 /* ${e.name} */ 406 % if e.core_version: 407 return ${e.core_version.c_vk_version()} <= core_version; 408 % elif e.extensions: 409 % for ext in e.extensions: 410 % if ext.type == 'instance': 411 if (instance->${ext.name[3:]}) return true; 412 % else: 413 /* All device extensions are considered enabled at the instance level */ 414 return true; 415 % endif 416 % endfor 417 return false; 418 % else: 419 return true; 420 % endif 421% endfor 422 default: 423 return false; 424 } 425} 426 427/** Return true if the core version or extension in which the given entrypoint 428 * is defined is enabled. 429 * 430 * If device is NULL, all device extensions are considered enabled. 431 */ 432static bool 433vk_device_entrypoint_is_enabled(int index, uint32_t core_version, 434 const struct vk_instance_extension_table *instance, 435 const struct vk_device_extension_table *device) 436{ 437 switch (index) { 438% for e in device_entrypoints: 439 case ${e.entry_table_index}: 440 /* ${e.name} */ 441 % if e.core_version: 442 return ${e.core_version.c_vk_version()} <= core_version; 443 % elif e.extensions: 444 % for ext in e.extensions: 445 % if ext.type == 'instance': 446 if (instance->${ext.name[3:]}) return true; 447 % else: 448 if (!device || device->${ext.name[3:]}) return true; 449 % endif 450 % endfor 451 return false; 452 % else: 453 return true; 454 % endif 455% endfor 456 default: 457 return false; 458 } 459} 460 461#ifdef _MSC_VER 462VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void) 463{ 464 unreachable("Entrypoint not implemented"); 465} 466 467static const void *get_function_target(const void *func) 468{ 469 const uint8_t *address = func; 470#ifdef _M_X64 471 /* Incremental linking may indirect through relative jump */ 472 if (*address == 0xE9) 473 { 474 /* Compute JMP target if the first byte is opcode 0xE9 */ 475 uint32_t offset; 476 memcpy(&offset, address + 1, 4); 477 address += offset + 5; 478 } 479#else 480 /* Add other platforms here if necessary */ 481#endif 482 return address; 483} 484 485static bool vk_function_is_stub(PFN_vkVoidFunction func) 486{ 487 return (func == vk_entrypoint_stub) || (get_function_target(func) == get_function_target(vk_entrypoint_stub)); 488} 489#endif 490 491<%def name="dispatch_table_from_entrypoints(type)"> 492void vk_${type}_dispatch_table_from_entrypoints( 493 struct vk_${type}_dispatch_table *dispatch_table, 494 const struct vk_${type}_entrypoint_table *entrypoint_table, 495 bool overwrite) 496{ 497 PFN_vkVoidFunction *disp = (PFN_vkVoidFunction *)dispatch_table; 498 PFN_vkVoidFunction *entry = (PFN_vkVoidFunction *)entrypoint_table; 499 500 if (overwrite) { 501 memset(dispatch_table, 0, sizeof(*dispatch_table)); 502 for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) { 503#ifdef _MSC_VER 504 assert(entry[i] != NULL); 505 if (vk_function_is_stub(entry[i])) 506#else 507 if (entry[i] == NULL) 508#endif 509 continue; 510 unsigned disp_index = ${type}_compaction_table[i]; 511 assert(disp[disp_index] == NULL); 512 disp[disp_index] = entry[i]; 513 } 514 } else { 515 for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) { 516 unsigned disp_index = ${type}_compaction_table[i]; 517#ifdef _MSC_VER 518 assert(entry[i] != NULL); 519 if (disp[disp_index] == NULL && !vk_function_is_stub(entry[i])) 520#else 521 if (disp[disp_index] == NULL) 522#endif 523 disp[disp_index] = entry[i]; 524 } 525 } 526} 527</%def> 528 529${dispatch_table_from_entrypoints('instance')} 530${dispatch_table_from_entrypoints('physical_device')} 531${dispatch_table_from_entrypoints('device')} 532 533<%def name="lookup_funcs(type)"> 534static PFN_vkVoidFunction 535vk_${type}_dispatch_table_get_for_entry_index( 536 const struct vk_${type}_dispatch_table *table, int entry_index) 537{ 538 assert(entry_index < ARRAY_SIZE(${type}_compaction_table)); 539 int disp_index = ${type}_compaction_table[entry_index]; 540 return ((PFN_vkVoidFunction *)table)[disp_index]; 541} 542 543PFN_vkVoidFunction 544vk_${type}_dispatch_table_get( 545 const struct vk_${type}_dispatch_table *table, const char *name) 546{ 547 int entry_index = ${type}_string_map_lookup(name); 548 if (entry_index < 0) 549 return NULL; 550 551 return vk_${type}_dispatch_table_get_for_entry_index(table, entry_index); 552} 553</%def> 554 555${lookup_funcs('instance')} 556${lookup_funcs('physical_device')} 557${lookup_funcs('device')} 558 559PFN_vkVoidFunction 560vk_instance_dispatch_table_get_if_supported( 561 const struct vk_instance_dispatch_table *table, 562 const char *name, 563 uint32_t core_version, 564 const struct vk_instance_extension_table *instance_exts) 565{ 566 int entry_index = instance_string_map_lookup(name); 567 if (entry_index < 0) 568 return NULL; 569 570 if (!vk_instance_entrypoint_is_enabled(entry_index, core_version, 571 instance_exts)) 572 return NULL; 573 574 return vk_instance_dispatch_table_get_for_entry_index(table, entry_index); 575} 576 577PFN_vkVoidFunction 578vk_physical_device_dispatch_table_get_if_supported( 579 const struct vk_physical_device_dispatch_table *table, 580 const char *name, 581 uint32_t core_version, 582 const struct vk_instance_extension_table *instance_exts) 583{ 584 int entry_index = physical_device_string_map_lookup(name); 585 if (entry_index < 0) 586 return NULL; 587 588 if (!vk_physical_device_entrypoint_is_enabled(entry_index, core_version, 589 instance_exts)) 590 return NULL; 591 592 return vk_physical_device_dispatch_table_get_for_entry_index(table, entry_index); 593} 594 595PFN_vkVoidFunction 596vk_device_dispatch_table_get_if_supported( 597 const struct vk_device_dispatch_table *table, 598 const char *name, 599 uint32_t core_version, 600 const struct vk_instance_extension_table *instance_exts, 601 const struct vk_device_extension_table *device_exts) 602{ 603 int entry_index = device_string_map_lookup(name); 604 if (entry_index < 0) 605 return NULL; 606 607 if (!vk_device_entrypoint_is_enabled(entry_index, core_version, 608 instance_exts, device_exts)) 609 return NULL; 610 611 return vk_device_dispatch_table_get_for_entry_index(table, entry_index); 612} 613""") 614 615U32_MASK = 2**32 - 1 616 617PRIME_FACTOR = 5024183 618PRIME_STEP = 19 619 620class StringIntMapEntry: 621 def __init__(self, string, num): 622 self.string = string 623 self.num = num 624 625 # Calculate the same hash value that we will calculate in C. 626 h = 0 627 for c in string: 628 h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK 629 self.hash = h 630 631 self.offset = None 632 633def round_to_pow2(x): 634 return 2**int(math.ceil(math.log(x, 2))) 635 636class StringIntMap: 637 def __init__(self): 638 self.baked = False 639 self.strings = {} 640 641 def add_string(self, string, num): 642 assert not self.baked 643 assert string not in self.strings 644 assert 0 <= num < 2**31 645 self.strings[string] = StringIntMapEntry(string, num) 646 647 def bake(self): 648 self.sorted_strings = \ 649 sorted(self.strings.values(), key=lambda x: x.string) 650 offset = 0 651 for entry in self.sorted_strings: 652 entry.offset = offset 653 offset += len(entry.string) + 1 654 655 # Save off some values that we'll need in C 656 self.hash_size = round_to_pow2(len(self.strings) * 1.25) 657 self.hash_mask = self.hash_size - 1 658 self.prime_factor = PRIME_FACTOR 659 self.prime_step = PRIME_STEP 660 661 self.mapping = [-1] * self.hash_size 662 self.collisions = [0] * 10 663 for idx, s in enumerate(self.sorted_strings): 664 level = 0 665 h = s.hash 666 while self.mapping[h & self.hash_mask] >= 0: 667 h = h + PRIME_STEP 668 level = level + 1 669 self.collisions[min(level, 9)] += 1 670 self.mapping[h & self.hash_mask] = idx 671 672def main(): 673 parser = argparse.ArgumentParser() 674 parser.add_argument('--out-c', help='Output C file.') 675 parser.add_argument('--out-h', help='Output H file.') 676 parser.add_argument('--beta', required=True, help='Enable beta extensions.') 677 parser.add_argument('--xml', 678 help='Vulkan API XML file.', 679 required=True, 680 action='append', 681 dest='xml_files') 682 args = parser.parse_args() 683 684 entrypoints = get_entrypoints_from_xml(args.xml_files, args.beta) 685 686 device_entrypoints = [] 687 physical_device_entrypoints = [] 688 instance_entrypoints = [] 689 for e in entrypoints: 690 if e.is_device_entrypoint(): 691 device_entrypoints.append(e) 692 elif e.is_physical_device_entrypoint(): 693 physical_device_entrypoints.append(e) 694 else: 695 instance_entrypoints.append(e) 696 697 for i, e in enumerate(e for e in device_entrypoints if not e.alias): 698 e.disp_table_index = i 699 700 device_strmap = StringIntMap() 701 for i, e in enumerate(device_entrypoints): 702 e.entry_table_index = i 703 device_strmap.add_string("vk" + e.name, e.entry_table_index) 704 device_strmap.bake() 705 706 for i, e in enumerate(e for e in physical_device_entrypoints if not e.alias): 707 e.disp_table_index = i 708 709 physical_device_strmap = StringIntMap() 710 for i, e in enumerate(physical_device_entrypoints): 711 e.entry_table_index = i 712 physical_device_strmap.add_string("vk" + e.name, e.entry_table_index) 713 physical_device_strmap.bake() 714 715 for i, e in enumerate(e for e in instance_entrypoints if not e.alias): 716 e.disp_table_index = i 717 718 instance_strmap = StringIntMap() 719 for i, e in enumerate(instance_entrypoints): 720 e.entry_table_index = i 721 instance_strmap.add_string("vk" + e.name, e.entry_table_index) 722 instance_strmap.bake() 723 724 # For outputting entrypoints.h we generate a anv_EntryPoint() prototype 725 # per entry point. 726 try: 727 if args.out_h: 728 with open(args.out_h, 'w') as f: 729 f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints, 730 physical_device_entrypoints=physical_device_entrypoints, 731 device_entrypoints=device_entrypoints, 732 filename=os.path.basename(__file__))) 733 if args.out_c: 734 with open(args.out_c, 'w') as f: 735 f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints, 736 physical_device_entrypoints=physical_device_entrypoints, 737 device_entrypoints=device_entrypoints, 738 instance_strmap=instance_strmap, 739 physical_device_strmap=physical_device_strmap, 740 device_strmap=device_strmap, 741 filename=os.path.basename(__file__))) 742 except Exception: 743 # In the event there's an error, this imports some helpers from mako 744 # to print a useful stack trace and prints it, then exits with 745 # status 1, if python is run with debug; otherwise it just raises 746 # the exception 747 import sys 748 from mako import exceptions 749 print(exceptions.text_error_template().render(), file=sys.stderr) 750 sys.exit(1) 751 752 753if __name__ == '__main__': 754 main() 755