Skip to content

Commit 2e95436

Browse files
committed
refactor modules; added support for check command
1 parent e6a5d61 commit 2e95436

File tree

11 files changed

+407
-280
lines changed

11 files changed

+407
-280
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*.pyc
22
*.pyo
33
*.so
4+
.eggs
45
/dist
56
/build
67
/*.egg-info

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGES
22
=======
33

4+
0.5.0 (2017-..-..)
5+
------------------
6+
7+
- Added support for "cargo check"
8+
9+
410
0.4.2 (2017-03-15)
511
------------------
612

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,13 @@ You can define rust extension with `RustExtension` class:
7373
:param bool debug: Controls whether --debug or --release is passed to cargo. If set to
7474
None then build type is auto-detect. Inplace build is debug build
7575
otherwise release. Default: None
76+
77+
78+
Commands
79+
--------
80+
81+
* `build` - Standard `build` command builds all rust extensions.
82+
83+
* `clean` - Standard `clean` command executes `cargo clean` for all rust extensions.
84+
85+
* `check` - Standard `checl` command executes `cargo check` for all rust extensions.

setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from setuptools import setup
22

3-
version = '0.4.2'
3+
version = '0.5.0'
44

55

66
setup(
@@ -15,7 +15,7 @@
1515
(open('README.rst').read(), open('CHANGES.rst').read())),
1616
license='MIT',
1717
packages=['setuptools_rust'],
18-
install_requires=['semantic_version==2.6.0'],
18+
install_requires=['semantic_version>=2.6.0'],
1919
zip_safe=True,
2020
classifiers=[
2121
"Topic :: Software Development :: Version Control",
@@ -33,6 +33,7 @@
3333
],
3434
entry_points="""
3535
[distutils.commands]
36+
check_rust=setuptools_rust.check:check_rust
3637
clean_rust=setuptools_rust:clean_rust
3738
build_ext=setuptools_rust:build_ext
3839
build_rust=setuptools_rust:build_rust

setuptools_rust/__init__.py

Lines changed: 8 additions & 278 deletions
Original file line numberDiff line numberDiff line change
@@ -1,284 +1,14 @@
11
from __future__ import print_function, absolute_import
2-
import glob
3-
import os
4-
import shutil
5-
import sys
6-
import subprocess
7-
from distutils.cmd import Command
8-
from distutils.errors import (
9-
CompileError, DistutilsExecError, DistutilsFileError,
10-
DistutilsPlatformError, DistutilsSetupError)
112

12-
import semantic_version
13-
14-
from . import patch # noqa
3+
from . import patch
4+
from .build import build_rust
155
from .build_ext import build_ext
6+
from .check import check_rust
7+
from .clean import clean_rust
8+
from .extension import RustExtension
169

17-
__all__ = ('RustExtension', 'clean_rust', 'build_ext', 'build_rust')
18-
19-
patch.monkey_patch_dist(build_ext)
20-
21-
22-
class RustExtension:
23-
"""Just a collection of attributes that describes an rust extension
24-
module and everything needed to build it
25-
26-
Instance attributes:
27-
name : string
28-
the full name of the extension, including any packages -- ie.
29-
*not* a filename or pathname, but Python dotted name
30-
path : string
31-
path to the cargo.toml manifest file
32-
args : [string]
33-
a list of extra argumenents to be passed to cargo.
34-
features : [string]
35-
a list of features to also build
36-
rust_version : string
37-
rust compiler version
38-
quiet : bool
39-
If True, doesn't echo cargo's output.
40-
debug : bool
41-
Controls whether --debug or --release is passed to cargo. If set to
42-
None then build type is auto-detect. Inplace build is debug build
43-
otherwise release. Default: None
44-
"""
45-
46-
def __init__(self, name, path,
47-
args=None, features=None, rust_version=None,
48-
quiet=False, debug=None):
49-
self.name = name
50-
self.args = args
51-
self.rust_version = rust_version
52-
self.quiet = quiet
53-
self.debug = debug
54-
55-
if features is None:
56-
features = []
57-
58-
self.features = [s.strip() for s in features]
59-
60-
# get absolute path to Cargo manifest file
61-
file = sys._getframe(1).f_globals.get('__file__')
62-
if file:
63-
dirname = os.path.dirname(file)
64-
if dirname:
65-
cwd = os.getcwd()
66-
os.chdir(dirname)
67-
path = os.path.abspath(path)
68-
os.chdir(cwd)
69-
70-
self.path = path
71-
72-
def get_rust_version(self):
73-
if self.rust_version is None:
74-
return None
75-
try:
76-
return semantic_version.Spec(self.rust_version)
77-
except:
78-
raise DistutilsSetupError(
79-
'Can not parse rust compiler version: %s', self.rust_version)
80-
81-
82-
def get_rust_version():
83-
try:
84-
output = subprocess.check_output(["rustc", "-V"])
85-
if isinstance(output, bytes):
86-
output = output.decode('latin-1')
87-
return semantic_version.Version(output.split(' ')[1], partial=True)
88-
except (subprocess.CalledProcessError, OSError):
89-
raise DistutilsPlatformError('Can not find Rust compiler')
90-
except Exception as exc:
91-
raise DistutilsPlatformError(
92-
'Can not get rustc version: %s' % str(exc))
93-
94-
95-
class build_rust(Command):
96-
""" Command for building rust crates via cargo. """
97-
98-
description = "build Rust extensions (compile/link to build directory)"
99-
100-
user_options = [
101-
('inplace', 'i',
102-
"ignore build-lib and put compiled extensions into the source " +
103-
"directory alongside your pure Python modules"),
104-
('debug', 'd',
105-
"Force debug to true for all rust extensions "),
106-
('release', 'r',
107-
"Force debug to false for all rust extensions "),
108-
('qbuild', None,
109-
"Force enable quiet option for all rust extensions "),
110-
]
111-
boolean_options = ['inplace', 'debug', 'release', 'qbuild']
112-
113-
def initialize_options(self):
114-
self.extensions = ()
115-
self.inplace = None
116-
self.debug = None
117-
self.release = None
118-
self.qbuild = None
119-
120-
def finalize_options(self):
121-
self.extensions = [ext for ext in self.distribution.rust_extensions
122-
if isinstance(ext, RustExtension)]
123-
124-
def _cpython_feature(self):
125-
version = sys.version_info
126-
if (2, 7) < version < (2, 8):
127-
return ("cpython/python27-sys", "cpython/extension-module-2-7")
128-
elif (3, 3) < version:
129-
return ("cpython/python3-sys", "cpython/extension-module")
130-
else:
131-
raise DistutilsPlatformError(
132-
"Unsupported python version: %s" % sys.version)
133-
134-
def build_extension(self, ext):
135-
# Make sure that if pythonXX-sys is used, it builds against the current
136-
# executing python interpreter.
137-
bindir = os.path.dirname(sys.executable)
138-
139-
env = os.environ.copy()
140-
env.update({
141-
# disables rust's pkg-config seeking for specified packages,
142-
# which causes pythonXX-sys to fall back to detecting the
143-
# interpreter from the path.
144-
"PYTHON_2.7_NO_PKG_CONFIG": "1",
145-
"PATH": bindir + os.pathsep + os.environ.get("PATH", "")
146-
})
147-
148-
if not os.path.exists(ext.path):
149-
raise DistutilsFileError(
150-
"Can not file rust extension project file: %s" % ext.path)
151-
152-
features = set(ext.features)
153-
features.update(self._cpython_feature())
154-
155-
if ext.debug is None:
156-
debug_build = self.inplace
157-
else:
158-
debug_build = ext.debug
10+
__all__ = ('RustExtension',
11+
'check_rust', 'clean_rust', 'build_ext', 'build_rust')
15912

160-
debug_build = self.debug if self.debug is not None else debug_build
161-
if self.release:
162-
debug_build = False
16313

164-
quiet = self.qbuild or ext.quiet
165-
166-
# build cargo command
167-
args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path,
168-
"--features", " ".join(features)]
169-
+ list(ext.args or []))
170-
if not debug_build:
171-
args.append("--release")
172-
if quiet:
173-
args.append("-q")
174-
175-
args.extend(["--", '--crate-type', 'cdylib'])
176-
177-
# OSX requires special linker argument
178-
if sys.platform == "darwin":
179-
args.extend(["-C", "link-arg=-undefined",
180-
"-C", "link-arg=dynamic_lookup"])
181-
182-
if not quiet:
183-
print(" ".join(args), file=sys.stderr)
184-
185-
# Execute cargo
186-
try:
187-
output = subprocess.check_output(args, env=env)
188-
except subprocess.CalledProcessError as e:
189-
raise CompileError(
190-
"cargo failed with code: %d\n%s" % (e.returncode, e.output))
191-
except OSError:
192-
raise DistutilsExecError(
193-
"Unable to execute 'cargo' - this package "
194-
"requires rust to be installed and cargo to be on the PATH")
195-
196-
if not quiet:
197-
if isinstance(output, bytes):
198-
output = output.decode('latin-1')
199-
print(output, file=sys.stderr)
200-
201-
# Find the shared library that cargo hopefully produced and copy
202-
# it into the build directory as if it were produced by build_ext.
203-
if debug_build:
204-
suffix = "debug"
205-
else:
206-
suffix = "release"
207-
208-
target_dir = os.path.join(os.path.dirname(ext.path), "target/", suffix)
209-
210-
if sys.platform == "win32":
211-
wildcard_so = "*.dll"
212-
elif sys.platform == "darwin":
213-
wildcard_so = "*.dylib"
214-
else:
215-
wildcard_so = "*.so"
216-
217-
try:
218-
dylib_path = glob.glob(os.path.join(target_dir, wildcard_so))[0]
219-
except IndexError:
220-
raise DistutilsExecError(
221-
"rust build failed; unable to find any .dylib in %s" %
222-
target_dir)
223-
224-
# Ask build_ext where the shared library would go if it had built it,
225-
# then copy it there.
226-
build_ext = self.get_finalized_command('build_ext')
227-
build_ext.inplace = self.inplace
228-
target_fname = ext.name
229-
if target_fname is None:
230-
target_fname = os.path.basename(os.path.splitext(
231-
os.path.basename(dylib_path)[3:])[0])
232-
233-
ext_path = build_ext.get_ext_fullpath(target_fname)
234-
try:
235-
os.makedirs(os.path.dirname(ext_path))
236-
except OSError:
237-
pass
238-
shutil.copyfile(dylib_path, ext_path)
239-
240-
def run(self):
241-
if not self.extensions:
242-
return
243-
244-
version = get_rust_version()
245-
246-
for ext in self.extensions:
247-
rust_version = ext.get_rust_version()
248-
if rust_version is not None and version not in rust_version:
249-
raise DistutilsPlatformError(
250-
"Rust %s does not match extension requirenment %s" % (
251-
version, ext.rust_version))
252-
253-
self.build_extension(ext)
254-
255-
256-
class clean_rust(Command):
257-
""" Clean rust extensions. """
258-
259-
description = "clean rust extensions (compile/link to build directory)"
260-
261-
def initialize_options(self):
262-
self.extensions = ()
263-
self.inplace = False
264-
265-
def finalize_options(self):
266-
self.extensions = [ext for ext in self.distribution.rust_extensions
267-
if isinstance(ext, RustExtension)]
268-
269-
def run(self):
270-
if not self.extensions:
271-
return
272-
273-
for ext in self.extensions:
274-
# build cargo command
275-
args = (["cargo", "clean", "--manifest-path", ext.path])
276-
277-
if not ext.quiet:
278-
print(" ".join(args), file=sys.stderr)
279-
280-
# Execute cargo command
281-
try:
282-
subprocess.check_output(args)
283-
except:
284-
pass
14+
patch.monkey_patch_dist(build_ext)

0 commit comments

Comments
 (0)