Skip to content

Commit 68e30cc

Browse files
messensedavidhewitt
authored andcommitted
Add support for cross
1 parent c36eeb0 commit 68e30cc

File tree

7 files changed

+129
-14
lines changed

7 files changed

+129
-14
lines changed

.github/workflows/ci.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ jobs:
9595
PYTHON: ${{ matrix.python-version }}
9696
run: |
9797
for example_dir in examples/*; do
98-
tox -c $example_dir -e py
98+
tox -c $example_dir -e py -vvvv
9999
done
100100
101101
- name: Test macOS universal2
@@ -224,3 +224,48 @@ jobs:
224224
pip3 install rust_with_cffi/dist/rust_with_cffi*.whl
225225
python3 -c "from rust_with_cffi import rust; assert rust.rust_func() == 14"
226226
python3 -c "from rust_with_cffi.cffi import lib; assert lib.cffi_func() == 15"
227+
228+
test-cross:
229+
runs-on: ubuntu-latest
230+
steps:
231+
- uses: actions/checkout@master
232+
- name: Setup python
233+
uses: actions/setup-python@v2
234+
with:
235+
python-version: 3.8
236+
- uses: actions-rs/toolchain@v1
237+
with:
238+
profile: minimal
239+
toolchain: stable
240+
override: true
241+
- name: Install cross
242+
run: cargo install cross
243+
- name: Build package
244+
run: pip install -e .
245+
- name: Build wheel using cross
246+
shell: bash
247+
env:
248+
CARGO: cross
249+
CARGO_BUILD_TARGET: aarch64-unknown-linux-gnu
250+
PYO3_CROSS_LIB_DIR: /opt/python/cp38-cp38/lib
251+
run: |
252+
cd examples/namespace_package
253+
docker build -t cross-pyo3:aarch64-unknown-linux-gnu .
254+
python -m pip install wheel
255+
python setup.py bdist_wheel --plat-name manylinux2014_aarch64
256+
ls -la dist/
257+
- uses: uraimo/[email protected]
258+
name: Install built wheel
259+
with:
260+
arch: aarch64
261+
distro: ubuntu20.04
262+
dockerRunArgs: |
263+
--volume "${PWD}/examples/namespace_package:/io"
264+
install: |
265+
apt-get update
266+
apt-get install -y --no-install-recommends python3 python3-pip
267+
pip3 install -U pip
268+
run: |
269+
pip3 install namespace_package --no-index --find-links /io/dist/ --force-reinstall
270+
python3 -c "from namespace_package import rust; assert rust.rust_func() == 14"
271+
python3 -c "from namespace_package import python; assert python.python_func() == 15"

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
### Added
6+
- Add support for cross-compiling using [`cross`](https://github.com/rust-embedded/cross). [#185](https://github.com/PyO3/setuptools-rust/pull/185)
7+
58
### Fixed
69
- Fix incompatibility with Python 3.6.0 using default values for NamedTuple classes. [#184](https://github.com/PyO3/setuptools-rust/pull/184)
710

examples/namespace_package/Cross.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[target.aarch64-unknown-linux-gnu]
2+
image = "cross-pyo3:aarch64-unknown-linux-gnu"
3+
4+
[build.env]
5+
passthrough = [
6+
"RUST_BACKTRACE",
7+
"RUST_LOG",
8+
"PYO3_CROSS_LIB_DIR",
9+
]

examples/namespace_package/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM quay.io/pypa/manylinux2014_aarch64 AS manylinux
2+
3+
FROM rustembedded/cross:aarch64-unknown-linux-gnu
4+
5+
RUN apt-get update && \
6+
apt-get install -y software-properties-common && \
7+
add-apt-repository ppa:deadsnakes/ppa && \
8+
apt-get update && \
9+
apt-get install -y python3.8 && \
10+
update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1
11+
12+
COPY --from=manylinux /opt/_internal /opt/_internal
13+
COPY --from=manylinux /opt/python /opt/python

setuptools_rust/build.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
binding_features,
2828
get_rust_target_info,
2929
get_rust_target_list,
30+
split_platform_and_extension,
3031
)
3132

3233

@@ -65,6 +66,7 @@ def initialize_options(self) -> None:
6566
self.build_temp = None
6667
self.plat_name = None
6768
self.target = os.getenv("CARGO_BUILD_TARGET")
69+
self.cargo = os.getenv("CARGO", "cargo")
6870

6971
def finalize_options(self) -> None:
7072
super().finalize_options()
@@ -246,7 +248,7 @@ def build_extension(
246248

247249
if executable:
248250
args = (
249-
["cargo", "build", "--manifest-path", ext.path]
251+
[self.cargo, "build", "--manifest-path", ext.path]
250252
+ feature_args
251253
+ target_args
252254
+ list(ext.args or [])
@@ -262,7 +264,7 @@ def build_extension(
262264

263265
else:
264266
args = (
265-
["cargo", "rustc", "--lib", "--manifest-path", ext.path]
267+
[self.cargo, "rustc", "--lib", "--manifest-path", ext.path]
266268
+ feature_args
267269
+ target_args
268270
+ list(ext.args or [])
@@ -396,13 +398,13 @@ def install_extension(
396398

397399
if executable:
398400
ext_path = build_ext.get_ext_fullpath(module_name)
399-
# remove .so extension
400-
ext_path, _ = os.path.splitext(ext_path)
401-
# remove python3 extension (i.e. cpython-36m)
402-
ext_path, _ = os.path.splitext(ext_path)
401+
# remove extensions
402+
ext_path, _, _ = split_platform_and_extension(ext_path)
403403

404404
# Add expected extension
405-
ext_path += sysconfig.get_config_var("EXE")
405+
exe = sysconfig.get_config_var("EXE")
406+
if exe is not None:
407+
ext_path += exe
406408

407409
os.makedirs(os.path.dirname(ext_path), exist_ok=True)
408410
ext.install_script(module_name.split(".")[-1], ext_path)
@@ -434,9 +436,10 @@ def install_extension(
434436
os.chmod(ext_path, mode)
435437

436438
def get_dylib_ext_path(self, ext: RustExtension, target_fname: str) -> str:
439+
assert self.plat_name is not None
437440
build_ext = cast(CommandBuildExt, self.get_finalized_command("build_ext"))
438441

439-
filename: str = build_ext.get_ext_fullpath(target_fname)
442+
ext_path: str = build_ext.get_ext_fullpath(target_fname)
440443

441444
if (ext.py_limited_api == "auto" and self._py_limited_api()) or (
442445
ext.py_limited_api
@@ -445,9 +448,34 @@ def get_dylib_ext_path(self, ext: RustExtension, target_fname: str) -> str:
445448
if abi3_suffix is not None:
446449
so_ext = get_config_var("EXT_SUFFIX")
447450
assert isinstance(so_ext, str)
448-
filename = filename[: -len(so_ext)] + get_abi3_suffix()
449-
450-
return filename
451+
ext_path = ext_path[: -len(so_ext)] + get_abi3_suffix()
452+
453+
if ".abi3." in ext_path:
454+
return ext_path
455+
# Examples: linux_x86_64, linux_i686, manylinux2014_aarch64, manylinux_2_24_armv7l
456+
plat_name = self.plat_name.lower().replace("-", "_").replace(".", "_")
457+
if not plat_name.startswith(("linux", "manylinux")):
458+
return ext_path
459+
460+
arch_parts = []
461+
arch_found = False
462+
for item in plat_name.split("_"):
463+
if item.startswith(("linux", "manylinux")):
464+
continue
465+
if item.isdigit() and not arch_found:
466+
# manylinux_2_24_armv7l arch should be armv7l
467+
continue
468+
arch_found = True
469+
arch_parts.append(item)
470+
target_arch = "_".join(arch_parts)
471+
host_platform = sysconfig.get_platform()
472+
host_arch = host_platform.rsplit("-", 1)[1]
473+
# Remove incorrect platform tag if we are cross compiling
474+
if target_arch and host_arch != target_arch:
475+
ext_path, _, extension = split_platform_and_extension(ext_path)
476+
# rust.so, removed platform tag
477+
ext_path += extension
478+
return ext_path
451479

452480
def _py_limited_api(self) -> PyLimitedApi:
453481
bdist_wheel = self.distribution.get_command_obj("bdist_wheel", create=False)

setuptools_rust/setuptools_ext.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,11 @@ def run(self) -> None:
124124
log.info("running build_rust")
125125
build_rust = self.get_finalized_command("build_rust")
126126
build_rust.inplace = self.inplace
127-
build_rust.plat_name = self.plat_name
128127
build_rust.target = self.target
129128
build_rust.verbose = self.verbose
129+
options = self.distribution.get_cmdline_options().get("bdist_wheel", {})
130+
plat_name = options.get("plat-name") or self.plat_name
131+
build_rust.plat_name = plat_name
130132
build_rust.run()
131133

132134
build_ext_base_class.run(self)

setuptools_rust/utils.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import os
12
import subprocess
23
from distutils.errors import DistutilsPlatformError
3-
from typing import List, Optional, Set, Union
4+
from typing import List, Optional, Set, Tuple, Union
45

56
from semantic_version import Version
67
from typing_extensions import Literal
@@ -58,3 +59,17 @@ def get_rust_target_list() -> List[str]:
5859
)
5960
_rust_target_list = output.splitlines()
6061
return _rust_target_list
62+
63+
64+
def split_platform_and_extension(ext_path: str) -> Tuple[str, str, str]:
65+
"""Splits an extension path into a tuple (ext_path, plat_tag, extension).
66+
67+
>>> split_platform_and_extension("foo/bar.platform.so")
68+
('foo/bar', '.platform', '.so')
69+
"""
70+
71+
# rust.cpython-38-x86_64-linux-gnu.so to (rust.cpython-38-x86_64-linux-gnu, .so)
72+
ext_path, extension = os.path.splitext(ext_path)
73+
# rust.cpython-38-x86_64-linux-gnu to (rust, .cpython-38-x86_64-linux-gnu)
74+
ext_path, platform_tag = os.path.splitext(ext_path)
75+
return (ext_path, platform_tag, extension)

0 commit comments

Comments
 (0)