Skip to content

Commit a8f3e5c

Browse files
committed
On *NIX systems, gather cross compiling info from sysconfig
Closes #138
1 parent aef6966 commit a8f3e5c

File tree

2 files changed

+94
-13
lines changed

2 files changed

+94
-13
lines changed

setuptools_rust/build.py

+78-11
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,25 @@
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 subprocess import check_output
1416

1517
from .command import RustCommand
1618
from .extension import Binding, RustExtension, Strip
17-
from .utils import rust_features, get_rust_target_info
19+
from .utils import rust_features, get_rust_target_info, get_rust_target_list
1820

21+
class _TargetInfo:
22+
def __init__(self, triple=None, cross_lib=None, linker=None, link_args=None):
23+
self.triple = triple
24+
self.cross_lib = cross_lib
25+
self.linker = linker
26+
self.link_args = link_args
1927

2028
class build_rust(RustCommand):
2129
""" Command for building Rust crates via cargo. """
@@ -60,20 +68,66 @@ def finalize_options(self):
6068
("inplace", "inplace"),
6169
)
6270

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

78132
def run_for_extension(self, ext: RustExtension):
79133
arch_flags = os.getenv("ARCHFLAGS")
@@ -95,7 +149,11 @@ def run_for_extension(self, ext: RustExtension):
95149
def build_extension(self, ext: RustExtension, target_triple=None):
96150
executable = ext.binding == Binding.Exec
97151

98-
rust_target_info = get_rust_target_info()
152+
if target_triple is None:
153+
target_info = self.get_target_info()
154+
else:
155+
target_info = _TargetInfo(target_triple)
156+
rust_target_info = get_rust_target_info(target_info.triple)
99157

100158
# Make sure that if pythonXX-sys is used, it builds against the current
101159
# executing python interpreter.
@@ -112,12 +170,15 @@ def build_extension(self, ext: RustExtension, target_triple=None):
112170
"PYO3_PYTHON": os.environ.get("PYO3_PYTHON", sys.executable),
113171
}
114172
)
173+
174+
if target_info.cross_lib:
175+
env.setdefault("PYO3_CROSS_LIB_DIR", target_info.cross_lib)
176+
115177
rustflags = ""
116178

117-
target_triple = target_triple or self.get_target_triple()
118179
target_args = []
119-
if target_triple is not None:
120-
target_args = ["--target", target_triple]
180+
if target_info.triple is not None:
181+
target_args = ["--target", target_info.triple]
121182

122183
# Find where to put the temporary build files created by `cargo`
123184
metadata_command = [
@@ -180,6 +241,12 @@ def build_extension(self, ext: RustExtension, target_triple=None):
180241
args.extend(["--", "--crate-type", "cdylib"])
181242
args.extend(ext.rustc_flags or [])
182243

244+
if target_info.linker is not None:
245+
args.extend(["-C", "linker=" + target_info.linker])
246+
# We're ignoring target_info.link_args for now because we're not
247+
# sure if they will always do the right thing. Might help with some
248+
# of the OS-specific logic below if it does.
249+
183250
# OSX requires special linker argument
184251
if sys.platform == "darwin":
185252
args.extend(
@@ -229,7 +296,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
229296
suffix = "release"
230297

231298
# location of cargo compiled files
232-
artifactsdir = os.path.join(target_dir, target_triple or "", suffix)
299+
artifactsdir = os.path.join(target_dir, target_info.triple or "", suffix)
233300
dylib_paths = []
234301

235302
if executable:

setuptools_rust/utils.py

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

6464

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

0 commit comments

Comments
 (0)