1 #!/usr/bin/env python 2 # Copyright 2016 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 """Generates a JSON typemap from its command-line arguments and dependencies. 6 7 Each typemap should be specified in an command-line argument of the form 8 key=value, with an argument of "--start-typemap" preceding each typemap. 9 10 For example, 11 generate_type_mappings.py --output=foo.typemap --start-typemap \\ 12 public_headers=foo.h traits_headers=foo_traits.h \\ 13 type_mappings=mojom.Foo=FooImpl 14 15 generates a foo.typemap containing 16 { 17 "c++": { 18 "mojom.Foo": { 19 "typename": "FooImpl", 20 "traits_headers": [ 21 "foo_traits.h" 22 ], 23 "public_headers": [ 24 "foo.h" 25 ] 26 } 27 } 28 } 29 30 Then, 31 generate_type_mappings.py --dependency foo.typemap --output=bar.typemap \\ 32 --start-typemap public_headers=bar.h traits_headers=bar_traits.h \\ 33 type_mappings=mojom.Bar=BarImpl 34 35 generates a bar.typemap containing 36 { 37 "c++": { 38 "mojom.Bar": { 39 "typename": "BarImpl", 40 "traits_headers": [ 41 "bar_traits.h" 42 ], 43 "public_headers": [ 44 "bar.h" 45 ] 46 }, 47 "mojom.Foo": { 48 "typename": "FooImpl", 49 "traits_headers": [ 50 "foo_traits.h" 51 ], 52 "public_headers": [ 53 "foo.h" 54 ] 55 } 56 } 57 } 58 """ 59 60 import argparse 61 import json 62 import os 63 import re 64 65 66 def ReadTypemap(path): 67 with open(path) as f: 68 return json.load(f)['c++'] 69 70 71 def ParseTypemapArgs(args): 72 typemaps = [s for s in '\n'.join(args).split('--start-typemap\n') if s] 73 result = {} 74 for typemap in typemaps: 75 result.update(ParseTypemap(typemap)) 76 return result 77 78 79 def ParseTypemap(typemap): 80 values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []} 81 for line in typemap.split('\n'): 82 if not line: 83 continue 84 key, _, value = line.partition('=') 85 values[key].append(value.lstrip('/')) 86 result = {} 87 mapping_pattern = \ 88 re.compile(r"""^([^=]+) # mojom type 89 = 90 ([^[]+) # native type 91 (?:\[([^]]+)\])?$ # optional attribute in square brackets 92 """, re.X) 93 for typename in values['type_mappings']: 94 match_result = mapping_pattern.match(typename) 95 assert match_result, ( 96 "Cannot parse entry in the \"type_mappings\" section: %s" % typename) 97 98 mojom_type = match_result.group(1) 99 native_type = match_result.group(2) 100 # The only attribute supported currently is "move_only". 101 move_only = match_result.group(3) and match_result.group(3) == "move_only" 102 103 assert mojom_type not in result, ( 104 "Cannot map multiple native types (%s, %s) to the same mojom type: %s" % 105 (result[mojom_type]['typename'], native_type, mojom_type)) 106 107 result[mojom_type] = { 108 'typename': native_type, 109 'move_only': move_only, 110 'public_headers': values['public_headers'], 111 'traits_headers': values['traits_headers'], 112 } 113 return result 114 115 116 def main(): 117 parser = argparse.ArgumentParser( 118 description=__doc__, 119 formatter_class=argparse.RawDescriptionHelpFormatter) 120 parser.add_argument( 121 '--dependency', 122 type=str, 123 action='append', 124 default=[], 125 help=('A path to another JSON typemap to merge into the output. ' 126 'This may be repeated to merge multiple typemaps.')) 127 parser.add_argument('--output', 128 type=str, 129 required=True, 130 help='The path to which to write the generated JSON.') 131 params, typemap_params = parser.parse_known_args() 132 typemaps = ParseTypemapArgs(typemap_params) 133 missing = [path for path in params.dependency if not os.path.exists(path)] 134 if missing: 135 raise IOError('Missing dependencies: %s' % ', '.join(missing)) 136 for path in params.dependency: 137 typemaps.update(ReadTypemap(path)) 138 with open(params.output, 'w') as f: 139 json.dump({'c++': typemaps}, f, indent=2) 140 141 142 if __name__ == '__main__': 143 main() 144