66import subprocess
77import sys
88import sysconfig
9+ from distutils .command .build import build as CommandBuild
910from distutils .errors import (
1011 CompileError ,
1112 DistutilsExecError ,
1415)
1516from distutils .sysconfig import get_config_var
1617from subprocess import check_output
17- from typing import List , NamedTuple , Optional , Tuple
18+ from typing import List , NamedTuple , Optional , cast
1819
20+ from setuptools .command .build_ext import build_ext as CommandBuildExt
1921from setuptools .command .build_ext import get_abi3_suffix
2022
2123from .command import RustCommand
@@ -52,7 +54,9 @@ class build_rust(RustCommand):
5254 ]
5355 boolean_options = ["inplace" , "debug" , "release" , "qbuild" ]
5456
55- def initialize_options (self ):
57+ plat_name : Optional [str ]
58+
59+ def initialize_options (self ) -> None :
5660 super ().initialize_options ()
5761 self .inplace = None
5862 self .debug = None
@@ -62,11 +66,14 @@ def initialize_options(self):
6266 self .plat_name = None
6367 self .target = os .getenv ("CARGO_BUILD_TARGET" )
6468
65- def finalize_options (self ):
69+ def finalize_options (self ) -> None :
6670 super ().finalize_options ()
6771
6872 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 )
7077
7178 # Inherit settings from the `build_ext` command
7279 self .set_undefined_options (
@@ -77,6 +84,8 @@ def finalize_options(self):
7784 )
7885
7986 def get_target_info (self ) -> "_TargetInfo" :
87+ assert self .plat_name is not None
88+
8089 # If we are on a 64-bit machine, but running a 32-bit Python, then
8190 # we'll target a 32-bit Rust build.
8291 # Automatic target detection can be overridden via the CARGO_BUILD_TARGET
@@ -115,7 +124,8 @@ def get_target_info(self) -> "_TargetInfo":
115124 % cross_compile_info .host_type
116125 )
117126
118- return _TargetInfo .for_triple (self .target )
127+ # FIXME!
128+ return _TargetInfo .for_triple (self .target ) # type: ignore[arg-type]
119129
120130 def get_nix_cross_compile_info (self ) -> Optional ["_CrossCompileInfo" ]:
121131 # See https://github.com/PyO3/setuptools-rust/issues/138
@@ -143,7 +153,9 @@ def get_nix_cross_compile_info(self) -> Optional["_CrossCompileInfo"]:
143153
144154 return _CrossCompileInfo (host_type , cross_lib , linker , linker_args )
145155
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+
147159 arch_flags = os .getenv ("ARCHFLAGS" )
148160 universal2 = False
149161 if self .plat_name .startswith ("macosx-" ) and arch_flags :
@@ -156,15 +168,15 @@ def run_for_extension(self, ext: RustExtension):
156168 arm64_dylib_paths , x86_64_dylib_paths
157169 ):
158170 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 ))
163173 else :
164174 dylib_paths = self .build_extension (ext )
165175 self .install_extension (ext , dylib_paths )
166176
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" ]:
168180 executable = ext .binding == Binding .Exec
169181
170182 if target_triple is None :
@@ -333,7 +345,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
333345
334346 path = os .path .join (artifactsdir , name )
335347 if os .access (path , os .X_OK ):
336- dylib_paths .append ((dest , path ))
348+ dylib_paths .append (_BuiltModule (dest , path ))
337349 else :
338350 raise DistutilsExecError (
339351 "Rust build failed; "
@@ -351,7 +363,7 @@ def build_extension(self, ext: RustExtension, target_triple=None):
351363
352364 try :
353365 dylib_paths .append (
354- (
366+ _BuiltModule (
355367 ext .name ,
356368 next (glob .iglob (os .path .join (artifactsdir , wildcard_so ))),
357369 )
@@ -362,15 +374,18 @@ def build_extension(self, ext: RustExtension, target_triple=None):
362374 )
363375 return dylib_paths
364376
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 :
366380 executable = ext .binding == Binding .Exec
367381 debug_build = ext .debug if ext .debug is not None else self .inplace
368382 debug_build = self .debug if self .debug is not None else debug_build
369383 if self .release :
370384 debug_build = False
385+
371386 # Ask build_ext where the shared library would go if it had built it,
372387 # then copy it there.
373- build_ext = self .get_finalized_command ("build_ext" )
388+ build_ext = cast ( CommandBuildExt , self .get_finalized_command ("build_ext" ) )
374389 build_ext .inplace = self .inplace
375390
376391 for module_name , dylib_path in dylib_paths :
@@ -419,55 +434,70 @@ def install_extension(self, ext: RustExtension, dylib_paths: List[Tuple[str, str
419434 os .chmod (ext_path , mode )
420435
421436 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" ) )
423438
424- filename = build_ext .get_ext_fullpath (target_fname )
439+ filename : str = build_ext .get_ext_fullpath (target_fname )
425440
426441 if (ext .py_limited_api == "auto" and self ._py_limited_api ()) or (
427442 ext .py_limited_api
428443 ):
429444 abi3_suffix = get_abi3_suffix ()
430445 if abi3_suffix is not None :
431446 so_ext = get_config_var ("EXT_SUFFIX" )
447+ assert isinstance (so_ext , str )
432448 filename = filename [: - len (so_ext )] + get_abi3_suffix ()
433449
434450 return filename
435451
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-
462452 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 )
464454
465455 if bdist_wheel is None :
466456 # wheel package is not installed, not building a limited-api wheel
467457 return False
468458 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
471501
472502
473503class _TargetInfo (NamedTuple ):
0 commit comments