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 #   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.
     93 STUB_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.
    111 VARIADIC_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.
    132 VOID_VARIADIC_STUB_FUNCTION_DEFINITION = (
    133     """extern void %(name)s(%(params)s) __attribute__((weak));
    134 void %(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.
    146 STUB_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 
    157 namespace %(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.
    164 STUB_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.
    171 IMPLEMENTATION_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.
    184 UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer.
    185 enum StubModules {
    186 """
    187 UMBRELLA_ENUM_END = """  kNumStubModules
    188 };
    189 
    190 """
    191 
    192 # Start and end of the extern "C" section for the implementation contents.
    193 IMPLEMENTATION_CONTENTS_C_START = """extern "C" {
    194 
    195 """
    196 IMPLEMENTATION_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.
    204 NAMESPACE_START = """namespace %s {
    205 
    206 """
    207 NAMESPACE_END = """}  // namespace %s
    208 
    209 """
    210 
    211 # Comment to include before the section declaring all the function pointers
    212 # used by the stub functions.
    213 FUNCTION_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.
    221 MODULE_INITIALIZATION_CHECK_FUNCTION = (
    222     """// Returns true if all stubs have been properly initialized.
    223 bool %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.
    238 STUB_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.
    247 MODULE_INITIALIZE_START = """// Initializes the module stubs.
    248 void %s(void* module) {
    249 """
    250 MODULE_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.
    256 MODULE_UNINITIALIZE_START = (
    257     """// Uninitialize the module stubs.  Reset pointers to NULL.
    258 void %s() {
    259 """)
    260 MODULE_UNINITIALIZE_END = """}
    261 
    262 """
    263 
    264 
    265 # Open namespace and add typedef for internal data structures used by the
    266 # umbrella initializer.
    267 UMBRELLA_INITIALIZER_START = """namespace %s {
    268 typedef std::map<StubModules, void*> StubHandleMap;
    269 """
    270 
    271 # Function close DSOs on error and clean up dangling references.
    272 UMBRELLA_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.
    285 UMBRELLA_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.
    325 UMBRELLA_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.
    343 MODULE_FUNCTION_PROTOTYPES = """bool %(is_initialized)s();
    344 void %(initialize)s(void* module);
    345 void %(uninitialize)s();
    346 
    347 """
    348 
    349 # Template for umbrella initializer declaration and associated datatypes.
    350 UMBRELLA_INITIALIZER_PROTOTYPE = (
    351     """typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
    352 
    353 // Umbrella initializer for all the modules in this stub file.
    354 bool InitializeStubs(const StubPathMap& path_map);
    355 """)
    356 
    357 
    358 def 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 
    384 def 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 
    428 def 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 
    448 def 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 
    459 def 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 
    503 class 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 
    877 def 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 
    946 def 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 
    980 def 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 
    992 def 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 
   1016 def 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 
   1042 def 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 
   1068 def 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 
   1138 def 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 
   1156 if __name__ == '__main__':
   1157   main()
   1158