Skip to content

Commit 6e46b00

Browse files
christhomaschyyran
authored andcommitted
feat: support *-pc-windows-gnullvm targets
The winfsp DLL ships only MSVC-format .lib import libraries, but lld-link (used by both *-pc-windows-msvc and *-pc-windows-gnullvm Rust toolchains) consumes those COFF imports natively. The previous build gates on `target_env == "msvc"` and panics on every other env, even though gnullvm is fully capable of linking against the same archives. Changes: * winfsp-sys/build.rs: - Introduce a `LinkEnv { Msvc, GnuLlvm }` enum so the linkage env flows through the build script as a typed value rather than stringly-typed flags. Architecture/clang-target selection and delay-load syntax both branch on the enum, which flattens the nested arch/env match logic and makes each case explicit. - Detect gnullvm via target_env=="gnu" + target_abi=="llvm" and use mingw clang target strings (`aarch64-w64-mingw32` etc.) for bindgen so it picks up llvm-mingw's bundled `windows.h` instead of the absent MSVC SDK headers; struct layout stays MS-ABI. - Force C11 + auto-include `<assert.h>` for gnullvm so the `static_assert(...)` in winfsp/fsctl.h parses (mingw clang defaults to C99). - Skip `delayimp` on gnullvm (rustc has no search path for it; the lld-link `--delayload` lowering provides the helper itself). - Emit `-Wl,--delayload=foo.dll` for gnullvm vs `/DELAYLOAD:foo.dll` for msvc — ld.lld in mingw mode rejects the MSVC slash form. - `system()` now tries `SOFTWARE\\WinFsp` as a fallback after `SOFTWARE\\WOW6432Node\\WinFsp`, so ARM64 native installs (which register outside WOW6432Node) still resolve headers and libs. * winfsp/src/init.rs: - Mirror the typed `LinkEnv` in winfsp_link_delayload, with explicit Msvc / GnuLlvm cases — no fallthrough to GNU linkage; anything that isn't one of the two recognised envs panics with `unsupported triple`. - Read CARGO_CFG_TARGET_* instead of cfg!() so cross-built build scripts get the target's view rather than the host's. - get_system_winfsp() falls back to `SOFTWARE\\WinFsp` after the WOW6432Node lookup, mirroring the build-time change.
1 parent b266475 commit 6e46b00

2 files changed

Lines changed: 142 additions & 35 deletions

File tree

winfsp-sys/build.rs

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,17 @@ fn system() -> String {
2929
panic!("'system' feature not supported for cross-platform compilation.");
3030
}
3131

32-
let directory = LOCAL_MACHINE
33-
.open("SOFTWARE\\WOW6432Node\\WinFsp")
34-
.ok()
35-
.and_then(|u| u.get_string("InstallDir").ok())
32+
// 32-bit installers land in WOW6432Node; ARM64 native installers
33+
// write directly under SOFTWARE. Try both so that an ARM64 host
34+
// with a native WinFsp install still resolves headers + libs.
35+
let directory = ["SOFTWARE\\WOW6432Node\\WinFsp", "SOFTWARE\\WinFsp"]
36+
.iter()
37+
.find_map(|p| {
38+
LOCAL_MACHINE
39+
.open(p)
40+
.ok()
41+
.and_then(|u| u.get_string("InstallDir").ok())
42+
})
3643
.expect("WinFsp installation directory not found.");
3744

3845
println!("cargo:rustc-link-search={}/lib", directory);
@@ -81,6 +88,41 @@ fn copy_winfsp_dll(winfsp_lib: &str) {
8188
}
8289
}
8390

91+
/// Linkage environment for the active target.
92+
///
93+
/// WinFSP ships MSVC-format `.lib` import libraries; both classic MSVC
94+
/// and `*-pc-windows-gnullvm` consume them through `lld-link`, so the
95+
/// link decisions are nearly identical between the two — but the clang
96+
/// `--target`, the delay-load syntax, and whether we ask rustc for
97+
/// `delayimp` all diverge, so we keep them as distinct cases.
98+
#[derive(Copy, Clone, PartialEq, Eq)]
99+
enum LinkEnv {
100+
Msvc,
101+
GnuLlvm,
102+
}
103+
104+
impl LinkEnv {
105+
fn detect(target_env: &str, target_abi: &str) -> Option<Self> {
106+
match (target_env, target_abi) {
107+
("msvc", _) => Some(Self::Msvc),
108+
("gnu", "llvm") => Some(Self::GnuLlvm),
109+
_ => None,
110+
}
111+
}
112+
113+
fn clang_target(self, target_arch: &str) -> Option<&'static str> {
114+
Some(match (self, target_arch) {
115+
(Self::Msvc, "x86_64") => "x86_64-pc-windows-msvc",
116+
(Self::Msvc, "x86") => "x86-pc-windows-msvc",
117+
(Self::Msvc, "aarch64") => "aarch64-pc-windows-msvc",
118+
(Self::GnuLlvm, "x86_64") => "x86_64-w64-mingw32",
119+
(Self::GnuLlvm, "x86") => "i686-w64-mingw32",
120+
(Self::GnuLlvm, "aarch64") => "aarch64-w64-mingw32",
121+
_ => return None,
122+
})
123+
}
124+
}
125+
84126
fn main() {
85127
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
86128

@@ -95,28 +137,45 @@ fn main() {
95137
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "unknown".to_string());
96138
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "unknown".to_string());
97139
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_else(|_| "unknown".to_string());
140+
let target_abi = env::var("CARGO_CFG_TARGET_ABI").unwrap_or_default();
98141

