1dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#!/usr/bin/env python
2dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#
3dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# Copyright 2008 Google Inc.  All Rights Reserved.
4dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#
5dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# Licensed under the Apache License, Version 2.0 (the "License");
6dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# you may not use this file except in compliance with the License.
7dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# You may obtain a copy of the License at
8dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#
9dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#      http://www.apache.org/licenses/LICENSE-2.0
10dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter#
11dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# Unless required by applicable law or agreed to in writing, software
12dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# distributed under the License is distributed on an "AS IS" BASIS,
13dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# See the License for the specific language governing permissions and
15dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# limitations under the License.
16dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
17dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter"""Generate Google Mock classes from base classes.
18dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
19dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken MixterThis program will read in a C++ source file and output the Google Mock
20dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterclasses for the specified classes.  If no class is specified, all
21dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterclasses in the source file are emitted.
22dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
23dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken MixterUsage:
24dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  gmock_class.py header-file.h [ClassName]...
25dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
26dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken MixterOutput is sent to stdout.
27dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter"""
28dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
29dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter__author__ = 'nnorwitz@google.com (Neal Norwitz)'
30dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
31dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
32dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterimport os
33dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterimport re
34dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterimport sys
35dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
36dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterfrom cpp import ast
37dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterfrom cpp import utils
38dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
3946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan# Preserve compatibility with Python 2.3.
4046108a219a4b812dd8f36fee479a0340ea5963f5Ben Chantry:
4146108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan  _dummy = set
4246108a219a4b812dd8f36fee479a0340ea5963f5Ben Chanexcept NameError:
4346108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan  import sets
4446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan  set = sets.Set
4546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan
46dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter_VERSION = (1, 0, 1)  # The version of this script.
47dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter# How many spaces to indent.  Can set me with the INDENT environment variable.
48dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter_INDENT = 2
49dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
50dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
51dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterdef _GenerateMethods(output_lines, source, class_node):
52dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  function_type = ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL
53dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR
5446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan  indent = ' ' * _INDENT
55dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
56dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  for node in class_node.body:
57dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    # We only care about virtual functions.
58dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    if (isinstance(node, ast.Function) and
59dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        node.modifiers & function_type and
60dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        not node.modifiers & ctor_or_dtor):
61dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Pick out all the elements we need from the original function.
62dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      const = ''
63dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if node.modifiers & ast.FUNCTION_CONST:
64dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        const = 'CONST_'
65dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      return_type = 'void'
66dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if node.return_type:
67dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        # Add modifiers like 'const'.
68dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        modifiers = ''
69dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        if node.return_type.modifiers:
70dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter          modifiers = ' '.join(node.return_type.modifiers) + ' '
71dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        return_type = modifiers + node.return_type.name
7246108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        template_args = [arg.name for arg in node.return_type.templated_types]
7346108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        if template_args:
7446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          return_type += '<' + ', '.join(template_args) + '>'
7546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          if len(template_args) > 1:
7646108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan            for line in [
7746108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan                '// The following line won\'t really compile, as the return',
7846108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan                '// type has multiple template arguments.  To fix it, use a',
7946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan                '// typedef for the return type.']:
8046108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan              output_lines.append(indent + line)
81dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        if node.return_type.pointer:
82dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter          return_type += '*'
83dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        if node.return_type.reference:
84dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter          return_type += '&'
8546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        num_parameters = len(node.parameters)
8646108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        if len(node.parameters) == 1:
8746108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          first_param = node.parameters[0]
8846108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          if source[first_param.start:first_param.end].strip() == 'void':
8946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan            # We must treat T(void) as a function with no parameters.
9046108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan            num_parameters = 0
9146108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan      mock_method_macro = 'MOCK_%sMETHOD%d' % (const, num_parameters)
92dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      args = ''
93dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if node.parameters:
9446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # Due to the parser limitations, it is impossible to keep comments
9546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # while stripping the default parameters.  When defaults are
9646108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # present, we choose to strip them and comments (and produce
9746108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # compilable code).
9846108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # TODO(nnorwitz@google.com): Investigate whether it is possible to
9946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # preserve parameter name when reconstructing parameter text from
10046108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        # the AST.
10146108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        if len([param for param in node.parameters if param.default]) > 0:
10246108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          args = ', '.join(param.type.name for param in node.parameters)
10346108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan        else:
10446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # Get the full text of the parameters from the start
10546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # of the first parameter to the end of the last parameter.
10646108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          start = node.parameters[0].start
10746108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          end = node.parameters[-1].end
10846108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # Remove // comments.
10946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          args_strings = re.sub(r'//.*', '', source[start:end])
11046108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # Condense multiple spaces and eliminate newlines putting the
11146108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # parameters together on a single line.  Ensure there is a
11246108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # space in an argument which is split by a newline without
11346108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          # intervening whitespace, e.g.: int\nBar
11446108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan          args = re.sub('  +', ' ', args_strings.replace('\n', ' '))
11546108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan
11646108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan      # Create the mock method definition.
11746108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan      output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name),
11846108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan                           '%s%s(%s));' % (indent*3, return_type, args)])
119dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
120dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
121dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterdef _GenerateMocks(filename, source, ast_list, desired_class_names):
12246108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan  processed_class_names = set()
123dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  lines = []
124dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  for node in ast_list:
125dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    if (isinstance(node, ast.Class) and node.body and
126dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        # desired_class_names being None means that all classes are selected.
127dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        (not desired_class_names or node.name in desired_class_names)):
128dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      class_name = node.name
129dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      processed_class_names.add(class_name)
130dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      class_node = node
131dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Add namespace before the class.
132dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if class_node.namespace:
133dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        lines.extend(['namespace %s {' % n for n in class_node.namespace])  # }
134dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        lines.append('')
135dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
136dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Add the class prolog.
137dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      lines.append('class Mock%s : public %s {' % (class_name, class_name))  # }
138dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      lines.append('%spublic:' % (' ' * (_INDENT // 2)))
139dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
140dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Add all the methods.
141dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      _GenerateMethods(lines, source, class_node)
142dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
143dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Close the class.
144dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if lines:
145dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        # If there are no virtual methods, no need for a public label.
146dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        if len(lines) == 2:
147dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter          del lines[-1]
148dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
149dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        # Only close the class if there really is a class.
150dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        lines.append('};')
151dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        lines.append('')  # Add an extra newline.
152dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
153dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      # Close the namespace.
154dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      if class_node.namespace:
155dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        for i in range(len(class_node.namespace)-1, -1, -1):
156dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter          lines.append('}  // namespace %s' % class_node.namespace[i])
157dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter        lines.append('')  # Add an extra newline.
158dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
159dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  if desired_class_names:
160dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    missing_class_name_list = list(desired_class_names - processed_class_names)
161dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    if missing_class_name_list:
162dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      missing_class_name_list.sort()
163dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter      sys.stderr.write('Class(es) not found in %s: %s\n' %
164dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter                       (filename, ', '.join(missing_class_name_list)))
165dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  elif not processed_class_names:
166dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    sys.stderr.write('No class found in %s\n' % filename)
167dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
168dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  return lines
169dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
170dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
171dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterdef main(argv=sys.argv):
172dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  if len(argv) < 2:
173dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    sys.stderr.write('Google Mock Class Generator v%s\n\n' %
174dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter                     '.'.join(map(str, _VERSION)))
175dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    sys.stderr.write(__doc__)
176dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    return 1
177dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
178dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  global _INDENT
179dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  try:
180dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    _INDENT = int(os.environ['INDENT'])
181dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  except KeyError:
182dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    pass
183dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  except:
184dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT'))
185dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
186dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  filename = argv[1]
187dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  desired_class_names = None  # None means all classes in the source file.
188dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  if len(argv) >= 3:
18946108a219a4b812dd8f36fee479a0340ea5963f5Ben Chan    desired_class_names = set(argv[2:])
190dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  source = utils.ReadFile(filename)
191dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  if source is None:
192dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    return 1
193dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
194dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  builder = ast.BuilderFromSource(source, filename)
195dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  try:
196dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    entire_ast = filter(None, builder.Generate())
197dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  except KeyboardInterrupt:
198dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    return
199dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  except:
200dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    # An error message was already printed since we couldn't parse.
201dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    pass
202dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  else:
203dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    lines = _GenerateMocks(filename, source, entire_ast, desired_class_names)
204dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter    sys.stdout.write('\n'.join(lines))
205dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
206dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter
207dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixterif __name__ == '__main__':
208dd1c93d5709e32713961cfd95fe30489a4ad2d26Ken Mixter  main(sys.argv)
209