github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/src/third_party/googlemock/scripts/generator/cpp/gmock_class.py (about)

     1  #!/usr/bin/env python
     2  #
     3  # Copyright 2008 Google Inc.  All Rights Reserved.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #      http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  """Generate Google Mock classes from base classes.
    18  
    19  This program will read in a C++ source file and output the Google Mock
    20  classes for the specified classes.  If no class is specified, all
    21  classes in the source file are emitted.
    22  
    23  Usage:
    24    gmock_class.py header-file.h [ClassName]...
    25  
    26  Output is sent to stdout.
    27  """
    28  
    29  __author__ = 'nnorwitz@google.com (Neal Norwitz)'
    30  
    31  
    32  import os
    33  import re
    34  import sys
    35  
    36  from cpp import ast
    37  from cpp import utils
    38  
    39  # Preserve compatibility with Python 2.3.
    40  try:
    41    _dummy = set
    42  except NameError:
    43    import sets
    44    set = sets.Set
    45  
    46  _VERSION = (1, 0, 1)  # The version of this script.
    47  # How many spaces to indent.  Can set me with the INDENT environment variable.
    48  _INDENT = 2
    49  
    50  
    51  def _GenerateMethods(output_lines, source, class_node):
    52    function_type = ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL
    53    ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR
    54    indent = ' ' * _INDENT
    55  
    56    for node in class_node.body:
    57      # We only care about virtual functions.
    58      if (isinstance(node, ast.Function) and
    59          node.modifiers & function_type and
    60          not node.modifiers & ctor_or_dtor):
    61        # Pick out all the elements we need from the original function.
    62        const = ''
    63        if node.modifiers & ast.FUNCTION_CONST:
    64          const = 'CONST_'
    65        return_type = 'void'
    66        if node.return_type:
    67          # Add modifiers like 'const'.
    68          modifiers = ''
    69          if node.return_type.modifiers:
    70            modifiers = ' '.join(node.return_type.modifiers) + ' '
    71          return_type = modifiers + node.return_type.name
    72          template_args = [arg.name for arg in node.return_type.templated_types]
    73          if template_args:
    74            return_type += '<' + ', '.join(template_args) + '>'
    75            if len(template_args) > 1:
    76              for line in [
    77                  '// The following line won\'t really compile, as the return',
    78                  '// type has multiple template arguments.  To fix it, use a',
    79                  '// typedef for the return type.']:
    80                output_lines.append(indent + line)
    81          if node.return_type.pointer:
    82            return_type += '*'
    83          if node.return_type.reference:
    84            return_type += '&'
    85          num_parameters = len(node.parameters)
    86          if len(node.parameters) == 1:
    87            first_param = node.parameters[0]
    88            if source[first_param.start:first_param.end].strip() == 'void':
    89              # We must treat T(void) as a function with no parameters.
    90              num_parameters = 0
    91        tmpl = ''
    92        if class_node.templated_types:
    93          tmpl = '_T'
    94        mock_method_macro = 'MOCK_%sMETHOD%d%s' % (const, num_parameters, tmpl)
    95  
    96        args = ''
    97        if node.parameters:
    98          # Due to the parser limitations, it is impossible to keep comments
    99          # while stripping the default parameters.  When defaults are
   100          # present, we choose to strip them and comments (and produce
   101          # compilable code).
   102          # TODO(nnorwitz@google.com): Investigate whether it is possible to
   103          # preserve parameter name when reconstructing parameter text from
   104          # the AST.
   105          if len([param for param in node.parameters if param.default]) > 0:
   106            args = ', '.join(param.type.name for param in node.parameters)
   107          else:
   108            # Get the full text of the parameters from the start
   109            # of the first parameter to the end of the last parameter.
   110            start = node.parameters[0].start
   111            end = node.parameters[-1].end
   112            # Remove // comments.
   113            args_strings = re.sub(r'//.*', '', source[start:end])
   114            # Condense multiple spaces and eliminate newlines putting the
   115            # parameters together on a single line.  Ensure there is a
   116            # space in an argument which is split by a newline without
   117            # intervening whitespace, e.g.: int\nBar
   118            args = re.sub('  +', ' ', args_strings.replace('\n', ' '))
   119  
   120        # Create the mock method definition.
   121        output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name),
   122                             '%s%s(%s));' % (indent*3, return_type, args)])
   123  
   124  
   125  def _GenerateMocks(filename, source, ast_list, desired_class_names):
   126    processed_class_names = set()
   127    lines = []
   128    for node in ast_list:
   129      if (isinstance(node, ast.Class) and node.body and
   130          # desired_class_names being None means that all classes are selected.
   131          (not desired_class_names or node.name in desired_class_names)):
   132        class_name = node.name
   133        parent_name = class_name
   134        processed_class_names.add(class_name)
   135        class_node = node
   136        # Add namespace before the class.
   137        if class_node.namespace:
   138          lines.extend(['namespace %s {' % n for n in class_node.namespace])  # }
   139          lines.append('')
   140  
   141        # Add template args for templated classes.
   142        if class_node.templated_types:
   143          # TODO(paulchang): The AST doesn't preserve template argument order,
   144          # so we have to make up names here.
   145          # TODO(paulchang): Handle non-type template arguments (e.g.
   146          # template<typename T, int N>).
   147          template_arg_count = len(class_node.templated_types.keys())
   148          template_args = ['T%d' % n for n in range(template_arg_count)]
   149          template_decls = ['typename ' + arg for arg in template_args]
   150          lines.append('template <' + ', '.join(template_decls) + '>')
   151          parent_name += '<' + ', '.join(template_args) + '>'
   152  
   153        # Add the class prolog.
   154        lines.append('class Mock%s : public %s {'  # }
   155                     % (class_name, parent_name))
   156        lines.append('%spublic:' % (' ' * (_INDENT // 2)))
   157  
   158        # Add all the methods.
   159        _GenerateMethods(lines, source, class_node)
   160  
   161        # Close the class.
   162        if lines:
   163          # If there are no virtual methods, no need for a public label.
   164          if len(lines) == 2:
   165            del lines[-1]
   166  
   167          # Only close the class if there really is a class.
   168          lines.append('};')
   169          lines.append('')  # Add an extra newline.
   170  
   171        # Close the namespace.
   172        if class_node.namespace:
   173          for i in range(len(class_node.namespace)-1, -1, -1):
   174            lines.append('}  // namespace %s' % class_node.namespace[i])
   175          lines.append('')  # Add an extra newline.
   176  
   177    if desired_class_names:
   178      missing_class_name_list = list(desired_class_names - processed_class_names)
   179      if missing_class_name_list:
   180        missing_class_name_list.sort()
   181        sys.stderr.write('Class(es) not found in %s: %s\n' %
   182                         (filename, ', '.join(missing_class_name_list)))
   183    elif not processed_class_names:
   184      sys.stderr.write('No class found in %s\n' % filename)
   185  
   186    return lines
   187  
   188  
   189  def main(argv=sys.argv):
   190    if len(argv) < 2:
   191      sys.stderr.write('Google Mock Class Generator v%s\n\n' %
   192                       '.'.join(map(str, _VERSION)))
   193      sys.stderr.write(__doc__)
   194      return 1
   195  
   196    global _INDENT
   197    try:
   198      _INDENT = int(os.environ['INDENT'])
   199    except KeyError:
   200      pass
   201    except:
   202      sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT'))
   203  
   204    filename = argv[1]
   205    desired_class_names = None  # None means all classes in the source file.
   206    if len(argv) >= 3:
   207      desired_class_names = set(argv[2:])
   208    source = utils.ReadFile(filename)
   209    if source is None:
   210      return 1
   211  
   212    builder = ast.BuilderFromSource(source, filename)
   213    try:
   214      entire_ast = filter(None, builder.Generate())
   215    except KeyboardInterrupt:
   216      return
   217    except:
   218      # An error message was already printed since we couldn't parse.
   219      pass
   220    else:
   221      lines = _GenerateMocks(filename, source, entire_ast, desired_class_names)
   222      sys.stdout.write('\n'.join(lines))
   223  
   224  
   225  if __name__ == '__main__':
   226    main(sys.argv)