1#!/usr/bin/env python 2# 3# BLE GATT configuration generator for use with BTstack, v0.1 4# Copyright 2011 Matthias Ringwald 5# 6# Format of input file: 7# PRIMARY_SERVICE, SERVICE_UUID 8# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9 10import codecs 11import csv 12import io 13import os 14import re 15import string 16import sys 17 18header = ''' 19// {0} generated from {1} for BTstack 20 21// binary representation 22// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 23 24#include <stdint.h> 25 26const uint8_t profile_data[] = 27''' 28 29usage = ''' 30Usage: ./compile_gatt.py profile.gatt profile.h 31''' 32 33 34print(''' 35BLE configuration generator for use with BTstack, v0.1 36Copyright 2011 Matthias Ringwald 37''') 38 39assigned_uuids = { 40 'GAP_SERVICE' : 0x1800, 41 'GATT_SERVICE' : 0x1801, 42 'GAP_DEVICE_NAME' : 0x2a00, 43 'GAP_APPEARANCE' : 0x2a01, 44 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 45 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 46 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 47 'GATT_SERVICE_CHANGED' : 0x2a05, 48} 49 50property_flags = { 51 'BROADCAST' : 0x01, 52 'READ' : 0x02, 53 'WRITE_WITHOUT_RESPONSE' : 0x04, 54 'WRITE' : 0x08, 55 'NOTIFY': 0x10, 56 'INDICATE' : 0x20, 57 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 58 'EXTENDED_PROPERTIES' : 0x80, 59 # custom BTstack extension 60 'DYNAMIC': 0x100, 61 'LONG_UUID': 0x200, 62 'AUTHENTICATION_REQUIRED': 0x400, 63 'AUTHORIZATION_REQUIRED': 0x800, 64 'ENCRYPTION_KEY_SIZE_7': 0x6000, 65 'ENCRYPTION_KEY_SIZE_8': 0x7000, 66 'ENCRYPTION_KEY_SIZE_9': 0x8000, 67 'ENCRYPTION_KEY_SIZE_10': 0x9000, 68 'ENCRYPTION_KEY_SIZE_11': 0xa000, 69 'ENCRYPTION_KEY_SIZE_12': 0xb000, 70 'ENCRYPTION_KEY_SIZE_13': 0xc000, 71 'ENCRYPTION_KEY_SIZE_14': 0xd000, 72 'ENCRYPTION_KEY_SIZE_15': 0xe000, 73 'ENCRYPTION_KEY_SIZE_16': 0xf000, 74 # only used by gatt compiler >= 0xffff 75 # Extended Properties 76 'RELIABLE_WRITE': 0x10000, 77} 78 79btstack_root = '' 80services = dict() 81characteristic_indices = dict() 82presentation_formats = dict() 83current_service_uuid_string = "" 84current_service_start_handle = 0 85current_characteristic_uuid_string = "" 86defines_for_characteristics = [] 87defines_for_services = [] 88 89handle = 1 90total_size = 0 91 92def read_defines(infile): 93 defines = dict() 94 with open (infile, 'rt') as fin: 95 for line in fin: 96 parts = re.match('#define\s+(\w+)\s+(\w+)',line) 97 if parts and len(parts.groups()) == 2: 98 (key, value) = parts.groups() 99 defines[key] = int(value, 16) 100 return defines 101 102def keyForUUID(uuid): 103 keyUUID = "" 104 for i in uuid: 105 keyUUID += "%02x" % i 106 return keyUUID 107 108def c_string_for_uuid(uuid): 109 return uuid.replace('-', '_') 110 111def twoByteLEFor(value): 112 return [ (value & 0xff), (value >> 8)] 113 114def is_128bit_uuid(text): 115 if re.match("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", text): 116 return True 117 return False 118 119def parseUUID128(uuid): 120 parts = re.match("([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})", uuid) 121 uuid_bytes = [] 122 for i in range(8, 0, -1): 123 uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 124 return uuid_bytes 125 126def parseUUID(uuid): 127 if uuid in assigned_uuids: 128 return twoByteLEFor(assigned_uuids[uuid]) 129 uuid_upper = uuid.upper().replace('.','_') 130 if uuid_upper in bluetooth_gatt: 131 return twoByteLEFor(bluetooth_gatt[uuid_upper]) 132 if is_128bit_uuid(uuid): 133 return parseUUID128(uuid) 134 uuidInt = int(uuid, 16) 135 return twoByteLEFor(uuidInt) 136 137def parseProperties(properties): 138 value = 0 139 parts = properties.split("|") 140 for property in parts: 141 property = property.strip() 142 if property in property_flags: 143 value |= property_flags[property] 144 else: 145 print("WARNING: property %s undefined" % (property)) 146 return value 147 148def write_8(fout, value): 149 fout.write( "0x%02x, " % (value & 0xff)) 150 151def write_16(fout, value): 152 fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 153 154def write_uuid(uuid): 155 for byte in uuid: 156 fout.write( "0x%02x, " % byte) 157 158def write_string(fout, text): 159 for l in text.lstrip('"').rstrip('"'): 160 write_8(fout, ord(l)) 161 162def write_sequence(fout, text): 163 parts = text.split() 164 for part in parts: 165 fout.write("0x%s, " % (part.strip())) 166 167def write_indent(fout): 168 fout.write(" ") 169 170def is_string(text): 171 for item in text.split(" "): 172 if not all(c in string.hexdigits for c in item): 173 return True 174 return False 175 176def add_client_characteristic_configuration(properties): 177 return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 178 179def serviceDefinitionComplete(fout): 180 global services 181 if current_service_uuid_string: 182 fout.write("\n") 183 # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 184 defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 185 defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 186 services[current_service_uuid_string] = [current_service_start_handle, handle-1] 187 188def parseService(fout, parts, service_type): 189 global handle 190 global total_size 191 global current_service_uuid_string 192 global current_service_start_handle 193 194 serviceDefinitionComplete(fout) 195 196 property = property_flags['READ']; 197 198 write_indent(fout) 199 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 200 201 uuid = parseUUID(parts[1]) 202 uuid_size = len(uuid) 203 204 size = 2 + 2 + 2 + uuid_size + 2 205 206 if service_type == 0x2802: 207 size += 4 208 209 write_indent(fout) 210 write_16(fout, size) 211 write_16(fout, property) 212 write_16(fout, handle) 213 write_16(fout, service_type) 214 write_uuid(uuid) 215 fout.write("\n") 216 217 current_service_uuid_string = c_string_for_uuid(parts[1]) 218 current_service_start_handle = handle 219 handle = handle + 1 220 total_size = total_size + size 221 222def parsePrimaryService(fout, parts): 223 parseService(fout, parts, 0x2800) 224 225def parseSecondaryService(fout, parts): 226 parseService(fout, parts, 0x2801) 227 228def parseIncludeService(fout, parts): 229 global handle 230 global total_size 231 232 property = property_flags['READ']; 233 234 write_indent(fout) 235 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 236 237 uuid = parseUUID(parts[1]) 238 uuid_size = len(uuid) 239 if uuid_size > 2: 240 uuid_size = 0 241 # print("Include Service ", c_string_for_uuid(uuid)) 242 243 size = 2 + 2 + 2 + 2 + 4 + uuid_size 244 245 keyUUID = c_string_for_uuid(parts[1]) 246 247 write_indent(fout) 248 write_16(fout, size) 249 write_16(fout, property) 250 write_16(fout, handle) 251 write_16(fout, 0x2802) 252 write_16(fout, services[keyUUID][0]) 253 write_16(fout, services[keyUUID][1]) 254 if uuid_size > 0: 255 write_uuid(uuid) 256 fout.write("\n") 257 258 handle = handle + 1 259 total_size = total_size + size 260 261 262def parseCharacteristic(fout, parts): 263 global handle 264 global total_size 265 global current_characteristic_uuid_string 266 global characteristic_indices 267 268 property_read = property_flags['READ']; 269 270 # enumerate characteristics with same UUID, using optional name tag if available 271 current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 272 index = 1 273 if current_characteristic_uuid_string in characteristic_indices: 274 index = characteristic_indices[current_characteristic_uuid_string] + 1 275 characteristic_indices[current_characteristic_uuid_string] = index 276 if len(parts) > 4: 277 current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 278 else: 279 current_characteristic_uuid_string += ('_%02x' % index) 280 281 uuid = parseUUID(parts[1]) 282 uuid_size = len(uuid) 283 properties = parseProperties(parts[2]) 284 value = ', '.join([str(x) for x in parts[3:]]) 285 286 # reliable writes is defined in an extended properties 287 if (properties & property_flags['RELIABLE_WRITE']): 288 properties = properties | property_flags['EXTENDED_PROPERTIES'] 289 290 write_indent(fout) 291 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 292 293 size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 294 write_indent(fout) 295 write_16(fout, size) 296 write_16(fout, property_read) 297 write_16(fout, handle) 298 write_16(fout, 0x2803) 299 write_8(fout, properties) 300 write_16(fout, handle+1) 301 write_uuid(uuid) 302 fout.write("\n") 303 handle = handle + 1 304 total_size = total_size + size 305 306 size = 2 + 2 + 2 + uuid_size 307 if is_string(value): 308 size = size + len(value) 309 else: 310 size = size + len(value.split()) 311 312 if uuid_size == 16: 313 properties = properties | property_flags['LONG_UUID']; 314 315 write_indent(fout) 316 fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 317 write_indent(fout) 318 write_16(fout, size) 319 write_16(fout, properties) 320 write_16(fout, handle) 321 write_uuid(uuid) 322 if is_string(value): 323 write_string(fout, value) 324 else: 325 write_sequence(fout,value) 326 327 fout.write("\n") 328 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 329 handle = handle + 1 330 331 if add_client_characteristic_configuration(properties): 332 size = 2 + 2 + 2 + 2 + 2 333 write_indent(fout) 334 fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 335 write_indent(fout) 336 write_16(fout, size) 337 write_16(fout, property_flags['READ'] | property_flags['WRITE'] | property_flags['DYNAMIC']) 338 write_16(fout, handle) 339 write_16(fout, 0x2902) 340 write_16(fout, 0) 341 fout.write("\n") 342 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 343 handle = handle + 1 344 345 if properties & property_flags['RELIABLE_WRITE']: 346 size = 2 + 2 + 2 + 2 + 2 347 write_indent(fout) 348 fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 349 write_indent(fout) 350 write_16(fout, size) 351 write_16(fout, property_flags['READ']) 352 write_16(fout, handle) 353 write_16(fout, 0x2900) 354 write_16(fout, 1) # Reliable Write 355 fout.write("\n") 356 handle = handle + 1 357 358def parseCharacteristicUserDescription(fout, parts): 359 global handle 360 global total_size 361 global current_characteristic_uuid_string 362 363 properties = parseProperties(parts[1]) 364 value = parts[2] 365 366 size = 2 + 2 + 2 + 2 367 if is_string(value): 368 size = size + len(value) - 2 369 else: 370 size = size + len(value.split()) 371 372 write_indent(fout) 373 fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 374 write_indent(fout) 375 write_16(fout, size) 376 write_16(fout, properties) 377 write_16(fout, handle) 378 write_16(fout, 0x2901) 379 if is_string(value): 380 write_string(fout, value) 381 else: 382 write_sequence(fout,value) 383 fout.write("\n") 384 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 385 handle = handle + 1 386 387def parseServerCharacteristicConfiguration(fout, parts): 388 global handle 389 global total_size 390 global current_characteristic_uuid_string 391 392 properties = parseProperties(parts[1]) 393 properties = properties | property_flags['DYNAMIC'] 394 size = 2 + 2 + 2 + 2 395 396 write_indent(fout) 397 fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 398 write_indent(fout) 399 write_16(fout, size) 400 write_16(fout, properties) 401 write_16(fout, handle) 402 write_16(fout, 0x2903) 403 fout.write("\n") 404 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 405 handle = handle + 1 406 407def parseCharacteristicFormat(fout, parts): 408 global handle 409 global total_size 410 411 property_read = property_flags['READ']; 412 413 identifier = parts[1] 414 presentation_formats[identifier] = handle 415 # print("format '%s' with handle %d\n" % (identifier, handle)) 416 417 format = parts[2] 418 exponent = parts[3] 419 unit = parseUUID(parts[4]) 420 name_space = parts[5] 421 description = parseUUID(parts[6]) 422 423 size = 2 + 2 + 2 + 2 + 7 424 425 write_indent(fout) 426 fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 427 write_indent(fout) 428 write_16(fout, size) 429 write_16(fout, property_read) 430 write_16(fout, handle) 431 write_16(fout, 0x2904) 432 write_sequence(fout, format) 433 write_sequence(fout, exponent) 434 write_uuid(unit) 435 write_sequence(fout, name_space) 436 write_uuid(description) 437 fout.write("\n") 438 handle = handle + 1 439 440 441def parseCharacteristicAggregateFormat(fout, parts): 442 global handle 443 global total_size 444 445 property_read = property_flags['READ']; 446 size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 447 448 write_indent(fout) 449 fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 450 write_indent(fout) 451 write_16(fout, size) 452 write_16(fout, property_read) 453 write_16(fout, handle) 454 write_16(fout, 0x2905) 455 for identifier in parts[1:]: 456 format_handle = presentation_formats[identifier] 457 if format == 0: 458 print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 459 sys.exit(1) 460 write_16(fout, format_handle) 461 fout.write("\n") 462 handle = handle + 1 463 464def parseReportReference(fout, parts): 465 global handle 466 global total_size 467 468 property_read = property_flags['READ']; 469 size = 2 + 2 + 2 + 2 + 1 + 1 470 471 properties = parseProperties(parts[1]) 472 473 report_id = parts[2] 474 report_type = parts[3] 475 476 write_indent(fout) 477 fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 478 write_indent(fout) 479 write_16(fout, size) 480 write_16(fout, property_read) 481 write_16(fout, handle) 482 write_16(fout, 0x2908) 483 write_sequence(fout, report_id) 484 write_sequence(fout, report_type) 485 fout.write("\n") 486 handle = handle + 1 487 488 489def parseNumberOfDigitals(fout, parts): 490 global handle 491 global total_size 492 493 property_read = property_flags['READ']; 494 size = 2 + 2 + 2 + 2 + 1 495 496 no_of_digitals = parts[1] 497 498 write_indent(fout) 499 fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 500 write_indent(fout) 501 write_16(fout, size) 502 write_16(fout, property_read) 503 write_16(fout, handle) 504 write_16(fout, 0x2909) 505 write_sequence(fout, no_of_digitals) 506 fout.write("\n") 507 handle = handle + 1 508 509def parseLines(fname_in, fin, fout): 510 global handle 511 global total_size 512 513 line_count = 0; 514 for line in fin: 515 line = line.strip("\n\r ") 516 line_count += 1 517 518 if line.startswith("//"): 519 fout.write(" //" + line.lstrip('/') + '\n') 520 continue 521 522 if line.startswith("#import"): 523 imported_file = '' 524 parts = re.match('#import\s+<(.*)>\w*',line) 525 if parts and len(parts.groups()) == 1: 526 imported_file = btstack_root+'/src/ble/gatt-service/' + parts.groups()[0] 527 parts = re.match('#import\s+"(.*)"\w*',line) 528 if parts and len(parts.groups()) == 1: 529 imported_file = os.path.abspath(os.path.dirname(fname_in) + '/'+parts.groups()[0]) 530 if len(imported_file) == 0: 531 print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 532 continue 533 534 print("Importing %s" % imported_file) 535 try: 536 imported_fin = codecs.open (imported_file, encoding='utf-8') 537 fout.write(' // ' + line + ' -- BEGIN\n') 538 parseLines(imported_file, imported_fin, fout) 539 fout.write(' // ' + line + ' -- END\n') 540 except IOError as e: 541 print('ERROR: Import failed. Please check path.') 542 543 continue 544 545 if line.startswith("#TODO"): 546 print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 547 print ("'%s'" % line) 548 fout.write("// " + line + '\n') 549 continue 550 551 if len(line) == 0: 552 continue 553 554 f = io.StringIO(line) 555 parts_list = csv.reader(f, delimiter=',', quotechar='"') 556 557 for parts in parts_list: 558 for index, object in enumerate(parts): 559 parts[index] = object.strip().lstrip('"').rstrip('"') 560 561 if parts[0] == 'PRIMARY_SERVICE': 562 parsePrimaryService(fout, parts) 563 continue 564 565 if parts[0] == 'SECONDARY_SERVICE': 566 parseSecondaryService(fout, parts) 567 continue 568 569 if parts[0] == 'INCLUDE_SERVICE': 570 parseIncludeService(fout, parts) 571 continue 572 573 # 2803 574 if parts[0] == 'CHARACTERISTIC': 575 parseCharacteristic(fout, parts) 576 continue 577 578 # 2900 Characteristic Extended Properties 579 580 # 2901 581 if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 582 parseCharacteristicUserDescription(fout, parts) 583 continue 584 585 586 # 2902 Client Characteristic Configuration - automatically included in Characteristic if 587 # notification / indication is supported 588 if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 589 continue 590 591 # 2903 592 if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 593 parseServerCharacteristicConfiguration(fout, parts) 594 continue 595 596 # 2904 597 if parts[0] == 'CHARACTERISTIC_FORMAT': 598 parseCharacteristicFormat(fout, parts) 599 continue 600 601 # 2905 602 if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 603 parseCharacteristicAggregateFormat(fout, parts) 604 continue 605 606 # 2906 607 if parts[0] == 'VALID_RANGE': 608 print("WARNING: %s not implemented yet\n" % (parts[0])) 609 continue 610 611 # 2907 612 if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 613 print("WARNING: %s not implemented yet\n" % (parts[0])) 614 continue 615 616 # 2908 617 if parts[0] == 'REPORT_REFERENCE': 618 parseReportReference(fout, parts) 619 continue 620 621 # 2909 622 if parts[0] == 'NUMBER_OF_DIGITALS': 623 parseNumberOfDigitals(fout, parts) 624 continue 625 626 # 290A 627 if parts[0] == 'VALUE_TRIGGER_SETTING': 628 print("WARNING: %s not implemented yet\n" % (parts[0])) 629 continue 630 631 # 290B 632 if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 633 print("WARNING: %s not implemented yet\n" % (parts[0])) 634 continue 635 636 # 290C 637 if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 638 print("WARNING: %s not implemented yet\n" % (parts[0])) 639 continue 640 641 # 290D 642 if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 643 print("WARNING: %s not implemented yet\n" % (parts[0])) 644 continue 645 646 # 2906 647 if parts[0] == 'VALID_RANGE': 648 print("WARNING: %s not implemented yet\n" % (parts[0])) 649 continue 650 651 print("WARNING: unknown token: %s\n" % (parts[0])) 652 653def parse(fname_in, fin, fname_out, fout): 654 global handle 655 global total_size 656 657 fout.write(header.format(fname_out, fname_in)) 658 fout.write('{\n') 659 660 parseLines(fname_in, fin, fout) 661 662 serviceDefinitionComplete(fout) 663 write_indent(fout) 664 fout.write("// END\n"); 665 write_indent(fout) 666 write_16(fout,0) 667 fout.write("\n") 668 total_size = total_size + 2 669 670 fout.write("}; // total size %u bytes \n" % total_size); 671 672def listHandles(fout): 673 fout.write('\n\n') 674 fout.write('//\n') 675 fout.write('// list service handle ranges\n') 676 fout.write('//\n') 677 for define in defines_for_services: 678 fout.write(define) 679 fout.write('\n') 680 fout.write('\n') 681 fout.write('//\n') 682 fout.write('// list mapping between characteristics and handles\n') 683 fout.write('//\n') 684 for define in defines_for_characteristics: 685 fout.write(define) 686 fout.write('\n') 687 688if (len(sys.argv) < 3): 689 print(usage) 690 sys.exit(1) 691try: 692 # read defines from bluetooth_gatt.h 693 btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 694 gen_path = btstack_root + '/src/bluetooth_gatt.h' 695 bluetooth_gatt = read_defines(gen_path) 696 697 filename = sys.argv[2] 698 fin = codecs.open (sys.argv[1], encoding='utf-8') 699 fout = open (filename, 'w') 700 parse(sys.argv[1], fin, filename, fout) 701 listHandles(fout) 702 fout.close() 703 print('Created %s' % filename) 704 705except IOError as e: 706 print(usage) 707 sys.exit(1) 708 709print('Compilation successful!\n') 710