ffiplatform.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import sys, os
  2. from .error import VerificationError
  3. LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
  4. 'extra_objects', 'depends']
  5. def get_extension(srcfilename, modname, sources=(), **kwds):
  6. _hack_at_distutils()
  7. from distutils.core import Extension
  8. allsources = [srcfilename]
  9. for src in sources:
  10. allsources.append(os.path.normpath(src))
  11. return Extension(name=modname, sources=allsources, **kwds)
  12. def compile(tmpdir, ext, compiler_verbose=0, debug=None):
  13. """Compile a C extension module using distutils."""
  14. _hack_at_distutils()
  15. saved_environ = os.environ.copy()
  16. try:
  17. outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
  18. outputfilename = os.path.abspath(outputfilename)
  19. finally:
  20. # workaround for a distutils bugs where some env vars can
  21. # become longer and longer every time it is used
  22. for key, value in saved_environ.items():
  23. if os.environ.get(key) != value:
  24. os.environ[key] = value
  25. return outputfilename
  26. def _build(tmpdir, ext, compiler_verbose=0, debug=None):
  27. # XXX compact but horrible :-(
  28. from distutils.core import Distribution
  29. import distutils.errors, distutils.log
  30. #
  31. dist = Distribution({'ext_modules': [ext]})
  32. dist.parse_config_files()
  33. options = dist.get_option_dict('build_ext')
  34. if debug is None:
  35. debug = sys.flags.debug
  36. options['debug'] = ('ffiplatform', debug)
  37. options['force'] = ('ffiplatform', True)
  38. options['build_lib'] = ('ffiplatform', tmpdir)
  39. options['build_temp'] = ('ffiplatform', tmpdir)
  40. #
  41. try:
  42. old_level = distutils.log.set_threshold(0) or 0
  43. try:
  44. distutils.log.set_verbosity(compiler_verbose)
  45. dist.run_command('build_ext')
  46. cmd_obj = dist.get_command_obj('build_ext')
  47. [soname] = cmd_obj.get_outputs()
  48. finally:
  49. distutils.log.set_threshold(old_level)
  50. except (distutils.errors.CompileError,
  51. distutils.errors.LinkError) as e:
  52. raise VerificationError('%s: %s' % (e.__class__.__name__, e))
  53. #
  54. return soname
  55. try:
  56. from os.path import samefile
  57. except ImportError:
  58. def samefile(f1, f2):
  59. return os.path.abspath(f1) == os.path.abspath(f2)
  60. def maybe_relative_path(path):
  61. if not os.path.isabs(path):
  62. return path # already relative
  63. dir = path
  64. names = []
  65. while True:
  66. prevdir = dir
  67. dir, name = os.path.split(prevdir)
  68. if dir == prevdir or not dir:
  69. return path # failed to make it relative
  70. names.append(name)
  71. try:
  72. if samefile(dir, os.curdir):
  73. names.reverse()
  74. return os.path.join(*names)
  75. except OSError:
  76. pass
  77. # ____________________________________________________________
  78. try:
  79. int_or_long = (int, long)
  80. import cStringIO
  81. except NameError:
  82. int_or_long = int # Python 3
  83. import io as cStringIO
  84. def _flatten(x, f):
  85. if isinstance(x, str):
  86. f.write('%ds%s' % (len(x), x))
  87. elif isinstance(x, dict):
  88. keys = sorted(x.keys())
  89. f.write('%dd' % len(keys))
  90. for key in keys:
  91. _flatten(key, f)
  92. _flatten(x[key], f)
  93. elif isinstance(x, (list, tuple)):
  94. f.write('%dl' % len(x))
  95. for value in x:
  96. _flatten(value, f)
  97. elif isinstance(x, int_or_long):
  98. f.write('%di' % (x,))
  99. else:
  100. raise TypeError(
  101. "the keywords to verify() contains unsupported object %r" % (x,))
  102. def flatten(x):
  103. f = cStringIO.StringIO()
  104. _flatten(x, f)
  105. return f.getvalue()
  106. def _hack_at_distutils():
  107. # Windows-only workaround for some configurations: see
  108. # https://bugs.python.org/issue23246 (Python 2.7 with
  109. # a specific MS compiler suite download)
  110. if sys.platform == "win32":
  111. try:
  112. import setuptools # for side-effects, patches distutils
  113. except ImportError:
  114. pass