1 """This module contains helper functions for code generation."""
2
3 import re, os
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 (Definitions to be placed in initial block with
52 C code as well as in the main section of the SWIG interface file)
53 - additional_declarations (Declarations to be placed in the main section
54 of the SWIG interface file)
55 - system_headers (A list of system headers with declarations needed by the wrapped code)
56 - local_headers (A list of local headers with declarations needed by the wrapped code)
57 - wrap_headers (A list of local headers that will be included in the code and wrapped by SWIG)
58 - arrays (A nested list, the inner lists describing the different arrays)
59
60 The result of this function is that a SWIG interface with
61 the name modulename.i is written to the current directory.
62 """
63 instant_debug("Generating SWIG interface file '%s'." % filename)
64
65
66 typemaps = ""
67 valid_types = ['float', 'double', 'short', 'int', 'long',
68 'unsigned short', 'unsigned int', 'unsigned long']
69 DATA_TYPE = 'double'
70 for a in arrays:
71 if type(a) == tuple:
72 a = list(a)
73 for vt in valid_types:
74 if vt in a:
75 DATA_TYPE = vt
76 a.remove(vt)
77 if 'in' in a:
78
79 a.remove('in')
80 instant_assert(len(a) > 1 and len(a) < 5, "Wrong number of elements in input array")
81 if len(a) == 2:
82
83 typemaps += reindent("""
84 %%apply (int DIM1, %(dtype)s* IN_ARRAY1) {(int %(n1)s, %(dtype)s* %(array)s)};
85 """ % { 'n1' : a[0], 'array' : a[1], 'dtype' : DATA_TYPE })
86 elif len(a) == 3:
87
88 typemaps += reindent("""
89 %%apply (int DIM1, int DIM2, %(dtype)s* IN_ARRAY2) {(int %(n1)s, int %(n2)s, %(dtype)s* %(array)s)};
90 """ % { 'n1' : a[0], 'n2' : a[1], 'array' : a[2], 'dtype' : DATA_TYPE })
91 else:
92
93 typemaps += reindent("""
94 %%apply (int DIM1, int DIM2, int DIM3, %(dtype)s* IN_ARRAY3) {(int %(n1)s, int %(n2)s, int %(n3)s, %(dtype)s* %(array)s)};
95 """ % { 'n1' : a[0], 'n2' : a[1], 'n3' : a[2], 'array' : a[3], 'dtype' : DATA_TYPE })
96 elif 'out' in a:
97
98 a.remove('out')
99 instant_assert(len(a) == 2, "Output array must be 1-dimensional")
100
101 typemaps += reindent("""
102 %%apply (int DIM1, %(dtype)s* ARGOUT_ARRAY1) {(int %(n1)s, %(dtype)s* %(array)s)};
103 """ % { 'n1' : a[0], 'array' : a[1], 'dtype' : DATA_TYPE })
104 else:
105
106 instant_assert(len(a) > 1 and len(a) < 5, "Wrong number of elements in output array")
107 if 'multi' in a:
108
109 a.remove('multi')
110 typemaps += reindent("""
111 %%typemap(in) (int %(n)s,int* %(ptv)s,%(dtype)s* %(array)s){
112 if (!PyArray_Check($input)) {
113 PyErr_SetString(PyExc_TypeError, "Not a NumPy array");
114 return NULL; ;
115 }
116 PyArrayObject* pyarray;
117 pyarray = (PyArrayObject*)$input;
118 $1 = int(pyarray->nd);
119 int* dims = new int($1);
120 for (int d=0; d<$1; d++) {
121 dims[d] = int(pyarray->dimensions[d]);
122 }
123
124 $2 = dims;
125 $3 = (%(dtype)s*)pyarray->data;
126 }
127 %%typemap(freearg) (int %(n)s,int* %(ptv)s,%(dtype)s* %(array)s){
128 // deleting dims
129 delete $2;
130 }
131 """ % { 'n' : a[0] , 'ptv' : a[1], 'array' : a[2], 'dtype' : DATA_TYPE })
132 elif len(a) == 2:
133
134 typemaps += reindent("""
135 %%apply (int DIM1, %(dtype)s* INPLACE_ARRAY1) {(int %(n1)s, %(dtype)s* %(array)s)};
136 """ % { 'n1' : a[0], 'array' : a[1], 'dtype' : DATA_TYPE })
137 elif len(a) == 3:
138
139 typemaps += reindent("""
140 %%apply (int DIM1, int DIM2, %(dtype)s* INPLACE_ARRAY2) {(int %(n1)s, int %(n2)s, %(dtype)s* %(array)s)};
141 """ % { 'n1' : a[0], 'n2' : a[1], 'array' : a[2], 'dtype' : DATA_TYPE })
142 else:
143
144 typemaps += reindent("""
145 %%apply (int DIM1, int DIM2, int DIM3, %(dtype)s* INPLACE_ARRAY3) {(int %(n1)s, int %(n2)s, int %(n3)s, %(dtype)s* %(array)s)};
146 """ % { 'n1' : a[0], 'n2' : a[1], 'n3' : a[2], 'array' : a[3], 'dtype' : DATA_TYPE})
147
148
149
150
151 system_headers_code = mapstrings('#include <%s>', system_headers)
152 local_headers_code = mapstrings('#include "%s"', local_headers)
153 wrap_headers_code1 = mapstrings('#include "%s"', wrap_headers)
154 wrap_headers_code2 = mapstrings('%%include "%s"', wrap_headers)
155
156 numpy_i_include = ''
157 if arrays:
158 numpy_i_include = r'%include "numpy.i"'
159
160 interface_string = reindent("""
161 %%module %(modulename)s
162 //%%module (directors="1") %(modulename)s
163
164 //%%feature("director");
165
166 %%{
167 #include <iostream>
168 %(additional_definitions)s
169 %(system_headers_code)s
170 %(local_headers_code)s
171 %(wrap_headers_code1)s
172 %(code)s
173 %%}
174
175 //%%feature("autodoc", "1");
176 %(numpy_i_include)s
177
178 %%init%%{
179 %(init_code)s
180 %%}
181
182 %(additional_definitions)s
183 %(additional_declarations)s
184 %(wrap_headers_code2)s
185 //%(typemaps)s
186 %(code)s;
187
188 """ % locals())
189
190 write_file(filename, interface_string)
191 instant_debug("Done generating interface file.")
192
193
194 -def write_setup(filename, modulename, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swig_include_dirs, swigargs, cppargs, lddargs):
195 """Generate a setup.py file. Intended for internal library use."""
196 instant_debug("Generating %s." % filename)
197
198 swig_include_dirs.append(os.path.join(os.path.dirname(__file__), 'swig'))
199
200
201 swigfilename = "%s.i" % modulename
202 wrapperfilename = "%s_wrap.cxx" % modulename
203
204
205 cppsrcs = cppsrcs + csrcs + [wrapperfilename]
206
207 swig_args = ""
208 if swigargs:
209 swig_args = " ".join(swigargs)
210
211 compile_args = ""
212 if cppargs:
213 compile_args = ", extra_compile_args=%r" % cppargs
214
215 link_args = ""
216 if lddargs:
217 link_args = ", extra_link_args=%r" % lddargs
218
219 swig_include_dirs = " ".join("-I%s"%d for d in swig_include_dirs)
220 if len(local_headers) > 0:
221 swig_include_dirs += " -I.."
222
223
224 code = reindent("""
225 import os
226 from distutils.core import setup, Extension
227 name = '%s'
228 swig_cmd =r'swig -python %s %s %s'
229 os.system(swig_cmd)
230 sources = %s
231 setup(name = '%s',
232 ext_modules = [Extension('_' + '%s',
233 sources,
234 include_dirs=%s,
235 library_dirs=%s,
236 libraries=%s %s %s)])
237 """ % (modulename, swig_include_dirs, swig_args, swigfilename, cppsrcs,
238 modulename, modulename, include_dirs, library_dirs, libraries, compile_args, link_args))
239
240 write_file(filename, code)
241 instant_debug("Done writing setup.py file.")
242
243
245 modulename = "testmodule"
246 code = "void foo() {}"
247 init_code = "/* custom init code */"
248 additional_definitions = "/* custom definitions */"
249 additional_declarations = "/* custom declarations */"
250 system_headers = ["system_header1.h", "system_header2.h"]
251 local_headers = ["local_header1.h", "local_header2.h"]
252 wrap_headers = ["wrap_header1.h", "wrap_header2.h"]
253 arrays = [["length1", "array1"], ["dims", "lengths", "array2"]]
254
255 write_interfacefile("%s.i" % modulename, modulename, code, init_code, additional_definitions, additional_declarations, system_headers, local_headers, wrap_headers, arrays)
256 print "".join(open("%s.i" % modulename).readlines())
257
258
260 modulename = "testmodule"
261 csrcs = ["csrc1.c", "csrc2.c"]
262 cppsrcs = ["cppsrc1.cpp", "cppsrc2.cpp"]
263 local_headers = ["local_header1.h", "local_header2.h"]
264 include_dirs = ["includedir1", "includedir2"]
265 library_dirs = ["librarydir1", "librarydir2"]
266 libraries = ["lib1", "lib2"]
267 swig_include_dirs = ["swigdir1", "swigdir2"],
268 swigargs = ["-Swigarg1", "-Swigarg2"]
269 cppargs = ["-cpparg1", "-cpparg2"]
270 lddargs = ["-Lddarg1", "-Lddarg2"]
271
272 write_setup("setup.py", modulename, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swig_include_dirs, swigargs, cppargs, lddargs)
273 print "".join(open("setup.py").readlines())
274
275
276 if __name__ == "__main__":
277 _test_write_interfacefile()
278 print "\n"*3
279 _test_write_setup()
280