@@ -102,6 +102,9 @@ const DLLTOOL_GNU_32: &str = "i686-w64-mingw32-dlltool";
102102/// Canonical `dlltool` program name for the MSVC environment ABI (LLVM dlltool)
103103const DLLTOOL_MSVC : & str = "llvm-dlltool" ;
104104
105+ /// Canonical `lib` program name for the MSVC environment ABI (MSVC lib.exe)
106+ const LIB_MSVC : & str = "lib.exe" ;
107+
105108/// Python Stable ABI symbol defs from the CPython repository
106109///
107110/// Upstream source: <https://github.com/python/cpython/blob/main/Misc/stable_abi.txt>
@@ -131,21 +134,27 @@ pub fn generate_implib_for_target(out_dir: &Path, arch: &str, env: &str) -> Resu
131134 drop ( writer) ;
132135
133136 // Try to guess the `dlltool` executable name from the target triple.
134- let dlltool = match ( arch, env) {
137+ let ( command , dlltool) = match ( arch, env) {
135138 // 64-bit MinGW-w64 (aka x86_64-pc-windows-gnu)
136- ( "x86_64" , "gnu" ) => DLLTOOL_GNU ,
139+ ( "x86_64" , "gnu" ) => ( Command :: new ( DLLTOOL_GNU ) , DLLTOOL_GNU ) ,
137140 // 32-bit MinGW-w64 (aka i686-pc-windows-gnu)
138- ( "x86" , "gnu" ) => DLLTOOL_GNU_32 ,
141+ ( "x86" , "gnu" ) => ( Command :: new ( DLLTOOL_GNU_32 ) , DLLTOOL_GNU_32 ) ,
139142 // MSVC ABI (multiarch)
140- ( _, "msvc" ) => DLLTOOL_MSVC ,
143+ ( _, "msvc" ) => {
144+ if let Some ( command) = find_lib_exe ( arch) {
145+ ( command, LIB_MSVC )
146+ } else {
147+ ( Command :: new ( DLLTOOL_MSVC ) , DLLTOOL_MSVC )
148+ }
149+ }
141150 _ => {
142151 let msg = format ! ( "Unsupported target arch '{arch}' or env ABI '{env}'" ) ;
143152 return Err ( Error :: new ( ErrorKind :: Other , msg) ) ;
144153 }
145154 } ;
146155
147156 // Run the selected `dlltool` executable to generate the import library.
148- let status = build_dlltool_command ( dlltool, arch, & defpath, out_dir) . status ( ) ?;
157+ let status = build_dlltool_command ( command , dlltool, arch, & defpath, out_dir) . status ( ) ?;
149158
150159 if status. success ( ) {
151160 Ok ( ( ) )
@@ -155,12 +164,34 @@ pub fn generate_implib_for_target(out_dir: &Path, arch: &str, env: &str) -> Resu
155164 }
156165}
157166
167+ /// Find Visual Studio lib.exe on Windows
168+ #[ cfg( windows) ]
169+ fn find_lib_exe ( arch : & str ) -> Option < Command > {
170+ let target = match arch {
171+ "x86_64" => "x86_64-pc-windows-msvc" ,
172+ "x86" => "i686-pc-windows-msvc" ,
173+ "aarch64" => "aarch64-pc-windows-msvc" ,
174+ _ => return None ,
175+ } ;
176+ cc:: windows_registry:: find ( target, LIB_MSVC )
177+ }
178+
179+ #[ cfg( not( windows) ) ]
180+ fn find_lib_exe ( _arch : & str ) -> Option < Command > {
181+ None
182+ }
183+
158184/// Generates the complete `dlltool` executable invocation command.
159185///
160- /// Supports both LLVM and MinGW `dlltool` flavors.
161- fn build_dlltool_command ( dlltool : & str , arch : & str , defpath : & Path , out_dir : & Path ) -> Command {
186+ /// Supports Visual Studio `lib.exe`, LLVM and MinGW `dlltool` flavors.
187+ fn build_dlltool_command (
188+ mut command : Command ,
189+ dlltool : & str ,
190+ arch : & str ,
191+ defpath : & Path ,
192+ out_dir : & Path ,
193+ ) -> Command {
162194 let mut libpath = out_dir. to_owned ( ) ;
163- let mut command = Command :: new ( dlltool) ;
164195
165196 // Check whether we are using LLVM `dlltool` or MinGW `dlltool`.
166197 if dlltool == DLLTOOL_MSVC {
@@ -181,6 +212,20 @@ fn build_dlltool_command(dlltool: &str, arch: &str, defpath: &Path, out_dir: &Pa
181212 . arg ( defpath)
182213 . arg ( "-l" )
183214 . arg ( libpath) ;
215+ } else if dlltool == LIB_MSVC {
216+ libpath. push ( IMPLIB_FILE_MSVC ) ;
217+
218+ // lib.exe use their own target architecure names...
219+ let machine = match arch {
220+ "x86_64" => "X64" ,
221+ "x86" => "X86" ,
222+ "aarch64" => "ARM64" ,
223+ _ => arch,
224+ } ;
225+ command
226+ . arg ( format ! ( "/MACHINE:{}" , machine) )
227+ . arg ( format ! ( "/DEF:{}" , defpath. display( ) ) )
228+ . arg ( format ! ( "/OUT:{}" , libpath. display( ) ) ) ;
184229 } else {
185230 libpath. push ( IMPLIB_FILE_GNU ) ;
186231
@@ -254,6 +299,7 @@ mod tests {
254299
255300 use super :: * ;
256301
302+ #[ cfg( unix) ]
257303 #[ test]
258304 fn generate ( ) {
259305 // FIXME: Use "target/<arch>" dirs for temporary files.
@@ -265,6 +311,7 @@ mod tests {
265311 generate_implib_for_target ( & dir, "x86_64" , "gnu" ) . unwrap ( ) ;
266312 }
267313
314+ #[ cfg( unix) ]
268315 #[ test]
269316 fn generate_gnu32 ( ) {
270317 let mut dir = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) ;
0 commit comments