Skip to content

Commit 6e2da70

Browse files
authored
Merge pull request #2 from messense/lib.exe
Add support for `lib.exe`
2 parents f240631 + 22b6226 commit 6e2da70

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

.github/workflows/rust.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ env:
1212
jobs:
1313
build:
1414
name: Build and test
15-
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
os:
18+
- ubuntu-latest
19+
- windows-latest
20+
runs-on: ${{ matrix.os }}
1621
steps:
1722
- uses: actions/checkout@v3
1823
- name: Install the MinGW and LLVM toolchains
24+
if: matrix.os == 'ubuntu-latest'
1925
run: sudo apt-get install -y mingw-w64 llvm
2026
- name: Build
2127
run: cargo build --verbose

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ license = "MIT AND PSF-2.0"
99
keywords = ["build-dependencies", "python", "windows", "mingw"]
1010
categories = ["development-tools::build-utils"]
1111
readme = "README.md"
12+
13+
[target.'cfg(windows)'.dependencies]
14+
cc = "1.0.73"

src/lib.rs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
103103
const 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

Comments
 (0)