1 """This module contains helper functions for code generation."""
2
3 import re
4 from output import instant_assert, instant_warning, instant_debug, write_file
5
6
8 return "\n".join(format % i for i in sequence)
9
10
12 '''Reindent a multiline string to allow easier to read syntax.
13
14 Each line will be indented relative to the first non-empty line.
15 Start the first line without text like shown in this example::
16
17 code = reindent("""
18 Foo
19 Bar
20 Blatti
21 Ping
22 """)
23
24 makes all indentation relative to Foo.
25 '''
26 lines = code.split("\n")
27 space = ""
28
29 for l in lines:
30 if l:
31 r = re.search(r"^( [ ]*)", l)
32 if r is not None:
33 space = r.groups()[0]
34 break
35 if not space:
36 return code
37 n = len(space)
38 instant_assert(space == " "*n, "Logic breach in reindent.")
39 return "\n".join(re.sub(r"^%s" % space, "", l) for l in lines)
40
41
42 -def write_interfacefile(filename, modulename, code, init_code,
43 additional_definitions, additional_declarations,
44 system_headers, local_headers, wrap_headers, arrays):
45 """Generate a SWIG interface file. Intended for internal library use.
46
47 The input arguments are as follows:
48 - modulename (Name of the module)
49 - code (Code to be wrapped)
50 - init_code (Code to put in the init section of the interface file)
51 - additional_definitions (FIXME: comment)
52 - additional_declarations (FIXME: comment)
53 - system_headers (A list of system headers with declarations needed by the wrapped code)
54 - local_headers (A list of local headers with declarations needed by the wrapped code)
55 - wrap_headers (A list of local headers that will be included in the code and wrapped by SWIG)
56 - arrays (FIXME: comment)
57
58 The result of this function is that a SWIG interface with
59 the name modulename.i is written to the current directory.
60 """
61 instant_debug("Generating SWIG interface file '%s'." % filename)
62
63
64 typemaps = ""
65 for a in arrays:
66
67 if (len(a) == 2):
68 typemaps += reindent("""
69 %%typemap(in) (int %(n)s,double* %(array)s){
70 if (!PyArray_Check($input)) {
71 PyErr_SetString(PyExc_TypeError, "Not a NumPy array");
72 return NULL; ;
73 }
74 PyArrayObject* pyarray;
75 pyarray = (PyArrayObject*)$input;
76 $1 = int(pyarray->dimensions[0]);
77 $2 = (double*)pyarray->data;
78 }
79 """ % { 'n' : a[0] , 'array' : a[1] })
80
81 elif (len(a) == 3):
82 typemaps += reindent("""
83 %%typemap(in) (int %(n)s,int* %(ptv)s,double* %(array)s){
84 if (!PyArray_Check($input)) {
85 PyErr_SetString(PyExc_TypeError, "Not a NumPy array");
86 return NULL; ;
87 }
88 PyArrayObject* pyarray;
89 pyarray = (PyArrayObject*)$input;
90 $1 = int(pyarray->nd);
91 int* dims = new int($1);
92 for (int d=0; d<$1; d++) {
93 dims[d] = int(pyarray->dimensions[d]);
94 }
95
96 $2 = dims;
97 $3 = (double*)pyarray->data;
98 }
99 %%typemap(freearg) (int %(n)s,int* %(ptv)s,double* %(array)s){
100 // deleting dims
101 delete $2;
102 }
103 """ % { 'n' : a[0] , 'ptv' : a[1], 'array' : a[2] })
104
105
106
107 system_headers_code = mapstrings('#include <%s>', system_headers)
108 local_headers_code = mapstrings('#include "%s"', local_headers)
109 wrap_headers_code1 = mapstrings('#include "%s"', wrap_headers)
110 wrap_headers_code2 = mapstrings('%%include "%s"', wrap_headers)
111
112 interface_string = reindent("""
113 %%module %(modulename)s
114 //%%module (directors="1") %(modulename)s
115
116 //%%feature("director");
117
118 %%{
119 #include <iostream>
120 %(additional_definitions)s
121 %(system_headers_code)s
122 %(local_headers_code)s
123 %(wrap_headers_code1)s
124 %(code)s
125 %%}
126
127 //%%feature("autodoc", "1");
128 %%init%%{
129 %(init_code)s
130 %%}
131
132 %(additional_definitions)s
133 %(additional_declarations)s
134 %(wrap_headers_code2)s
135 //%(typemaps)s
136 %(code)s;
137
138 """ % locals())
139
140 write_file(filename, interface_string)
141 instant_debug("Done generating interface file.")
142
143
144 -def write_setup(filename, modulename, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swigargs, cppargs, lddargs):
145 """Generate a setup.py file. Intended for internal library use."""
146 instant_debug("Generating %s." % filename)
147
148
149
150 swigfilename = "%s.i" % modulename
151 wrapperfilename = "%s_wrap.cxx" % modulename
152
153 cppsrcs = cppsrcs + [wrapperfilename]
154
155 swig_args = ""
156 if swigargs:
157 swig_args = " ".join(swigargs)
158
159 compile_args = ""
160 if cppargs:
161 compile_args = ", extra_compile_args=%r" % cppargs
162
163 link_args = ""
164 if lddargs:
165 link_args = ", extra_link_args=%r" % lddargs
166
167 inc_dir = ""
168 if len(local_headers) > 0:
169 inc_dir = "-I.."
170
171
172 code = reindent("""
173 import os
174 from distutils.core import setup, Extension
175 name = '%s'
176 swig_cmd ='swig -python %s %s %s'
177 os.system(swig_cmd)
178 sources = %s
179 setup(name = '%s',
180 ext_modules = [Extension('_' + '%s',
181 sources,
182 include_dirs=%s,
183 library_dirs=%s,
184 libraries=%s %s %s)])
185 """ % (modulename, inc_dir, swig_args, swigfilename, cppsrcs,
186 modulename, modulename, include_dirs, library_dirs, libraries, compile_args, link_args))
187
188 write_file(filename, code)
189 instant_debug("Done writing setup.py file.")
190
191
193 modulename = "testmodule"
194 code = "void foo() {}"
195 init_code = "/* custom init code */"
196 additional_definitions = "/* custom definitions */"
197 additional_declarations = "/* custom declarations */"
198 system_headers = ["system_header1.h", "system_header2.h"]
199 local_headers = ["local_header1.h", "local_header2.h"]
200 wrap_headers = ["wrap_header1.h", "wrap_header2.h"]
201 arrays = []
202
203 write_interfacefile("%s.i" % modulename, modulename, code, init_code, additional_definitions, additional_declarations, system_headers, local_headers, wrap_headers, arrays)
204 print "".join(open("%s.i" % modulename).readlines())
205
206
208 modulename = "testmodule"
209 csrcs = ["csrc1.c", "csrc2.c"]
210 cppsrcs = ["cppsrc1.cpp", "cppsrc2.cpp"]
211 local_headers = ["local_header1.h", "local_header2.h"]
212 include_dirs = ["includedir1", "includedir2"]
213 library_dirs = ["librarydir1", "librarydir2"]
214 libraries = ["lib1", "lib2"]
215 swigargs = ["-Swigarg1", "-Swigarg2"]
216 cppargs = ["-cpparg1", "-cpparg2"]
217 lddargs = ["-Lddarg1", "-Lddarg2"]
218
219 write_setup("setup.py", modulename, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swigargs, cppargs, lddargs)
220 print "".join(open("setup.py").readlines())
221
222
223 if __name__ == "__main__":
224 _test_write_interfacefile()
225 print "\n"*3
226 _test_write_setup()
227