1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Creates windows and posix stub files for a given set of signatures.
7
8For libraries that need to be loaded outside of the standard executable startup
9path mechanism, stub files need to be generated for the wanted functions.  In
10windows, this is done via "def" files and the delay load mechanism.  On a posix
11system, a set of stub functions need to be generated that dispatch to functions
12found via dlsym.
13
14This script takes a set of files, where each file is a list of C-style
15signatures (one signature per line).  The output is either a windows def file,
16or a header + implementation file of stubs suitable for use in a posix system.
17
18This script also handles varidiac functions, e.g.
19void printf(const char* s, ...);
20
21TODO(hclam): Fix the situation for varidiac functions.
22Stub for the above function will be generated and inside the stub function it
23is translated to:
24void printf(const char* s, ...) {
25  printf_ptr(s, (void*)arg1);
26}
27
28Only one argument from the varidiac arguments is used and it will be used as
29type void*.
30"""
31
32__author__ = 'ajwong@chromium.org (Albert J. Wong)'
33
34import optparse
35import os
36import re
37import string
38import subprocess
39import sys
40
41
42class Error(Exception):
43  pass
44
45
46class BadSignatureError(Error):
47  pass
48
49
50class SubprocessError(Error):
51  def __init__(self, message, error_code):
52    Error.__init__(self)
53    self.message = message
54    self.error_code = error_code
55
56  def __str__(self):
57    return 'Failed with code %s: %s' % (self.message, repr(self.error_code))
58
59
60# Regular expression used to parse function signatures in the input files.
61# The regex is built around identifying the "identifier" for the function name.
62# We consider the identifier to be the string that follows these constraints:
63#
64#   1) Starts with [_a-ZA-Z] (C++ spec 2.10).
65#   2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
66#   3) Preceeds an opening parenthesis by 0 or more whitespace chars.
67#
68# From that, all preceeding characters are considered the return value.
69# Trailing characters should have a substring matching the form (.*).  That
70# is considered the arguments.
71SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)'
72                             '(?P<name>[_a-zA-Z][_a-zA-Z0-9]+)\s*'
73                             '\((?P<params>.*?)\)')
74
75# Used for generating C++ identifiers.
76INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')
77
78# Constants defning the supported file types options.
79FILE_TYPE_WIN_X86 = 'windows_lib'
80FILE_TYPE_WIN_X64 = 'windows_lib_x64'
81FILE_TYPE_POSIX_STUB = 'posix_stubs'
82FILE_TYPE_WIN_DEF = 'windows_def'
83
84# Template for generating a stub function definition.  Includes a forward
85# declaration marking the symbol as weak.  This template takes the following
86# named parameters.
87#   return_type: The return type.
88#   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.
92STUB_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.
109VARIADIC_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.
129VOID_VARIADIC_STUB_FUNCTION_DEFINITION = (
130    """extern void %(name)s(%(params)s) __attribute__((weak));
131void %(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.
143STUB_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
154namespace %(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.
161STUB_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.
168IMPLEMENTATION_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.
181UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer.
182enum StubModules {
183"""
184UMBRELLA_ENUM_END = """  kNumStubModules
185};
186
187"""
188
189# Start and end of the extern "C" section for the implementation contents.
190IMPLEMENTATION_CONTENTS_C_START = """extern "C" {
191
192"""
193IMPLEMENTATION_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.
201NAMESPACE_START = """namespace %s {
202
203"""
204NAMESPACE_END = """}  // namespace %s
205
206"""
207
208# Comment to include before the section declaring all the function pointers
209# used by the stub functions.
210FUNCTION_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.
218MODULE_INITIALIZATION_CHECK_FUNCTION = (
219    """// Returns true if all stubs have been properly initialized.
220bool %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.
235STUB_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.
244MODULE_INITIALIZE_START = """// Initializes the module stubs.
245void %s(void* module) {
246"""
247MODULE_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.
253MODULE_UNINITIALIZE_START = (
254    """// Uninitialize the module stubs.  Reset pointers to NULL.
255void %s() {
256""")
257MODULE_UNINITIALIZE_END = """}
258
259"""
260
261
262# Open namespace and add typedef for internal data structures used by the
263# umbrella initializer.
264UMBRELLA_INITIALIZER_START = """namespace %s {
265typedef std::map<StubModules, void*> StubHandleMap;
266"""
267
268# Function close DSOs on error and clean up dangling references.
269UMBRELLA_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.
282UMBRELLA_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.
322UMBRELLA_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.
340MODULE_FUNCTION_PROTOTYPES = """bool %(is_initialized)s();
341void %(initialize)s(void* module);
342void %(uninitialize)s();
343
344"""
345
346# Template for umbrella initializer declaration and associated datatypes.
347UMBRELLA_INITIALIZER_PROTOTYPE = (
348    """typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
349
350// Umbrella initializer for all the modules in this stub file.
351bool InitializeStubs(const StubPathMap& path_map);
352""")
353
354
355def 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
381def 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
425def 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
445def 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
456def 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
500class 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
867def 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
929def 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
963def 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
975def 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
999def 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
1020def 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
1046def 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
1114def 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
1130if __name__ == '__main__':
1131  main()
1132