1e35fdd936d133bf8a48de140a3c666897588a05shiqian#!/usr/bin/env python 2e35fdd936d133bf8a48de140a3c666897588a05shiqian# 3c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan# Copyright 2008 Google Inc. All Rights Reserved. 4e35fdd936d133bf8a48de140a3c666897588a05shiqian# 5e35fdd936d133bf8a48de140a3c666897588a05shiqian# Licensed under the Apache License, Version 2.0 (the "License"); 6e35fdd936d133bf8a48de140a3c666897588a05shiqian# you may not use this file except in compliance with the License. 7e35fdd936d133bf8a48de140a3c666897588a05shiqian# You may obtain a copy of the License at 8e35fdd936d133bf8a48de140a3c666897588a05shiqian# 9e35fdd936d133bf8a48de140a3c666897588a05shiqian# http://www.apache.org/licenses/LICENSE-2.0 10e35fdd936d133bf8a48de140a3c666897588a05shiqian# 11e35fdd936d133bf8a48de140a3c666897588a05shiqian# Unless required by applicable law or agreed to in writing, software 12e35fdd936d133bf8a48de140a3c666897588a05shiqian# distributed under the License is distributed on an "AS IS" BASIS, 13e35fdd936d133bf8a48de140a3c666897588a05shiqian# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14e35fdd936d133bf8a48de140a3c666897588a05shiqian# See the License for the specific language governing permissions and 15e35fdd936d133bf8a48de140a3c666897588a05shiqian# limitations under the License. 16e35fdd936d133bf8a48de140a3c666897588a05shiqian 1784b8e4c65d0847ab4262bb70619182292482529azhanyong.wan"""Generate Google Mock classes from base classes. 18e35fdd936d133bf8a48de140a3c666897588a05shiqian 1984b8e4c65d0847ab4262bb70619182292482529azhanyong.wanThis program will read in a C++ source file and output the Google Mock 2084b8e4c65d0847ab4262bb70619182292482529azhanyong.wanclasses for the specified classes. If no class is specified, all 2184b8e4c65d0847ab4262bb70619182292482529azhanyong.wanclasses in the source file are emitted. 22e35fdd936d133bf8a48de140a3c666897588a05shiqian 23e35fdd936d133bf8a48de140a3c666897588a05shiqianUsage: 2484b8e4c65d0847ab4262bb70619182292482529azhanyong.wan gmock_class.py header-file.h [ClassName]... 25e35fdd936d133bf8a48de140a3c666897588a05shiqian 26e35fdd936d133bf8a48de140a3c666897588a05shiqianOutput is sent to stdout. 27e35fdd936d133bf8a48de140a3c666897588a05shiqian""" 28e35fdd936d133bf8a48de140a3c666897588a05shiqian 29e35fdd936d133bf8a48de140a3c666897588a05shiqian__author__ = 'nnorwitz@google.com (Neal Norwitz)' 30e35fdd936d133bf8a48de140a3c666897588a05shiqian 31e35fdd936d133bf8a48de140a3c666897588a05shiqian 32e35fdd936d133bf8a48de140a3c666897588a05shiqianimport os 33e35fdd936d133bf8a48de140a3c666897588a05shiqianimport re 34e35fdd936d133bf8a48de140a3c666897588a05shiqianimport sys 35e35fdd936d133bf8a48de140a3c666897588a05shiqian 36e35fdd936d133bf8a48de140a3c666897588a05shiqianfrom cpp import ast 37e35fdd936d133bf8a48de140a3c666897588a05shiqianfrom cpp import utils 38e35fdd936d133bf8a48de140a3c666897588a05shiqian 394b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan# Preserve compatibility with Python 2.3. 404b16e8ed2785136d863fb52961539c27c9716497zhanyong.wantry: 414b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan _dummy = set 424b16e8ed2785136d863fb52961539c27c9716497zhanyong.wanexcept NameError: 434b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan import sets 444b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan set = sets.Set 454b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan 4684b8e4c65d0847ab4262bb70619182292482529azhanyong.wan_VERSION = (1, 0, 1) # The version of this script. 4784b8e4c65d0847ab4262bb70619182292482529azhanyong.wan# How many spaces to indent. Can set me with the INDENT environment variable. 48e35fdd936d133bf8a48de140a3c666897588a05shiqian_INDENT = 2 49e35fdd936d133bf8a48de140a3c666897588a05shiqian 50e35fdd936d133bf8a48de140a3c666897588a05shiqian 51e35fdd936d133bf8a48de140a3c666897588a05shiqiandef _GenerateMethods(output_lines, source, class_node): 52c26f969579d62444ae7d422b37e0037ceca97a7akosak function_type = (ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL | 53c26f969579d62444ae7d422b37e0037ceca97a7akosak ast.FUNCTION_OVERRIDE) 54e35fdd936d133bf8a48de140a3c666897588a05shiqian ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR 554b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan indent = ' ' * _INDENT 56e35fdd936d133bf8a48de140a3c666897588a05shiqian 57e35fdd936d133bf8a48de140a3c666897588a05shiqian for node in class_node.body: 58e35fdd936d133bf8a48de140a3c666897588a05shiqian # We only care about virtual functions. 59e35fdd936d133bf8a48de140a3c666897588a05shiqian if (isinstance(node, ast.Function) and 60e35fdd936d133bf8a48de140a3c666897588a05shiqian node.modifiers & function_type and 61e35fdd936d133bf8a48de140a3c666897588a05shiqian not node.modifiers & ctor_or_dtor): 62e35fdd936d133bf8a48de140a3c666897588a05shiqian # Pick out all the elements we need from the original function. 63e35fdd936d133bf8a48de140a3c666897588a05shiqian const = '' 64e35fdd936d133bf8a48de140a3c666897588a05shiqian if node.modifiers & ast.FUNCTION_CONST: 65e35fdd936d133bf8a48de140a3c666897588a05shiqian const = 'CONST_' 66e35fdd936d133bf8a48de140a3c666897588a05shiqian return_type = 'void' 67e35fdd936d133bf8a48de140a3c666897588a05shiqian if node.return_type: 6884b8e4c65d0847ab4262bb70619182292482529azhanyong.wan # Add modifiers like 'const'. 69987a978c3c525cbc796824493436195872b89a0bnnorwitz modifiers = '' 70987a978c3c525cbc796824493436195872b89a0bnnorwitz if node.return_type.modifiers: 71987a978c3c525cbc796824493436195872b89a0bnnorwitz modifiers = ' '.join(node.return_type.modifiers) + ' ' 72987a978c3c525cbc796824493436195872b89a0bnnorwitz return_type = modifiers + node.return_type.name 734b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan template_args = [arg.name for arg in node.return_type.templated_types] 744b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan if template_args: 754b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan return_type += '<' + ', '.join(template_args) + '>' 764b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan if len(template_args) > 1: 774b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan for line in [ 784b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan '// The following line won\'t really compile, as the return', 794b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan '// type has multiple template arguments. To fix it, use a', 804b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan '// typedef for the return type.']: 814b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan output_lines.append(indent + line) 82e35fdd936d133bf8a48de140a3c666897588a05shiqian if node.return_type.pointer: 83e35fdd936d133bf8a48de140a3c666897588a05shiqian return_type += '*' 84e35fdd936d133bf8a48de140a3c666897588a05shiqian if node.return_type.reference: 85e35fdd936d133bf8a48de140a3c666897588a05shiqian return_type += '&' 86f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev num_parameters = len(node.parameters) 87f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev if len(node.parameters) == 1: 88f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev first_param = node.parameters[0] 89f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev if source[first_param.start:first_param.end].strip() == 'void': 90f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # We must treat T(void) as a function with no parameters. 91f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev num_parameters = 0 9245fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan tmpl = '' 9345fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan if class_node.templated_types: 9445fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan tmpl = '_T' 9545fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan mock_method_macro = 'MOCK_%sMETHOD%d%s' % (const, num_parameters, tmpl) 9645fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan 97e35fdd936d133bf8a48de140a3c666897588a05shiqian args = '' 98e35fdd936d133bf8a48de140a3c666897588a05shiqian if node.parameters: 99f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # Due to the parser limitations, it is impossible to keep comments 100f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # while stripping the default parameters. When defaults are 101f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # present, we choose to strip them and comments (and produce 102f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # compilable code). 103f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # TODO(nnorwitz@google.com): Investigate whether it is possible to 104f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # preserve parameter name when reconstructing parameter text from 105f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # the AST. 106f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev if len([param for param in node.parameters if param.default]) > 0: 107f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev args = ', '.join(param.type.name for param in node.parameters) 108f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev else: 109f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # Get the full text of the parameters from the start 110f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # of the first parameter to the end of the last parameter. 111f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev start = node.parameters[0].start 112f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev end = node.parameters[-1].end 113f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # Remove // comments. 114f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev args_strings = re.sub(r'//.*', '', source[start:end]) 115f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # Condense multiple spaces and eliminate newlines putting the 116f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # parameters together on a single line. Ensure there is a 117f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # space in an argument which is split by a newline without 118f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev # intervening whitespace, e.g.: int\nBar 119f4eeaedb39b6935f6236fe55a52bd9af0b8390efvladlosev args = re.sub(' +', ' ', args_strings.replace('\n', ' ')) 120e35fdd936d133bf8a48de140a3c666897588a05shiqian 1214b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan # Create the mock method definition. 1224b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name), 1234b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan '%s%s(%s));' % (indent*3, return_type, args)]) 124e35fdd936d133bf8a48de140a3c666897588a05shiqian 125e35fdd936d133bf8a48de140a3c666897588a05shiqian 12684b8e4c65d0847ab4262bb70619182292482529azhanyong.wandef _GenerateMocks(filename, source, ast_list, desired_class_names): 1274b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan processed_class_names = set() 128e35fdd936d133bf8a48de140a3c666897588a05shiqian lines = [] 129e35fdd936d133bf8a48de140a3c666897588a05shiqian for node in ast_list: 13084b8e4c65d0847ab4262bb70619182292482529azhanyong.wan if (isinstance(node, ast.Class) and node.body and 13184b8e4c65d0847ab4262bb70619182292482529azhanyong.wan # desired_class_names being None means that all classes are selected. 13284b8e4c65d0847ab4262bb70619182292482529azhanyong.wan (not desired_class_names or node.name in desired_class_names)): 133ce60784fb51a5a0e28c14edd53bacbf0d2abb36bnnorwitz class_name = node.name 13445fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan parent_name = class_name 13584b8e4c65d0847ab4262bb70619182292482529azhanyong.wan processed_class_names.add(class_name) 136e35fdd936d133bf8a48de140a3c666897588a05shiqian class_node = node 137e35fdd936d133bf8a48de140a3c666897588a05shiqian # Add namespace before the class. 138e35fdd936d133bf8a48de140a3c666897588a05shiqian if class_node.namespace: 139e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.extend(['namespace %s {' % n for n in class_node.namespace]) # } 140e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('') 141e35fdd936d133bf8a48de140a3c666897588a05shiqian 14245fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan # Add template args for templated classes. 14345fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan if class_node.templated_types: 14445fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan # TODO(paulchang): The AST doesn't preserve template argument order, 14545fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan # so we have to make up names here. 14645fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan # TODO(paulchang): Handle non-type template arguments (e.g. 14745fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan # template<typename T, int N>). 14845fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan template_arg_count = len(class_node.templated_types.keys()) 14945fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan template_args = ['T%d' % n for n in range(template_arg_count)] 15045fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan template_decls = ['typename ' + arg for arg in template_args] 15145fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan lines.append('template <' + ', '.join(template_decls) + '>') 15245fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan parent_name += '<' + ', '.join(template_args) + '>' 15345fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan 154e35fdd936d133bf8a48de140a3c666897588a05shiqian # Add the class prolog. 15545fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan lines.append('class Mock%s : public %s {' # } 15645fef502fac471efa4bf25b3d4104943463912ebzhanyong.wan % (class_name, parent_name)) 157e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('%spublic:' % (' ' * (_INDENT // 2))) 158e35fdd936d133bf8a48de140a3c666897588a05shiqian 159e35fdd936d133bf8a48de140a3c666897588a05shiqian # Add all the methods. 160e35fdd936d133bf8a48de140a3c666897588a05shiqian _GenerateMethods(lines, source, class_node) 161e35fdd936d133bf8a48de140a3c666897588a05shiqian 162e35fdd936d133bf8a48de140a3c666897588a05shiqian # Close the class. 163e35fdd936d133bf8a48de140a3c666897588a05shiqian if lines: 164e35fdd936d133bf8a48de140a3c666897588a05shiqian # If there are no virtual methods, no need for a public label. 165e35fdd936d133bf8a48de140a3c666897588a05shiqian if len(lines) == 2: 166e35fdd936d133bf8a48de140a3c666897588a05shiqian del lines[-1] 167e35fdd936d133bf8a48de140a3c666897588a05shiqian 168e35fdd936d133bf8a48de140a3c666897588a05shiqian # Only close the class if there really is a class. 169e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('};') 170e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('') # Add an extra newline. 171e35fdd936d133bf8a48de140a3c666897588a05shiqian 172e35fdd936d133bf8a48de140a3c666897588a05shiqian # Close the namespace. 173e35fdd936d133bf8a48de140a3c666897588a05shiqian if class_node.namespace: 174e35fdd936d133bf8a48de140a3c666897588a05shiqian for i in range(len(class_node.namespace)-1, -1, -1): 175e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('} // namespace %s' % class_node.namespace[i]) 176e35fdd936d133bf8a48de140a3c666897588a05shiqian lines.append('') # Add an extra newline. 177e35fdd936d133bf8a48de140a3c666897588a05shiqian 17884b8e4c65d0847ab4262bb70619182292482529azhanyong.wan if desired_class_names: 179d955e83bee3919b871616223b777bab2f04942d9zhanyong.wan missing_class_name_list = list(desired_class_names - processed_class_names) 180d955e83bee3919b871616223b777bab2f04942d9zhanyong.wan if missing_class_name_list: 181d955e83bee3919b871616223b777bab2f04942d9zhanyong.wan missing_class_name_list.sort() 18284b8e4c65d0847ab4262bb70619182292482529azhanyong.wan sys.stderr.write('Class(es) not found in %s: %s\n' % 183d955e83bee3919b871616223b777bab2f04942d9zhanyong.wan (filename, ', '.join(missing_class_name_list))) 18484b8e4c65d0847ab4262bb70619182292482529azhanyong.wan elif not processed_class_names: 185c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan sys.stderr.write('No class found in %s\n' % filename) 186c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan 187c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan return lines 188e35fdd936d133bf8a48de140a3c666897588a05shiqian 189e35fdd936d133bf8a48de140a3c666897588a05shiqian 190e35fdd936d133bf8a48de140a3c666897588a05shiqiandef main(argv=sys.argv): 191ce60784fb51a5a0e28c14edd53bacbf0d2abb36bnnorwitz if len(argv) < 2: 19284b8e4c65d0847ab4262bb70619182292482529azhanyong.wan sys.stderr.write('Google Mock Class Generator v%s\n\n' % 19384b8e4c65d0847ab4262bb70619182292482529azhanyong.wan '.'.join(map(str, _VERSION))) 19484b8e4c65d0847ab4262bb70619182292482529azhanyong.wan sys.stderr.write(__doc__) 195e35fdd936d133bf8a48de140a3c666897588a05shiqian return 1 196e35fdd936d133bf8a48de140a3c666897588a05shiqian 197e35fdd936d133bf8a48de140a3c666897588a05shiqian global _INDENT 198e35fdd936d133bf8a48de140a3c666897588a05shiqian try: 199e35fdd936d133bf8a48de140a3c666897588a05shiqian _INDENT = int(os.environ['INDENT']) 200e35fdd936d133bf8a48de140a3c666897588a05shiqian except KeyError: 201e35fdd936d133bf8a48de140a3c666897588a05shiqian pass 202e35fdd936d133bf8a48de140a3c666897588a05shiqian except: 203e35fdd936d133bf8a48de140a3c666897588a05shiqian sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT')) 204e35fdd936d133bf8a48de140a3c666897588a05shiqian 205ce60784fb51a5a0e28c14edd53bacbf0d2abb36bnnorwitz filename = argv[1] 20684b8e4c65d0847ab4262bb70619182292482529azhanyong.wan desired_class_names = None # None means all classes in the source file. 207ce60784fb51a5a0e28c14edd53bacbf0d2abb36bnnorwitz if len(argv) >= 3: 2084b16e8ed2785136d863fb52961539c27c9716497zhanyong.wan desired_class_names = set(argv[2:]) 209e35fdd936d133bf8a48de140a3c666897588a05shiqian source = utils.ReadFile(filename) 210e35fdd936d133bf8a48de140a3c666897588a05shiqian if source is None: 211e35fdd936d133bf8a48de140a3c666897588a05shiqian return 1 212e35fdd936d133bf8a48de140a3c666897588a05shiqian 213e35fdd936d133bf8a48de140a3c666897588a05shiqian builder = ast.BuilderFromSource(source, filename) 214e35fdd936d133bf8a48de140a3c666897588a05shiqian try: 215e35fdd936d133bf8a48de140a3c666897588a05shiqian entire_ast = filter(None, builder.Generate()) 216e35fdd936d133bf8a48de140a3c666897588a05shiqian except KeyboardInterrupt: 217e35fdd936d133bf8a48de140a3c666897588a05shiqian return 218e35fdd936d133bf8a48de140a3c666897588a05shiqian except: 219e35fdd936d133bf8a48de140a3c666897588a05shiqian # An error message was already printed since we couldn't parse. 220055b6b17d2354691af4b20f035f36c134fba2ac9kosak sys.exit(1) 221e35fdd936d133bf8a48de140a3c666897588a05shiqian else: 222c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan lines = _GenerateMocks(filename, source, entire_ast, desired_class_names) 223c2ad46a5df4414fc2b804c53525f4578f01a3dfezhanyong.wan sys.stdout.write('\n'.join(lines)) 224e35fdd936d133bf8a48de140a3c666897588a05shiqian 225e35fdd936d133bf8a48de140a3c666897588a05shiqian 226e35fdd936d133bf8a48de140a3c666897588a05shiqianif __name__ == '__main__': 227e35fdd936d133bf8a48de140a3c666897588a05shiqian main(sys.argv) 228