6
6
import subprocess
7
7
import sys
8
8
import sysconfig
9
+ from distutils .command .build import build as CommandBuild
9
10
from distutils .errors import (
10
11
CompileError ,
11
12
DistutilsExecError ,
14
15
)
15
16
from distutils .sysconfig import get_config_var
16
17
from subprocess import check_output
17
- from typing import List , NamedTuple , Optional , Tuple
18
+ from typing import List , NamedTuple , Optional , cast
18
19
20
+ from setuptools .command .build_ext import build_ext as CommandBuildExt
19
21
from setuptools .command .build_ext import get_abi3_suffix
20
22
21
23
from .command import RustCommand
@@ -52,7 +54,9 @@ class build_rust(RustCommand):
52
54
]
53
55
boolean_options = ["inplace" , "debug" , "release" , "qbuild" ]
54
56
55
- def initialize_options (self ):
57
+ plat_name : Optional [str ]
58
+
59
+ def initialize_options (self ) -> None :
56
60
super ().initialize_options ()
57
61
self .inplace = None
58
62
self .debug = None
@@ -62,11 +66,14 @@ def initialize_options(self):
62
66
self .plat_name = None
63
67
self .target = os .getenv ("CARGO_BUILD_TARGET" )
64
68
65
- def finalize_options (self ):
69
+ def finalize_options (self ) -> None :
66
70
super ().finalize_options ()
67
71
68
72
if self .plat_name is None :
69
- self .plat_name = self .get_finalized_command ("build" ).plat_name
73
+ self .plat_name = cast (
74
+ CommandBuild , self .get_finalized_command ("build" )
75
+ ).plat_name
76
+ assert isinstance (self .plat_name , str )
70
77
71
78
# Inherit settings from the `build_ext` command
72
79
self .set_undefined_options (
@@ -77,6 +84,8 @@ def finalize_options(self):
77
84
)
78
85
79
86
def get_target_info (self ) -> "_TargetInfo" :
87
+ assert self .plat_name is not None
88
+
80
89
# If we are on a 64-bit machine, but running a 32-bit Python, then
81
90
# we'll target a 32-bit Rust build.
82
91
# Automatic target detection can be overridden via the CARGO_BUILD_TARGET
@@ -115,7 +124,8 @@ def get_target_info(self) -> "_TargetInfo":
115
124
% cross_compile_info .host_type
116
125
)
117
126
118
- return _TargetInfo .for_triple (self .target )
127
+ # FIXME!
128
+ return _TargetInfo .for_triple (self .target ) # type: ignore[arg-type]
119
129
120
130
def get_nix_cross_compile_info (self ) -> Optional ["_CrossCompileInfo" ]:
121
131
# See https://github.com/PyO3/setuptools-rust/issues/138
@@ -143,7 +153,9 @@ def get_nix_cross_compile_info(self) -> Optional["_CrossCompileInfo"]:
143
153
144
154
return _CrossCompileInfo (host_type , cross_lib , linker , linker_args )
145
155
146
- def run_for_extension (self , ext : RustExtension ):
156
+ def run_for_extension (self , ext : RustExtension ) -> None :
157
+ assert self .plat_name is not None
158
+
147
159
arch_flags = os .getenv ("ARCHFLAGS" )
148
160
universal2 = False
149
161
if self .plat_name .startswith ("macosx-" ) and arch_flags :
@@ -156,15 +168,15 @@ def run_for_extension(self, ext: RustExtension):
156
168
arm64_dylib_paths , x86_64_dylib_paths
157
169
):
158
170
fat_dylib_path = arm64_dylib .replace ("aarch64-apple-darwin/" , "" )
159
- self .create_universal2_binary (
160
- fat_dylib_path , [arm64_dylib , x86_64_dylib ]
161
- )
162
- dylib_paths .append ((target_fname , fat_dylib_path ))
171
+ create_universal2_binary (fat_dylib_path , [arm64_dylib , x86_64_dylib ])
172
+ dylib_paths .append (_BuiltModule (target_fname , fat_dylib_path ))
163
173
else :
164
174
dylib_paths = self .build_extension (ext )
165
175
self .install_extension (ext , dylib_paths )
166
176
167
- def build_extension (self , ext : RustExtension , target_triple = None ):
177
+ def build_extension (
178
+ self , ext : RustExtension , target_triple : Optional [str ] = None
179
+ ) -> List ["_BuiltModule" ]:
168
180
executable = ext .binding == Binding .Exec
169
181
170
182
if target_triple is None :
@@ -333,7 +345,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
333
345
334
346
path = os .path .join (artifactsdir , name )
335
347
if os .access (path , os .X_OK ):
336
- dylib_paths .append ((dest , path ))
348
+ dylib_paths .append (_BuiltModule (dest , path ))
337
349
else :
338
350
raise DistutilsExecError (
339
351
"Rust build failed; "
@@ -351,7 +363,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
351
363
352
364
try :
353
365
dylib_paths .append (
354
- (
366
+ _BuiltModule (
355
367
ext .name ,
356
368
next (glob .iglob (os .path .join (artifactsdir , wildcard_so ))),
357
369
)
@@ -362,15 +374,18 @@ def build_extension(self, ext: RustExtension, target_triple=None):
362
374
)
363
375
return dylib_paths
364
376
365
- def install_extension (self , ext : RustExtension , dylib_paths : List [Tuple [str , str ]]):
377
+ def install_extension (
378
+ self , ext : RustExtension , dylib_paths : List ["_BuiltModule" ]
379
+ ) -> None :
366
380
executable = ext .binding == Binding .Exec
367
381
debug_build = ext .debug if ext .debug is not None else self .inplace
368
382
debug_build = self .debug if self .debug is not None else debug_build
369
383
if self .release :
370
384
debug_build = False
385
+
371
386
# Ask build_ext where the shared library would go if it had built it,
372
387
# then copy it there.
373
- build_ext = self .get_finalized_command ("build_ext" )
388
+ build_ext = cast ( CommandBuildExt , self .get_finalized_command ("build_ext" ) )
374
389
build_ext .inplace = self .inplace
375
390
376
391
for module_name , dylib_path in dylib_paths :
@@ -419,55 +434,70 @@ def install_extension(self, ext: RustExtension, dylib_paths: List[Tuple[str, str
419
434
os .chmod (ext_path , mode )
420
435
421
436
def get_dylib_ext_path (self , ext : RustExtension , target_fname : str ) -> str :
422
- build_ext = self .get_finalized_command ("build_ext" )
437
+ build_ext = cast ( CommandBuildExt , self .get_finalized_command ("build_ext" ) )
423
438
424
- filename = build_ext .get_ext_fullpath (target_fname )
439
+ filename : str = build_ext .get_ext_fullpath (target_fname )
425
440
426
441
if (ext .py_limited_api == "auto" and self ._py_limited_api ()) or (
427
442
ext .py_limited_api
428
443
):
429
444
abi3_suffix = get_abi3_suffix ()
430
445
if abi3_suffix is not None :
431
446
so_ext = get_config_var ("EXT_SUFFIX" )
447
+ assert isinstance (so_ext , str )
432
448
filename = filename [: - len (so_ext )] + get_abi3_suffix ()
433
449
434
450
return filename
435
451
436
- @staticmethod
437
- def create_universal2_binary (output_path , input_paths ):
438
- # Try lipo first
439
- command = ["lipo" , "-create" , "-output" , output_path , * input_paths ]
440
- try :
441
- subprocess .check_output (command )
442
- except subprocess .CalledProcessError as e :
443
- output = e .output
444
- if isinstance (output , bytes ):
445
- output = e .output .decode ("latin-1" ).strip ()
446
- raise CompileError ("lipo failed with code: %d\n %s" % (e .returncode , output ))
447
- except OSError :
448
- # lipo not found, try using the fat-macho library
449
- try :
450
- from fat_macho import FatWriter
451
- except ImportError :
452
- raise DistutilsExecError (
453
- "failed to locate `lipo` or import `fat_macho.FatWriter`. "
454
- "Try installing with `pip install fat-macho` "
455
- )
456
- fat = FatWriter ()
457
- for input_path in input_paths :
458
- with open (input_path , "rb" ) as f :
459
- fat .add (f .read ())
460
- fat .write_to (output_path )
461
-
462
452
def _py_limited_api (self ) -> PyLimitedApi :
463
- bdist_wheel = self .distribution .get_command_obj ("bdist_wheel" , create = 0 )
453
+ bdist_wheel = self .distribution .get_command_obj ("bdist_wheel" , create = False )
464
454
465
455
if bdist_wheel is None :
466
456
# wheel package is not installed, not building a limited-api wheel
467
457
return False
468
458
else :
469
- bdist_wheel .ensure_finalized ()
470
- return bdist_wheel .py_limited_api
459
+ from wheel .bdist_wheel import bdist_wheel as CommandBdistWheel
460
+
461
+ bdist_wheel_command = cast (CommandBdistWheel , bdist_wheel ) # type: ignore[no-any-unimported]
462
+ bdist_wheel_command .ensure_finalized ()
463
+ return cast (PyLimitedApi , bdist_wheel_command .py_limited_api )
464
+
465
+
466
+ def create_universal2_binary (output_path : str , input_paths : List [str ]) -> None :
467
+ # Try lipo first
468
+ command = ["lipo" , "-create" , "-output" , output_path , * input_paths ]
469
+ try :
470
+ subprocess .check_output (command )
471
+ except subprocess .CalledProcessError as e :
472
+ output = e .output
473
+ if isinstance (output , bytes ):
474
+ output = e .output .decode ("latin-1" ).strip ()
475
+ raise CompileError ("lipo failed with code: %d\n %s" % (e .returncode , output ))
476
+ except OSError :
477
+ # lipo not found, try using the fat-macho library
478
+ try :
479
+ from fat_macho import FatWriter
480
+ except ImportError :
481
+ raise DistutilsExecError (
482
+ "failed to locate `lipo` or import `fat_macho.FatWriter`. "
483
+ "Try installing with `pip install fat-macho` "
484
+ )
485
+ fat = FatWriter ()
486
+ for input_path in input_paths :
487
+ with open (input_path , "rb" ) as f :
488
+ fat .add (f .read ())
489
+ fat .write_to (output_path )
490
+
491
+
492
+ class _BuiltModule (NamedTuple ):
493
+ """
494
+ Attributes:
495
+ - module_name: dotted python import path of the module
496
+ - path: the location the module has been installed at
497
+ """
498
+
499
+ module_name : str
500
+ path : str
471
501
472
502
473
503
class _TargetInfo (NamedTuple ):
0 commit comments