Skip to content

Commit

Permalink
Allow to build executable and pack with python package
Browse files Browse the repository at this point in the history
  • Loading branch information
fafhrd91 committed Aug 11, 2017
1 parent 703254f commit 67932dc
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 39 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGES
=======


0.7.0 (2017-08-11)
------------------

- Allow to build executable and pack with python package.

- Use PyO3 0.1 for example.


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from setuptools import setup

version = '0.6.4'
version = '0.7.0'


setup(
Expand Down
98 changes: 70 additions & 28 deletions setuptools_rust/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
DistutilsPlatformError, DistutilsSetupError)

from .extension import RustExtension
from .utils import cpython_feature, get_rust_version
from .utils import Binding, cpython_feature, get_rust_version


class build_rust(Command):
Expand Down Expand Up @@ -43,6 +43,8 @@ def finalize_options(self):
if isinstance(ext, RustExtension)]

def build_extension(self, ext):
executable = ext.binding == Binding.Exec

# Make sure that if pythonXX-sys is used, it builds against the current
# executing python interpreter.
bindir = os.path.dirname(sys.executable)
Expand Down Expand Up @@ -75,20 +77,30 @@ def build_extension(self, ext):

# build cargo command
feature_args = ["--features", " ".join(features)] if features else []
args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path]
+ feature_args
+ list(ext.args or []))
if not debug_build:
args.append("--release")
if quiet:
args.append("-q")

args.extend(["--", '--crate-type', 'cdylib'])
if executable:
args = (["cargo", "build", "--manifest-path", ext.path]
+ feature_args
+ list(ext.args or []))
if not debug_build:
args.append("--release")
if quiet:
args.append("-q")
else:
args = (["cargo", "rustc", "--lib", "--manifest-path", ext.path]
+ feature_args
+ list(ext.args or []))
if not debug_build:
args.append("--release")
if quiet:
args.append("-q")

args.extend(["--", '--crate-type', 'cdylib'])

# OSX requires special linker argument
if sys.platform == "darwin":
args.extend(["-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup"])
# OSX requires special linker argument
if sys.platform == "darwin":
args.extend(["-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup"])

if not quiet:
print(" ".join(args), file=sys.stderr)
Expand Down Expand Up @@ -129,19 +141,37 @@ def build_extension(self, ext):
target_dir = os.path.join(
os.path.dirname(ext.path), "target/", suffix)

if sys.platform == "win32":
wildcard_so = "*.dll"
elif sys.platform == "darwin":
wildcard_so = "*.dylib"
if executable:
# search executable
dylib_path = None
for name in os.listdir(target_dir):
path = os.path.join(target_dir, name)
if name.startswith(".") or not os.path.isfile(path):
continue

if os.access(path, os.X_OK):
dylib_path = path
break

if dylib_path is None:
raise DistutilsExecError(
"rust build failed; unable to find executable in %s" %
target_dir)
else:
wildcard_so = "*.so"
if sys.platform == "win32":
wildcard_so = "*.dll"
elif sys.platform == "darwin":
wildcard_so = "*.dylib"
else:
wildcard_so = "*.so"

try:
dylib_path = glob.glob(os.path.join(target_dir, wildcard_so))[0]
except IndexError:
raise DistutilsExecError(
"rust build failed; unable to find any %s in %s" %
(wildcard_so, target_dir))
try:
dylib_path = glob.glob(
os.path.join(target_dir, wildcard_so))[0]
except IndexError:
raise DistutilsExecError(
"rust build failed; unable to find any %s in %s" %
(wildcard_so, target_dir))

# Ask build_ext where the shared library would go if it had built it,
# then copy it there.
Expand All @@ -152,13 +182,23 @@ def build_extension(self, ext):
target_fname = os.path.basename(os.path.splitext(
os.path.basename(dylib_path)[3:])[0])

ext_path = build_ext.get_ext_fullpath(target_fname)
if executable:
ext_path = build_ext.get_ext_fullpath(target_fname)
ext_path, _ = os.path.splitext(ext_path)
else:
ext_path = build_ext.get_ext_fullpath(target_fname)

try:
os.makedirs(os.path.dirname(ext_path))
except OSError:
pass
shutil.copyfile(dylib_path, ext_path)

if executable:
mode = os.stat(ext_path).st_mode
mode |= (mode & 0o444) >> 2 # copy R bits to X
os.chmod(ext_path, mode)

def run(self):
if not self.extensions:
return
Expand All @@ -182,10 +222,12 @@ def run(self):
version, ext.rust_version))

self.build_extension(ext)
except (DistutilsSetupError, DistutilsFileError, DistutilsExecError,
DistutilsPlatformError, CompileError) as e:
except (DistutilsSetupError, DistutilsFileError,
DistutilsExecError, DistutilsPlatformError,
CompileError) as e:
if not ext.optional:
raise
else:
print('Build optional Rust extension %s failed.' % ext.name)
print('Build optional Rust extension %s failed.' %
ext.name)
print(str(e))
8 changes: 5 additions & 3 deletions setuptools_rust/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ def run(self):
try:
if not os.path.exists(ext.path):
raise DistutilsFileError(
"Can not file rust extension project file: %s" % ext.path)
"Can not file rust extension project file: %s" %
ext.path)

features = set(ext.features)
features.update(cpython_feature(binding=ext.binding))

# check cargo command
feature_args = [
"--features", " ".join(features)] if features else []
args = (["cargo", "check", "--lib", "--manifest-path", ext.path]
args = (["cargo", "check", "--manifest-path", ext.path]
+ feature_args
+ list(ext.args or []))

Expand All @@ -92,5 +93,6 @@ def run(self):
if not ext.optional:
raise
else:
print('Check optional Rust extension %s failed.' % ext.name)
print('Check optional Rust extension %s failed.' %
ext.name)
print(str(e))
5 changes: 3 additions & 2 deletions setuptools_rust/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ class RustExtension:
Binding.PyO3 uses PyO3
Binding.RustCPython uses Rust CPython.
Binding.NoBinding uses no binding.
Binding.Exec build executable.
optional : bool
if it is true, a build failure in the extension will not abort the build process,
but instead simply not install the failing extension.
if it is true, a build failure in the extension will not abort the
build process, but instead simply not install the failing extension.
"""

def __init__(self, name, path,
Expand Down
2 changes: 1 addition & 1 deletion setuptools_rust/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def run(self):
# test cargo command
feature_args = [
"--features", " ".join(features)] if features else []
args = (["cargo", "test", "--lib", "--manifest-path", ext.path]
args = (["cargo", "test", "--manifest-path", ext.path]
+ feature_args
+ list(ext.args or []))

Expand Down
10 changes: 6 additions & 4 deletions setuptools_rust/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ class Binding:
"""
Binding Options
"""
# https://github.com/PyO3/PyO3
# https://github.com/PyO3/PyO3
PyO3 = 0
# https://github.com/dgrunwald/rust-cpython
# https://github.com/dgrunwald/rust-cpython
RustCPython = 1
# Bring your own binding
# Bring your own binding
NoBinding = 2
# Build executable
Exec = 3


def cpython_feature(ext=True, binding=Binding.PyO3):
version = sys.version_info

if binding is Binding.NoBinding:
if binding in (Binding.NoBinding, Binding.Exec):
return ()
elif binding is Binding.PyO3:
if (2, 7) < version < (2, 8):
Expand Down

0 comments on commit 67932dc

Please sign in to comment.