2 # Copyright (c) 2012 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.
6 """ Generator for C style prototypes and definitions """
13 from idl_log
import ErrOut
, InfoOut
, WarnOut
14 from idl_node
import IDLAttribute
, IDLNode
15 from idl_ast
import IDLAst
16 from idl_option
import GetOption
, Option
, ParseOptions
17 from idl_outfile
import IDLOutFile
18 from idl_parser
import ParseFiles
19 from idl_c_proto
import CGen
, GetNodeComments
, CommentLines
, Comment
20 from idl_generator
import Generator
, GeneratorByFile
22 Option('dstroot', 'Base directory of output', default
=os
.path
.join('..', 'c'))
23 Option('guard', 'Include guard prefix', default
=os
.path
.join('ppapi', 'c'))
24 Option('out', 'List of output files', default
='')
27 def GetOutFileName(filenode
, relpath
=None, prefix
=None):
28 path
, name
= os
.path
.split(filenode
.GetProperty('NAME'))
29 name
= os
.path
.splitext(name
)[0] + '.h'
30 if prefix
: name
= '%s%s' % (prefix
, name
)
31 if path
: name
= os
.path
.join(path
, name
)
32 if relpath
: name
= os
.path
.join(relpath
, name
)
36 def WriteGroupMarker(out
, node
, last_group
):
37 # If we are part of a group comment marker...
38 if last_group
and last_group
!= node
.cls
:
39 pre
= CommentLines(['*',' @}', '']) + '\n'
43 if node
.cls
in ['Typedef', 'Interface', 'Struct', 'Enum']:
44 if last_group
!= node
.cls
:
45 pre
+= CommentLines(['*',' @addtogroup %ss' % node
.cls
, ' @{', ''])
53 def GenerateHeader(out
, filenode
, releases
):
58 # Generate definitions.
60 top_types
= ['Typedef', 'Interface', 'Struct', 'Enum', 'Inline']
61 for node
in filenode
.GetListOf(*top_types
):
62 # Skip if this node is not in this release
63 if not node
.InReleases(releases
):
64 print "Skiping %s" % node
67 # End/Start group marker
69 last_group
= WriteGroupMarker(out
, node
, last_group
)
71 if node
.IsA('Inline'):
72 item
= node
.GetProperty('VALUE')
73 # If 'C++' use __cplusplus wrapper
74 if node
.GetName() == 'cc':
75 item
= '#ifdef __cplusplus\n%s\n#endif // __cplusplus\n\n' % item
76 # If not C++ or C, then skip it
77 elif not node
.GetName() == 'c':
79 if item
: out
.Write(item
)
83 # Otherwise we are defining a file level object, so generate the
84 # correct document notation.
86 item
= cgen
.Define(node
, releases
, prefix
=pref
, comment
=True)
88 asize
= node
.GetProperty('assert_size()')
90 name
= '%s%s' % (pref
, node
.GetName())
91 if node
.IsA('Struct'):
92 form
= 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n'
93 elif node
.IsA('Enum'):
94 if node
.GetProperty('notypedef'):
95 form
= 'PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(%s, %s);\n'
97 form
= 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n'
99 form
= 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n'
100 item
+= form
% (name
, asize
[0])
102 if item
: out
.Write(item
)
104 out
.Write(CommentLines(['*',' @}', '']) + '\n')
107 class HGen(GeneratorByFile
):
109 Generator
.__init
__(self
, 'C Header', 'cgen', 'Generate the C headers.')
111 def GenerateFile(self
, filenode
, releases
, options
):
112 savename
= GetOutFileName(filenode
, GetOption('dstroot'))
113 unique_releases
= filenode
.GetUniqueReleases(releases
)
114 if not unique_releases
:
115 if os
.path
.isfile(savename
):
116 print "Removing stale %s for this range." % filenode
.GetName()
117 os
.remove(os
.path
.realpath(savename
))
120 out
= IDLOutFile(savename
)
121 self
.GenerateHead(out
, filenode
, releases
, options
)
122 self
.GenerateBody(out
, filenode
, releases
, options
)
123 self
.GenerateTail(out
, filenode
, releases
, options
)
126 def GenerateHead(self
, out
, filenode
, releases
, options
):
127 __pychecker__
= 'unusednames=options'
129 gpath
= GetOption('guard')
130 def_guard
= GetOutFileName(filenode
, relpath
=gpath
)
131 def_guard
= def_guard
.replace(os
.sep
,'_').replace('.','_').upper() + '_'
133 cright_node
= filenode
.GetChildren()[0]
134 assert(cright_node
.IsA('Copyright'))
135 fileinfo
= filenode
.GetChildren()[1]
136 assert(fileinfo
.IsA('Comment'))
138 out
.Write('%s\n' % cgen
.Copyright(cright_node
))
140 # Wrap the From ... modified ... comment if it would be >80 characters.
141 from_text
= 'From %s' % (
142 filenode
.GetProperty('NAME').replace(os
.sep
,'/'))
143 modified_text
= 'modified %s.' % (
144 filenode
.GetProperty('DATETIME'))
145 if len(from_text
) + len(modified_text
) < 74:
146 out
.Write('/* %s %s */\n\n' % (from_text
, modified_text
))
148 out
.Write('/* %s,\n * %s\n */\n\n' % (from_text
, modified_text
))
150 out
.Write('#ifndef %s\n#define %s\n\n' % (def_guard
, def_guard
))
151 # Generate set of includes
154 for release
in releases
:
155 deps |
= filenode
.GetDeps(release
)
159 depfile
= dep
.GetProperty('FILE')
161 includes
.add(depfile
)
162 includes
= [GetOutFileName(
163 include
, relpath
=gpath
).replace(os
.sep
, '/') for include
in includes
]
164 includes
.append('ppapi/c/pp_macros.h')
166 # Assume we need stdint if we "include" C or C++ code
167 if filenode
.GetListOf('Include'):
168 includes
.append('ppapi/c/pp_stdint.h')
170 includes
= sorted(set(includes
))
171 cur_include
= GetOutFileName(filenode
, relpath
=gpath
).replace(os
.sep
, '/')
172 for include
in includes
:
173 if include
== cur_include
: continue
174 out
.Write('#include "%s"\n' % include
)
176 # If we are generating a single release, then create a macro for the highest
177 # available release number.
178 if filenode
.GetProperty('NAME').endswith('pp_macros.idl'):
179 releasestr
= GetOption('release')
181 release_numbers
= re
.findall('\d+', releasestr
)
183 out
.Write('\n#define PPAPI_RELEASE %s\n' % release_numbers
[0])
185 # Generate all interface defines
187 for node
in filenode
.GetListOf('Interface'):
189 macro
= cgen
.GetInterfaceMacro(node
)
190 unique
= node
.GetUniqueReleases(releases
)
192 # Skip this interface if there are no matching versions
193 if not unique
: continue
196 version
= node
.GetVersion(rel
)
197 name
= cgen
.GetInterfaceString(node
, version
)
198 strver
= str(version
).replace('.', '_')
199 idefs
+= cgen
.GetDefine('%s_%s' % (macro
, strver
), '"%s"' % name
)
200 idefs
+= cgen
.GetDefine(macro
, '%s_%s' % (macro
, strver
)) + '\n'
203 # Generate the @file comment
204 out
.Write('%s\n' % Comment(fileinfo
, prefix
='*\n @file'))
206 def GenerateBody(self
, out
, filenode
, releases
, options
):
207 __pychecker__
= 'unusednames=options'
208 GenerateHeader(out
, filenode
, releases
)
210 def GenerateTail(self
, out
, filenode
, releases
, options
):
211 __pychecker__
= 'unusednames=options,releases'
212 gpath
= GetOption('guard')
213 def_guard
= GetOutFileName(filenode
, relpath
=gpath
)
214 def_guard
= def_guard
.replace(os
.sep
,'_').replace('.','_').upper() + '_'
215 out
.Write('#endif /* %s */\n\n' % def_guard
)
221 # Default invocation will verify the golden files are unchanged.
224 args
= ['--wnone', '--diff', '--test', '--dstroot=.']
228 idldir
= os
.path
.split(sys
.argv
[0])[0]
229 idldir
= os
.path
.join(idldir
, 'test_cgen', '*.idl')
230 filenames
= glob
.glob(idldir
)
231 ast
= ParseFiles(filenames
)
232 if hgen
.GenerateRelease(ast
, 'M14', {}):
233 print "Golden file for M14 failed."
236 print "Golden file for M14 passed."
239 idldir
= os
.path
.split(sys
.argv
[0])[0]
240 idldir
= os
.path
.join(idldir
, 'test_cgen_range', '*.idl')
241 filenames
= glob
.glob(idldir
)
243 ast
= ParseFiles(filenames
)
244 if hgen
.GenerateRange(ast
, ['M13', 'M14', 'M15'], {}):
245 print "Golden file for M13-M15 failed."
248 print "Golden file for M13-M15 passed."
252 if __name__
== '__main__':
253 sys
.exit(Main(sys
.argv
[1:]))