#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Creates Windows and POSIX stub files for a given set of signatures.

For libraries that need to be loaded outside of the standard executable startup
path mechanism, stub files need to be generated for the wanted functions.  In
Windows, this is done via "def" files and the delay load mechanism.  On a POSIX
system, a set of stub functions need to be generated that dispatch to functions
found via dlsym.

This script takes a set of files, where each file is a list of C-style
signatures (one signature per line).  The output is either a Windows def file,
or a header + implementation file of stubs suitable for use in a POSIX system.

This script also handles variadic functions, e.g.
void printf(const char* s, ...);

TODO(hclam): Fix the situation for variadic functions.
Stub for the above function will be generated and inside the stub function it
is translated to:
v
oid printf(const char* s, ...) {
  printf_ptr(s, (void*)arg1);
}

Only one argument from the variadic arguments is used and it will be used as
type void*.
"""

__author__ = 'ajwong@chromium.org (Albert J. Wong)'

import optparse
import os
import re
import string

import subprocess
import sys


class Error(Exception):
  pass


class BadSignatureError(Error):
  pass


class SubprocessError(Error):
  def __init__(self, message, error_code):
    Error.__init__(self)
    self.message = message
    self.error_code = error_code

  def __str__(self):
    return 'Failed with code %s: %s' % (self.message, repr(self.error_code))


# Regular expression used to parse function signatures in the input files.
# The regex is built around identifying the "identifier" for the function name.
# We consider the identifier to be the string that follows these constraints:
#
#   1) Starts with [_a-ZA-Z] (C++ spec 2.10).
#   2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
#   3) Precedes an opening parenthesis by 0 or more whitespace chars.
#
# From that, all preceding characters are considered the return value.
# Trailing characters should have a substring matching the form (.*).  That
# is considered the arguments.
SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)'
                             '(?P<name>[_a-zA-Z][_a-zA-Z0-9]+)\s*'
                             '\((?P<params>.*?)\)')

# Used for generating C++ identifiers.
INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')

# Constants defining the supported file types options.
FILE_TYPE_WIN_X86 = 'windows_lib'
FILE_TYPE_WIN_X64 = 'windows_lib_x64'
FILE_TYPE_POSIX_STUB = 'posix_stubs'
FILE_TYPE_WIN_DEF = 'windows_def'

# Template for generating a stub function definition.  Includes a forward
# declaration marking the symbol as weak.  This template takes the following
# named parameters.
#   return_type: The return type.
#   export: The macro used to alter the stub's visibility.
#   name: The name of the function.
#   params: The parameters to the function.
#   return_prefix: 'return ' if this function is not void. '' otherwise.
#   arg_list: The arguments used to call the stub function.
STUB_FUNCTION_DEFINITION = (
    """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
%(return_type)s %(export)s %(name)s(%(params)s) {
  %(return_prefix)s%(name)s_ptr(%(arg_list)s);
}""")

# Template for generating a variadic stub function definition with return
# value.
# Includes a forward declaration marking the symbol as weak.
# This template takes the following named parameters.
#   return_type: The return type.
#   export: The macro used to alter the stub's visibility.
#   name: The name of the function.
#   params: The parameters to the function.
#   arg_list: The arguments used to call the stub function without the
#             variadic argument.
#   last_named_arg: Name of the last named argument before the variadic
#                   argument.
VARIADIC_STUB_FUNCTION_DEFINITION = (
    """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
%(return_type)s %(export)s %(name)s(%(params)s) {
  va_list args___;
  va_start(args___, %(last_named_arg)s);
  %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
  va_end(args___);
  return ret___;
}""")

# Template for generating a variadic stub function definition without
# return value.
# Includes a forward declaration marking the symbol as weak.
# This template takes the following named parameters.
#   name: The name of the function.
#   params: The parameters to the function.
#   export: The macro used to alter the stub's visibility.
#   arg_list: The arguments used to call the stub function without the
#             variadic argument.
#   last_named_arg: Name of the last named argument before the variadic
#                   argument.
VOID_VARIADIC_STUB_FUNCTION_DEFINITION = (
    """extern void %(name)s(%(params)s) __attribute__((weak));
void %(export)s %(name)s(%(params)s) {
  va_list args___;
  va_start(args___, %(last_named_arg)s);
  %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
  va_end(args___);
}""")

# Template for the preamble for the stub header file with the header guards,
# standard set of includes, and namespace opener.  This template takes the
# following named parameters:
#   guard_name: The macro to use as the header guard.
#   namespace: The namespace for the stub functions.
#   logging_include: Header file where the logging function is defined.
STUB_HEADER_PREAMBLE = """// This is generated file. Do not modify directly.

#ifndef %(guard_name)s
#define %(guard_name)s

#include <stdarg.h>
#include <map>
#include <string>
#include <vector>

#include "%(logging_include)s"

namespace %(namespace)s {
"""

# Template for the end of the stub header. This closes the namespace and the
# header guards.  This template takes the following named parameters:
#   guard_name: The macro to use as the header guard.
#   namespace: The namespace for the stub functions.
STUB_HEADER_CLOSER = """}  // namespace %(namespace)s

#endif  // %(guard_name)s
"""

# The standard includes needed for the stub implementation file.  Takes one
# string substitution with the path to the associated stub header file.
IMPLEMENTATION_PREAMBLE = """// This is generated file. Do not modify directly.

#include "%s"

#include <stdlib.h>  // For NULL.
#include <dlfcn.h>   // For dlsym, dlopen.

#include <map>
#include <vector>
"""

# The start and end templates for the enum definitions used by the Umbrella
# initializer.
UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer.
enum StubModules {
"""
UMBRELLA_ENUM_END = """  kNumStubModules
};

"""

# Start and end of the extern "C" section for the implementation contents.
IMPLEMENTATION_CONTENTS_C_START = """extern "C" {

"""
IMPLEMENTATION_CONTENTS_C_END = """
}  // extern "C"


"""

# Templates for the start and end of a namespace.  Takes one parameter, the
# namespace name.
NAMESPACE_START = """namespace %s {

"""
NAMESPACE_END = """}  // namespace %s

"""

# Comment to include before the section declaring all the function pointers
# used by the stub functions.
FUNCTION_POINTER_SECTION_COMMENT = (
    """// Static pointers that will hold the location of the real function
// implementations after the module has been loaded.
""")

# Template for the module initialization check function.  This template
# takes two parameters: the function name, and the conditional used to
# verify the module's initialization.
MODULE_INITIALIZATION_CHECK_FUNCTION = (
    """// Returns true if all stubs have been properly initialized.
bool %s() {
  if (%s) {
    return true;
  } else {
    return false;
  }
}

""")

# Template for the line that initialize the stub pointer.  This template takes
# the following named parameters:
#   name: The name of the function.
#   return_type: The return type.
#   params: The parameters to the function.
#   logging_function: Function call for error logging.
STUB_POINTER_INITIALIZER = """  %(name)s_ptr =
    reinterpret_cast<%(return_type)s (*)(%(parameters)s)>(
      dlsym(module, "%(name)s"));
  if (!%(na