1
1
import os
2
2
from os .path import splitext , sep as filesep , join as pjoin , relpath
3
3
from hashlib import sha1
4
- from subprocess import check_call
5
4
6
5
from distutils .command .build_ext import build_ext
7
6
from distutils .command .sdist import sdist
@@ -95,6 +94,8 @@ def cyproc_exts(exts, cython_min_version,
95
94
Can be ``build_ext`` input (if we have good c files) or cython
96
95
``build_ext`` if we have a good cython, or a class raising an informative
97
96
error on ``run()``
97
+ need_cython : bool
98
+ True if we need Cython to build extensions, False otherwise.
98
99
"""
99
100
if stamped_pyx_ok (exts , hash_stamps_fname ):
100
101
# Replace pyx with c files, use standard builder
@@ -107,29 +108,33 @@ def cyproc_exts(exts, cython_min_version,
107
108
else :
108
109
sources .append (source )
109
110
mod .sources = sources
110
- return build_ext
111
+ return build_ext , False
111
112
# We need cython
112
113
try :
113
114
from Cython .Compiler .Version import version as cyversion
114
115
except ImportError :
115
- cython_ok = False
116
- else :
117
- cython_ok = LooseVersion (cyversion ) >= cython_min_version
118
- if cython_ok :
116
+ return derror_maker (build_ext ,
117
+ 'Need cython>={0} to build extensions '
118
+ 'but cannot import "Cython"' .format (
119
+ cython_min_version )), True
120
+ if LooseVersion (cyversion ) >= cython_min_version :
119
121
from Cython .Distutils import build_ext as extbuilder
120
- return extbuilder
122
+ return extbuilder , True
121
123
return derror_maker (build_ext ,
122
- 'Need cython>=%s to build extensions'
123
- % cython_min_version )
124
+ 'Need cython>={0} to build extensions'
125
+ 'but found cython version {1}' .format (
126
+ cython_min_version , cyversion )), True
124
127
125
128
126
- def build_stamp (pyxes ):
129
+ def build_stamp (pyxes , include_dirs = () ):
127
130
""" Cythonize files in `pyxes`, return pyx, C filenames, hashes
128
131
129
132
Parameters
130
133
----------
131
134
pyxes : sequence
132
135
sequence of filenames of files on which to run Cython
136
+ include_dirs : sequence
137
+ Any extra include directories in which to find Cython files.
133
138
134
139
Returns
135
140
-------
@@ -139,11 +144,17 @@ def build_stamp(pyxes):
139
144
hash>; "c_filename", <c filemane>; "c_hash", <c file SHA1 hash>.
140
145
"""
141
146
pyx_defs = {}
147
+ from Cython .Compiler .Main import compile
148
+ from Cython .Compiler .CmdLine import parse_command_line
149
+ includes = sum ([['--include-dir' , d ] for d in include_dirs ], [])
142
150
for source in pyxes :
143
151
base , ext = splitext (source )
144
152
pyx_hash = sha1 (open (source , 'rt' ).read ()).hexdigest ()
145
153
c_filename = base + '.c'
146
- check_call ('cython ' + source , shell = True )
154
+ options , sources = parse_command_line (includes + [source ])
155
+ result = compile (sources , options )
156
+ if result .num_errors > 0 :
157
+ raise RuntimeError ('Cython failed to compile ' + source )
147
158
c_hash = sha1 (open (c_filename , 'rt' ).read ()).hexdigest ()
148
159
pyx_defs [source ] = dict (pyx_hash = pyx_hash ,
149
160
c_filename = c_filename ,
@@ -173,22 +184,19 @@ def write_stamps(pyx_defs, stamp_fname='pyx-stamps'):
173
184
pyx_info ['c_hash' ]))
174
185
175
186
176
- def find_pyx (root_dir = None ):
187
+ def find_pyx (root_dir ):
177
188
""" Recursively find files with extension '.pyx' starting at `root_dir`
178
189
179
190
Parameters
180
191
----------
181
- root_dir : None or str, optional
182
- Directory from which to search for pyx files. If None, use current
183
- working directory.
192
+ root_dir : str
193
+ Directory from which to search for pyx files.
184
194
185
195
Returns
186
196
-------
187
197
pyxes : list
188
198
list of filenames relative to `root_dir`
189
199
"""
190
- if root_dir is None :
191
- root_dir = os .getcwd ()
192
200
pyxes = []
193
201
for dirpath , dirnames , filenames in os .walk (root_dir ):
194
202
for filename in filenames :
@@ -199,7 +207,8 @@ def find_pyx(root_dir=None):
199
207
return pyxes
200
208
201
209
202
- def get_pyx_sdist (sdist_like = sdist , hash_stamps_fname = 'pyx-stamps' ):
210
+ def get_pyx_sdist (sdist_like = sdist , hash_stamps_fname = 'pyx-stamps' ,
211
+ include_dirs = ()):
203
212
""" Add pyx->c conversion, hash recording to sdist command `sdist_like`
204
213
205
214
Parameters
@@ -210,6 +219,8 @@ def get_pyx_sdist(sdist_like=sdist, hash_stamps_fname='pyx-stamps'):
210
219
hash_stamps_fname : str, optional
211
220
filename to which to write hashes of pyx / py and c files. Default is
212
221
``pyx-stamps``
222
+ include_dirs : sequence
223
+ Any extra include directories in which to find Cython files.
213
224
214
225
Returns
215
226
-------
@@ -240,7 +251,7 @@ def make_distribution(self):
240
251
base , ext = splitext (source )
241
252
if ext in ('.pyx' , '.py' ):
242
253
pyxes .append (source )
243
- self .pyx_defs = build_stamp (pyxes )
254
+ self .pyx_defs = build_stamp (pyxes , include_dirs )
244
255
for pyx_fname , pyx_info in self .pyx_defs .items ():
245
256
self .filelist .append (pyx_info ['c_filename' ])
246
257
sdist_like .make_distribution (self )
@@ -254,7 +265,8 @@ def make_release_tree(self, base_dir, files):
254
265
return PyxSDist
255
266
256
267
257
- def build_stamp_source (root_dir = None , stamp_fname = 'pyx-stamps' ):
268
+ def build_stamp_source (root_dir = None , stamp_fname = 'pyx-stamps' ,
269
+ include_dirs = None ):
258
270
""" Build cython c files, make stamp file in source tree `root_dir`
259
271
260
272
Parameters
@@ -264,7 +276,13 @@ def build_stamp_source(root_dir=None, stamp_fname='pyx-stamps'):
264
276
working directory.
265
277
stamp_fname : str, optional
266
278
Filename for stamp file we will write
279
+ include_dirs : None or sequence
280
+ Any extra Cython include directories
267
281
"""
282
+ if root_dir is None :
283
+ root_dir = os .getcwd ()
284
+ if include_dirs is None :
285
+ include_dirs = [pjoin (root_dir , 'src' )]
268
286
pyxes = find_pyx (root_dir )
269
- pyx_defs = build_stamp (pyxes )
287
+ pyx_defs = build_stamp (pyxes , include_dirs = include_dirs )
270
288
write_stamps (pyx_defs , stamp_fname )
0 commit comments