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