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