Skip to content

Commit bb4c9a7

Browse files
committed
refactor: Add DllToolCommand builder
Convert `build_dlltool_command()` megamethod into a new builder type using two-stage construction. Implement preliminary support for `zig dlltool` using the new `DllToolCommand` capabilities.
1 parent 160085d commit bb4c9a7

File tree

1 file changed

+127
-78
lines changed

1 file changed

+127
-78
lines changed

src/lib.rs

Lines changed: 127 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
#![deny(missing_docs)]
7777
#![allow(clippy::needless_doctest_main)]
7878

79-
use std::ffi::OsStr;
8079
use std::fs::{create_dir_all, write};
8180
use std::io::{Error, ErrorKind, Result};
8281
use std::path::{Path, PathBuf};
@@ -98,6 +97,7 @@ const DLLTOOL_GNU_32: &str = "i686-w64-mingw32-dlltool";
9897
const DLLTOOL_MSVC: &str = "llvm-dlltool";
9998

10099
/// Canonical `lib` program name for the MSVC environment ABI (MSVC lib.exe)
100+
#[cfg(windows)]
101101
const LIB_MSVC: &str = "lib.exe";
102102

103103
/// Windows import library generator for Python
@@ -150,27 +150,10 @@ impl ImportLibraryGenerator {
150150
let implib_file = self.implib_file_path(out_dir);
151151

152152
// Try to guess the `dlltool` executable name from the target triple.
153-
let mut command = match (self.arch.as_str(), self.env.as_str()) {
154-
// 64-bit MinGW-w64 (aka x86_64-pc-windows-gnu)
155-
("x86_64", "gnu") => self.build_dlltool_command(DLLTOOL_GNU, &implib_file, &defpath),
156-
// 32-bit MinGW-w64 (aka i686-pc-windows-gnu)
157-
("x86", "gnu") => self.build_dlltool_command(DLLTOOL_GNU_32, &implib_file, &defpath),
158-
// MSVC ABI (multiarch)
159-
(_, "msvc") => {
160-
if let Some(command) = find_lib_exe(&self.arch) {
161-
self.build_dlltool_command(command.get_program(), &implib_file, &defpath)
162-
} else {
163-
self.build_dlltool_command(DLLTOOL_MSVC, &implib_file, &defpath)
164-
}
165-
}
166-
_ => {
167-
let msg = format!(
168-
"Unsupported target arch '{}' or env ABI '{}'",
169-
self.arch, self.env
170-
);
171-
return Err(Error::new(ErrorKind::Other, msg));
172-
}
173-
};
153+
let dlltool_command = DllToolCommand::find_for_target(&self.arch, &self.env)?;
154+
155+
// Build the complete `dlltool` command with all required arguments.
156+
let mut command = dlltool_command.build(&defpath, &implib_file);
174157

175158
// Run the selected `dlltool` executable to generate the import library.
176159
let status = command.status().map_err(|e| {
@@ -230,62 +213,6 @@ impl ImportLibraryGenerator {
230213

231214
libpath
232215
}
233-
234-
/// Generates the complete `dlltool` executable invocation command.
235-
///
236-
/// Supports Visual Studio `lib.exe`, LLVM and MinGW `dlltool` flavors.
237-
fn build_dlltool_command(
238-
&self,
239-
dlltool: impl AsRef<OsStr>,
240-
libpath: &Path,
241-
defpath: &Path,
242-
) -> Command {
243-
let dlltool = dlltool.as_ref();
244-
let mut command = if self.env == "msvc" {
245-
find_lib_exe(&self.arch).unwrap_or_else(|| Command::new(dlltool))
246-
} else {
247-
Command::new(dlltool)
248-
};
249-
250-
// Check whether we are using LLVM `dlltool` or MinGW `dlltool`.
251-
if dlltool == DLLTOOL_MSVC {
252-
// LLVM tools use their own target architecture names...
253-
let machine = match self.arch.as_str() {
254-
"x86_64" => "i386:x86-64",
255-
"x86" => "i386",
256-
"aarch64" => "arm64",
257-
arch => arch,
258-
};
259-
260-
command
261-
.arg("-m")
262-
.arg(machine)
263-
.arg("-d")
264-
.arg(defpath)
265-
.arg("-l")
266-
.arg(libpath);
267-
} else if Path::new(dlltool).file_name() == Some(LIB_MSVC.as_ref()) {
268-
// lib.exe use their own target architecure names...
269-
let machine = match self.arch.as_str() {
270-
"x86_64" => "X64",
271-
"x86" => "X86",
272-
"aarch64" => "ARM64",
273-
arch => arch,
274-
};
275-
command
276-
.arg(format!("/MACHINE:{}", machine))
277-
.arg(format!("/DEF:{}", defpath.display()))
278-
.arg(format!("/OUT:{}", libpath.display()));
279-
} else {
280-
command
281-
.arg("--input-def")
282-
.arg(defpath)
283-
.arg("--output-lib")
284-
.arg(libpath);
285-
}
286-
287-
command
288-
}
289216
}
290217

291218
/// Generates `python3.dll` import library directly from the embedded
@@ -303,6 +230,128 @@ pub fn generate_implib_for_target(out_dir: &Path, arch: &str, env: &str) -> Resu
303230
ImportLibraryGenerator::new(arch, env).generate(out_dir)
304231
}
305232

233+
/// `dlltool` utility command builder
234+
///
235+
/// Supports Visual Studio `lib.exe`, MinGW, LLVM and Zig `dlltool` flavors.
236+
#[derive(Debug)]
237+
enum DllToolCommand {
238+
/// MinGW `dlltool` program (with prefix)
239+
Mingw { command: Command },
240+
/// LLVM `llvm-dlltool` program (no prefix)
241+
Llvm { command: Command, machine: String },
242+
/// MSVC `lib.exe` program (no prefix)
243+
LibExe { command: Command, machine: String },
244+
/// `zig dlltool` wrapper (no prefix)
245+
#[allow(dead_code)]
246+
Zig { command: Command, machine: String },
247+
}
248+
249+
impl DllToolCommand {
250+
/// Attempts to find the best matching `dlltool` flavor for the target.
251+
fn find_for_target(arch: &str, env: &str) -> Result<DllToolCommand> {
252+
match (arch, env) {
253+
// 64-bit MinGW-w64 (aka `x86_64-pc-windows-gnu`)
254+
("x86_64", "gnu") => Ok(DllToolCommand::Mingw {
255+
command: Command::new(DLLTOOL_GNU),
256+
}),
257+
258+
// 32-bit MinGW-w64 (aka `i686-pc-windows-gnu`)
259+
("x86", "gnu") => Ok(DllToolCommand::Mingw {
260+
command: Command::new(DLLTOOL_GNU_32),
261+
}),
262+
263+
// MSVC ABI (multiarch)
264+
(_, "msvc") => {
265+
if let Some(command) = find_lib_exe(arch) {
266+
// MSVC tools use their own target architecture names...
267+
let machine = match arch {
268+
"x86_64" => "X64",
269+
"x86" => "X86",
270+
"aarch64" => "ARM64",
271+
arch => arch,
272+
}
273+
.to_owned();
274+
275+
Ok(DllToolCommand::LibExe { command, machine })
276+
} else {
277+
// LLVM tools use their own target architecture names...
278+
let machine = match arch {
279+
"x86_64" => "i386:x86-64",
280+
"x86" => "i386",
281+
"aarch64" => "arm64",
282+
arch => arch,
283+
}
284+
.to_owned();
285+
286+
let command = Command::new(DLLTOOL_MSVC);
287+
288+
Ok(DllToolCommand::Llvm { command, machine })
289+
}
290+
}
291+
_ => {
292+
let msg = format!("Unsupported target arch '{}' or env ABI '{}'", arch, env);
293+
Err(Error::new(ErrorKind::Other, msg))
294+
}
295+
}
296+
}
297+
298+
/// Generates the complete `dlltool` executable invocation command.
299+
fn build(self, defpath: &Path, libpath: &Path) -> Command {
300+
match self {
301+
Self::Mingw { mut command } => {
302+
command
303+
.arg("--input-def")
304+
.arg(defpath)
305+
.arg("--output-lib")
306+
.arg(libpath);
307+
308+
command
309+
}
310+
Self::Llvm {
311+
mut command,
312+
machine,
313+
} => {
314+
command
315+
.arg("-m")
316+
.arg(machine)
317+
.arg("-d")
318+
.arg(defpath)
319+
.arg("-l")
320+
.arg(libpath);
321+
322+
command
323+
}
324+
Self::LibExe {
325+
mut command,
326+
machine,
327+
} => {
328+
command
329+
.arg(format!("/MACHINE:{}", machine))
330+
.arg(format!("/DEF:{}", defpath.display()))
331+
.arg(format!("/OUT:{}", libpath.display()));
332+
333+
command
334+
}
335+
Self::Zig {
336+
mut command,
337+
machine,
338+
} => {
339+
// Same as `llvm-dlltool`, but invoked as `zig dlltool`.
340+
command
341+
.arg("dlltool")
342+
.arg("-m")
343+
.arg(machine)
344+
.arg("-d")
345+
.arg(defpath)
346+
.arg("-l")
347+
.arg(libpath);
348+
349+
command
350+
}
351+
}
352+
}
353+
}
354+
306355
/// Finds Visual Studio `lib.exe` when running on Windows.
307356
#[cfg(windows)]
308357
fn find_lib_exe(arch: &str) -> Option<Command> {

0 commit comments

Comments
 (0)