Skip to content

Commit 503c339

Browse files
authored
Merge pull request #139 from benfogle/cross-compile-info-from-sysconfig
On *NIX systems, gather cross compiling info from sysconfig
2 parents 934d688 + 035a402 commit 503c339

File tree

3 files changed

+96
-13
lines changed

3 files changed

+96
-13
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
- Remove `test_rust` command. (`python setup.py test` is deprecated.) [#129](https://github.com/PyO3/setuptools-rust/pull/129)
1111
- Remove `check_rust` command. [#131](https://github.com/PyO3/setuptools-rust/pull/131)
1212

13+
### Fixed
14+
- Use info from sysconfig when cross-compiling. [#139](https://github.com/PyO3/setuptools-rust/pull/139)
15+
1316
## 0.12.1 (2021-03-11)
1417
### Fixed
1518
- Fix some files unexpectedly missing from `sdist` command output. [#125](https://github.com/PyO3/setuptools-rust/pull/125)

setuptools_rust/build.py

+77-11
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@
55
import shutil
66
import sys
77
import subprocess
8+
import sysconfig
89
from distutils.errors import (
910
CompileError,
1011
DistutilsExecError,
1112
DistutilsFileError,
13+
DistutilsPlatformError,
1214
)
1315
from distutils.sysconfig import get_config_var
1416
from setuptools.command.build_ext import get_abi3_suffix
1517
from subprocess import check_output
1618

1719
from .command import RustCommand
1820
from .extension import Binding, RustExtension, Strip
19-
from .utils import binding_features, get_rust_target_info
21+
from .utils import binding_features, get_rust_target_info, get_rust_target_list
2022

23+
class _TargetInfo:
24+
def __init__(self, triple=None, cross_lib=None, linker=None, link_args=None):
25+
self.triple = triple
26+
self.cross_lib = cross_lib
27+
self.linker = linker
28+
self.link_args = link_args
2129

2230
class build_rust(RustCommand):
2331
""" Command for building Rust crates via cargo. """
@@ -64,20 +72,65 @@ def finalize_options(self):
6472
("inplace", "inplace"),
6573
)
6674

67-
def get_target_triple(self):
75+
def get_target_info(self):
6876
# If we are on a 64-bit machine, but running a 32-bit Python, then
6977
# we'll target a 32-bit Rust build.
7078
# Automatic target detection can be overridden via the CARGO_BUILD_TARGET
7179
# environment variable or --target command line option
7280
if self.target:
73-
return self.target
81+
return _TargetInfo(self.target)
7482
elif self.plat_name == "win32":
75-
return "i686-pc-windows-msvc"
83+
return _TargetInfo("i686-pc-windows-msvc")
7684
elif self.plat_name == "win-amd64":
77-
return "x86_64-pc-windows-msvc"
85+
return _TargetInfo("x86_64-pc-windows-msvc")
7886
elif self.plat_name.startswith("macosx-") and platform.machine() == "x86_64":
7987
# x86_64 or arm64 macOS targeting x86_64
80-
return "x86_64-apple-darwin"
88+
return _TargetInfo("x86_64-apple-darwin")
89+
else:
90+
return self.get_nix_target_info()
91+
92+
def get_nix_target_info(self):
93+
# See https://github.com/PyO3/setuptools-rust/issues/138
94+
# This is to support cross compiling on *NIX, where plat_name isn't
95+
# necessarily the same as the system we are running on. *NIX systems
96+
# have more detailed information available in sysconfig. We need that
97+
# because plat_name doesn't give us information on e.g., glibc vs musl.
98+
host_type = sysconfig.get_config_var('HOST_GNU_TYPE')
99+
build_type = sysconfig.get_config_var('BUILD_GNU_TYPE')
100+
101+
if not host_type or host_type == build_type:
102+
# not *NIX, or not cross compiling
103+
return _TargetInfo()
104+
105+
stdlib = sysconfig.get_path('stdlib')
106+
cross_lib = os.path.dirname(stdlib)
107+
108+
bldshared = sysconfig.get_config_var('BLDSHARED')
109+
if not bldshared:
110+
linker = None
111+
linker_args = None
112+
else:
113+
bldshared = bldshared.split()
114+
linker = bldshared[0]
115+
linker_args = bldshared[1:]
116+
117+
# hopefully an exact match
118+
targets = get_rust_target_list()
119+
if host_type in targets:
120+
return _TargetInfo(host_type, cross_lib, linker, linker_args)
121+
122+
# the vendor field can be ignored, so x86_64-pc-linux-gnu is compatible
123+
# with x86_64-unknown-linux-gnu
124+
components = host_type.split('-')
125+
if len(components) == 4:
126+
components[1] = 'unknown'
127+
host_type2 = '-'.join(components)
128+
if host_type2 in targets:
129+
return _TargetInfo(host_type2, cross_lib, linker, linker_args)
130+
131+
raise DistutilsPlatformError(
132+
"Don't know the correct rust target for system type %s. Please "
133+
"set the CARGO_BUILD_TARGET environment variable." % host_type)
81134

82135
def run_for_extension(self, ext: RustExtension):
83136
arch_flags = os.getenv("ARCHFLAGS")
@@ -99,7 +152,11 @@ def run_for_extension(self, ext: RustExtension):
99152
def build_extension(self, ext: RustExtension, target_triple=None):
100153
executable = ext.binding == Binding.Exec
101154

102-
rust_target_info = get_rust_target_info()
155+
if target_triple is None:
156+
target_info = self.get_target_info()
157+
else:
158+
target_info = _TargetInfo(target_triple)
159+
rust_target_info = get_rust_target_info(target_info.triple)
103160

104161
# Make sure that if pythonXX-sys is used, it builds against the current
105162
# executing python interpreter.
@@ -116,12 +173,15 @@ def build_extension(self, ext: RustExtension, target_triple=None):
116173
"PYO3_PYTHON": os.environ.get("PYO3_PYTHON", sys.executable),
117174
}
118175
)
176+
177+
if target_info.cross_lib:
178+
env.setdefault("PYO3_CROSS_LIB_DIR", target_info.cross_lib)
179+
119180
rustflags = ""
120181

121-
target_triple = target_triple or self.get_target_triple()
122182
target_args = []
123-
if target_triple is not None:
124-
target_args = ["--target", target_triple]
183+
if target_info.triple is not None:
184+
target_args = ["--target", target_info.triple]
125185

126186
# Find where to put the temporary build files created by `cargo`
127187
metadata_command = [
@@ -191,6 +251,12 @@ def build_extension(self, ext: RustExtension, target_triple=None):
191251
args.extend(["--", "--crate-type", "cdylib"])
192252
args.extend(ext.rustc_flags or [])
193253

254+
if target_info.linker is not None:
255+
args.extend(["-C", "linker=" + target_info.linker])
256+
# We're ignoring target_info.link_args for now because we're not
257+
# sure if they will always do the right thing. Might help with some
258+
# of the OS-specific logic below if it does.
259+
194260
# OSX requires special linker argument
195261
if sys.platform == "darwin":
196262
args.extend(
@@ -240,7 +306,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
240306
suffix = "release"
241307

242308
# location of cargo compiled files
243-
artifactsdir = os.path.join(target_dir, target_triple or "", suffix)
309+
artifactsdir = os.path.join(target_dir, target_info.triple or "", suffix)
244310
dylib_paths = []
245311

246312
if executable:

setuptools_rust/utils.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ def get_rust_version(min_version=None):
5959
raise DistutilsPlatformError(f"can't get rustc version: {str(exc)}")
6060

6161

62-
def get_rust_target_info():
63-
output = subprocess.check_output(["rustc", "--print", "cfg"])
62+
def get_rust_target_info(target_triple=None):
63+
cmd = ["rustc", "--print", "cfg"]
64+
if target_triple:
65+
cmd.extend(['--target', target_triple])
66+
output = subprocess.check_output(cmd)
6467
return output.splitlines()
68+
69+
70+
_rust_target_list = None
71+
72+
def get_rust_target_list():
73+
global _rust_target_list
74+
if _rust_target_list is None:
75+
output = subprocess.check_output(["rustc", "--print", "target-list"],
76+
universal_newlines=True)
77+
_rust_target_list = output.splitlines()
78+
return _rust_target_list

0 commit comments

Comments
 (0)