Skip to content

Commit 0006319

Browse files
committed
support multiple binaries #24
1 parent d54dd8d commit 0006319

File tree

5 files changed

+99
-58
lines changed

5 files changed

+99
-58
lines changed

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.8.0 (2017-09-05)
5+
------------------
6+
7+
- Support multiple rust binaries #24
8+
9+
410
0.7.2 (2017-09-01)
511
------------------
612

README.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ You can define rust extension with `RustExtension` class:
7070
The class for creating rust extensions.
7171

7272
:param str name: the full name of the extension, including any packages -- ie.
73-
*not* a filename or pathname, but Python dotted name
73+
*not* a filename or pathname, but Python dotted name.
74+
It is possible to specify multiple binaries, if extension uses
75+
`Binsing.Exec` binding mode. In that case first argument has to be dictionary.
76+
Keys of the dictionary corresponds to compiled rust binaries and values are
77+
full name of the executable inside python package.
7478

7579
:param str path: path to the Cargo.toml manifest file
7680

@@ -91,6 +95,12 @@ You can define rust extension with `RustExtension` class:
9195
`Binding.PyO3` uses PyO3
9296
`Binding.RustCPython` uses rust-cpython
9397
`Binding.NoBinding` uses no binding.
98+
`Binding.Exec` build executable.
99+
100+
:param int strip: Strip symbols from final file. Does nothing for debug build.
101+
`Strip.No` - do not strip symbols (default)
102+
`Strip.Debug` - strip debug symbols
103+
`Strip.All` - strip all symbols
94104

95105
:param bool script: Generate console script for executable
96106
if `Binding.Exec` is used.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from setuptools import setup
22

3-
version = '0.7.2'
3+
version = '0.8.0'
44

55

