Home | History | Annotate | Download | only in generate_stubs
      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