7
7
from distutils .cmd import Command
8
8
from distutils .dist import Distribution
9
9
from distutils .command .build import build as Build
10
- from distutils .command .build_ext import build_ext
11
- from distutils .command .install_lib import install_lib
12
- from setuptools import dist , setup , Extension
13
- from setuptools .command import develop , bdist_egg
10
+ from distutils .errors import (
11
+ DistutilsExecError , DistutilsFileError , DistutilsPlatformError )
12
+ from setuptools .command import develop
13
+
14
+ import semver
14
15
15
16
__all__ = ('RustExtension' , 'build_rust' )
16
17
17
18
18
19
# allow to use 'rust_extensions' parameter for setup() call
19
20
Distribution .rust_extensions = ()
20
21
22
+
21
23
def has_ext_modules (self ):
22
24
return (self .ext_modules and len (self .ext_modules ) > 0 or
23
25
self .rust_extensions and len (self .rust_extensions ) > 0 )
24
26
27
+
25
28
Distribution .has_ext_modules = has_ext_modules
26
- #dist.Distribution.has_ext_modules = has_ext_modules
27
29
28
- # add support for build_rust sub0command
30
+
31
+ # add support for build_rust sub-command
29
32
def has_rust_extensions (self ):
30
33
exts = [ext for ext in self .distribution .rust_extensions
31
34
if isinstance (ext , RustExtension )]
@@ -38,13 +41,15 @@ def has_rust_extensions(self):
38
41
# monkey patch "develop" command
39
42
orig_run_command = develop .develop .run_command
40
43
44
+
41
45
def monkey_run_command (self , cmd ):
42
46
orig_run_command (self , cmd )
43
47
44
48
if cmd == 'build_ext' :
45
49
self .reinitialize_command ('build_rust' , inplace = 1 )
46
50
orig_run_command (self , 'build_rust' )
47
51
52
+
48
53
develop .develop .run_command = monkey_run_command
49
54
50
55
@@ -60,18 +65,33 @@ class RustExtension:
60
65
path to the cargo.toml manifest
61
66
args : [stirng]
62
67
a list of extra argumenents to be passed to cargo.
68
+ version : string
69
+ rust compiler version
63
70
quiet : bool
64
71
If True, doesn't echo cargo's output.
65
72
debug : bool
66
73
Controls whether --debug or --release is passed to cargo.
67
74
"""
68
75
69
- def __init__ (self , name , path , args = None , quiet = False , debug = False ):
70
- self .name = name
71
- self .path = path
72
- self .args = args
73
- self .quiet = quiet
74
- self .debug = debug
76
+ def __init__ (self , name , path ,
77
+ args = None , version = None , quiet = False , debug = False ):
78
+ self .name = name
79
+ self .path = path
80
+ self .args = args
81
+ self .version = version
82
+ self .quiet = quiet
83
+ self .debug = debug
84
+
85
+ @staticmethod
86
+ def get_version ():
87
+ env = os .environ .copy ()
88
+ try :
89
+ output = subprocess .check_output (["rustc" , "-V" ], env = env )
90
+ return output .split (' ' )[1 ]
91
+ except subprocess .CalledProcessError :
92
+ return None
93
+ except OSError :
94
+ return None
75
95
76
96
77
97
class build_rust (Command ):
@@ -99,12 +119,13 @@ def finalize_options(self):
99
119
100
120
def features (self ):
101
121
version = sys .version_info
102
- if (2 ,7 ) < version < (2 ,8 ):
122
+ if (2 , 7 ) < version < (2 , 8 ):
103
123
return "python27-sys"
104
- elif (3 ,3 ) < version :
124
+ elif (3 , 3 ) < version :
105
125
return "python3-sys"
106
126
else :
107
- raise ValueError ("Unsupported python version: %s" % sys .version )
127
+ raise DistutilsPlatformError (
128
+ "Unsupported python version: %s" % sys .version )
108
129
109
130
def build_extension (self , ext ):
110
131
# Make sure that if pythonXX-sys is used, it builds against the current
@@ -120,6 +141,10 @@ def build_extension(self, ext):
120
141
"PATH" : bindir + os .pathsep + os .environ .get ("PATH" , "" )
121
142
})
122
143
144
+ if not os .path .exists (ext .path ):
145
+ raise DistutilsFileError (
146
+ "Can not file rust extension project file: %s" % ext .path )
147
+
123
148
# Execute cargo.
124
149
try :
125
150
args = (["cargo" , "build" , "--manifest-path" , ext .path ,
@@ -130,11 +155,12 @@ def build_extension(self, ext):
130
155
print (" " .join (args ), file = sys .stderr )
131
156
output = subprocess .check_output (args , env = env )
132
157
except subprocess .CalledProcessError as e :
133
- msg = "cargo failed with code: %d \n %s" % ( e . returncode , e . output )
134
- raise Exception ( msg )
158
+ raise DistutilsExecError (
159
+ "cargo failed with code: %d \n %s" % ( e . returncode , e . output ) )
135
160
except OSError :
136
- raise Exception ("Unable to execute 'cargo' - this package "
137
- "requires rust to be installed and cargo to be on the PATH" )
161
+ raise DistutilsExecError (
162
+ "Unable to execute 'cargo' - this package "
163
+ "requires rust to be installed and cargo to be on the PATH" )
138
164
139
165
if not ext .quiet :
140
166
print (output , file = sys .stderr )
@@ -158,7 +184,7 @@ def build_extension(self, ext):
158
184
try :
159
185
dylib_path = glob .glob (os .path .join (target_dir , wildcard_so ))[0 ]
160
186
except IndexError :
161
- raise Exception (
187
+ raise DistutilsExecError (
162
188
"rust build failed; unable to find any .dylib in %s" %
163
189
target_dir )
164
190
@@ -179,5 +205,15 @@ def build_extension(self, ext):
179
205
shutil .copyfile (dylib_path , ext_path )
180
206
181
207
def run (self ):
208
+ version = RustExtension .get_version ()
209
+ if self .extensions and version is None :
210
+ raise DistutilsPlatformError ('Can not find Rust compiler' )
211
+
182
212
for ext in self .extensions :
213
+ if ext .version is not None :
214
+ if not semver .match (version , ext .version ):
215
+ raise DistutilsPlatformError (
216
+ "Rust %s does not match extension requirenment %s" % (
217
+ version , ext .version ))
218
+
183
219
self .build_extension (ext )
0 commit comments