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 from config import get_swig_binary
6
8 return "\n".join(format % i for i in sequence)
9
11 '''Reindent a multiline string to allow easier to read syntax.
12
13 Each line will be indented relative to the first non-empty line.
14 Start the first line without text like shown in this example::
15
16 code = reindent("""
17 Foo
18 Bar
19 Blatti
20 Ping
21 """)
22
23 makes all indentation relative to Foo.
24 '''
25 lines = code.split("\n")
26 space = ""
27
28 for l in lines:
29 if l:
30 r = re.search(r"^( [ ]*)", l)
31 if r is not None:
32 space = r.groups()[0]
33 break
34 if not space:
35 return code
36 n = len(space)
37 instant_assert(space == " "*n, "Logic breach in reindent.")
38 return "\n".join(re.sub(r"^%s" % space, "", l) for l in lines)
39
40
41 -def write_interfacefile(filename, modulename, code, init_code,
42 additional_definitions, additional_declarations,
43 system_headers, local_headers, wrap_headers, arrays):
44 """Generate a SWIG interface file. Intended for internal library use.
45
46 The input arguments are as follows:
47 - modulename (Name of the module)
48 - code (Code to be wrapped)
49 - init_code (Code to put in the init section of the interface file)
50 - additional_definitions (Definitions to be placed in initial block with
51 C code as well as in the main section of the SWIG interface file)
52 - additional_declarations (Declarations to be placed in the main section
53 of the SWIG interface file)
54 - system_headers (A list of system headers with declarations needed by the wrapped code)
55 - local_headers (A list of local headers with declarations needed by the wrapped code)
56 - wrap_headers (A list of local headers that will be included in the code and wrapped by SWIG)
57 - arrays (A nested list, the inner lists describing the different arrays)
58
59 The result of this function is that a SWIG interface with
60 the name modulename.i is written to the current directory.
61 """
62 instant_debug("Generating SWIG interface file '%s'." % filename)
63
64
65 typemaps = ""
66 valid_types = ['float', 'double', 'short', 'int', 'long', 'long long',
67 'unsigned short', 'unsigned int', 'unsigned long',
68 'unsigned long long']
69 for a in arrays:
70 if type(a) == tuple:
71 a = list(a)
72 DATA_TYPE = 'double'
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
161 interface_string = """%%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 -def write_setup(filename, modulename, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swig_include_dirs, swigargs, cppargs, lddargs):
194 """Generate a setup.py file. Intended for internal library use."""
195 instant_debug("Generating %s." % filename)
196
197 swig_include_dirs.append(os.path.join(os.path.dirname(__file__), 'swig'))
198
199
200 swigfilename = "%s.i" % modulename
201 wrapperfilename = "%s_wrap.cxx" % modulename
202
203
204 cppsrcs = cppsrcs + csrcs + [wrapperfilename]
205
206 swig_args = ""
207 if swigargs:
208 swig_args = " ".join(swigargs)
209
210 compile_args = ""
211 if cppargs:
212 compile_args = ", extra_compile_args=%r" % cppargs
213
214 link_args = ""
215 if lddargs:
216 link_args = ", extra_link_args=%r" % lddargs
217
218 swig_include_dirs = " ".join("-I%s"%d for d in swig_include_dirs)
219 if len(local_headers) > 0:
220 swig_include_dirs += " -I.."
221
222
223 code = reindent("""
224 import os
225 from distutils.core import setup, Extension
226 name = '%s'
227 swig_cmd =r'%s -python %s %s %s'
228 os.system(swig_cmd)
229 sources = %s
230 setup(name = '%s',
231 ext_modules = [Extension('_' + '%s',
232 sources,
233 include_dirs=%s,
234 library_dirs=%s,
235 libraries=%s %s %s)])
236 """ % (modulename, get_swig_binary(), swig_include_dirs, swig_args, \
237 swigfilename, cppsrcs, modulename, modulename, include_dirs, \
238 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, \
256 additional_definitions, additional_declarations, \
257 system_headers, local_headers, wrap_headers, arrays)
258 print "".join(open("%s.i" % modulename).readlines())
259
261 modulename = "testmodule"
262 csrcs = ["csrc1.c", "csrc2.c"]
263 cppsrcs = ["cppsrc1.cpp", "cppsrc2.cpp"]
264 local_headers = ["local_header1.h", "local_header2.h"]
265 include_dirs = ["includedir1", "includedir2"]
266 library_dirs = ["librarydir1", "librarydir2"]
267 libraries = ["lib1", "lib2"]
268 swig_include_dirs = ["swigdir1", "swigdir2"],
269 swigargs = ["-Swigarg1", "-Swigarg2"]
270 cppargs = ["-cpparg1", "-cpparg2"]
271 lddargs = ["-Lddarg1", "-Lddarg2"]
272
273 write_setup("setup.py", modulename, csrcs, cppsrcs, local_headers, \
274 include_dirs, library_dirs, libraries, swig_include_dirs, \
275 swigargs, cppargs, lddargs)
276 print "".join(open("setup.py").readlines())
277
279 set = {}
280 map(set.__setitem__, list, [])
281 return set.keys()
282
283
285 pattern = "vtk\w*"
286 l = unique(re.findall(pattern, str))
287 return l
288
290 s = ""
291
292 typemap_template = """
293 %%typemap(in) %(class_name)s * {
294 vtkObjectBase* obj = vtkPythonGetPointerFromObject($input, "%(class_name)s");
295 %(class_name)s * oobj = NULL;
296 if (obj->IsA("%(class_name)s")) {
297 oobj = %(class_name)s::SafeDownCast(obj);
298 $1 = oobj;
299 }
300 }
301
302 %%typemap(out) %(class_name)s * {
303 $result = vtkPythonGetObjectFromPointer($1);
304 }
305
306 """
307
308 for cl in classes:
309 s += typemap_template % { "class_name" : cl }
310
311 return s
312
313
315 s = """
316 #include "vtkPythonUtil.h"
317 """
318 for cl in classes:
319 s += """
320 #include \"%s.h\" """ % cl
321 return s
322
323
325
326 interface_template = """
327 %%module test
328 %%{
329
330 %(includes)s
331
332 %(code)s
333
334 %%}
335
336 %(typemaps)s
337
338 %(code)s
339
340 """
341 class_list = find_vtk_classes(code)
342 includes = generate_vtk_includes(class_list)
343 typemaps = create_typemaps(class_list)
344 s = interface_template % { "typemaps" : typemaps, "code" : code, "includes" : includes }
345 return s
346
347 -def write_cmakefile(module_name, cmake_packages, csrcs, cppsrcs, local_headers, include_dirs, library_dirs, libraries, swig_include_dirs, swigargs, cppargs, lddargs):
348
349 find_package_template = """
350 # Configuration for package %(package)s
351 FIND_PACKAGE(%(package)s REQUIRED)
352 IF(%(package)s_FOUND)
353 INCLUDE(${%(PACKAGE)s_USE_FILE})
354 ENDIF(%(package)s_FOUND)
355
356 if (NOT $ENV{CXX})
357 set(CMAKE_CXX_COMPILER ${%(PACKAGE)s_CXX_COMPILER})
358 endif()
359 """
360
361 cmake_form = dict(module_name=module_name)
362
363 cmake_form["extra_libraries"] = ";".join(libraries)
364 cmake_form["extra_include_dirs"] = ";".join(include_dirs)
365 cmake_form["extra_swig_include_dirs"] = " -I".join([" "] + swig_include_dirs)
366
367 cmake_form["extra_swigargs"] = " ".join(swigargs)
368
369
370 cmake_form["find_packages"] = "\n\n".join(find_package_template % \
371 dict(package=package,
372 PACKAGE=package.upper())\
373 for package in cmake_packages)
374 cmake_form["packages_definitions"] = "\n".join(
375 "${%s_CXX_DEFINITIONS}" % package.upper()
376 for package in cmake_packages)
377
378 cmake_form["packages_definitions"] += "\n"+"\n".join(
379 "${%s_PYTHON_DEFINITIONS}" % package.upper()
380 for package in cmake_packages)
381
382 cmake_form["package_include_dirs"] = "\n".join(\
383 "include_directories(${%s_PYTHON_INCLUDE_DIRS} ${${NAME}_SOURCE_DIR})" %
384 package.upper() for package in cmake_packages)
385
386 cmake_form["package_flags"] = "\n".join(\
387 """set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} ${%(package)s_CXX_FLAGS}\")
388 set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} ${%(package)s_LINK_FLAGS}\")
389 set(CMAKE_SHARED_LINKER_FLAGS \"${CMAKE_SHARED_LINKER_FLAGS} ${%(package)s_LINK_FLAGS}\")
390 """ %
391 dict(package=package.upper()) for package in cmake_packages)
392
393 cmake_form["package_swig_link_libraries"] = "\n".join(\
394 """if (DEFINED %(package)s_LIBRARIES OR DEFINED %(package)s_3RD_PARTY_LIBRARIES OR DEFINED %(package)s_PYTHON_LIBRARIES)
395 swig_link_libraries(${SWIG_MODULE_NAME} ${%(package)s_LIBRARIES} ${%(package)s_3RD_PARTY_LIBRARIES} ${%(package)s_PYTHON_LIBRARIES} ${EXTRA_SOURCE_LIB})
396 endif()""" %
397 dict(package=package.upper()) for package in cmake_packages)
398
399 cmake_form["package_python_definitions"] = "\n".join(\
400 """if (DEFINED %(package)s_PYTHON_DEFINITIONS)
401 add_definitions(${%(package)s_PYTHON_DEFINITIONS})
402 endif()""" %
403 dict(package=package.upper()) for package in cmake_packages)
404
405 cppsrcs.extend(csrcs)
406 if len(cppsrcs) > 0:
407 cmake_form["extra_sources_files"] = "set(SOURCE_FILES %s) " % " ".join(cppsrcs)
408 else:
409 cmake_form["extra_sources_files"] = "set(SOURCE_FILES)"
410
411 if cppargs:
412 cmake_form["cppargs"] = "set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} %s\")" % \
413 (" ".join(cppargs))
414 else:
415 cmake_form["cppargs"] = ""
416
417 if lddargs:
418 cmake_form["lddargs"] = "set(CMAKE_EXE_LINKER_FLAGS \""\
419 "${CMAKE_EXE_LINKER_FLAGS} %s\")" % (" ".join(lddargs))
420 else:
421 cmake_form["lddargs"] = ""
422
423 cmake_template = """
424 cmake_minimum_required(VERSION 2.6.0)
425
426 # This project is designed to be built outside the Insight source tree.
427 set (NAME %(module_name)s)
428
429 %(find_packages)s
430
431 PROJECT(${NAME})
432
433 %(cppargs)s
434 %(lddargs)s
435
436 find_package(SWIG REQUIRED)
437 include(${SWIG_USE_FILE})
438
439 set(SWIG_MODULE_NAME ${NAME})
440 set(CMAKE_SWIG_FLAGS
441 -module ${SWIG_MODULE_NAME}
442 -shadow
443 -modern
444 -modernargs
445 -fastdispatch
446 -fvirtual
447 -nosafecstrings
448 -noproxydel
449 -fastproxy
450 -fastinit
451 -fastunpack
452 -fastquery
453 -nobuildnone
454 %(packages_definitions)s
455 %(extra_swigargs)s
456 %(extra_swig_include_dirs)s
457 )
458
459 set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR})
460
461 set(SWIG_SOURCES ${NAME}.i)
462
463 set_source_files_properties(${SWIG_SOURCES} PROPERTIES CPLUSPLUS ON)
464
465 set(EXTRA_INCLUDE_DIRS %(extra_include_dirs)s)
466 if(EXTRA_INCLUDE_DIRS)
467 include_directories(${EXTRA_INCLUDE_DIRS})
468 endif()
469 %(package_include_dirs)s
470
471 %(package_flags)s
472
473 %(extra_sources_files)s
474
475 %(package_python_definitions)s
476
477 swig_add_module(${SWIG_MODULE_NAME} python ${SWIG_SOURCES} ${SOURCE_FILES})
478
479 set(EXTRA_LINK_LIBRARIES %(extra_libraries)s)
480 if(EXTRA_LIBRARIES)
481 swig_link_libraries(${EXTRA_LIBRARIES})
482 endif()
483
484 %(package_swig_link_libraries)s
485
486 """ % cmake_form
487
488 filename = "CMakeLists.txt"
489 write_file(filename, cmake_template)
490
492 file_template = """
493 cmake_minimum_required(VERSION 2.6.0)
494
495 # This project is designed to be built outside the Insight source tree.
496 PROJECT(%(name)%s)
497
498 # Find ITK.
499 FIND_PACKAGE(ITK REQUIRED)
500 IF(ITK_FOUND)
501 INCLUDE(${ITK_USE_FILE})
502 ENDIF(ITK_FOUND)
503
504 # Find VTK.
505 FIND_PACKAGE(VTK REQUIRED)
506 IF(VTK_FOUND)
507 INCLUDE(${VTK_USE_FILE})
508 ENDIF(VTK_FOUND)
509
510 find_package(SWIG REQUIRED)
511 include(${SWIG_USE_FILE})
512
513
514 set(SWIG_MODULE_NAME %(name)s)
515 set(CMAKE_SWIG_FLAGS
516 -module ${SWIG_MODULE_NAME}
517 -shadow
518 -modern
519 -modernargs
520 -fastdispatch
521 -fvirtual
522 -nosafecstrings
523 -noproxydel
524 -fastproxy
525 -fastinit
526 -fastunpack
527 -fastquery
528 -nobuildnone
529 -Iinclude/swig
530 )
531
532 set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR})
533
534 set(SWIG_SOURCES %(name)s.i)
535
536 set_source_files_properties(${SWIG_SOURCES} PROPERTIES CPLUSPLUS ON)
537
538 include_directories(${PYTHON_INCLUDE_PATH} ${%(name)s_SOURCE_DIR})
539
540 set(VTK_LIBS ITKCommon vtkCommon vtkImaging vtkIO vtkFiltering vtkRendering vtkGraphics vtkCommonPythonD vtkFilteringPythonD)
541
542 swig_add_module(${SWIG_MODULE_NAME} python ${SWIG_SOURCES})
543
544 swig_link_libraries(${SWIG_MODULE_NAME} ${PYTHON_LIBRARIES} ${VTK_LIBS})
545
546
547 """ % { "name" : name }
548
549 f = open("CMakeLists.txt", 'w')
550
551 f.write(file_template)
552
553
555 file_template = """
556 cmake_minimum_required(VERSION 2.6.0)
557
558 # This project is designed to be built outside the Insight source tree.
559 PROJECT(%(name)%s)
560
561 # Find ITK.
562 FIND_PACKAGE(ITK REQUIRED)
563 IF(ITK_FOUND)
564 INCLUDE(${ITK_USE_FILE})
565 ENDIF(ITK_FOUND)
566
567 # Find VTK.
568 FIND_PACKAGE(VTK REQUIRED)
569 IF(VTK_FOUND)
570 INCLUDE(${VTK_USE_FILE})
571 ENDIF(VTK_FOUND)
572
573 # Find VMTK.
574 #FIND_PACKAGE(VMTK REQUIRED)
575 #IF(VMTK_FOUND)
576 # INCLUDE(${VMTK_USE_FILE})
577 #ENDIF(ITK_FOUND)
578
579
580
581 find_package(SWIG REQUIRED)
582 include(${SWIG_USE_FILE})
583
584
585 set(SWIG_MODULE_NAME %(name)s)
586 set(CMAKE_SWIG_FLAGS
587 -module ${SWIG_MODULE_NAME}
588 -shadow
589 -modern
590 -modernargs
591 -fastdispatch
592 -fvirtual
593 -nosafecstrings
594 -noproxydel
595 -fastproxy
596 -fastinit
597 -fastunpack
598 -fastquery
599 -nobuildnone
600 -Iinclude/swig
601 )
602
603 set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR})
604
605 set(SWIG_SOURCES %(name)s.i)
606
607 set_source_files_properties(${SWIG_SOURCES} PROPERTIES CPLUSPLUS ON)
608
609 include_directories(${PYTHON_INCLUDE_PATH} ${%(name)s_SOURCE_DIR} /usr/local/include/vmtk)
610 link_directories(/usr/local/lib/vmtk .)
611
612 set(VTK_LIBS ITKCommon vtkCommon vtkImaging vtkIO vtkFiltering vtkRendering vtkGraphics vtkCommonPythonD vtkFilteringPythonD)
613 set(VMTK_LIBS vtkvmtkCommonPythonD vtkvmtkITKPythonD vtkvmtkCommon vtkvmtkITK vtkvmtkComputationalGeometryPythonD vtkvmtkMiscPythonD vtkvmtkComputationalGeometry vtkvmtkMisc vtkvmtkDifferentialGeometryPythonD vtkvmtkSegmentationPythonD vtkvmtkDifferentialGeometry vtkvmtkSegmentation vtkvmtkIOPythonD)
614
615 swig_add_module(${SWIG_MODULE_NAME} python ${SWIG_SOURCES})
616
617 swig_link_libraries(${SWIG_MODULE_NAME} ${PYTHON_LIBRARIES} ${VTK_LIBS} ${VMTK_LIBS})
618
619
620 """ % { "name" : name }
621
622 f = open("CMakeLists.txt", 'w')
623
624 f.write(file_template)
625
627 filename = signature
628 ifile = filename + ".i"
629 iff = open(ifile, 'w')
630 ifile_code = generate_interface_file_vtk(signature, code)
631 iff.write(ifile_code)
632
633
634 if __name__ == "__main__":
635 _test_write_interfacefile()
636 print "\n"*3
637 _test_write_setup()
638