99142
if target_os != "windows" {
100143
panic!("WinFSP is only supported on Windows.");
101144
}
102145

146+
let link_env = LinkEnv::detect(&target_env, &target_abi)
147+
.unwrap_or_else(|| panic!("unsupported triple {}", env::var("TARGET").unwrap()));
148+
103149
#[cfg(feature = "system")]
104150
let link_include = system();
105151
#[cfg(not(feature = "system"))]
106152
let link_include = local();
107153

108-
println!("cargo:rustc-link-lib=dylib=delayimp");
109-
110-
// Architecture-specific configuration
111-
let (winfsp_lib, clang_target) = match (target_arch.as_str(), target_env.as_str()) {
112-
("x86_64", "msvc") => ("winfsp-x64", "x86_64-pc-windows-msvc"),
113-
("x86", "msvc") => ("winfsp-x86", "x86-pc-windows-msvc"),
114-
("aarch64", "msvc") => ("winfsp-a64", "aarch64-pc-windows-msvc"),
154+
let winfsp_lib = match target_arch.as_str() {
155+
"x86_64" => "winfsp-x64",
156+
"x86" => "winfsp-x86",
157+
"aarch64" => "winfsp-a64",
115158
_ => panic!("unsupported triple {}", env::var("TARGET").unwrap()),
116159
};
160+
let clang_target = link_env
161+
.clang_target(&target_arch)
162+
.unwrap_or_else(|| panic!("unsupported triple {}", env::var("TARGET").unwrap()));
117163

118164
println!("cargo:rustc-link-lib=dylib={}", winfsp_lib);
119-
println!("cargo:rustc-link-arg=/DELAYLOAD:{}.dll", winfsp_lib);
165+
match link_env {
166+
LinkEnv::Msvc => {
167+
// delayimp.lib provides __delayLoadHelper2 under MSVC.
168+
println!("cargo:rustc-link-lib=dylib=delayimp");
169+
println!("cargo:rustc-link-arg=/DELAYLOAD:{}.dll", winfsp_lib);
170+
}
171+
LinkEnv::GnuLlvm => {
172+
// LLVM-MinGW's libdelayimp.a isn't on rustc's link search
173+
// path by default; lld-link's --delayload lowering provides
174+
// the helper itself, and ld.lld in mingw mode requires the
175+
// GNU-style flag rather than MSVC's `/DELAYLOAD:`.
176+
println!("cargo:rustc-link-arg=-Wl,--delayload={}.dll", winfsp_lib);
177+
}
178+
}
120179

121180
let bindings_path_str = out_dir.join("bindings.rs");
122181

@@ -142,6 +201,18 @@ fn main() {
142201

143202
let bindings = bindings.clang_arg(&format!("--target={}", clang_target));
144203

204+
// Under mingw/llvm-mingw clang defaults to C99, which rejects the
205+
// `static_assert(e,m)` MSVC C extension used in winfsp/fsctl.h.
206+
// C11 has it via <assert.h> macro mapping; ensure that mode + the
207+
// standard headers are pulled in.
208+
let bindings = match link_env {
209+
LinkEnv::GnuLlvm => bindings
210+
.clang_arg("-std=c11")
211+
.clang_arg("-include")
212+
.clang_arg("assert.h"),
213+
LinkEnv::Msvc => bindings,
214+
};
215+
145216
let bindings = bindings
146217
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
147218
.generate()

winfsp/src/init.rs

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::Result;
1717
pub struct FspInit;
1818

1919
#[cfg(feature = "system")]
20-
fn get_system_winfsp() -> Option<windows::core::HSTRING> {
20+
fn read_install_dir(subkey: PCWSTR) -> Option<std::ffi::OsString> {
2121
use crate::constants::MAX_PATH;
2222
use windows::Win32::System::Registry::{HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ, RegGetValueW};
2323

@@ -26,7 +26,7 @@ fn get_system_winfsp() -> Option<windows::core::HSTRING> {
2626
let status = unsafe {
2727
RegGetValueW(
2828
HKEY_LOCAL_MACHINE,
29-
w!("SOFTWARE\\WOW6432Node\\WinFsp"),
29+
subkey,
3030
w!("InstallDir"),
3131
RRF_RT_REG_SZ,
3232
None,
@@ -36,14 +36,17 @@ fn get_system_winfsp() -> Option<windows::core::HSTRING> {
3636
};
3737
if status.is_err() {
3838
return None;
39-
};
40-
41-
let Ok(path) = U16CStr::from_slice(&path[0..(size as usize) / std::mem::size_of::<u16>()])
42-
else {
43-
return None;
44-
};
39+
}
40+
let path = U16CStr::from_slice(&path[0..(size as usize) / std::mem::size_of::<u16>()]).ok()?;
41+
Some(path.to_os_string())
42+
}
4543

46-
let mut directory = path.to_os_string();
44+
#[cfg(feature = "system")]
45+
fn get_system_winfsp() -> Option<windows::core::HSTRING> {
46+
// 32-bit installers land in WOW6432Node; ARM64 native installers
47+
// write directly under SOFTWARE.
48+
let mut directory = read_install_dir(w!("SOFTWARE\\WOW6432Node\\WinFsp"))
49+
.or_else(|| read_install_dir(w!("SOFTWARE\\WinFsp")))?;
4750
directory.push("\\bin\\");
4851

4952
if cfg!(target_arch = "x86_64") {
@@ -113,24 +116,57 @@ pub fn winfsp_init_or_die() -> FspInit {
113116
FspInit
114117
}
115118

119+
/// Linkage environment for the active build target.
120+
#[derive(Copy, Clone, PartialEq, Eq)]
121+
enum LinkEnv {
122+
Msvc,
123+
GnuLlvm,
124+
}
125+
126+
impl LinkEnv {
127+
fn detect(target_env: &str, target_abi: &str) -> Option<Self> {
128+
match (target_env, target_abi) {
129+
("msvc", _) => Some(Self::Msvc),
130+
("gnu", "llvm") => Some(Self::GnuLlvm),
131+
_ => None,
132+
}
133+
}
134+
}
135+
116136
/// Build-time helper to enable `DELAYLOAD` linking to the system WinFSP.
117137
///
118-
/// This function should be called from `build.rs`.
138+
/// This function should be called from `build.rs`. Reads target config
139+
/// from cargo env vars so cross-compiled build scripts work correctly.
119140
pub fn winfsp_link_delayload() {
120-
if cfg!(all(target_os = "windows", target_env = "msvc")) {
121-
if cfg!(target_arch = "x86_64") {
122-
println!("cargo:rustc-link-lib=dylib=delayimp");
123-
println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x64.dll");
124-
} else if cfg!(target_arch = "x86") {
125-
println!("cargo:rustc-link-lib=dylib=delayimp");
126-
println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x86.dll");
127-
} else if cfg!(target_arch = "aarch64") {
141+
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
142+
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
143+
let target_abi = std::env::var("CARGO_CFG_TARGET_ABI").unwrap_or_default();
144+
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
145+
146+
if target_os != "windows" {
147+
panic!("unsupported triple");
148+
}
149+
let link_env = LinkEnv::detect(&target_env, &target_abi)
150+
.unwrap_or_else(|| panic!("unsupported triple"));
151+
152+
let dll = match target_arch.as_str() {
153+
"x86_64" => "winfsp-x64.dll",
154+
"x86" => "winfsp-x86.dll",
155+
"aarch64" => "winfsp-a64.dll",
156+
_ => panic!("unsupported architecture"),
157+
};
158+
159+
match link_env {
160+
LinkEnv::Msvc => {
128161
println!("cargo:rustc-link-lib=dylib=delayimp");
129-
println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-a64.dll");
130-
} else {
131-
panic!("unsupported architecture")
162+
println!("cargo:rustc-link-arg=/DELAYLOAD:{dll}");
163+
}
164+
LinkEnv::GnuLlvm => {
165+
// LLVM-MinGW's libdelayimp.a isn't on rustc's link search
166+
// path by default; lld-link's --delayload lowering provides
167+
// the helper itself, and ld.lld in mingw mode requires the
168+
// GNU-style flag rather than MSVC's `/DELAYLOAD:`.
169+
println!("cargo:rustc-link-arg=-Wl,--delayload={dll}");
132170
}
133-
} else {
134-
panic!("unsupported triple")
135171
}
136172
}

0 commit comments

Comments
 (0)