Make a status test pass against old servers.
[svn.git] / build / generator / swig / header_wrappers.py
blobe0dbcd3ec54edef60216e6edec41115b8bccf2f6
1 #!/usr/bin/env python
4 # header_wrappers.py: Generates SWIG proxy wrappers around Subversion
5 # header files
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
13 import generator.swig
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
31 header files."""
32 wrapper_fnames = []
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)
40 makefile.write(
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,
72 callee):
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
80 if params == "void":
81 param_names = ""
82 params = "%s _obj" % type
83 else:
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
92 self.ofile.write(
93 "static %s %s_invoke_%s(\n" % (return_type, module, function) +
94 " %s) {\n" % params +
95 " %s;\n" % invoke_callback +
96 "}\n\n")
99 def _write_callback_typemaps(self, callbacks):
100 """Apply the CALLABLE_CALLBACK typemap to all callbacks"""
102 self.ofile.write('\n/* Callback typemaps */\n')
103 types = [];
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)
109 types.append(type)
111 if types:
112 self.ofile.write(
113 "#ifdef SWIGPYTHON\n"
114 "%%apply CALLABLE_CALLBACK {\n"
115 " %s\n"
116 "};\n"
117 "%%apply CALLABLE_CALLBACK * {\n"
118 " %s *\n"
119 "};\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')
129 if batons:
130 self.ofile.write(
131 "#ifdef SWIGPYTHON\n"
132 "%%apply void *PY_AS_VOID {\n"
133 " void *%s\n"
134 "};\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")
144 struct = None
145 for match in callbacks:
147 if match[0] and not match[1]:
148 # Struct definitions
149 struct = match[0]
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,
156 "(_obj->%s)" % name)
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,
164 "_obj")
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]:
173 # Struct definitions
174 struct = match[0]
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:
196 if structDefinition:
197 self.ofile.write('%%proxy(%s);\n' % structName)
198 else:
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*]+)'
214 r'\(\*(\w+)\)'
215 r'\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*'
222 r'\(([^)]+)\);');
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,
234 callbacks):
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
247 # by default
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
266 self.ofile.close()
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()
274 # Remove comments
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))
283 # Get list of batons
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,
295 callbacks)
297 def write(self):
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])
309 else:
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)
314 else:
315 gen.write()