_fortran.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import re
  2. import os
  3. import glob
  4. from distutils.dep_util import newer
  5. __all__ = ['needs_g77_abi_wrapper', 'split_fortran_files',
  6. 'get_g77_abi_wrappers']
  7. def uses_mkl(info):
  8. r_mkl = re.compile("mkl")
  9. libraries = info.get('libraries', '')
  10. for library in libraries:
  11. if r_mkl.search(library):
  12. return True
  13. return False
  14. def needs_g77_abi_wrapper(info):
  15. """Returns True if g77 ABI wrapper must be used."""
  16. return uses_mkl(info)
  17. def get_g77_abi_wrappers(info):
  18. """
  19. Returns file names of source files containing Fortran ABI wrapper
  20. routines.
  21. """
  22. wrapper_sources = []
  23. path = os.path.abspath(os.path.dirname(__file__))
  24. if needs_g77_abi_wrapper(info):
  25. wrapper_sources += [
  26. os.path.join(path, 'src', 'wrap_g77_abi_f.f'),
  27. os.path.join(path, 'src', 'wrap_g77_abi_c.c'),
  28. ]
  29. else:
  30. wrapper_sources += [
  31. os.path.join(path, 'src', 'wrap_dummy_g77_abi.f'),
  32. ]
  33. return wrapper_sources
  34. def split_fortran_files(source_dir, subroutines=None):
  35. """Split each file in `source_dir` into separate files per subroutine.
  36. Parameters
  37. ----------
  38. source_dir : str
  39. Full path to directory in which sources to be split are located.
  40. subroutines : list of str, optional
  41. Subroutines to split. (Default: all)
  42. Returns
  43. -------
  44. fnames : list of str
  45. List of file names (not including any path) that were created
  46. in `source_dir`.
  47. Notes
  48. -----
  49. This function is useful for code that can't be compiled with g77 because of
  50. type casting errors which do work with gfortran.
  51. Created files are named: ``original_name + '_subr_i' + '.f'``, with ``i``
  52. starting at zero and ending at ``num_subroutines_in_file - 1``.
  53. """
  54. if subroutines is not None:
  55. subroutines = [x.lower() for x in subroutines]
  56. def split_file(fname):
  57. with open(fname, 'rb') as f:
  58. lines = f.readlines()
  59. subs = []
  60. need_split_next = True
  61. # find lines with SUBROUTINE statements
  62. for ix, line in enumerate(lines):
  63. m = re.match(b'^\\s+subroutine\\s+([a-z0-9_]+)\\s*\\(', line, re.I)
  64. if m and line[0] not in b'Cc!*':
  65. if subroutines is not None:
  66. subr_name = m.group(1).decode('ascii').lower()
  67. subr_wanted = (subr_name in subroutines)
  68. else:
  69. subr_wanted = True
  70. if subr_wanted or need_split_next:
  71. need_split_next = subr_wanted
  72. subs.append(ix)
  73. # check if no split needed
  74. if len(subs) <= 1:
  75. return [fname]
  76. # write out one file per subroutine
  77. new_fnames = []
  78. num_files = len(subs)
  79. for nfile in range(num_files):
  80. new_fname = fname[:-2] + '_subr_' + str(nfile) + '.f'
  81. new_fnames.append(new_fname)
  82. if not newer(fname, new_fname):
  83. continue
  84. with open(new_fname, 'wb') as fn:
  85. if nfile + 1 == num_files:
  86. fn.writelines(lines[subs[nfile]:])
  87. else:
  88. fn.writelines(lines[subs[nfile]:subs[nfile+1]])
  89. return new_fnames
  90. exclude_pattern = re.compile('_subr_[0-9]')
  91. source_fnames = [f for f in sorted(glob.glob(os.path.join(source_dir, '*.f')))
  92. if not exclude_pattern.search(os.path.basename(f))]
  93. fnames = []
  94. for source_fname in source_fnames:
  95. created_files = split_file(source_fname)
  96. if created_files is not None:
  97. for cfile in created_files:
  98. fnames.append(os.path.basename(cfile))
  99. return fnames