Skip to content

Commit

Permalink
refactoring: tidy up library and reuse more logic (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt authored Feb 15, 2021
1 parent 1d7ee9f commit 1e37cc8
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 277 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Changelog

## Unreleased
### Packaging
- Bump minimum Python version to Python 3.6.

### Changed
- Respect `PYO3_PYTHON` and `PYTHON_SYS_EXECUTABLE` environment variables if set. [#96](https://github.com/PyO3/setuptools-rust/pull/96)
- Add runtime dependency on setuptools >= 46.1. [#102](https://github.com/PyO3/setuptools-rust/pull/102)
- Append to, rather than replace, existing RUSTFLAGS when building. [#103](https://github.com/PyO3/setuptools-rust/pull/103)
- Append to, rather than replace, existing `RUSTFLAGS` when building. [#103](https://github.com/PyO3/setuptools-rust/pull/103)

### Fixed
- Set executable bit on shared library. [#110](https://github.com/PyO3/setuptools-rust/pull/110)
- Don't require optional wheel dependency. [#111](https://github.com/PyO3/setuptools-rust/pull/111)
- Don't require optional `wheel` dependency. [#111](https://github.com/PyO3/setuptools-rust/pull/111)

## 0.11.6 (2020-12-13)

Expand Down
2 changes: 1 addition & 1 deletion examples/namespace_package/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = attr: setuptools_rust.__version__
author = Nikolay Kim
author_email = [email protected]
license = MIT
description = Setuptools rust extension plugin
description = Setuptools Rust extension plugin
keywords = distutils, setuptools, rust
url = https://github.com/PyO3/setuptools-rust
long_description = file: README.md
Expand All @@ -29,6 +29,7 @@ packages = setuptools_rust
zip_safe = True
install_requires = setuptools>=46.1; semantic_version>=2.6.0; toml>=0.9.0
setup_requires = setuptools>=46.1; setuptools_scm[toml]>=3.4.3
python_requires = >=3.6

[options.entry_points]
distutils.commands =
Expand Down
4 changes: 1 addition & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@
from setuptools import setup

if __name__ == "__main__":
setup(
python_requires='>=3.5'
)
setup()
2 changes: 0 additions & 2 deletions setuptools_rust/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import print_function, absolute_import

from .build import build_rust
from .check import check_rust
from .clean import clean_rust
Expand Down
98 changes: 21 additions & 77 deletions setuptools_rust/build.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
from __future__ import print_function, absolute_import
import glob
import json
import os
import shutil
import sys
import subprocess
from distutils.cmd import Command
from distutils.errors import (
CompileError,
DistutilsExecError,
DistutilsFileError,
DistutilsPlatformError,
DistutilsSetupError,
)
from subprocess import check_output

from .command import RustCommand
from .extension import RustExtension
from .utils import (
Binding, Strip, cpython_feature, get_rust_version, get_rust_target_info
Binding, Strip, rust_features, get_rust_target_info
)


class build_rust(Command):
""" Command for building rust crates via cargo. """
class build_rust(RustCommand):
""" Command for building Rust crates via cargo. """

description = "build Rust extensions (compile/link to build directory)"

Expand All @@ -33,9 +30,9 @@ class build_rust(Command):
"ignore build-lib and put compiled extensions into the source "
+ "directory alongside your pure Python modules",
),
("debug", "d", "Force debug to true for all rust extensions "),
("release", "r", "Force debug to false for all rust extensions "),
("qbuild", None, "Force enable quiet option for all rust extensions "),
("debug", "d", "Force debug to true for all Rust extensions "),
("release", "r", "Force debug to false for all Rust extensions "),
("qbuild", None, "Force enable quiet option for all Rust extensions "),
(
"build-temp",
"t",
Expand All @@ -45,7 +42,7 @@ class build_rust(Command):
boolean_options = ["inplace", "debug", "release", "qbuild"]

def initialize_options(self):
self.extensions = ()
super().initialize_options()
self.inplace = None
self.debug = None
self.release = None
Expand All @@ -54,11 +51,7 @@ def initialize_options(self):
self.plat_name = None

def finalize_options(self):
self.extensions = [
ext
for ext in self.distribution.rust_extensions
if isinstance(ext, RustExtension)
]
super().finalize_options()

# Inherit settings from the `build_ext` command
self.set_undefined_options(
Expand All @@ -68,7 +61,7 @@ def finalize_options(self):
("inplace", "inplace"),
)

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

rust_target_info = get_rust_target_info()
Expand All @@ -94,7 +87,6 @@ def build_extension(self, ext):
# we'll target a 32-bit Rust build.
# Automatic target detection can be overridden via the CARGO_BUILD_TARGET
# environment variable.
# TODO: include --target for all platforms so env vars can't break the build
target_triple = None
target_args = []
if os.getenv("CARGO_BUILD_TARGET"):
Expand All @@ -116,17 +108,16 @@ def build_extension(self, ext):
"--format-version",
"1",
]
# The decoding is needed for python 3.5 compatibility
metadata = json.loads(check_output(metadata_command).decode("utf-8"))
metadata = json.loads(check_output(metadata_command))
target_dir = metadata["target_directory"]

if not os.path.exists(ext.path):
raise DistutilsFileError(
"Can not find rust extension project file: %s" % ext.path
f"can't find Rust extension project file: {ext.path}"
)

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

debug_build = ext.debug if ext.debug is not None else self.inplace
debug_build = self.debug if self.debug is not None else debug_build
Expand Down Expand Up @@ -190,24 +181,19 @@ def build_extension(self, ext):

# Execute cargo
try:
output = subprocess.check_output(args, env=env)
output = subprocess.check_output(args, env=env, encoding="latin-1")
except subprocess.CalledProcessError as e:
output = e.output
if isinstance(output, bytes):
output = e.output.decode("latin-1").strip()
raise CompileError(
"cargo failed with code: %d\n%s" % (e.returncode, output)
f"cargo failed with code: {e.returncode}\n{e.output}"
)

except OSError:
raise DistutilsExecError(
"Unable to execute 'cargo' - this package "
"requires rust to be installed and cargo to be on the PATH"
"requires Rust to be installed and cargo to be on the PATH"
)

if not quiet:
if isinstance(output, bytes):
output = output.decode("latin-1")
if output:
print(output, file=sys.stderr)

Expand All @@ -231,8 +217,8 @@ def build_extension(self, ext):
continue
else:
raise DistutilsExecError(
"rust build failed; "
'unable to find executable "%s" in %s' % (name, target_dir)
"Rust build failed; "
f"unable to find executable '{name}' in '{target_dir}'"
)
else:
# search executable
Expand All @@ -247,7 +233,7 @@ def build_extension(self, ext):

if not dylib_paths:
raise DistutilsExecError(
"rust build failed; unable to find executable in %s" % target_dir
f"Rust build failed; unable to find executable in {target_dir}"
)
else:
if sys.platform == "win32" or sys.platform == "cygwin":
Expand All @@ -268,8 +254,7 @@ def build_extension(self, ext):
)
except StopIteration:
raise DistutilsExecError(
"rust build failed; unable to find any %s in %s"
% (wildcard_so, artifactsdir)
f"Rust build failed; unable to find any {wildcard_so} in {artifactsdir}"
)

# Ask build_ext where the shared library would go if it had built it,
Expand Down Expand Up @@ -303,11 +288,7 @@ def build_extension(self, ext):
finally:
del build_ext.ext_map[modpath]

try:
os.makedirs(os.path.dirname(ext_path))
except OSError:
pass

os.makedirs(os.path.dirname(ext_path), exist_ok=True)
shutil.copyfile(dylib_path, ext_path)

if sys.platform != "win32" and not debug_build:
Expand All @@ -330,40 +311,3 @@ def build_extension(self, ext):
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

all_optional = all(ext.optional for ext in self.extensions)
try:
version = get_rust_version()
except DistutilsPlatformError as e:
if not all_optional:
raise
else:
print(str(e))
return

for ext in self.extensions:
try:
rust_version = ext.get_rust_version()
if rust_version is not None and version not in rust_version:
raise DistutilsPlatformError(
"Rust %s does not match extension requirement %s"
% (version, ext.rust_version)
)

self.build_extension(ext)
except (
DistutilsSetupError,
DistutilsFileError,
DistutilsExecError,
DistutilsPlatformError,
CompileError,
) as e:
if not ext.optional:
raise
else:
print("Build optional Rust extension %s failed." % ext.name)
print(str(e))
Loading

0 comments on commit 1e37cc8

Please sign in to comment.