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