123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- """Build a Pyrex file from .pyx source to .so loadable module using
- the installed distutils infrastructure. Call:
- out_fname = pyx_to_dll("foo.pyx")
- """
- import os
- import sys
- from distutils.errors import DistutilsArgError, DistutilsError, CCompilerError
- from distutils.extension import Extension
- from distutils.util import grok_environment_error
- try:
- from Cython.Distutils.old_build_ext import old_build_ext as build_ext
- HAS_CYTHON = True
- except ImportError:
- HAS_CYTHON = False
- DEBUG = 0
- _reloads={}
- def pyx_to_dll(filename, ext=None, force_rebuild=0, build_in_temp=False, pyxbuild_dir=None,
- setup_args=None, reload_support=False, inplace=False):
- """Compile a PYX file to a DLL and return the name of the generated .so
- or .dll ."""
- assert os.path.exists(filename), "Could not find %s" % os.path.abspath(filename)
- path, name = os.path.split(os.path.abspath(filename))
- if not ext:
- modname, extension = os.path.splitext(name)
- assert extension in (".pyx", ".py"), extension
- if not HAS_CYTHON:
- filename = filename[:-len(extension)] + '.c'
- ext = Extension(name=modname, sources=[filename])
- if setup_args is None:
- setup_args = {}
- if not pyxbuild_dir:
- pyxbuild_dir = os.path.join(path, "_pyxbld")
- package_base_dir = path
- for package_name in ext.name.split('.')[-2::-1]:
- package_base_dir, pname = os.path.split(package_base_dir)
- if pname != package_name:
- # something is wrong - package path doesn't match file path
- package_base_dir = None
- break
- script_args=setup_args.get("script_args",[])
- if DEBUG or "--verbose" in script_args:
- quiet = "--verbose"
- else:
- quiet = "--quiet"
- args = [quiet, "build_ext"]
- if force_rebuild:
- args.append("--force")
- if inplace and package_base_dir:
- args.extend(['--build-lib', package_base_dir])
- if ext.name == '__init__' or ext.name.endswith('.__init__'):
- # package => provide __path__ early
- if not hasattr(ext, 'cython_directives'):
- ext.cython_directives = {'set_initial_path' : 'SOURCEFILE'}
- elif 'set_initial_path' not in ext.cython_directives:
- ext.cython_directives['set_initial_path'] = 'SOURCEFILE'
- if HAS_CYTHON and build_in_temp:
- args.append("--pyrex-c-in-temp")
- sargs = setup_args.copy()
- sargs.update({
- "script_name": None,
- "script_args": args + script_args,
- })
- # late import, in case setuptools replaced it
- from distutils.dist import Distribution
- dist = Distribution(sargs)
- if not dist.ext_modules:
- dist.ext_modules = []
- dist.ext_modules.append(ext)
- if HAS_CYTHON:
- dist.cmdclass = {'build_ext': build_ext}
- build = dist.get_command_obj('build')
- build.build_base = pyxbuild_dir
- cfgfiles = dist.find_config_files()
- dist.parse_config_files(cfgfiles)
- try:
- ok = dist.parse_command_line()
- except DistutilsArgError:
- raise
- if DEBUG:
- print("options (after parsing command line):")
- dist.dump_option_dicts()
- assert ok
- try:
- obj_build_ext = dist.get_command_obj("build_ext")
- dist.run_commands()
- so_path = obj_build_ext.get_outputs()[0]
- if obj_build_ext.inplace:
- # Python distutils get_outputs()[ returns a wrong so_path
- # when --inplace ; see http://bugs.python.org/issue5977
- # workaround:
- so_path = os.path.join(os.path.dirname(filename),
- os.path.basename(so_path))
- if reload_support:
- org_path = so_path
- timestamp = os.path.getmtime(org_path)
- global _reloads
- last_timestamp, last_path, count = _reloads.get(org_path, (None,None,0) )
- if last_timestamp == timestamp:
- so_path = last_path
- else:
- basename = os.path.basename(org_path)
- while count < 100:
- count += 1
- r_path = os.path.join(obj_build_ext.build_lib,
- basename + '.reload%s'%count)
- try:
- import shutil # late import / reload_support is: debugging
- try:
- # Try to unlink first --- if the .so file
- # is mmapped by another process,
- # overwriting its contents corrupts the
- # loaded image (on Linux) and crashes the
- # other process. On Windows, unlinking an
- # open file just fails.
- if os.path.isfile(r_path):
- os.unlink(r_path)
- except OSError:
- continue
- shutil.copy2(org_path, r_path)
- so_path = r_path
- except IOError:
- continue
- break
- else:
- # used up all 100 slots
- raise ImportError("reload count for %s reached maximum"%org_path)
- _reloads[org_path]=(timestamp, so_path, count)
- return so_path
- except KeyboardInterrupt:
- sys.exit(1)
- except (IOError, os.error):
- exc = sys.exc_info()[1]
- error = grok_environment_error(exc)
- if DEBUG:
- sys.stderr.write(error + "\n")
- raise
- if __name__=="__main__":
- pyx_to_dll("dummy.pyx")
- from . import test
|