1
- from __future__ import print_function , absolute_import
2
1
import glob
3
2
import json
4
3
import os
5
4
import shutil
6
5
import sys
7
6
import subprocess
8
- from distutils .cmd import Command
9
7
from distutils .errors import (
10
8
CompileError ,
11
9
DistutilsExecError ,
12
10
DistutilsFileError ,
13
- DistutilsPlatformError ,
14
- DistutilsSetupError ,
15
11
)
16
12
from subprocess import check_output
17
13
14
+ from .command import RustCommand
18
15
from .extension import RustExtension
19
16
from .utils import (
20
- Binding , Strip , cpython_feature , get_rust_version , get_rust_target_info
17
+ Binding , Strip , rust_features , get_rust_target_info
21
18
)
22
19
23
20
24
- class build_rust (Command ):
25
- """ Command for building rust crates via cargo. """
21
+ class build_rust (RustCommand ):
22
+ """ Command for building Rust crates via cargo. """
26
23
27
24
description = "build Rust extensions (compile/link to build directory)"
28
25
@@ -33,9 +30,9 @@ class build_rust(Command):
33
30
"ignore build-lib and put compiled extensions into the source "
34
31
+ "directory alongside your pure Python modules" ,
35
32
),
36
- ("debug" , "d" , "Force debug to true for all rust extensions " ),
37
- ("release" , "r" , "Force debug to false for all rust extensions " ),
38
- ("qbuild" , None , "Force enable quiet option for all rust extensions " ),
33
+ ("debug" , "d" , "Force debug to true for all Rust extensions " ),
34
+ ("release" , "r" , "Force debug to false for all Rust extensions " ),
35
+ ("qbuild" , None , "Force enable quiet option for all Rust extensions " ),
39
36
(
40
37
"build-temp" ,
41
38
"t" ,
@@ -45,7 +42,7 @@ class build_rust(Command):
45
42
boolean_options = ["inplace" , "debug" , "release" , "qbuild" ]
46
43
47
44
def initialize_options (self ):
48
- self . extensions = ()
45
+ super (). initialize_options ()
49
46
self .inplace = None
50
47
self .debug = None
51
48
self .release = None
@@ -54,11 +51,7 @@ def initialize_options(self):
54
51
self .plat_name = None
55
52
56
53
def finalize_options (self ):
57
- self .extensions = [
58
- ext
59
- for ext in self .distribution .rust_extensions
60
- if isinstance (ext , RustExtension )
61
- ]
54
+ super ().finalize_options ()
62
55
63
56
# Inherit settings from the `build_ext` command
64
57
self .set_undefined_options (
@@ -68,7 +61,7 @@ def finalize_options(self):
68
61
("inplace" , "inplace" ),
69
62
)
70
63
71
- def build_extension (self , ext ):
64
+ def run_for_extension (self , ext : RustExtension ):
72
65
executable = ext .binding == Binding .Exec
73
66
74
67
rust_target_info = get_rust_target_info ()
@@ -94,7 +87,6 @@ def build_extension(self, ext):
94
87
# we'll target a 32-bit Rust build.
95
88
# Automatic target detection can be overridden via the CARGO_BUILD_TARGET
96
89
# environment variable.
97
- # TODO: include --target for all platforms so env vars can't break the build
98
90
target_triple = None
99
91
target_args = []
100
92
if os .getenv ("CARGO_BUILD_TARGET" ):
@@ -116,17 +108,16 @@ def build_extension(self, ext):
116
108
"--format-version" ,
117
109
"1" ,
118
110
]
119
- # The decoding is needed for python 3.5 compatibility
120
- metadata = json .loads (check_output (metadata_command ).decode ("utf-8" ))
111
+ metadata = json .loads (check_output (metadata_command ))
121
112
target_dir = metadata ["target_directory" ]
122
113
123
114
if not os .path .exists (ext .path ):
124
115
raise DistutilsFileError (
125
- "Can not find rust extension project file: %s" % ext .path
116
+ f"can't find Rust extension project file: { ext .path } "
126
117
)
127
118
128
119
features = set (ext .features )
129
- features .update (cpython_feature (binding = ext .binding ))
120
+ features .update (rust_features (binding = ext .binding ))
130
121
131
122
debug_build = ext .debug if ext .debug is not None else self .inplace
132
123
debug_build = self .debug if self .debug is not None else debug_build
@@ -190,24 +181,19 @@ def build_extension(self, ext):
190
181
191
182
# Execute cargo
192
183
try :
193
- output = subprocess .check_output (args , env = env )
184
+ output = subprocess .check_output (args , env = env , encoding = "latin-1" )
194
185
except subprocess .CalledProcessError as e :
195
- output = e .output
196
- if isinstance (output , bytes ):
197
- output = e .output .decode ("latin-1" ).strip ()
198
186
raise CompileError (
199
- "cargo failed with code: %d \n %s" % ( e . returncode , output )
187
+ f "cargo failed with code: { e . returncode } \n { e . output } "
200
188
)
201
189
202
190
except OSError :
203
191
raise DistutilsExecError (
204
192
"Unable to execute 'cargo' - this package "
205
- "requires rust to be installed and cargo to be on the PATH"
193
+ "requires Rust to be installed and cargo to be on the PATH"
206
194
)
207
195
208
196
if not quiet :
209
- if isinstance (output , bytes ):
210
- output = output .decode ("latin-1" )
211
197
if output :
212
198
print (output , file = sys .stderr )
213
199
@@ -231,8 +217,8 @@ def build_extension(self, ext):
231
217
continue
232
218
else :
233
219
raise DistutilsExecError (
234
- "rust build failed; "
235
- ' unable to find executable "%s" in %s' % ( name , target_dir )
220
+ "Rust build failed; "
221
+ f" unable to find executable ' { name } ' in ' { target_dir } '"
236
222
)
237
223
else :
238
224
# search executable
@@ -247,7 +233,7 @@ def build_extension(self, ext):
247
233
248
234
if not dylib_paths :
249
235
raise DistutilsExecError (
250
- "rust build failed; unable to find executable in %s" % target_dir
236
+ f"Rust build failed; unable to find executable in { target_dir } "
251
237
)
252
238
else :
253
239
if sys .platform == "win32" or sys .platform == "cygwin" :
@@ -268,8 +254,7 @@ def build_extension(self, ext):
268
254
)
269
255
except StopIteration :
270
256
raise DistutilsExecError (
271
- "rust build failed; unable to find any %s in %s"
272
- % (wildcard_so , artifactsdir )
257
+ f"Rust build failed; unable to find any { wildcard_so } in { artifactsdir } "
273
258
)
274
259
275
260
# Ask build_ext where the shared library would go if it had built it,
@@ -303,11 +288,7 @@ def build_extension(self, ext):
303
288
finally :
304
289
del build_ext .ext_map [modpath ]
305
290
306
- try :
307
- os .makedirs (os .path .dirname (ext_path ))
308
- except OSError :
309
- pass
310
-
291
+ os .makedirs (os .path .dirname (ext_path ), exist_ok = True )
311
292
shutil .copyfile (dylib_path , ext_path )
312
293
313
294
if sys .platform != "win32" and not debug_build :
@@ -330,40 +311,3 @@ def build_extension(self, ext):
330
311
mode = os .stat (ext_path ).st_mode
331
312
mode |= (mode & 0o444 ) >> 2 # copy R bits to X
332
313
os .chmod (ext_path , mode )
333
-
334
- def run (self ):
335
- if not self .extensions :
336
- return
337
-
338
- all_optional = all (ext .optional for ext in self .extensions )
339
- try :
340
- version = get_rust_version ()
341
- except DistutilsPlatformError as e :
342
- if not all_optional :
343
- raise
344
- else :
345
- print (str (e ))
346
- return
347
-
348
- for ext in self .extensions :
349
- try :
350
- rust_version = ext .get_rust_version ()
351
- if rust_version is not None and version not in rust_version :
352
- raise DistutilsPlatformError (
353
- "Rust %s does not match extension requirement %s"
354
- % (version , ext .rust_version )
355
- )
356
-
357
- self .build_extension (ext )
358
- except (
359
- DistutilsSetupError ,
360
- DistutilsFileError ,
361
- DistutilsExecError ,
362
- DistutilsPlatformError ,
363
- CompileError ,
364
- ) as e :
365
- if not ext .optional :
366
- raise
367
- else :
368
- print ("Build optional Rust extension %s failed." % ext .name )
369
- print (str (e ))
0 commit comments