4 # header_wrappers.py: Generates SWIG proxy wrappers around Subversion
8 import os
, re
, string
, sys
, glob
, shutil
9 if __name__
== "__main__":
10 parent_dir
= os
.path
.dirname(os
.path
.abspath(os
.path
.dirname(sys
.argv
[0])))
11 sys
.path
[0:0] = [ parent_dir
, os
.path
.dirname(parent_dir
) ]
12 from gen_base
import unique
, native_path
, build_path_basename
, build_path_join
15 class Generator(generator
.swig
.Generator
):
16 """Generate SWIG proxy wrappers around Subversion header files"""
18 def __init__(self
, conf
, swig_path
):
19 """Initialize Generator object"""
20 generator
.swig
.Generator
.__init
__(self
, conf
, swig_path
)
22 # Build list of header files
23 self
.header_files
= map(native_path
, self
.includes
)
24 self
.header_basenames
= map(os
.path
.basename
, self
.header_files
)
26 # Ignore svn_repos_parse_fns_t because SWIG can't parse it
27 _ignores
= ["svn_repos_parse_fns_t"]
29 def write_makefile_rules(self
, makefile
):
30 """Write makefile rules for generating SWIG wrappers for Subversion
33 python_script
= '$(abs_srcdir)/build/generator/swig/header_wrappers.py'
34 makefile
.write('GEN_SWIG_WRAPPER = cd $(top_srcdir) && $(PYTHON)' +
35 ' %s build.conf $(SWIG)\n\n' % python_script
)
36 for fname
in self
.includes
:
37 wrapper_fname
= build_path_join(self
.proxy_dir
,
38 self
.proxy_filename(build_path_basename(fname
)))
39 wrapper_fnames
.append(wrapper_fname
)
41 '%s: %s %s\n' % (wrapper_fname
, fname
, python_script
) +
42 '\t$(GEN_SWIG_WRAPPER) %s\n\n' % fname
44 makefile
.write('SWIG_WRAPPERS = %s\n\n' % string
.join(wrapper_fnames
))
45 for short_name
in self
.short
.values():
46 makefile
.write('autogen-swig-%s: $(SWIG_WRAPPERS)\n' % short_name
)
47 makefile
.write('\n\n')
49 def proxy_filename(self
, include_filename
):
50 """Convert a .h filename into a _h.swg filename"""
51 return string
.replace(include_filename
,".h","_h.swg")
53 def _write_nodefault_calls(self
, structs
):
54 """Write proxy definitions to a SWIG interface file"""
55 self
.ofile
.write("\n/* No default constructors for opaque structs */\n")
56 self
.ofile
.write('#ifdef SWIGPYTHON\n');
57 for structName
, structDefinition
in structs
:
58 if not structDefinition
:
59 self
.ofile
.write('%%nodefault %s;\n' % structName
)
60 self
.ofile
.write('#endif\n');
62 def _write_includes(self
, includes
, base_fname
):
63 """Write includes to a SWIG interface file"""
65 self
.ofile
.write('\n/* Includes */\n')
66 self
.ofile
.write('%%{\n#include "%s"\n%%}\n' % base_fname
)
67 if base_fname
not in self
._ignores
:
68 self
.ofile
.write('%%include %s\n' % base_fname
)
71 def _write_callback(self
, type, return_type
, module
, function
, params
,
73 """Write out an individual callback"""
75 # Get rid of any extra spaces or newlines
76 return_type
= string
.join(string
.split(return_type
))
77 params
= string
.join(string
.split(params
))
79 # Calculate parameters
82 params
= "%s _obj" % type
84 param_names
= string
.join(self
._re
_param
_names
.findall(params
), ", ")
85 params
= "%s _obj, %s" % (type, params
)
87 invoke_callback
= "%s(%s)" % (callee
, param_names
)
88 if return_type
!= "void":
89 invoke_callback
= "return %s" % (invoke_callback
)
91 # Write out the declaration
93 "static %s %s_invoke_%s(\n" % (return_type
, module
, function
) +
95 " %s;\n" % invoke_callback
+
99 def _write_callback_typemaps(self
, callbacks
):
100 """Apply the CALLABLE_CALLBACK typemap to all callbacks"""
102 self
.ofile
.write('\n/* Callback typemaps */\n')
104 for match
in callbacks
:
105 if match
[0] and match
[1]:
106 # Callbacks declared as a typedef
107 return_type
, module
, function
, params
= match
108 type = "%s_%s_t" % (module
, function
)
113 "#ifdef SWIGPYTHON\n"
114 "%%apply CALLABLE_CALLBACK {\n"
117 "%%apply CALLABLE_CALLBACK * {\n"
120 "#endif\n" % ( ",\n ".join(types
), " *,\n ".join(types
) )
124 def _write_baton_typemaps(self
, batons
):
125 """Apply the PY_AS_VOID typemap to all batons"""
127 self
.ofile
.write('\n/* Baton typemaps */\n')
131 "#ifdef SWIGPYTHON\n"
132 "%%apply void *PY_AS_VOID {\n"
135 "#endif\n" % ( ",\n void *".join(batons
) )
139 def _write_callbacks(self
, callbacks
):
140 """Write invoker functions for callbacks"""
141 self
.ofile
.write('\n/* Callbacks */\n')
142 self
.ofile
.write("\n%inline %{\n")
145 for match
in callbacks
:
147 if match
[0] and not match
[1]:
150 elif not match
[0] and struct
not in self
._ignores
:
151 # Struct member callbacks
152 return_type
, name
, params
= match
[1:]
153 type = "%s *" % struct
155 self
._write
_callback
(type, return_type
, struct
[:-2], name
, params
,
158 elif match
[0] and match
[1]:
159 # Callbacks declared as a typedef
160 return_type
, module
, function
, params
= match
161 type = "%s_%s_t" % (module
, function
)
163 self
._write
_callback
(type, return_type
, module
, function
, params
,
167 self
.ofile
.write("%}\n")
169 self
.ofile
.write("\n#ifdef SWIGPYTHON\n")
170 for match
in callbacks
:
172 if match
[0] and not match
[1]:
175 elif not match
[0] and struct
not in self
._ignores
:
176 # Using funcptr_member_proxy, add proxy methods to anonymous
177 # struct member callbacks, so that they can be invoked directly.
178 return_type
, name
, params
= match
[1:]
179 self
.ofile
.write('%%funcptr_member_proxy(%s, %s, %s_invoke_%s);\n'
180 % (struct
, name
, struct
[:-2], name
))
181 elif match
[0] and match
[1]:
182 # Using funcptr_proxy, create wrapper objects for each typedef'd
183 # callback, so that they can be invoked directly. The
184 # CALLABLE_CALLBACK typemap (used in _write_callback_typemaps)
185 # ensures that these wrapper objects are actually used.
186 return_type
, module
, function
, params
= match
187 self
.ofile
.write('%%funcptr_proxy(%s_%s_t, %s_invoke_%s);\n'
188 % (module
, function
, module
, function
))
189 self
.ofile
.write("\n#endif\n")
191 def _write_proxy_definitions(self
, structs
):
192 """Write proxy definitions to a SWIG interface file"""
193 self
.ofile
.write('\n/* Structure definitions */\n')
194 self
.ofile
.write('#ifdef SWIGPYTHON\n');
195 for structName
, structDefinition
in structs
:
197 self
.ofile
.write('%%proxy(%s);\n' % structName
)
199 self
.ofile
.write('%%opaque_proxy(%s);\n' % structName
)
200 self
.ofile
.write('#endif\n');
202 """Regular expression for parsing includes from a C header file"""
203 _re_includes
= re
.compile(r
'#\s*include\s*[<"]([^<">;\s]+)')
205 """Regular expression for parsing structs from a C header file"""
206 _re_structs
= re
.compile(r
'\btypedef\s+(?:struct|union)\s+'
207 r
'(svn_[a-z_0-9]+)\b\s*(\{?)')
209 """Regular expression for parsing callbacks declared inside structs
210 from a C header file"""
211 _re_struct_callbacks
= re
.compile(r
'\btypedef\s+(?:struct|union)\s+'
212 r
'(svn_[a-z_0-9]+)\b|'
213 r
'\n[ \t]+((?!typedef)[a-z_0-9\s*]+)'
218 """Regular expression for parsing callbacks declared as a typedef
219 from a C header file"""
220 _re_typed_callbacks
= re
.compile(r
'typedef\s+([a-z_0-9\s*]+)'
221 r
'\(\*(svn_[a-z]+)_([a-z_0-9]+)_t\)\s*'
224 """Regular expression for parsing batons"""
225 _re_batons
= re
.compile(r
'void\s*\*\s*(\w*baton\w*)');
227 """Regular expression for parsing parameter names from a parameter list"""
228 _re_param_names
= re
.compile(r
'\b(\w+)\s*\)*\s*(?:,|$)')
230 """Regular expression for parsing comments"""
231 _re_comments
= re
.compile(r
'/\*.*?\*/')
233 def _write_swig_interface_file(self
, base_fname
, batons
, includes
, structs
,
235 """Convert a header file into a SWIG header file"""
237 # Calculate output filename from base filename
238 output_fname
= os
.path
.join(self
.proxy_dir
,
239 self
.proxy_filename(base_fname
))
241 # Open the output file
242 self
.ofile
= open(output_fname
, 'w')
243 self
.ofile
.write('/* Proxy classes for %s\n' % base_fname
)
244 self
.ofile
.write(' * DO NOT EDIT -- AUTOMATICALLY GENERATED */\n')
246 # Write list of structs for which we shouldn't define constructors
248 self
._write
_nodefault
_calls
(structs
)
250 # Write typemaps for the callbacks
251 self
._write
_callback
_typemaps
(callbacks
)
253 # Write typemaps for the batons
254 self
._write
_baton
_typemaps
(batons
)
256 # Write includes into the SWIG interface file
257 self
._write
_includes
(includes
, base_fname
)
259 # Write proxy definitions into the SWIG interface file
260 self
._write
_proxy
_definitions
(structs
)
262 # Write callback definitions into the SWIG interface file
263 self
._write
_callbacks
(callbacks
)
265 # Close our output file
268 def process_header_file(self
, fname
):
269 """Generate a wrapper around a header file"""
271 # Read the contents of the header file
272 contents
= open(fname
).read()
275 contents
= self
._re
_comments
.sub("", contents
)
277 # Get list of includes
278 includes
= unique(self
._re
_includes
.findall(contents
))
280 # Get list of structs
281 structs
= unique(self
._re
_structs
.findall(contents
))
284 batons
= unique(self
._re
_batons
.findall(contents
))
286 # Get list of callbacks
287 callbacks
= (self
._re
_struct
_callbacks
.findall(contents
) +
288 self
._re
_typed
_callbacks
.findall(contents
))
290 # Get the location of the output file
291 base_fname
= os
.path
.basename(fname
)
293 # Write the SWIG interface file
294 self
._write
_swig
_interface
_file
(base_fname
, batons
, includes
, structs
,
298 """Generate wrappers for all header files"""
300 for fname
in self
.header_files
:
301 self
.process_header_file(fname
)
303 if __name__
== "__main__":
304 if len(sys
.argv
) < 3:
305 print """Usage: %s build.conf swig [ subversion/include/header_file.h ]
306 Generates SWIG proxy wrappers around Subversion header files. If no header
307 files are specified, generate wrappers for subversion/include/*.h. """ % \
308 os
.path
.basename(sys
.argv
[0])
310 gen
= Generator(sys
.argv
[1], sys
.argv
[2])
311 if len(sys
.argv
) > 3:
312 for fname
in sys
.argv
[3:]:
313 gen
.process_header_file(fname
)