66
setup(

setuptools_rust/build.py

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -141,19 +141,32 @@ def build_extension(self, ext):
141141
target_dir = os.path.join(
142142
os.path.dirname(ext.path), "target/", suffix)
143143

144+
dylib_paths = []
145+
144146
if executable:
145-
# search executable
146-
dylib_path = None
147-
for name in os.listdir(target_dir):
148-
path = os.path.join(target_dir, name)
149-
if name.startswith(".") or not os.path.isfile(path):
150-
continue
151-
152-
if os.access(path, os.X_OK):
153-
dylib_path = path
154-
break
155-
156-
if dylib_path is None:
147+
for name, dest in ext.target.items():
148+
if name:
149+
path = os.path.join(target_dir, name)
150+
if os.access(path, os.X_OK):
151+
dylib_paths.append((dest, path))
152+
continue
153+
else:
154+
raise DistutilsExecError(
155+
'rust build failed; '
156+
'unable to find executable "%s" in %s' % (
157+
name, target_dir))
158+
else:
159+
# search executable
160+
for name in os.listdir(target_dir):
161+
path = os.path.join(target_dir, name)
162+
if name.startswith(".") or not os.path.isfile(path):
163+
continue
164+
165+
if os.access(path, os.X_OK):
166+
dylib_paths.append((ext.name, path))
167+
break
168+
169+
if not dylib_paths:
157170
raise DistutilsExecError(
158171
"rust build failed; unable to find executable in %s" %
159172
target_dir)
@@ -166,8 +179,9 @@ def build_extension(self, ext):
166179
wildcard_so = "*.so"
167180

168181
try:
169-
dylib_path = glob.glob(
170-
os.path.join(target_dir, wildcard_so))[0]
182+
dylib_paths.append(
183+
(ext.name, glob.glob(
184+
os.path.join(target_dir, wildcard_so))[0]))
171185
except IndexError:
172186
raise DistutilsExecError(
173187
"rust build failed; unable to find any %s in %s" %
@@ -177,47 +191,49 @@ def build_extension(self, ext):
177191
# then copy it there.
178192
build_ext = self.get_finalized_command('build_ext')
179193
build_ext.inplace = self.inplace
180-
target_fname = ext.name
181-
if target_fname is None:
182-
target_fname = os.path.basename(os.path.splitext(
183-
os.path.basename(dylib_path)[3:])[0])
184194

185-
if executable:
186-
ext_path = build_ext.get_ext_fullpath(target_fname)
187-
# remove .so extension
188-
ext_path, _ = os.path.splitext(ext_path)
189-
# remove python3 extension (i.e. cpython-36m)
190-
ext_path, _ = os.path.splitext(ext_path)
195+
for target_fname, dylib_path in dylib_paths:
196+
if not target_fname:
197+
target_fname = os.path.basename(os.path.splitext(
198+
os.path.basename(dylib_path)[3:])[0])
191199

192-
ext.install_script(ext_path)
193-
else:
194-
ext_path = build_ext.get_ext_fullpath(target_fname)
200+
if executable:
201+
ext_path = build_ext.get_ext_fullpath(target_fname)
202+
# remove .so extension
203+
ext_path, _ = os.path.splitext(ext_path)
204+
# remove python3 extension (i.e. cpython-36m)
205+
ext_path, _ = os.path.splitext(ext_path)
195206

196-
try:
197-
os.makedirs(os.path.dirname(ext_path))
198-
except OSError:
199-
pass
200-
shutil.copyfile(dylib_path, ext_path)
201-
202-
if not debug_build:
203-
args = []
204-
if ext.strip == Strip.All:
205-
args.append('-x')
206-
elif ext.strip == Strip.Debug:
207-
args.append('-S')
208-
209-
if args:
210-
args.insert(0, 'strip')
211-
args.append(ext_path)
212-
try:
213-
output = subprocess.check_output(args, env=env)
214-
except subprocess.CalledProcessError as e:
215-
pass
207+
ext.install_script(ext_path)
208+
else:
209+
ext_path = build_ext.get_ext_fullpath(target_fname)
216210

217-
if executable:
218-
mode = os.stat(ext_path).st_mode
219-
mode |= (mode & 0o444) >> 2 # copy R bits to X
220-
os.chmod(ext_path, mode)
211+
try:
212+
os.makedirs(os.path.dirname(ext_path))
213+
except OSError:
214+
pass
215+
216+
shutil.copyfile(dylib_path, ext_path)
217+
218+
if not debug_build:
219+
args = []
220+
if ext.strip == Strip.All:
221+
args.append('-x')
222+
elif ext.strip == Strip.Debug:
223+
args.append('-S')
224+
225+
if args:
226+
args.insert(0, 'strip')
227+
args.append(ext_path)
228+
try:
229+
output = subprocess.check_output(args, env=env)
230+
except subprocess.CalledProcessError as e:
231+
pass
232+
233+
if executable:
234+
mode = os.stat(ext_path).st_mode
235+
mode |= (mode & 0o444) >> 2 # copy R bits to X
236+
os.chmod(ext_path, mode)
221237

222238
def run(self):
223239
if not self.extensions:

setuptools_rust/extension.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class RustExtension:
3737
Binding.RustCPython uses Rust CPython.
3838
Binding.NoBinding uses no binding.
3939
Binding.Exec build executable.
40-
strip : setuptools_rust.Binding
40+
strip : setuptools_rust.Strip
4141
Strip symbols from final file. Does nothing for debug build.
4242
* Strip.No - do not strip symbols
4343
* Strip.Debug - strip debug symbols
@@ -49,11 +49,19 @@ class RustExtension:
4949
build process, but instead simply not install the failing extension.
5050
"""
5151

52-
def __init__(self, name, path,
52+
def __init__(self, target, path,
5353
args=None, features=None, rust_version=None,
5454
quiet=False, debug=None, binding=Binding.PyO3,
5555
strip=Strip.No, script=False, optional=False):
56+
if isinstance(target, dict):
57+
name = '; '.join('%s=%s' % (key, val)
58+
for key, val in target.items())
59+
else:
60+
name = target
61+
target = {'': target}
62+
5663
self.name = name
64+
self.target = target
5765
self.args = args
5866
self.binding = binding
5967
self.rust_version = rust_version
@@ -92,9 +100,10 @@ def get_rust_version(self):
92100
def entry_points(self):
93101
entry_points = []
94102
if self.script and self.binding == Binding.Exec:
95-
base_mod, name = self.name.rsplit('.')
96-
script = '%s=%s.%s:run' % (name, base_mod, '_gen_%s' % name)
97-
entry_points.append(script)
103+
for name, mod in self.target.items():
104+
base_mod, name = mod.rsplit('.')
105+
script = '%s=%s.%s:run' % (name, base_mod, '_gen_%s' % name)
106+
entry_points.append(script)
98107

99108
return entry_points
100109

0 commit comments

Comments
 (0)