old_build_ext.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. """Cython.Distutils.old_build_ext
  2. Implements a version of the Distutils 'build_ext' command, for
  3. building Cython extension modules.
  4. Note that this module is deprecated. Use cythonize() instead.
  5. """
  6. __revision__ = "$Id:$"
  7. import inspect
  8. import sys
  9. import os
  10. from distutils.errors import DistutilsPlatformError
  11. from distutils.dep_util import newer, newer_group
  12. from distutils import log
  13. from distutils.command import build_ext as _build_ext
  14. from distutils import sysconfig
  15. import warnings
  16. try:
  17. from __builtin__ import basestring
  18. except ImportError:
  19. basestring = str
  20. def _check_stack(path):
  21. try:
  22. for frame in inspect.getouterframes(inspect.currentframe(), 0):
  23. if path in frame[1].replace(os.sep, '/'):
  24. return True
  25. except Exception:
  26. pass
  27. return False
  28. if (not _check_stack('setuptools/extensions.py')
  29. and not _check_stack('pyximport/pyxbuild.py')
  30. and not _check_stack('Cython/Distutils/build_ext.py')):
  31. warnings.warn(
  32. "Cython.Distutils.old_build_ext does not properly handle dependencies "
  33. "and is deprecated.")
  34. extension_name_re = _build_ext.extension_name_re
  35. show_compilers = _build_ext.show_compilers
  36. class Optimization(object):
  37. def __init__(self):
  38. self.flags = (
  39. 'OPT',
  40. 'CFLAGS',
  41. 'CPPFLAGS',
  42. 'EXTRA_CFLAGS',
  43. 'BASECFLAGS',
  44. 'PY_CFLAGS',
  45. )
  46. self.state = sysconfig.get_config_vars(*self.flags)
  47. self.config_vars = sysconfig.get_config_vars()
  48. def disable_optimization(self):
  49. "disable optimization for the C or C++ compiler"
  50. badoptions = ('-O1', '-O2', '-O3')
  51. for flag, option in zip(self.flags, self.state):
  52. if option is not None:
  53. L = [opt for opt in option.split() if opt not in badoptions]
  54. self.config_vars[flag] = ' '.join(L)
  55. def restore_state(self):
  56. "restore the original state"
  57. for flag, option in zip(self.flags, self.state):
  58. if option is not None:
  59. self.config_vars[flag] = option
  60. optimization = Optimization()
  61. class old_build_ext(_build_ext.build_ext):
  62. description = "build C/C++ and Cython extensions (compile/link to build directory)"
  63. sep_by = _build_ext.build_ext.sep_by
  64. user_options = _build_ext.build_ext.user_options[:]
  65. boolean_options = _build_ext.build_ext.boolean_options[:]
  66. help_options = _build_ext.build_ext.help_options[:]
  67. # Add the pyrex specific data.
  68. user_options.extend([
  69. ('cython-cplus', None,
  70. "generate C++ source files"),
  71. ('cython-create-listing', None,
  72. "write errors to a listing file"),
  73. ('cython-line-directives', None,
  74. "emit source line directives"),
  75. ('cython-include-dirs=', None,
  76. "path to the Cython include files" + sep_by),
  77. ('cython-c-in-temp', None,
  78. "put generated C files in temp directory"),
  79. ('cython-gen-pxi', None,
  80. "generate .pxi file for public declarations"),
  81. ('cython-directives=', None,
  82. "compiler directive overrides"),
  83. ('cython-gdb', None,
  84. "generate debug information for cygdb"),
  85. ('cython-compile-time-env', None,
  86. "cython compile time environment"),
  87. # For backwards compatibility.
  88. ('pyrex-cplus', None,
  89. "generate C++ source files"),
  90. ('pyrex-create-listing', None,
  91. "write errors to a listing file"),
  92. ('pyrex-line-directives', None,
  93. "emit source line directives"),
  94. ('pyrex-include-dirs=', None,
  95. "path to the Cython include files" + sep_by),
  96. ('pyrex-c-in-temp', None,
  97. "put generated C files in temp directory"),
  98. ('pyrex-gen-pxi', None,
  99. "generate .pxi file for public declarations"),
  100. ('pyrex-directives=', None,
  101. "compiler directive overrides"),
  102. ('pyrex-gdb', None,
  103. "generate debug information for cygdb"),
  104. ])
  105. boolean_options.extend([
  106. 'cython-cplus', 'cython-create-listing', 'cython-line-directives',
  107. 'cython-c-in-temp', 'cython-gdb',
  108. # For backwards compatibility.
  109. 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives',
  110. 'pyrex-c-in-temp', 'pyrex-gdb',
  111. ])
  112. def initialize_options(self):
  113. _build_ext.build_ext.initialize_options(self)
  114. self.cython_cplus = 0
  115. self.cython_create_listing = 0
  116. self.cython_line_directives = 0
  117. self.cython_include_dirs = None
  118. self.cython_directives = None
  119. self.cython_c_in_temp = 0
  120. self.cython_gen_pxi = 0
  121. self.cython_gdb = False
  122. self.no_c_in_traceback = 0
  123. self.cython_compile_time_env = None
  124. def __getattr__(self, name):
  125. if name[:6] == 'pyrex_':
  126. return getattr(self, 'cython_' + name[6:])
  127. else:
  128. return _build_ext.build_ext.__getattr__(self, name)
  129. def __setattr__(self, name, value):
  130. if name[:6] == 'pyrex_':
  131. return setattr(self, 'cython_' + name[6:], value)
  132. else:
  133. # _build_ext.build_ext.__setattr__(self, name, value)
  134. self.__dict__[name] = value
  135. def finalize_options (self):
  136. _build_ext.build_ext.finalize_options(self)
  137. if self.cython_include_dirs is None:
  138. self.cython_include_dirs = []
  139. elif isinstance(self.cython_include_dirs, basestring):
  140. self.cython_include_dirs = \
  141. self.cython_include_dirs.split(os.pathsep)
  142. if self.cython_directives is None:
  143. self.cython_directives = {}
  144. # finalize_options ()
  145. def run(self):
  146. # We have one shot at this before build_ext initializes the compiler.
  147. # If --pyrex-gdb is in effect as a command line option or as option
  148. # of any Extension module, disable optimization for the C or C++
  149. # compiler.
  150. if self.cython_gdb or [1 for ext in self.extensions
  151. if getattr(ext, 'cython_gdb', False)]:
  152. optimization.disable_optimization()
  153. _build_ext.build_ext.run(self)
  154. def build_extensions(self):
  155. # First, sanity-check the 'extensions' list
  156. self.check_extensions_list(self.extensions)
  157. for ext in self.extensions:
  158. ext.sources = self.cython_sources(ext.sources, ext)
  159. # Call original build_extensions
  160. _build_ext.build_ext.build_extensions(self)
  161. def cython_sources(self, sources, extension):
  162. """
  163. Walk the list of source files in 'sources', looking for Cython
  164. source files (.pyx and .py). Run Cython on all that are
  165. found, and return a modified 'sources' list with Cython source
  166. files replaced by the generated C (or C++) files.
  167. """
  168. try:
  169. from Cython.Compiler.Main \
  170. import CompilationOptions, \
  171. default_options as cython_default_options, \
  172. compile as cython_compile
  173. from Cython.Compiler.Errors import PyrexError
  174. except ImportError:
  175. e = sys.exc_info()[1]
  176. print("failed to import Cython: %s" % e)
  177. raise DistutilsPlatformError("Cython does not appear to be installed")
  178. new_sources = []
  179. cython_sources = []
  180. cython_targets = {}
  181. # Setup create_list and cplus from the extension options if
  182. # Cython.Distutils.extension.Extension is used, otherwise just
  183. # use what was parsed from the command-line or the configuration file.
  184. # cplus will also be set to true is extension.language is equal to
  185. # 'C++' or 'c++'.
  186. #try:
  187. # create_listing = self.cython_create_listing or \
  188. # extension.cython_create_listing
  189. # cplus = self.cython_cplus or \
  190. # extension.cython_cplus or \
  191. # (extension.language != None and \
  192. # extension.language.lower() == 'c++')
  193. #except AttributeError:
  194. # create_listing = self.cython_create_listing
  195. # cplus = self.cython_cplus or \
  196. # (extension.language != None and \
  197. # extension.language.lower() == 'c++')
  198. create_listing = self.cython_create_listing or \
  199. getattr(extension, 'cython_create_listing', 0)
  200. line_directives = self.cython_line_directives or \
  201. getattr(extension, 'cython_line_directives', 0)
  202. no_c_in_traceback = self.no_c_in_traceback or \
  203. getattr(extension, 'no_c_in_traceback', 0)
  204. cplus = self.cython_cplus or getattr(extension, 'cython_cplus', 0) or \
  205. (extension.language and extension.language.lower() == 'c++')
  206. cython_gen_pxi = self.cython_gen_pxi or getattr(extension, 'cython_gen_pxi', 0)
  207. cython_gdb = self.cython_gdb or getattr(extension, 'cython_gdb', False)
  208. cython_compile_time_env = self.cython_compile_time_env or \
  209. getattr(extension, 'cython_compile_time_env', None)
  210. # Set up the include_path for the Cython compiler:
  211. # 1. Start with the command line option.
  212. # 2. Add in any (unique) paths from the extension
  213. # cython_include_dirs (if Cython.Distutils.extension is used).
  214. # 3. Add in any (unique) paths from the extension include_dirs
  215. includes = self.cython_include_dirs
  216. try:
  217. for i in extension.cython_include_dirs:
  218. if not i in includes:
  219. includes.append(i)
  220. except AttributeError:
  221. pass
  222. # In case extension.include_dirs is a generator, evaluate it and keep
  223. # result
  224. extension.include_dirs = list(extension.include_dirs)
  225. for i in extension.include_dirs:
  226. if not i in includes:
  227. includes.append(i)
  228. # Set up Cython compiler directives:
  229. # 1. Start with the command line option.
  230. # 2. Add in any (unique) entries from the extension
  231. # cython_directives (if Cython.Distutils.extension is used).
  232. directives = self.cython_directives
  233. if hasattr(extension, "cython_directives"):
  234. directives.update(extension.cython_directives)
  235. # Set the target_ext to '.c'. Cython will change this to '.cpp' if
  236. # needed.
  237. if cplus:
  238. target_ext = '.cpp'
  239. else:
  240. target_ext = '.c'
  241. # Decide whether to drop the generated C files into the temp dir
  242. # or the source tree.
  243. if not self.inplace and (self.cython_c_in_temp
  244. or getattr(extension, 'cython_c_in_temp', 0)):
  245. target_dir = os.path.join(self.build_temp, "pyrex")
  246. for package_name in extension.name.split('.')[:-1]:
  247. target_dir = os.path.join(target_dir, package_name)
  248. else:
  249. target_dir = None
  250. newest_dependency = None
  251. for source in sources:
  252. (base, ext) = os.path.splitext(os.path.basename(source))
  253. if ext == ".py":
  254. # FIXME: we might want to special case this some more
  255. ext = '.pyx'
  256. if ext == ".pyx": # Cython source file
  257. output_dir = target_dir or os.path.dirname(source)
  258. new_sources.append(os.path.join(output_dir, base + target_ext))
  259. cython_sources.append(source)
  260. cython_targets[source] = new_sources[-1]
  261. elif ext == '.pxi' or ext == '.pxd':
  262. if newest_dependency is None \
  263. or newer(source, newest_dependency):
  264. newest_dependency = source
  265. else:
  266. new_sources.append(source)
  267. if not cython_sources:
  268. return new_sources
  269. module_name = extension.name
  270. for source in cython_sources:
  271. target = cython_targets[source]
  272. depends = [source] + list(extension.depends or ())
  273. if(source[-4:].lower()==".pyx" and os.path.isfile(source[:-3]+"pxd")):
  274. depends += [source[:-3]+"pxd"]
  275. rebuild = self.force or newer_group(depends, target, 'newer')
  276. if not rebuild and newest_dependency is not None:
  277. rebuild = newer(newest_dependency, target)
  278. if rebuild:
  279. log.info("cythoning %s to %s", source, target)
  280. self.mkpath(os.path.dirname(target))
  281. if self.inplace:
  282. output_dir = os.curdir
  283. else:
  284. output_dir = self.build_lib
  285. options = CompilationOptions(cython_default_options,
  286. use_listing_file = create_listing,
  287. include_path = includes,
  288. compiler_directives = directives,
  289. output_file = target,
  290. cplus = cplus,
  291. emit_linenums = line_directives,
  292. c_line_in_traceback = not no_c_in_traceback,
  293. generate_pxi = cython_gen_pxi,
  294. output_dir = output_dir,
  295. gdb_debug = cython_gdb,
  296. compile_time_env = cython_compile_time_env)
  297. result = cython_compile(source, options=options,
  298. full_module_name=module_name)
  299. else:
  300. log.info("skipping '%s' Cython extension (up-to-date)", target)
  301. return new_sources
  302. # cython_sources ()
  303. # class build_ext