7979use std:: ffi:: OsStr ;
8080use std:: fs:: { create_dir_all, write} ;
8181use std:: io:: { Error , ErrorKind , Result } ;
82- use std:: path:: Path ;
82+ use std:: path:: { Path , PathBuf } ;
8383use std:: process:: Command ;
8484
8585/// Import library file extension for the GNU environment ABI (MinGW-w64)
@@ -110,12 +110,18 @@ pub struct ImportLibraryGenerator {
110110 arch : String ,
111111 // The compile target environment ABI name (as in `CARGO_CFG_TARGET_ENV`)
112112 env : String ,
113- /// Python major and minor version
113+ /// Major and minor Python version (for `pythonXY.dll` only)
114114 version : Option < ( u8 , u8 ) > ,
115115}
116116
117117impl ImportLibraryGenerator {
118- /// Creates a new import library generator for the specified compile target
118+ /// Creates a new import library generator for the specified compile target.
119+ ///
120+ /// The compile target architecture name (as in `CARGO_CFG_TARGET_ARCH`)
121+ /// is passed in `arch`.
122+ ///
123+ /// The compile target environment ABI name (as in `CARGO_CFG_TARGET_ENV`)
124+ /// is passed in `env`.
119125 pub fn new ( arch : & str , env : & str ) -> Self {
120126 Self {
121127 arch : arch. to_string ( ) ,
@@ -124,62 +130,37 @@ impl ImportLibraryGenerator {
124130 }
125131 }
126132
127- /// Set python major and minor version
133+ /// Sets major and minor version for the `pythonXY.dll` import library.
134+ ///
135+ /// The version-agnostic `python3.dll` is generated by default.
128136 pub fn version ( & mut self , version : Option < ( u8 , u8 ) > ) -> & mut Self {
129137 self . version = version;
130138 self
131139 }
132140
133- /// Generates the import library in `out_dir`
141+ /// Generates the Python DLL import library in `out_dir`.
142+ ///
143+ /// The version-agnostic `python3.dll` import library is generated
144+ /// by default unless the version-specific `pythonXY.dll` import
145+ /// was requested via `version()`.
134146 pub fn generate ( & self , out_dir : & Path ) -> Result < ( ) > {
135147 create_dir_all ( out_dir) ?;
136148
137- let mut defpath = out_dir. to_owned ( ) ;
138- let ( def_file, def_file_content) = match self . version {
139- None => ( "python3.def" , include_str ! ( "python3.def" ) ) ,
140- Some ( ( 3 , 7 ) ) => ( "python37.def" , include_str ! ( "python37.def" ) ) ,
141- Some ( ( 3 , 8 ) ) => ( "python38.def" , include_str ! ( "python38.def" ) ) ,
142- Some ( ( 3 , 9 ) ) => ( "python39.def" , include_str ! ( "python39.def" ) ) ,
143- Some ( ( 3 , 10 ) ) => ( "python310.def" , include_str ! ( "python310.def" ) ) ,
144- Some ( ( 3 , 11 ) ) => ( "python311.def" , include_str ! ( "python311.def" ) ) ,
145- _ => return Err ( Error :: new ( ErrorKind :: Other , "Unsupported Python version" ) ) ,
146- } ;
147- defpath. push ( def_file) ;
148-
149- write ( & defpath, def_file_content) ?;
149+ let defpath = self . write_def_file ( out_dir) ?;
150+ let implib_file = self . implib_file_path ( out_dir) ;
150151
151- let impllib_ext = if self . env == "msvc" {
152- IMPLIB_EXT_MSVC
153- } else {
154- IMPLIB_EXT_GNU
155- } ;
156- let implib_file = match self . version {
157- Some ( ( major, minor) ) => {
158- format ! ( "python{}{}{}" , major, minor, impllib_ext)
159- }
160- None => format ! ( "python3{}" , impllib_ext) ,
161- } ;
162152 // Try to guess the `dlltool` executable name from the target triple.
163153 let mut command = match ( self . arch . as_str ( ) , self . env . as_str ( ) ) {
164154 // 64-bit MinGW-w64 (aka x86_64-pc-windows-gnu)
165- ( "x86_64" , "gnu" ) => {
166- self . build_dlltool_command ( DLLTOOL_GNU , & implib_file, & defpath, out_dir)
167- }
155+ ( "x86_64" , "gnu" ) => self . build_dlltool_command ( DLLTOOL_GNU , & implib_file, & defpath) ,
168156 // 32-bit MinGW-w64 (aka i686-pc-windows-gnu)
169- ( "x86" , "gnu" ) => {
170- self . build_dlltool_command ( DLLTOOL_GNU_32 , & implib_file, & defpath, out_dir)
171- }
157+ ( "x86" , "gnu" ) => self . build_dlltool_command ( DLLTOOL_GNU_32 , & implib_file, & defpath) ,
172158 // MSVC ABI (multiarch)
173159 ( _, "msvc" ) => {
174160 if let Some ( command) = find_lib_exe ( & self . arch ) {
175- self . build_dlltool_command (
176- command. get_program ( ) ,
177- & implib_file,
178- & defpath,
179- out_dir,
180- )
161+ self . build_dlltool_command ( command. get_program ( ) , & implib_file, & defpath)
181162 } else {
182- self . build_dlltool_command ( DLLTOOL_MSVC , & implib_file, & defpath, out_dir )
163+ self . build_dlltool_command ( DLLTOOL_MSVC , & implib_file, & defpath)
183164 }
184165 }
185166 _ => {
@@ -205,18 +186,61 @@ impl ImportLibraryGenerator {
205186 }
206187 }
207188
189+ /// Writes out the embedded Python library definitions file to `out_dir`.
190+ ///
191+ /// Returns the newly created `python3.def` or `pythonXY.def` file path.
192+ fn write_def_file ( & self , out_dir : & Path ) -> Result < PathBuf > {
193+ let ( def_file, def_file_content) = match self . version {
194+ None => ( "python3.def" , include_str ! ( "python3.def" ) ) ,
195+ Some ( ( 3 , 7 ) ) => ( "python37.def" , include_str ! ( "python37.def" ) ) ,
196+ Some ( ( 3 , 8 ) ) => ( "python38.def" , include_str ! ( "python38.def" ) ) ,
197+ Some ( ( 3 , 9 ) ) => ( "python39.def" , include_str ! ( "python39.def" ) ) ,
198+ Some ( ( 3 , 10 ) ) => ( "python310.def" , include_str ! ( "python310.def" ) ) ,
199+ Some ( ( 3 , 11 ) ) => ( "python311.def" , include_str ! ( "python311.def" ) ) ,
200+ _ => return Err ( Error :: new ( ErrorKind :: Other , "Unsupported Python version" ) ) ,
201+ } ;
202+
203+ let mut defpath = out_dir. to_owned ( ) ;
204+ defpath. push ( def_file) ;
205+
206+ write ( & defpath, def_file_content) ?;
207+
208+ Ok ( defpath)
209+ }
210+
211+ /// Builds the generated import library file name.
212+ ///
213+ /// Returns the full import library file path under `out_dir`.
214+ fn implib_file_path ( & self , out_dir : & Path ) -> PathBuf {
215+ let libext = if self . env == "msvc" {
216+ IMPLIB_EXT_MSVC
217+ } else {
218+ IMPLIB_EXT_GNU
219+ } ;
220+
221+ let libname = match self . version {
222+ Some ( ( major, minor) ) => {
223+ format ! ( "python{}{}{}" , major, minor, libext)
224+ }
225+ None => format ! ( "python3{}" , libext) ,
226+ } ;
227+
228+ let mut libpath = out_dir. to_owned ( ) ;
229+ libpath. push ( libname) ;
230+
231+ libpath
232+ }
233+
208234 /// Generates the complete `dlltool` executable invocation command.
209235 ///
210236 /// Supports Visual Studio `lib.exe`, LLVM and MinGW `dlltool` flavors.
211237 fn build_dlltool_command (
212238 & self ,
213239 dlltool : impl AsRef < OsStr > ,
214- implib_file : & str ,
240+ libpath : & Path ,
215241 defpath : & Path ,
216- out_dir : & Path ,
217242 ) -> Command {
218243 let dlltool = dlltool. as_ref ( ) ;
219- let mut libpath = out_dir. to_owned ( ) ;
220244 let mut command = if self . env == "msvc" {
221245 find_lib_exe ( & self . arch ) . unwrap_or_else ( || Command :: new ( dlltool) )
222246 } else {
@@ -225,8 +249,6 @@ impl ImportLibraryGenerator {
225249
226250 // Check whether we are using LLVM `dlltool` or MinGW `dlltool`.
227251 if dlltool == DLLTOOL_MSVC {
228- libpath. push ( implib_file) ;
229-
230252 // LLVM tools use their own target architecture names...
231253 let machine = match self . arch . as_str ( ) {
232254 "x86_64" => "i386:x86-64" ,
@@ -243,8 +265,6 @@ impl ImportLibraryGenerator {
243265 . arg ( "-l" )
244266 . arg ( libpath) ;
245267 } else if Path :: new ( dlltool) . file_name ( ) == Some ( LIB_MSVC . as_ref ( ) ) {
246- libpath. push ( implib_file) ;
247-
248268 // lib.exe use their own target architecure names...
249269 let machine = match self . arch . as_str ( ) {
250270 "x86_64" => "X64" ,
@@ -257,8 +277,6 @@ impl ImportLibraryGenerator {
257277 . arg ( format ! ( "/DEF:{}" , defpath. display( ) ) )
258278 . arg ( format ! ( "/OUT:{}" , libpath. display( ) ) ) ;
259279 } else {
260- libpath. push ( implib_file) ;
261-
262280 command
263281 . arg ( "--input-def" )
264282 . arg ( defpath)
@@ -285,7 +303,7 @@ pub fn generate_implib_for_target(out_dir: &Path, arch: &str, env: &str) -> Resu
285303 ImportLibraryGenerator :: new ( arch, env) . generate ( out_dir)
286304}
287305
288- /// Find Visual Studio lib.exe on Windows
306+ /// Finds Visual Studio ` lib.exe` when running on Windows.
289307#[ cfg( windows) ]
290308fn find_lib_exe ( arch : & str ) -> Option < Command > {
291309 let target = match arch {
@@ -294,6 +312,7 @@ fn find_lib_exe(arch: &str) -> Option<Command> {
294312 "aarch64" => "aarch64-pc-windows-msvc" ,
295313 _ => return None ,
296314 } ;
315+
297316 cc:: windows_registry:: find ( target, LIB_MSVC )
298317}
299318
0 commit comments