Skip to content

Commit f839c7f

Browse files
committed
feat: Add support for the LLVM dlltool flavor
Try to support `*-windows-msvc` rustc compile targets as well as the default `*-windows-gnu` targets. MinGW `ld` import libraries are known to be ABI-incompatible with MSVC `link.exe` and LLVM `lld` import libraries. Call LLVM `dlltool` to generate MSVC-compatible DLL import libraries which should work for both `lld` and `link.exe`. TODO: `*-windows-msvc` unit tests and documentation.
1 parent c1f64c0 commit f839c7f

File tree

1 file changed

+58
-18
lines changed

1 file changed

+58
-18
lines changed

src/lib.rs

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,29 @@
5959
use std::fs::create_dir_all;
6060
use std::fs::File;
6161
use std::io::{BufWriter, Error, ErrorKind, Result, Write};
62-
use std::path::PathBuf;
62+
use std::path::{Path, PathBuf};
6363
use std::process::Command;
6464

6565
/// Stable ABI Python DLL file name
6666
const DLL_FILE: &str = "python3.dll";
6767

68-
/// Canonical `python3.dll` import library file name for MinGW-w64
69-
const IMPLIB_FILE: &str = "python3.dll.a";
70-
7168
/// Module-Definition file name for `python3.dll`
7269
const DEF_FILE: &str = "python3.def";
7370

71+
/// Canonical `python3.dll` import library file name for the GNU environment ABI (MinGW-w64)
72+
const IMPLIB_FILE_GNU: &str = "python3.dll.a";
73+
74+
/// Canonical `python3.dll` import library file name for the MSVC environment ABI
75+
const IMPLIB_FILE_MSVC: &str = "python3.lib";
76+
7477
/// Canonical MinGW-w64 `dlltool` program name
75-
const DLLTOOL: &str = "x86_64-w64-mingw32-dlltool";
78+
const DLLTOOL_GNU: &str = "x86_64-w64-mingw32-dlltool";
7679

7780
/// Canonical MinGW-w64 `dlltool` program name (32-bit version)
78-
const DLLTOOL_32: &str = "i686-w64-mingw32-dlltool";
81+
const DLLTOOL_GNU_32: &str = "i686-w64-mingw32-dlltool";
82+
83+
/// Canonical `dlltool` program name for the MSVC environment ABI (LLVM dlltool)
84+
const DLLTOOL_MSVC: &str = "llvm-dlltool";
7985

8086
/// Python Stable ABI symbol defs from the CPython repository
8187
///
@@ -96,10 +102,7 @@ const STABLE_ABI_DEFS: &str = include_str!("../Misc/stable_abi.txt");
96102
pub fn generate_implib_for_target(out_dir: &str, arch: &str, env: &str) -> Result<()> {
97103
create_dir_all(out_dir)?;
98104

99-
let mut libpath = PathBuf::from(out_dir);
100-
let mut defpath = libpath.clone();
101-
102-
libpath.push(IMPLIB_FILE);
105+
let mut defpath = PathBuf::from(out_dir);
103106
defpath.push(DEF_FILE);
104107

105108
let stable_abi_exports = parse_stable_abi_defs(STABLE_ABI_DEFS);
@@ -111,21 +114,19 @@ pub fn generate_implib_for_target(out_dir: &str, arch: &str, env: &str) -> Resul
111114
// Try to guess the `dlltool` executable name from the target triple.
112115
let dlltool = match (arch, env) {
113116
// 64-bit MinGW-w64 (aka x86_64-pc-windows-gnu)
114-
("x86_64", "gnu") => DLLTOOL,
117+
("x86_64", "gnu") => DLLTOOL_GNU,
115118
// 32-bit MinGW-w64 (aka i686-pc-windows-gnu)
116-
("x86", "gnu") => DLLTOOL_32,
119+
("x86", "gnu") => DLLTOOL_GNU_32,
120+
// MSVC ABI (multiarch)
121+
(_, "msvc") => DLLTOOL_MSVC,
117122
_ => {
118123
let msg = format!("Unsupported target arch '{arch}' or env ABI '{env}'");
119124
return Err(Error::new(ErrorKind::Other, msg));
120125
}
121126
};
122127

123-
let status = Command::new(dlltool)
124-
.arg("--input-def")
125-
.arg(defpath)
126-
.arg("--output-lib")
127-
.arg(libpath)
128-
.status()?;
128+
// Run the selected `dlltool` executable to generate the import library.
129+
let status = build_dlltool_command(dlltool, arch, &defpath, out_dir).status()?;
129130

130131
if status.success() {
131132
Ok(())
@@ -135,6 +136,45 @@ pub fn generate_implib_for_target(out_dir: &str, arch: &str, env: &str) -> Resul
135136
}
136137
}
137138

139+
/// Generates the complete `dlltool` executable invocation command.
140+
///
141+
/// Supports both LLVM and MinGW `dlltool` flavors.
142+
fn build_dlltool_command(dlltool: &str, arch: &str, defpath: &Path, out_dir: &str) -> Command {
143+
let mut libpath = PathBuf::from(out_dir);
144+
let mut command = Command::new(dlltool);
145+
146+
// Check whether we are using LLVM `dlltool` or MinGW `dlltool`.
147+
if dlltool == DLLTOOL_MSVC {
148+
libpath.push(IMPLIB_FILE_MSVC);
149+
150+
// LLVM tools use their own target architecture names...
151+
let machine = match arch {
152+
"x86_64" => "i386:x86-64",
153+
"x86" => "i386",
154+
"aarch64" => "arm64",
155+
_ => arch,
156+
};
157+
158+
command
159+
.arg("-m")
160+
.arg(machine)
161+
.arg("-d")
162+
.arg(defpath)
163+
.arg("-l")
164+
.arg(libpath);
165+
} else {
166+
libpath.push(IMPLIB_FILE_GNU);
167+
168+
command
169+
.arg("--input-def")
170+
.arg(defpath)
171+
.arg("--output-lib")
172+
.arg(libpath);
173+
}
174+
175+
command
176+
}
177+
138178
/// Generates `python3.dll` import library directly from the embedded
139179
/// Python Stable ABI definitions data for the default 64-bit MinGW-w64
140180
/// compile target.

0 commit comments

Comments
 (0)