1 #!/usr/bin/env python 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 """Creates windows and posix stub files for a given set of signatures. 7 8 For libraries that need to be loaded outside of the standard executable startup 9 path mechanism, stub files need to be generated for the wanted functions. In 10 windows, this is done via "def" files and the delay load mechanism. On a posix 11 system, a set of stub functions need to be generated that dispatch to functions 12 found via dlsym. 13 14 This script takes a set of files, where each file is a list of C-style 15 signatures (one signature per line). The output is either a windows def file, 16 or a header + implementation file of stubs suitable for use in a posix system. 17 18 This script also handles varidiac functions, e.g. 19 void printf(const char* s, ...); 20 21 TODO(hclam): Fix the situation for varidiac functions. 22 Stub for the above function will be generated and inside the stub function it 23 is translated to: 24 void printf(const char* s, ...) { 25 printf_ptr(s, (void*)arg1); 26 } 27 28 Only one argument from the varidiac arguments is used and it will be used as 29 type void*. 30 """ 31 32 __author__ = 'ajwong (at] chromium.org (Albert J. Wong)' 33 34 import optparse 35 import os 36 import re 37 import string 38 import subprocess 39 import sys 40 41 42 class Error(Exception): 43 pass 44 45 46 class BadSignatureError(Error): 47 pass 48 49 50 class SubprocessError(Error): 51 def __init__(self, message, error_code): 52 Error.__init__(self) 53 self.message = message 54 self.error_code = error_code 55 56 def __str__(self): 57 return 'Failed with code %s: %s' % (self.message, repr(self.error_code)) 58 59 60 # Regular expression used to parse function signatures in the input files. 61 # The regex is built around identifying the "identifier" for the function name. 62 # We consider the identifier to be the string that follows these constraints: 63 # 64 # 1) Starts with [_a-ZA-Z] (C++ spec 2.10). 65 # 2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10). 66 # 3) Preceeds an opening parenthesis by 0 or more whitespace chars. 67 # 68 # From that, all preceeding characters are considered the return value. 69 # Trailing characters should have a substring matching the form (.*). That 70 # is considered the arguments. 71 SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)' 72 '(?P<name>[_a-zA-Z][_a-zA-Z0-9]+)\s*' 73 '\((?P<params>.*?)\)') 74 75 # Used for generating C++ identifiers. 76 INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]') 77 78 # Constants defning the supported file types options. 79 FILE_TYPE_WIN_X86 = 'windows_lib' 80 FILE_TYPE_WIN_X64 = 'windows_lib_x64' 81 FILE_TYPE_POSIX_STUB = 'posix_stubs' 82 FILE_TYPE_WIN_DEF = 'windows_def' 83 84 # Template for generating a stub function definition. Includes a forward 85 # declaration marking the symbol as weak. This template takes the following 86 # named parameters. 87 # return_type: The return type. 88 # name: The name of the function. 89 # params: The parameters to the function. 90 # return_prefix: 'return ' if this function is not void. '' otherwise. 91 # arg_list: The arguments used to call the stub function. 92 STUB_FUNCTION_DEFINITION = ( 93 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak)); 94 %(return_type)s %(name)s(%(params)s) { 95 %(return_prefix)s%(name)s_ptr(%(arg_list)s); 96 }""") 97 98 # Template for generating a variadic stub function definition with return 99 # value. 100 # Includes a forward declaration marking the symbol as weak. 101 # This template takes the following named parameters. 102 # return_type: The return type. 103 # name: The name of the function. 104 # params: The parameters to the function. 105 # arg_list: The arguments used to call the stub function without the 106 # variadic argument. 107 # last_named_arg: Name of the last named argument before the variadic 108 # argument. 109 VARIADIC_STUB_FUNCTION_DEFINITION = ( 110 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak)); 111 %(return_type)s %(name)s(%(params)s) { 112 va_list args___; 113 va_start(args___, %(last_named_arg)s); 114 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*)); 115 va_end(args___); 116 return ret___; 117 }""") 118 119 # Template for generating a variadic stub function definition without 120 # return value. 121 # Includes a forward declaration marking the symbol as weak. 122 # This template takes the following named parameters. 123 # name: The name of the function. 124 # params: The parameters to the function. 125 # arg_list: The arguments used to call the stub function without the 126 # variadic argument. 127 # last_named_arg: Name of the last named argument before the variadic 128 # argument. 129 VOID_VARIADIC_STUB_FUNCTION_DEFINITION = ( 130 """extern void %(name)s(%(params)s) __attribute__((weak)); 131 void %(name)s(%(params)s) { 132 va_list args___; 133 va_start(args___, %(last_named_arg)s); 134 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*)); 135 va_end(args___); 136 }""") 137 138 # Template for the preamble for the stub header file with the header guards, 139 # standard set of includes, and namespace opener. This template takes the 140 # following named parameters: 141 # guard_name: The macro to use as the header guard. 142 # namespace: The namespace for the stub functions. 143 STUB_HEADER_PREAMBLE = """// This is generated file. Do not modify directly. 144 145 #ifndef %(guard_name)s 146 #define %(guard_name)s 147 148 #include <map> 149 #include <string> 150 #include <vector> 151 152 #include "base/logging.h" 153 154 namespace %(namespace)s { 155 """ 156 157 # Template for the end of the stub header. This closes the namespace and the 158 # header guards. This template takes the following named parameters: 159 # guard_name: The macro to use as the header guard. 160 # namespace: The namespace for the stub functions. 161 STUB_HEADER_CLOSER = """} // namespace %(namespace)s 162 163 #endif // %(guard_name)s 164 """ 165 166 # The standard includes needed for the stub implementation file. Takes one 167 # string substition with the path to the associated stub header file. 168 IMPLEMENTATION_PREAMBLE = """// This is generated file. Do not modify directly. 169 170 #include "%s" 171 172 #include <stdlib.h> // For NULL. 173 #include <dlfcn.h> // For dysym, dlopen. 174 175 #include <map> 176 #include <vector> 177 """ 178 179 # The start and end templates for the enum definitions used by the Umbrella 180 # initializer. 181 UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer. 182 enum StubModules { 183 """ 184 UMBRELLA_ENUM_END = """ kNumStubModules 185 }; 186 187 """ 188 189 # Start and end of the extern "C" section for the implementation contents. 190 IMPLEMENTATION_CONTENTS_C_START = """extern "C" { 191 192 """ 193 IMPLEMENTATION_CONTENTS_C_END = """ 194 } // extern "C" 195 196 197 """ 198 199 # Templates for the start and end of a namespace. Takes one parameter, the 200 # namespace name. 201 NAMESPACE_START = """namespace %s { 202 203 """ 204 NAMESPACE_END = """} // namespace %s 205 206 """ 207 208 # Comment to include before the section declaring all the function pointers 209 # used by the stub functions. 210 FUNCTION_POINTER_SECTION_COMMENT = ( 211 """// Static pointers that will hold the location of the real function 212 // implementations after the module has been loaded. 213 """) 214 215 # Template for the module initialization check function. This template 216 # takes two parameteres: the function name, and the conditional used to 217 # verify the module's initialization. 218 MODULE_INITIALIZATION_CHECK_FUNCTION = ( 219 """// Returns true if all stubs have been properly initialized. 220 bool %s() { 221 if (%s) { 222 return true; 223 } else { 224 return false; 225 } 226 } 227 228 """) 229 230 # Template for the line that initialize the stub pointer. This template takes 231 # the following named parameters: 232 # name: The name of the function. 233 # return_type: The return type. 234 # params: The parameters to the function. 235 STUB_POINTER_INITIALIZER = """ %(name)s_ptr = 236 reinterpret_cast<%(return_type)s (*)(%(parameters)s)>( 237 dlsym(module, "%(name)s")); 238 VLOG_IF(1, !%(name)s_ptr) << "Couldn't load %(name)s, dlerror() says:\\n" 239 << dlerror(); 240 """ 241 242 # Template for module initializer function start and end. This template takes 243 # one parameter which is the initializer function name. 244 MODULE_INITIALIZE_START = """// Initializes the module stubs. 245 void %s(void* module) { 246 """ 247 MODULE_INITIALIZE_END = """} 248 249 """ 250 251 # Template for module uninitializer function start and end. This template 252 # takes one parameter which is the initializer function name. 253 MODULE_UNINITIALIZE_START = ( 254 """// Uninitialize the module stubs. Reset pointers to NULL. 255 void %s() { 256 """) 257 MODULE_UNINITIALIZE_END = """} 258 259 """ 260 261 262 # Open namespace and add typedef for internal data structures used by the 263 # umbrella initializer. 264 UMBRELLA_INITIALIZER_START = """namespace %s { 265 typedef std::map<StubModules, void*> StubHandleMap; 266 """ 267 268 # Function close DSOs on error and clean up dangling references. 269 UMBRELLA_INITIALIZER_CLEANUP_FUNCTION = ( 270 """static void CloseLibraries(StubHandleMap* stub_handles) { 271 for (StubHandleMap::const_iterator it = stub_handles->begin(); 272 it != stub_handles->end(); 273 ++it) { 274 dlclose(it->second); 275 } 276 277 stub_handles->clear(); 278 } 279 """) 280 281 # Function to initialize each DSO for the given paths. 282 UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START = ( 283 """bool InitializeStubs(const StubPathMap& path_map) { 284 StubHandleMap opened_libraries; 285 for (int i = 0; i < kNumStubModules; ++i) { 286 StubModules cur_module = static_cast<StubModules>(i); 287 // If a module is missing, we fail. 288 StubPathMap::const_iterator it = path_map.find(cur_module); 289 if (it == path_map.end()) { 290 CloseLibraries(&opened_libraries); 291 return false; 292 } 293 294 // Otherwise, attempt to dlopen the library. 295 const std::vector<std::string>& paths = it->second; 296 bool module_opened = false; 297 for (std::vector<std::string>::const_iterator dso_path = paths.begin(); 298 !module_opened && dso_path != paths.end(); 299 ++dso_path) { 300 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY); 301 if (handle != NULL) { 302 module_opened = true; 303 opened_libraries[cur_module] = handle; 304 } else { 305 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, " 306 << "dlerror() says:\\n" << dlerror(); 307 } 308 } 309 310 if (!module_opened) { 311 CloseLibraries(&opened_libraries); 312 return false; 313 } 314 } 315 """) 316 317 # Template to generate code to check if each module initializer correctly 318 # completed, and cleanup on failures. This template takes the following 319 # named parameters. 320 # conditional: The conditional expression for successful initialization. 321 # uninitializers: The statements needed to uninitialize the modules. 322 UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP = ( 323 """ // Check that each module is initialized correctly. 324 // Close all previously opened libraries on failure. 325 if (%(conditional)s) { 326 %(uninitializers)s; 327 CloseLibraries(&opened_libraries); 328 return false; 329 } 330 331 return true; 332 } 333 """) 334 335 # Template for Initialize, Unininitialize, and IsInitialized functions for each 336 # module. This template takes the following named parameters: 337 # initialize: Name of the Initialize function. 338 # uninitialize: Name of the Uninitialize function. 339 # is_initialized: Name of the IsInitialized function. 340 MODULE_FUNCTION_PROTOTYPES = """bool %(is_initialized)s(); 341 void %(initialize)s(void* module); 342 void %(uninitialize)s(); 343 344 """ 345 346 # Template for umbrella initializer declaration and associated datatypes. 347 UMBRELLA_INITIALIZER_PROTOTYPE = ( 348 """typedef std::map<StubModules, std::vector<std::string> > StubPathMap; 349 350 // Umbrella initializer for all the modules in this stub file. 351 bool InitializeStubs(const StubPathMap& path_map); 352 """) 353 354 355 def ExtractModuleName(infile_path): 356 """Infers the module name from the input file path. 357 358 The input filename is supposed to be in the form "ModuleName.sigs". 359 This function splits the filename from the extention on that basename of 360 the path and returns that as the module name. 361 362 Args: 363 infile_path: String holding the path to the input file. 364 365 Returns: 366 The module name as a string. 367 """ 368 basename = os.path.basename(infile_path) 369 370 # This loop continously removes suffixes of the filename separated by a "." 371 # character. 372 while 1: 373 new_basename = os.path.splitext(basename)[0] 374 if basename == new_basename: 375 break 376 else: 377 basename = new_basename 378 return basename 379 380 381 def ParseSignatures(infile): 382 """Parses function signatures in the input file. 383 384 This function parses a file of signatures into a list of dictionaries that 385 represent the function signatures in the input file. Each dictionary has 386 the following keys: 387 return_type: A string with the return type. 388 name: A string with the name of the function. 389 params: A list of each function parameter declaration (type + name) 390 391 The format of the input file is one C-style function signature per line, no 392 trailing semicolon. Empty lines are allowed. An empty line is a line that 393 consists purely of whitespace. Lines that begin with a # are considered 394 comment lines and are ignored. 395 396 We assume that "int foo(void)" is the same as "int foo()", which is not 397 true in C where "int foo()" is equivalent to "int foo(...)". Our generated 398 code is C++, and we do not handle varargs, so this is a case that can be 399 ignored for now. 400 401 Args: 402 infile: File object holding a text file of function signatures. 403 404 Returns: 405 A list of dictionaries, where each dictionary represents one function 406 signature. 407 408 Raises: 409 BadSignatureError: A line could not be parsed as a signature. 410 """ 411 signatures = [] 412 for line in infile: 413 line = line.strip() 414 if line and line[0] != '#': 415 m = SIGNATURE_REGEX.match(line) 416 if m is None: 417 raise BadSignatureError('Unparsable line: %s' % line) 418 signatures.append( 419 {'return_type': m.group('return_type').strip(), 420 'name': m.group('name').strip(), 421 'params': [arg.strip() for arg in m.group('params').split(',')]}) 422 return signatures 423 424 425 def WriteWindowsDefFile(module_name, signatures, outfile): 426 """Writes a windows def file to the given output file object. 427 428 The def file format is basically a list of function names. Generation is 429 simple. After outputting the LIBRARY and EXPORTS lines, print out each 430 function name, one to a line, preceeded by 2 spaces. 431 432 Args: 433 module_name: The name of the module we are writing a stub for. 434 signatures: The list of signature hashes, as produced by ParseSignatures, 435 to create stubs for. 436 outfile: File handle to populate with definitions. 437 """ 438 outfile.write('LIBRARY %s\n' % module_name) 439 outfile.write('EXPORTS\n') 440 441 for sig in signatures: 442 outfile.write(' %s\n' % sig['name']) 443 444 445 def QuietRun(args, filter=None, write_to=sys.stdout): 446 """Invoke |args| as command via subprocess.Popen, filtering lines starting 447 with |filter|.""" 448 popen = subprocess.Popen(args, stdout=subprocess.PIPE) 449 out, _ = popen.communicate() 450 for line in out.splitlines(): 451 if not filter or not line.startswith(filter): 452 write_to.write(line + '\n') 453 return popen.returncode 454 455 456 def CreateWindowsLib(module_name, signatures, intermediate_dir, outdir_path, 457 machine): 458 """Creates a windows library file. 459 460 Calling this function will create a lib file in the outdir_path that exports 461 the signatures passed into the object. A temporary def file will be created 462 in the intermediate_dir. 463 464 Args: 465 module_name: The name of the module we are writing a stub for. 466 signatures: The list of signature hashes, as produced by ParseSignatures, 467 to create stubs for. 468 intermediate_dir: The directory where the generated .def files should go. 469 outdir_path: The directory where generated .lib files should go. 470 machine: String holding the machine type, 'X86' or 'X64'. 471 472 Raises: 473 SubprocessError: If invoking the windows "lib" tool fails, this is raised 474 with the error code. 475 """ 476 def_file_path = os.path.join(intermediate_dir, 477 module_name + '.def') 478 lib_file_path = os.path.join(outdir_path, 479 module_name + '.lib') 480 outfile = open(def_file_path, 'w') 481 try: 482 WriteWindowsDefFile(module_name, signatures, outfile) 483 finally: 484 outfile.close() 485 486 # Invoke the "lib" program on Windows to create stub .lib files for the 487 # generated definitions. These .lib files can then be used during 488 # delayloading of the dynamic libraries. 489 ret = QuietRun(['lib', '/nologo', 490 '/machine:' + machine, 491 '/def:' + def_file_path, 492 '/out:' + lib_file_path], 493 filter=' Creating library') 494 if ret != 0: 495 raise SubprocessError( 496 'Failed creating %s for %s' % (lib_file_path, def_file_path), 497 ret) 498 499 500 class PosixStubWriter(object): 501 """Creates a file of stub functions for a library that is opened via dlopen. 502 503 Windows provides a function in their compiler known as delay loading, which 504 effectively generates a set of stub functions for a dynamic library that 505 delays loading of the dynamic library/resolution of the symbols until one of 506 the needed functions are accessed. 507 508 In posix, RTLD_LAZY does something similar with DSOs. This is the default 509 link mode for DSOs. However, even though the symbol is not resolved until 510 first usage, the DSO must be present at load time of the main binary. 511 512 To simulate the windows delay load procedure, we need to create a set of 513 stub functions that allow for correct linkage of the main binary, but 514 dispatch to the dynamically resolved symbol when the module is initialized. 515 516 This class takes a list of function signatures, and generates a set of stub 517 functions plus initialization code for them. 518 """ 519 520 def __init__(self, module_name, signatures): 521 """Initializes PosixStubWriter for this set of signatures and module_name. 522 523 Args: 524 module_name: The name of the module we are writing a stub for. 525 signatures: The list of signature hashes, as produced by ParseSignatures, 526 to create stubs for. 527 """ 528 self.signatures = signatures 529 self.module_name = module_name 530 531 @classmethod 532 def CStyleIdentifier(cls, identifier): 533 """Generates a C style identifier. 534 535 The module_name has all invalid identifier characters removed (anything 536 that's not [_a-zA-Z0-9]) and is run through string.capwords to try 537 and approximate camel case. 538 539 Args: 540 identifier: The string with the module name to turn to C-style. 541 542 Returns: 543 A string that can be used as part of a C identifier. 544 """ 545 return string.capwords(re.sub(INVALID_C_IDENT_CHARS, '', identifier)) 546 547 @classmethod 548 def EnumName(cls, module_name): 549 """Gets the enum name for the module. 550 551 Takes the module name and creates a suitable enum name. The module_name 552 is munged to be a valid C identifier then prefixed with the string 553 "kModule" to generate a Google style enum name. 554 555 Args: 556 module_name: The name of the module to generate an enum name for. 557 558 Returns: 559 A string with the name of the enum value representing this module. 560 """ 561 return 'kModule%s' % PosixStubWriter.CStyleIdentifier(module_name) 562 563 @classmethod 564 def IsInitializedName(cls, module_name): 565 """Gets the name of function that checks initialization of this module. 566 567 The name is in the format IsModuleInitialized. Where "Module" is replaced 568 with the module name, munged to be a valid C identifier. 569 570 Args: 571 module_name: The name of the module to generate the function name for. 572 573 Returns: 574 A string with the name of the initialization check function. 575 """ 576 return 'Is%sInitialized' % PosixStubWriter.CStyleIdentifier(module_name) 577 578 @classmethod 579 def InitializeModuleName(cls, module_name): 580 """Gets the name of the function that initializes this module. 581 582 The name is in the format InitializeModule. Where "Module" is replaced 583 with the module name, munged to be a valid C identifier. 584 585 Args: 586 module_name: The name of the module to generate the function name for. 587 588 Returns: 589 A string with the name of the initialization function. 590 """ 591 return 'Initialize%s' % PosixStubWriter.CStyleIdentifier(module_name) 592 593 @classmethod 594 def UninitializeModuleName(cls, module_name): 595 """Gets the name of the function that uninitializes this module. 596 597 The name is in the format UninitializeModule. Where "Module" is replaced 598 with the module name, munged to be a valid C identifier. 599 600 Args: 601 module_name: The name of the module to generate the function name for. 602 603 Returns: 604 A string with the name of the uninitialization function. 605 """ 606 return 'Uninitialize%s' % PosixStubWriter.CStyleIdentifier(module_name) 607 608 @classmethod 609 def StubFunctionPointer(cls, signature): 610 """Generates a function pointer declaration for the given signature. 611 612 Args: 613 signature: A signature hash, as produced by ParseSignatures, 614 representating the function signature. 615 616 Returns: 617 A string with the declaration of the function pointer for the signature. 618 """ 619 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature['return_type'], 620 signature['name'], 621 ', '.join(signature['params'])) 622 623 @classmethod 624 def StubFunction(cls, signature): 625 """Generates a stub function definition for the given signature. 626 627 The function definitions are created with __attribute__((weak)) so that 628 they may be overridden by a real static link or mock versions to be used 629 when testing. 630 631 Args: 632 signature: A signature hash, as produced by ParseSignatures, 633 representating the function signature. 634 635 Returns: 636 A string with the stub function definition. 637 """ 638 return_prefix = '' 639 if signature['return_type'] != 'void': 640 return_prefix = 'return ' 641 642 # Generate the argument list. 643 arguments = [re.split('[\*& ]', arg)[-1].strip() for arg in 644 signature['params']] 645 arg_list = ', '.join(arguments) 646 if arg_list == 'void': 647 arg_list = '' 648 649 if arg_list != '' and len(arguments) > 1 and arguments[-1] == '...': 650 # If the last argment is ... then this is a variadic function. 651 if return_prefix != '': 652 return VARIADIC_STUB_FUNCTION_DEFINITION % { 653 'return_type': signature['return_type'], 654 'name': signature['name'], 655 'params': ', '.join(signature['params']), 656 'arg_list': ', '.join(arguments[0:-1]), 657 'last_named_arg': arguments[-2]} 658 else: 659 return VOID_VARIADIC_STUB_FUNCTION_DEFINITION % { 660 'name': signature['name'], 661 'params': ', '.join(signature['params']), 662 'arg_list': ', '.join(arguments[0:-1]), 663 'last_named_arg': arguments[-2]} 664 else: 665 # This is a regular function. 666 return STUB_FUNCTION_DEFINITION % { 667 'return_type': signature['return_type'], 668 'name': signature['name'], 669 'params': ', '.join(signature['params']), 670 'return_prefix': return_prefix, 671 'arg_list': arg_list} 672 673 @classmethod 674 def WriteImplementationPreamble(cls, header_path, outfile): 675 """Write the necessary includes for the implementation file. 676 677 Args: 678 header_path: The path to the header file. 679 outfile: The file handle to populate. 680 """ 681 outfile.write(IMPLEMENTATION_PREAMBLE % header_path) 682 683 @classmethod 684 def WriteUmbrellaInitializer(cls, module_names, namespace, outfile): 685 """Writes a single function that will open + initialize each module. 686 687 This intializer will take in an stl map of that lists the correct 688 dlopen target for each module. The map type is 689 std::map<enum StubModules, vector<std::string>> which matches one module 690 to a list of paths to try in dlopen. 691 692 This function is an all-or-nothing function. If any module fails to load, 693 all other modules are dlclosed, and the function returns. Though it is 694 not enforced, this function should only be called once. 695 696 Args: 697 module_names: A list with the names of the modules in this stub file. 698 namespace: The namespace these functions should be in. 699 outfile: The file handle to populate with pointer definitions. 700 """ 701 outfile.write(UMBRELLA_INITIALIZER_START % namespace) 702 outfile.write(UMBRELLA_INITIALIZER_CLEANUP_FUNCTION) 703 704 # Create the initializaiton function that calls all module initializers, 705 # checks if they succeeded, and backs out module loads on an error. 706 outfile.write(UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START) 707 outfile.write( 708 '\n // Initialize each module if we have not already failed.\n') 709 for module in module_names: 710 outfile.write(' %s(opened_libraries[%s]);\n' % 711 (PosixStubWriter.InitializeModuleName(module), 712 PosixStubWriter.EnumName(module))) 713 outfile.write('\n') 714 715 # Output code to check the initialization status, clean up on error. 716 initializer_checks = ['!%s()' % PosixStubWriter.IsInitializedName(name) 717 for name in module_names] 718 uninitializers = ['%s()' % PosixStubWriter.UninitializeModuleName(name) 719 for name in module_names] 720 outfile.write(UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP % { 721 'conditional': ' ||\n '.join(initializer_checks), 722 'uninitializers': ';\n '.join(uninitializers)}) 723 outfile.write('\n} // namespace %s\n' % namespace) 724 725 @classmethod 726 def WriteHeaderContents(cls, module_names, namespace, header_guard, outfile): 727 """Writes a header file for the stub file generated for module_names. 728 729 The header file exposes the following: 730 1) An enum, StubModules, listing with an entry for each enum. 731 2) A typedef for a StubPathMap allowing for specification of paths to 732 search for each module. 733 3) The IsInitialized/Initialize/Uninitialize functions for each module. 734 4) An umbrella initialize function for all modules. 735 736 Args: 737 module_names: A list with the names of each module in this stub file. 738 namespace: The namespace these functions should be in. 739 header_guard: The macro to use as our header guard. 740 outfile: The output handle to populate. 741 """ 742 outfile.write(STUB_HEADER_PREAMBLE % 743 {'guard_name': header_guard, 'namespace': namespace}) 744 745 # Generate the Initializer protoypes for each module. 746 outfile.write('// Individual module initializer functions.\n') 747 for name in module_names: 748 outfile.write(MODULE_FUNCTION_PROTOTYPES % { 749 'is_initialized': PosixStubWriter.IsInitializedName(name), 750 'initialize': PosixStubWriter.InitializeModuleName(name), 751 'uninitialize': PosixStubWriter.UninitializeModuleName(name)}) 752 753 # Generate the enum for umbrella initializer. 754 outfile.write(UMBRELLA_ENUM_START) 755 outfile.write(' %s = 0,\n' % PosixStubWriter.EnumName(module_names[0])) 756 for name in module_names[1:]: 757 outfile.write(' %s,\n' % PosixStubWriter.EnumName(name)) 758 outfile.write(UMBRELLA_ENUM_END) 759 760 outfile.write(UMBRELLA_INITIALIZER_PROTOTYPE) 761 outfile.write(STUB_HEADER_CLOSER % { 762 'namespace': namespace, 'guard_name': 763 header_guard}) 764 765 def WriteImplementationContents(self, namespace, outfile): 766 """Given a file handle, write out the stub definitions for this module. 767 768 Args: 769 namespace: The namespace these functions should be in. 770 outfile: The file handle to populate. 771 """ 772 outfile.write(IMPLEMENTATION_CONTENTS_C_START) 773 self.WriteFunctionPointers(outfile) 774 self.WriteStubFunctions(outfile) 775 outfile.write(IMPLEMENTATION_CONTENTS_C_END) 776 777 outfile.write(NAMESPACE_START % namespace) 778 self.WriteModuleInitializeFunctions(outfile) 779 outfile.write(NAMESPACE_END % namespace) 780 781 def WriteFunctionPointers(self, outfile): 782 """Write the function pointer declarations needed by the stubs. 783 784 We need function pointers to hold the actual location of the function 785 implementation returned by dlsym. This function outputs a pointer 786 definition for each signature in the module. 787 788 Pointers will be named with the following pattern "FuntionName_ptr". 789 790 Args: 791 outfile: The file handle to populate with pointer definitions. 792 """ 793 outfile.write(FUNCTION_POINTER_SECTION_COMMENT) 794 795 for sig in self.signatures: 796 outfile.write('%s\n' % PosixStubWriter.StubFunctionPointer(sig)) 797 outfile.write('\n') 798 799 def WriteStubFunctions(self, outfile): 800 """Write the function stubs to handle dispatching to real implementations. 801 802 Functions that have a return type other than void will look as follows: 803 804 ReturnType FunctionName(A a) { 805 return FunctionName_ptr(a); 806 } 807 808 Functions with a return type of void will look as follows: 809 810 void FunctionName(A a) { 811 FunctionName_ptr(a); 812 } 813 814 Args: 815 outfile: The file handle to populate. 816 """ 817 outfile.write('// Stubs that dispatch to the real implementations.\n') 818 for sig in self.signatures: 819 outfile.write('%s\n' % PosixStubWriter.StubFunction(sig)) 820 821 def WriteModuleInitializeFunctions(self, outfile): 822 """Write functions to initialize/query initlialization of the module. 823 824 This creates 2 functions IsModuleInitialized and InitializeModule where 825 "Module" is replaced with the module name, first letter capitalized. 826 827 The InitializeModule function takes a handle that is retrieved from dlopen 828 and attempts to assign each function pointer above via dlsym. 829 830 The IsModuleInitialized returns true if none of the required functions 831 pointers are NULL. 832 833 Args: 834 outfile: The file handle to populate. 835 """ 836 ptr_names = ['%s_ptr' % sig['name'] for sig in self.signatures] 837 838 # Construct the conditional expression to check the initialization of 839 # all the function pointers above. It should generate a conjuntion 840 # with each pointer on its own line, indented by six spaces to match 841 # the indentation level of MODULE_INITIALIZATION_CHECK_FUNCTION. 842 initialization_conditional = ' &&\n '.join(ptr_names) 843 844 outfile.write(MODULE_INITIALIZATION_CHECK_FUNCTION % ( 845 PosixStubWriter.IsInitializedName(self.module_name), 846 initialization_conditional)) 847 848 # Create function that initializes the module. 849 outfile.write(MODULE_INITIALIZE_START % 850 PosixStubWriter.InitializeModuleName(self.module_name)) 851 for sig in self.signatures: 852 outfile.write(STUB_POINTER_INITIALIZER % { 853 'name': sig['name'], 854 'return_type': sig['return_type'], 855 'parameters': ', '.join(sig['params'])}) 856 outfile.write(MODULE_INITIALIZE_END) 857 858 # Create function that uninitializes the module (sets all pointers to 859 # NULL). 860 outfile.write(MODULE_UNINITIALIZE_START % 861 PosixStubWriter.UninitializeModuleName(self.module_name)) 862 for sig in self.signatures: 863 outfile.write(' %s_ptr = NULL;\n' % sig['name']) 864 outfile.write(MODULE_UNINITIALIZE_END) 865 866 867 def CreateOptionParser(): 868 """Creates an OptionParser for the configuration options of script. 869 870 Returns: 871 A OptionParser object. 872 """ 873 parser = optparse.OptionParser(usage='usage: %prog [options] input') 874 parser.add_option('-o', 875 '--output', 876 dest='out_dir', 877 default=None, 878 help='Output location.') 879 parser.add_option('-i', 880 '--intermediate_dir', 881 dest='intermediate_dir', 882 default=None, 883 help=('Location of intermediate files. Ignored for %s type' 884 % FILE_TYPE_WIN_DEF)) 885 parser.add_option('-t', 886 '--type', 887 dest='type', 888 default=None, 889 help=('Type of file. Valid types are "%s" or "%s" or "%s" ' 890 'or "%s"' % 891 (FILE_TYPE_POSIX_STUB, FILE_TYPE_WIN_X86, 892 FILE_TYPE_WIN_X64, FILE_TYPE_WIN_DEF))) 893 parser.add_option('-s', 894 '--stubfile_name', 895 dest='stubfile_name', 896 default=None, 897 help=('Name of posix_stubs output file. Only valid with ' 898 '%s type.' % FILE_TYPE_POSIX_STUB)) 899 parser.add_option('-p', 900 '--path_from_source', 901 dest='path_from_source', 902 default=None, 903 help=('The relative path from the project root that the ' 904 'generated file should consider itself part of (eg. ' 905 'third_party/ffmpeg). This is used to generate the ' 906 'header guard and namespace for our initializer ' 907 'functions and does NOT affect the physical output ' 908 'location of the file like -o does. Ignored for ' 909 '%s and %s types.' % 910 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64))) 911 parser.add_option('-e', 912 '--extra_stub_header', 913 dest='extra_stub_header', 914 default=None, 915 help=('File to insert after the system includes in the ' 916 'generated stub implemenation file. Ignored for ' 917 '%s and %s types.' % 918 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64))) 919 parser.add_option('-m', 920 '--module_name', 921 dest='module_name', 922 default=None, 923 help=('Name of output DLL or LIB for DEF creation using ' 924 '%s type.' % FILE_TYPE_WIN_DEF)) 925 926 return parser 927 928 929 def ParseOptions(): 930 """Parses the options and terminates program if they are not sane. 931 932 Returns: 933 The pair (optparse.OptionValues, [string]), that is the output of 934 a successful call to parser.parse_args(). 935 """ 936 parser = CreateOptionParser() 937 options, args = parser.parse_args() 938 939 if not args: 940 parser.error('No inputs specified') 941 942 if options.out_dir is None: 943 parser.error('Output location not specified') 944 945 if (options.type not in 946 [FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64, FILE_TYPE_POSIX_STUB, 947 FILE_TYPE_WIN_DEF]): 948 parser.error('Invalid output file type: %s' % options.type) 949 950 if options.type == FILE_TYPE_POSIX_STUB: 951 if options.stubfile_name is None: 952 parser.error('Output file name needed for %s' % FILE_TYPE_POSIX_STUB) 953 if options.path_from_source is None: 954 parser.error('Path from source needed for %s' % FILE_TYPE_POSIX_STUB) 955 956 if options.type == FILE_TYPE_WIN_DEF: 957 if options.module_name is None: 958 parser.error('Module name needed for %s' % FILE_TYPE_WIN_DEF) 959 960 return options, args 961 962 963 def EnsureDirExists(dir): 964 """Creates a directory. Does not use the more obvious 'if not exists: create' 965 to avoid race with other invocations of the same code, which will error out 966 on makedirs if another invocation has succeeded in creating the directory 967 since the existence check.""" 968 try: 969 os.makedirs(dir) 970 except: 971 if not os.path.isdir(dir): 972 raise 973 974 975 def CreateOutputDirectories(options): 976 """Creates the intermediate and final output directories. 977 978 Given the parsed options, create the intermediate and final output 979 directories if they do not exist. Returns the paths to both directories 980 as a pair. 981 982 Args: 983 options: An OptionParser.OptionValues object with the parsed options. 984 985 Returns: 986 The pair (out_dir, intermediate_dir), both of which are strings. 987 """ 988 out_dir = os.path.normpath(options.out_dir) 989 intermediate_dir = os.path.normpath(options.intermediate_dir) 990 if intermediate_dir is None: 991 intermediate_dir = out_dir 992 993 EnsureDirExists(out_dir) 994 EnsureDirExists(intermediate_dir) 995 996 return out_dir, intermediate_dir 997 998 999 def CreateWindowsLibForSigFiles(sig_files, out_dir, intermediate_dir, machine): 1000 """For each signature file, create a windows lib. 1001 1002 Args: 1003 sig_files: Array of strings with the paths to each signature file. 1004 out_dir: String holding path to directory where the generated libs go. 1005 intermediate_dir: String holding path to directory generated intermdiate 1006 artifacts. 1007 machine: String holding the machine type, 'X86' or 'X64'. 1008 """ 1009 for input_path in sig_files: 1010 infile = open(input_path, 'r') 1011 try: 1012 signatures = ParseSignatures(infile) 1013 module_name = ExtractModuleName(os.path.basename(input_path)) 1014 CreateWindowsLib(module_name, signatures, intermediate_dir, out_dir, 1015 machine) 1016 finally: 1017 infile.close() 1018 1019 1020 def CreateWindowsDefForSigFiles(sig_files, out_dir, module_name): 1021 """For all signature files, create a single windows def file. 1022 1023 Args: 1024 sig_files: Array of strings with the paths to each signature file. 1025 out_dir: String holding path to directory where the generated def goes. 1026 module_name: Name of the output DLL or LIB which will link in the def file. 1027 """ 1028 signatures = [] 1029 for input_path in sig_files: 1030 infile = open(input_path, 'r') 1031 try: 1032 signatures += ParseSignatures(infile) 1033 finally: 1034 infile.close() 1035 1036 def_file_path = os.path.join( 1037 out_dir, os.path.splitext(os.path.basename(module_name))[0] + '.def') 1038 outfile = open(def_file_path, 'w') 1039 1040 try: 1041 WriteWindowsDefFile(module_name, signatures, outfile) 1042 finally: 1043 outfile.close() 1044 1045 1046 def CreatePosixStubsForSigFiles(sig_files, stub_name, out_dir, 1047 intermediate_dir, path_from_source, 1048 extra_stub_header): 1049 """Create a posix stub library with a module for each signature file. 1050 1051 Args: 1052 sig_files: Array of strings with the paths to each signature file. 1053 stub_name: String with the basename of the generated stub file. 1054 out_dir: String holding path to directory for the .h files. 1055 intermediate_dir: String holding path to directory for the .cc files. 1056 path_from_source: String with relative path of generated files from the 1057 project root. 1058 extra_stub_header: String with path to file of extra lines to insert 1059 into the generated header for the stub library. 1060 """ 1061 header_base_name = stub_name + '.h' 1062 header_path = os.path.join(out_dir, header_base_name) 1063 impl_path = os.path.join(intermediate_dir, stub_name + '.cc') 1064 1065 module_names = [ExtractModuleName(path) for path in sig_files] 1066 namespace = path_from_source.replace('/', '_').lower() 1067 header_guard = '%s_' % namespace.upper() 1068 header_include_path = os.path.join(path_from_source, header_base_name) 1069 1070 # First create the implementation file. 1071 impl_file = open(impl_path, 'w') 1072 try: 1073 # Open the file, and create the preamble which consists of a file 1074 # header plus any necessary includes. 1075 PosixStubWriter.WriteImplementationPreamble(header_include_path, 1076 impl_file) 1077 if extra_stub_header is not None: 1078 extra_header_file = open(extra_stub_header, 'r') 1079 try: 1080 impl_file.write('\n') 1081 for line in extra_header_file: 1082 impl_file.write(line) 1083 impl_file.write('\n') 1084 finally: 1085 extra_header_file.close() 1086 1087 # For each signature file, generate the stub population functions 1088 # for that file. Each file represents one module. 1089 for input_path in sig_files: 1090 name = ExtractModuleName(input_path) 1091 infile = open(input_path, 'r') 1092 try: 1093 signatures = ParseSignatures(infile) 1094 finally: 1095 infile.close() 1096 writer = PosixStubWriter(name, signatures) 1097 writer.WriteImplementationContents(namespace, impl_file) 1098 1099 # Lastly, output the umbrella function for the file. 1100 PosixStubWriter.WriteUmbrellaInitializer(module_names, namespace, 1101 impl_file) 1102 finally: 1103 impl_file.close() 1104 1105 # Then create the associated header file. 1106 header_file = open(header_path, 'w') 1107 try: 1108 PosixStubWriter.WriteHeaderContents(module_names, namespace, 1109 header_guard, header_file) 1110 finally: 1111 header_file.close() 1112 1113 1114 def main(): 1115 options, args = ParseOptions() 1116 out_dir, intermediate_dir = CreateOutputDirectories(options) 1117 1118 if options.type == FILE_TYPE_WIN_X86: 1119 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X86') 1120 elif options.type == FILE_TYPE_WIN_X64: 1121 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X64') 1122 elif options.type == FILE_TYPE_POSIX_STUB: 1123 CreatePosixStubsForSigFiles(args, options.stubfile_name, out_dir, 1124 intermediate_dir, options.path_from_source, 1125 options.extra_stub_header) 1126 elif options.type == FILE_TYPE_WIN_DEF: 1127 CreateWindowsDefForSigFiles(args, out_dir, options.module_name) 1128 1129 1130 if __name__ == '__main__': 1131 main() 1132