1 """Build a Pyrex file from .pyx source to .so loadable module using
2 the installed distutils infrastructure. Call:
4 out_fname = pyx_to_dll("foo.pyx")
9 from distutils
.dist
import Distribution
10 from distutils
.errors
import DistutilsArgError
, DistutilsError
, CCompilerError
11 from distutils
.extension
import Extension
12 from distutils
.util
import grok_environment_error
14 from Cython
.Distutils
import build_ext
23 def pyx_to_dll(filename
, ext
= None, force_rebuild
= 0,
24 build_in_temp
=False, pyxbuild_dir
=None, setup_args
={},
25 reload_support
=False, inplace
=False):
26 """Compile a PYX file to a DLL and return the name of the generated .so
28 assert os
.path
.exists(filename
), "Could not find %s" % os
.path
.abspath(filename
)
30 path
, name
= os
.path
.split(os
.path
.abspath(filename
))
33 modname
, extension
= os
.path
.splitext(name
)
34 assert extension
in (".pyx", ".py"), extension
36 filename
= filename
[:-len(extension
)] + '.c'
37 ext
= Extension(name
=modname
, sources
=[filename
])
40 pyxbuild_dir
= os
.path
.join(path
, "_pyxbld")
42 package_base_dir
= path
43 for package_name
in ext
.name
.split('.')[-2::-1]:
44 package_base_dir
, pname
= os
.path
.split(package_base_dir
)
45 if pname
!= package_name
:
46 # something is wrong - package path doesn't match file path
47 package_base_dir
= None
50 script_args
=setup_args
.get("script_args",[])
51 if DEBUG
or "--verbose" in script_args
:
55 args
= [quiet
, "build_ext"]
57 args
.append("--force")
58 if inplace
and package_base_dir
:
59 args
.extend(['--build-lib', package_base_dir
])
60 if ext
.name
== '__init__' or ext
.name
.endswith('.__init__'):
61 # package => provide __path__ early
62 if not hasattr(ext
, 'cython_directives'):
63 ext
.cython_directives
= {'set_initial_path' : 'SOURCEFILE'}
64 elif 'set_initial_path' not in ext
.cython_directives
:
65 ext
.cython_directives
['set_initial_path'] = 'SOURCEFILE'
67 if HAS_CYTHON
and build_in_temp
:
68 args
.append("--pyrex-c-in-temp")
69 sargs
= setup_args
.copy()
72 "script_args": args
+ script_args
} )
73 dist
= Distribution(sargs
)
74 if not dist
.ext_modules
:
76 dist
.ext_modules
.append(ext
)
78 dist
.cmdclass
= {'build_ext': build_ext
}
79 build
= dist
.get_command_obj('build')
80 build
.build_base
= pyxbuild_dir
82 config_files
= dist
.find_config_files()
83 try: config_files
.remove('setup.cfg')
84 except ValueError: pass
85 dist
.parse_config_files(config_files
)
87 cfgfiles
= dist
.find_config_files()
88 try: cfgfiles
.remove('setup.cfg')
89 except ValueError: pass
90 dist
.parse_config_files(cfgfiles
)
92 ok
= dist
.parse_command_line()
93 except DistutilsArgError
:
97 print("options (after parsing command line):")
98 dist
.dump_option_dicts()
103 obj_build_ext
= dist
.get_command_obj("build_ext")
105 so_path
= obj_build_ext
.get_outputs()[0]
106 if obj_build_ext
.inplace
:
107 # Python distutils get_outputs()[ returns a wrong so_path
108 # when --inplace ; see http://bugs.python.org/issue5977
110 so_path
= os
.path
.join(os
.path
.dirname(filename
),
111 os
.path
.basename(so_path
))
114 timestamp
= os
.path
.getmtime(org_path
)
116 last_timestamp
, last_path
, count
= _reloads
.get(org_path
, (None,None,0) )
117 if last_timestamp
== timestamp
:
120 basename
= os
.path
.basename(org_path
)
123 r_path
= os
.path
.join(obj_build_ext
.build_lib
,
124 basename
+ '.reload%s'%count
)
126 import shutil
# late import / reload_support is: debugging
128 # Try to unlink first --- if the .so file
129 # is mmapped by another process,
130 # overwriting its contents corrupts the
131 # loaded image (on Linux) and crashes the
132 # other process. On Windows, unlinking an
133 # open file just fails.
134 if os
.path
.isfile(r_path
):
138 shutil
.copy2(org_path
, r_path
)
144 # used up all 100 slots
145 raise ImportError("reload count for %s reached maximum"%org_path
)
146 _reloads
[org_path
]=(timestamp
, so_path
, count
)
148 except KeyboardInterrupt:
150 except (IOError, os
.error
):
151 exc
= sys
.exc_info()[1]
152 error
= grok_environment_error(exc
)
155 sys
.stderr
.write(error
+ "\n")
158 if __name__
=="__main__":
159 pyx_to_dll("dummy.pyx")