instant.codegeneration

Package instant :: Module codegeneration |
[hide private]
[frames] | no frames]

# Source Code for Module instant.codegeneration

      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
      7 -def mapstrings(format, sequence):
      8      return "\n".join(format % i for i in sequence)
      9
     10
     11 -def reindent(code):
     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      # Get initial spaces from first non-empty line:
     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      # create typemaps
     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              # input arrays
     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                  # 1-dimensional arrays, i.e. vectors
     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                  # 2-dimensional arrays, i.e. matrices
     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                  # 3-dimensional arrays, i.e. tensors
     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              # output arrays
     98              a.remove('out')
     99              instant_assert(len(a) == 2, "Output array must be 1-dimensional")
    100              # 1-dimensional arrays, i.e. vectors
    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              # in-place arrays
    106              instant_assert(len(a) > 1 and len(a) < 5, "Wrong number of elements in output array")
    107              if 'multi' in a:
    108                  # n-dimensional arrays, i.e. tensors > 3-dimensional
    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                  # 1-dimensional arrays, i.e. vectors
    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                  # 2-dimensional arrays, i.e. matrices
    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                  # 3-dimensional arrays, i.e. tensors
    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              # end
    148          # end if
    149      # end for
    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      # Handle arguments
    201      swigfilename = "%s.i" % modulename
    202      wrapperfilename = "%s_wrap.cxx" % modulename
    203
    204      # Treat C and C++ files in the same way for now
    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      # Generate code
    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
    244 -def _test_write_interfacefile():
    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
    259 -def _test_write_setup():
    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