OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # | 2 # |
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """ Generator for C style prototypes and definitions """ | 7 """ Generator for C style prototypes and definitions """ |
8 | 8 |
9 import glob | 9 import glob |
10 import os | 10 import os |
11 import sys | 11 import sys |
12 import subprocess | 12 import subprocess |
13 | 13 |
14 from idl_log import ErrOut, InfoOut, WarnOut | 14 from idl_log import ErrOut, InfoOut, WarnOut |
15 from idl_node import IDLAttribute, IDLNode | 15 from idl_node import IDLAttribute, IDLNode |
16 from idl_ast import IDLAst | 16 from idl_ast import IDLAst |
17 from idl_option import GetOption, Option, ParseOptions | 17 from idl_option import GetOption, Option, ParseOptions |
18 from idl_outfile import IDLOutFile | 18 from idl_outfile import IDLOutFile |
19 from idl_parser import ParseFiles | 19 from idl_parser import ParseFiles |
20 from idl_c_proto import CGen | 20 from idl_c_proto import CGen, GetNodeComments, CommentLines, Comment |
21 from idl_generator import Generator | 21 from idl_generator import Generator, GeneratorByFile |
22 | 22 |
23 Option('dstroot', 'Base directory of output', default='../c') | 23 Option('dstroot', 'Base directory of output', default='../c') |
24 Option('guard', 'Include guard prefix', default='ppapi/c') | 24 Option('guard', 'Include guard prefix', default='ppapi/c') |
25 Option('out', 'List of output files', default='') | 25 Option('out', 'List of output files', default='') |
26 | 26 |
27 cgen = CGen() | 27 def GetOutFileName(filenode, relpath=None, prefix=None): |
28 | |
29 def IDLToHeader(filenode, relpath=None, prefix=None): | |
30 path, name = os.path.split(filenode.GetProperty('NAME')) | 28 path, name = os.path.split(filenode.GetProperty('NAME')) |
31 name = os.path.splitext(name)[0] + '.h' | 29 name = os.path.splitext(name)[0] + '.h' |
32 if prefix: name = '%s%s' % (prefix, name) | 30 if prefix: name = '%s%s' % (prefix, name) |
33 if path: name = os.path.join(path, name) | 31 if path: name = os.path.join(path, name) |
34 if relpath: name = os.path.join(relpath, name) | 32 if relpath: name = os.path.join(relpath, name) |
35 return name | 33 return name |
36 | 34 |
| 35 def WriteGroupMarker(out, node, last_group): |
| 36 # If we are part of a group comment marker... |
| 37 if last_group and last_group != node.cls: |
| 38 pre = CommentLines(['*',' @}', '']) + '\n' |
| 39 else: |
| 40 pre = '\n' |
37 | 41 |
38 def GenerateHeader(filenode, release, pref, inline=True): | 42 if node.cls in ['Typedef', 'Interface', 'Struct', 'Enum']: |
39 name = filenode.GetProperty('NAME') | 43 if last_group != node.cls: |
40 # if name == 'pp_stdint.idl': return | 44 pre += CommentLines(['*',' @addtogroup %ss' % node.cls, ' @{', '']) |
| 45 last_group = node.cls |
| 46 else: |
| 47 last_group = None |
| 48 out.Write(pre) |
| 49 return last_group |
41 | 50 |
42 # If we have an 'out' filter list, then check if we should output this file. | |
43 outlist = GetOption('out') | |
44 if outlist: | |
45 outlist = outlist.split(',') | |
46 if name not in outlist: | |
47 return | |
48 | 51 |
49 savename = IDLToHeader(filenode, relpath=GetOption('dstroot'), prefix=pref) | 52 def GenerateHeader(out, filenode, releases): |
50 out = IDLOutFile(savename) | |
51 | |
52 gpath = GetOption('guard') | 53 gpath = GetOption('guard') |
53 def_guard = IDLToHeader(filenode, relpath=gpath, prefix=pref) | 54 cgen = CGen() |
54 def_guard = def_guard.replace('/','_').replace('.','_').upper() + '_' | 55 pref = '' |
55 cright_node = filenode.GetChildren()[0] | 56 do_comments = True |
56 assert(cright_node.IsA('Copyright')) | |
57 | |
58 fileinfo = filenode.GetChildren()[1] | |
59 assert(fileinfo.IsA('Comment')) | |
60 | |
61 out.Write('%s\n' % cgen.Copyright(cright_node)) | |
62 out.Write('/* From %s modified %s. */\n\n'% ( | |
63 filenode.GetProperty('NAME'), filenode.GetProperty('DATETIME'))) | |
64 out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard)) | |
65 | |
66 for label in filenode.GetListOf('Label'): | |
67 if label.GetName() == GetOption('label'): | |
68 cgen.SetVersionMap(label) | |
69 | |
70 deps = filenode.GetDeps(release) | |
71 # Generate set of includes | |
72 includes = set([]) | |
73 for dep in deps: | |
74 depfile = dep.GetProperty('FILE') | |
75 if depfile: | |
76 includes.add(depfile) | |
77 includes = [IDLToHeader(include, relpath=gpath) for include in includes] | |
78 includes.append('ppapi/c/pp_macros.h') | |
79 | |
80 # Assume we need stdint if we "include" C or C++ code | |
81 if filenode.GetListOf('Include'): | |
82 includes.append('ppapi/c/pp_stdint.h') | |
83 | |
84 includes = sorted(set(includes)) | |
85 cur_include = IDLToHeader(filenode, relpath=gpath) | |
86 for include in includes: | |
87 if include == cur_include: continue | |
88 out.Write('#include "%s"\n' % include) | |
89 | |
90 # Generate all interface defines | |
91 out.Write('\n') | |
92 for node in filenode.GetListOf('Interface'): | |
93 out.Write( cgen.InterfaceDefs(node) ) | |
94 | |
95 # Generate the @file comment | |
96 out.Write('%s\n' % cgen.Comment(fileinfo, prefix='*\n @file')) | |
97 | 57 |
98 # Generate definitions. | 58 # Generate definitions. |
99 last_group = None | 59 last_group = None |
100 for node in filenode.GetChildren()[2:]: | 60 top_types = ['Typedef', 'Interface', 'Struct', 'Enum', 'Inline'] |
101 # If we are part of a group comment marker... | 61 for node in filenode.GetListOf(*top_types): |
102 if last_group and last_group != node.cls: | 62 # Skip if this node is not in this release |
103 pre = cgen.CommentLines(['*',' @}', '']) + '\n' | 63 if not node.InReleases(releases): |
104 else: | 64 print "Skiping %s" % node |
105 pre = '\n' | 65 continue |
106 | 66 |
107 if node.cls in ['Typedef', 'Interface', 'Struct', 'Enum']: | 67 # End/Start group marker |
108 if last_group != node.cls: | 68 if do_comments: |
109 pre += cgen.CommentLines(['*',' @addtogroup %ss' % node.cls, ' @{', '']) | 69 last_group = WriteGroupMarker(out, node, last_group) |
110 last_group = node.cls | |
111 else: | |
112 last_group = None | |
113 | 70 |
114 if node.IsA('Comment'): | 71 if node.IsA('Inline'): |
115 item = '%s\n\n' % cgen.Comment(node) | 72 item = node.GetProperty('VALUE') |
116 elif node.IsA('Inline'): | 73 # If 'C++' use __cplusplus wrapper |
117 if not inline: continue | |
118 if node.GetName() == 'cc': | 74 if node.GetName() == 'cc': |
119 item = cgen.Define(node, prefix=pref, comment=True) | |
120 item = '#ifdef __cplusplus\n%s\n#endif // __cplusplus\n\n' % item | 75 item = '#ifdef __cplusplus\n%s\n#endif // __cplusplus\n\n' % item |
121 elif node.GetName() == 'c': | 76 # If not C++ or C, then skip it |
122 item = cgen.Define(node, prefix=pref, comment=True) | 77 elif not node.GetName() == 'c': |
| 78 continue |
| 79 if item: out.Write(item) |
| 80 continue |
| 81 |
| 82 # |
| 83 # Otherwise we are defining a file level object, so generate the |
| 84 # correct document notation. |
| 85 # |
| 86 item = cgen.Define(node, releases, prefix=pref, comment=True) |
| 87 if not item: continue |
| 88 asize = node.GetProperty('assert_size()') |
| 89 if asize: |
| 90 name = '%s%s' % (pref, node.GetName()) |
| 91 if node.IsA('Struct'): |
| 92 form = 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n' |
123 else: | 93 else: |
124 continue | 94 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n' |
125 if not item: continue | 95 item += form % (name, asize[0]) |
126 else: | |
127 # | |
128 # Otherwise we are defining a file level object, so generate the | |
129 # correct document notation. | |
130 # | |
131 item = cgen.Define(node, prefix=pref, comment=True) | |
132 if not item: continue | |
133 asize = node.GetProperty('assert_size()') | |
134 if asize: | |
135 name = '%s%s' % (pref, node.GetName()) | |
136 if node.IsA('Struct'): | |
137 form = 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n' | |
138 else: | |
139 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n' | |
140 item += form % (name, asize[0]) | |
141 | 96 |
142 if item: out.Write('%s%s' % (pre, item)) | 97 if item: out.Write(item) |
143 if last_group: | 98 if last_group: |
144 out.Write(cgen.CommentLines(['*',' @}', '']) + '\n') | 99 out.Write(CommentLines(['*',' @}', '']) + '\n') |
145 | 100 |
146 out.Write('#endif /* %s */\n\n' % def_guard) | |
147 return out.Close() | |
148 | 101 |
149 class HGen(Generator): | 102 class HGen(GeneratorByFile): |
150 def __init__(self): | 103 def __init__(self): |
151 Generator.__init__(self, 'C Header', 'cgen', 'Generate the C headers.') | 104 Generator.__init__(self, 'C Header', 'cgen', 'Generate the C headers.') |
152 | 105 |
153 def GenerateVersion(self, ast, release, options): | 106 def GetMacro(self, node): |
154 outdir = GetOption('dstroot') | 107 name = node.GetName() |
155 skipList= [] | 108 name = name.upper() |
156 prefix = '' | 109 return "%s_INTERFACE" % name |
157 cfile = None | |
158 cnt = 0 | |
159 | 110 |
160 for filenode in ast.GetListOf('File'): | 111 def GetDefine(self, name, value): |
161 if GetOption('verbose'): | 112 out = '#define %s %s' % (name, value) |
162 print "Working on %s" % filenode | 113 if len(out) > 80: |
| 114 out = '#define %s \\\n %s' % (name, value) |
| 115 return '%s\n' % out |
163 | 116 |
164 # If this file has errors, skip it | |
165 if filenode.GetProperty('ERRORS') > 0: | |
166 skipList.append(filenode) | |
167 continue | |
168 | 117 |
169 if GenerateHeader(filenode, release, prefix): | 118 def GetOutFile(self, filenode, options): |
170 cnt = cnt + 1 | 119 savename = GetOutFileName(filenode, GetOption('dstroot')) |
| 120 return IDLOutFile(savename) |
171 | 121 |
172 for filenode in skipList: | 122 def GenerateHead(self, out, filenode, releases, options): |
173 errcnt = filenode.GetProperty('ERRORS') | 123 cgen = CGen() |
174 ErrOut.Log('%s : Skipped because of %d errors.' % ( | 124 gpath = GetOption('guard') |
175 filenode.GetName(), errcnt)) | 125 release = releases[0] |
| 126 def_guard = GetOutFileName(filenode, relpath=gpath) |
| 127 def_guard = def_guard.replace('/','_').replace('.','_').upper() + '_' |
176 | 128 |
177 if skipList: | 129 cright_node = filenode.GetChildren()[0] |
178 return -len(skipList) | 130 assert(cright_node.IsA('Copyright')) |
| 131 fileinfo = filenode.GetChildren()[1] |
| 132 assert(fileinfo.IsA('Comment')) |
179 | 133 |
180 if GetOption('diff'): | 134 out.Write('%s\n' % cgen.Copyright(cright_node)) |
181 return -cnt | 135 out.Write('/* From %s modified %s. */\n\n'% ( |
182 return cnt | 136 filenode.GetProperty('NAME'), filenode.GetProperty('DATETIME'))) |
| 137 out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard)) |
| 138 # Generate set of includes |
| 139 |
| 140 deps = set() |
| 141 for release in releases: |
| 142 deps |= filenode.GetDeps(release) |
| 143 |
| 144 includes = set([]) |
| 145 for dep in deps: |
| 146 depfile = dep.GetProperty('FILE') |
| 147 if depfile: |
| 148 includes.add(depfile) |
| 149 includes = [GetOutFileName(include, relpath=gpath) for include in includes] |
| 150 includes.append('ppapi/c/pp_macros.h') |
| 151 |
| 152 # Assume we need stdint if we "include" C or C++ code |
| 153 if filenode.GetListOf('Include'): |
| 154 includes.append('ppapi/c/pp_stdint.h') |
| 155 |
| 156 includes = sorted(set(includes)) |
| 157 cur_include = GetOutFileName(filenode, relpath=gpath) |
| 158 for include in includes: |
| 159 if include == cur_include: continue |
| 160 out.Write('#include "%s"\n' % include) |
| 161 |
| 162 # Generate all interface defines |
| 163 out.Write('\n') |
| 164 for node in filenode.GetListOf('Interface'): |
| 165 idefs = '' |
| 166 name = node.GetName() |
| 167 macro = node.GetProperty('macro') |
| 168 if not macro: |
| 169 macro = self.GetMacro(node) |
| 170 label = node.GetLabel() |
| 171 if label: |
| 172 for vers in label.versions: |
| 173 strver = str(vers).replace('.', '_') |
| 174 idefs += self.GetDefine('%s_%s' % (macro, strver), |
| 175 '"%s;%s"' % (name, vers)) |
| 176 if label.GetRelease(vers) == releases[-1]: |
| 177 idefs += self.GetDefine(macro, '%s_%s' % (macro, strver)) |
| 178 idefs += '\n' |
| 179 out.Write(idefs) |
| 180 |
| 181 # Generate the @file comment |
| 182 out.Write('%s\n' % Comment(fileinfo, prefix='*\n @file')) |
| 183 |
| 184 def GenerateBody(self, out, filenode, releases, options): |
| 185 GenerateHeader(out, filenode, releases) |
| 186 |
| 187 def GenerateTail(self, out, filenode, releases, options): |
| 188 gpath = GetOption('guard') |
| 189 def_guard = GetOutFileName(filenode, relpath=gpath) |
| 190 def_guard = def_guard.replace('/','_').replace('.','_').upper() + '_' |
| 191 out.Write('#endif /* %s */\n\n' % def_guard) |
183 | 192 |
184 | 193 |
185 hgen = HGen() | 194 hgen = HGen() |
186 | 195 |
187 def Main(args): | 196 def Main(args): |
188 # Default invocation will verify the golden files are unchanged. | 197 # Default invocation will verify the golden files are unchanged. |
| 198 failed = 0 |
189 if not args: | 199 if not args: |
190 args = ['--wnone', '--diff', '--test', '--dstroot=.'] | 200 args = ['--wnone', '--diff', '--test', '--dstroot=.'] |
191 | 201 |
192 ParseOptions(args) | 202 ParseOptions(args) |
| 203 |
193 idldir = os.path.split(sys.argv[0])[0] | 204 idldir = os.path.split(sys.argv[0])[0] |
194 idldir = os.path.join(idldir, 'test_cgen', '*.idl') | 205 idldir = os.path.join(idldir, 'test_cgen', '*.idl') |
195 filenames = glob.glob(idldir) | 206 filenames = glob.glob(idldir) |
| 207 ast = ParseFiles(filenames) |
| 208 if hgen.GenerateRelease(ast, 'M14', {}): |
| 209 print "Golden file for M14 failed." |
| 210 failed = 1 |
| 211 else: |
| 212 print "Golden file for M14 passed." |
| 213 |
| 214 |
| 215 idldir = os.path.split(sys.argv[0])[0] |
| 216 idldir = os.path.join(idldir, 'test_cgen_range', '*.idl') |
| 217 filenames = glob.glob(idldir) |
196 | 218 |
197 ast = ParseFiles(filenames) | 219 ast = ParseFiles(filenames) |
198 return hgen.GenerateVersion(ast, 'M14', {}) | 220 if hgen.GenerateRange(ast, ['M13', 'M14', 'M15'], {}): |
| 221 print "Golden file for M13-M15 failed." |
| 222 failed =1 |
| 223 else: |
| 224 print "Golden file for M13-M15 passed." |
| 225 |
| 226 return failed |
199 | 227 |
200 if __name__ == '__main__': | 228 if __name__ == '__main__': |
201 retval = Main(sys.argv[1:]) | 229 retval = Main(sys.argv[1:]) |
202 sys.exit(retval) | 230 sys.exit(retval) |
203 | 231 |
OLD | NEW |